diff --git a/code/elpa/archives/melpa/archive-contents b/code/elpa/archives/melpa/archive-contents index 1c28d88..40c2a33 100644 --- a/code/elpa/archives/melpa/archive-contents +++ b/code/elpa/archives/melpa/archive-contents @@ -42,14 +42,14 @@ (ac-php . [(20200916 751) ((ac-php-core (2 0)) (auto-complete (1 4 0)) (yasnippet (0 8 0))) "Auto Completion source for PHP." single ((:commit . "dc563f4b1efeac8ae75f217532f4c99b4ba417de") (:authors ("jim" . "xcwenn@qq.com")) (:maintainer "jim") (:keywords "completion" "convenience" "intellisense") (:url . "https://github.com/xcwen/ac-php"))]) (ac-php-core . [(20220701 253) ((emacs (24 4)) (dash (1)) (php-mode (1)) (s (1)) (f (0 17 0)) (popup (0 5 0)) (xcscope (1 0))) "The core library of the ac-php" tar ((:commit . "dc563f4b1efeac8ae75f217532f4c99b4ba417de") (:authors ("jim" . "xcwenn@qq.com") ("Serghei Iakovlev" . "sadhooklay@gmail.com")) (:maintainer "jim") (:keywords "completion" "convenience" "intellisense") (:url . "https://github.com/xcwen/ac-php"))]) (ac-racer . [(20170114 809) ((emacs (24 3)) (auto-complete (1 5 0)) (racer (0 0 2))) "auto-complete source of racer" single ((:commit . "4408c2d652dec0432e20c05e001db8222d778c6b") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-ac-racer"))]) - (ac-rtags . [(20191222 920) ((auto-complete (1 4 0)) (rtags (2 10))) "auto-complete back-end for RTags" single ((:commit . "c628efc9b485470a48aec2692d79f7c140bc5b92") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) + (ac-rtags . [(20191222 920) ((auto-complete (1 4 0)) (rtags (2 10))) "auto-complete back-end for RTags" single ((:commit . "b9c680e7ca003c103687e790f740d86daa6b4b17") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) (ac-skk . [(20141230 119) ((auto-complete (1 3 1)) (ddskk (16 0 50)) (tinysegmenter (0)) (cl-lib (0 5))) "auto-complete-mode source for DDSKK a.k.a Japanese input method" single ((:commit . "d25a265930430d080329789fb253d786c01dfa24") (:authors ("lugecy ")) (:maintainer "myuhe") (:keywords "convenience" "auto-complete") (:url . "https://github.com/myuhe/ac-skk.el"))]) (ac-slime . [(20171027 2100) ((auto-complete (1 4)) (slime (2 9)) (cl-lib (0 5))) "An auto-complete source using slime completions" single ((:commit . "6c80cb602ddad46486288f94ad7546396c6e4b1a") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/ac-slime"))]) (ac-sly . [(20170728 1027) ((sly (1 0 0 -3)) (auto-complete (1 4)) (cl-lib (0 5))) "An auto-complete source using sly completions" single ((:commit . "bf69c687c4ecf1994349d20c182e9b567399912e") (:authors ("Damian T. Dobroczy\\'nski" . "qoocku@gmail.com")) (:maintainer "Damian T. Dobroczy\\'nski" . "qoocku@gmail.com") (:url . "https://github.com/qoocku/ac-sly"))]) (academic-phrases . [(20180723 1021) ((dash (2 12 0)) (s (1 12 0)) (ht (2 0)) (emacs (24))) "Bypass that mental block when writing your papers." single ((:commit . "25d9cf67feac6359cb213f061735e2679c84187f") (:authors ("Nasser Alshammari" . "designernasser@gmail.com")) (:maintainer "Nasser Alshammari" . "designernasser@gmail.com") (:keywords "academic" "convenience" "papers" "writing" "wp") (:url . "https://github.com/nashamri/academic-phrases"))]) (accent . [(20220202 1312) ((emacs (24 3)) (popup (0 5 8))) "Popup for accented characters (diacritics)" single ((:commit . "23af25e3f1629755b1b245364fbb93bf4ba933b9") (:authors ("Elia Scotto" . "eliascotto94@gmail.com")) (:maintainer "Elia Scotto" . "eliascotto94@gmail.com") (:keywords "i18n") (:url . "https://github.com/elias94/accent"))]) (ace-flyspell . [(20170309 509) ((avy (0 4 0))) "Jump to and correct spelling errors using `ace-jump-mode' and flyspell" single ((:commit . "538d4f8508d305262ba0228dfe7c819fb65b53c9") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions") (:url . "https://github.com/cute-jumper/ace-flyspell"))]) - (ace-isearch . [(20210830 746) ((emacs (24))) "A seamless bridge between isearch, ace-jump-mode, avy, helm-swoop and swiper" single ((:commit . "8439136206a42e41ef95af923e0dc3bbd4fa306c") (:authors ("Akira Tamamori")) (:maintainer "Akira Tamamori") (:url . "https://github.com/tam17aki/ace-isearch"))]) + (ace-isearch . [(20220809 1748) ((emacs (24))) "A seamless bridge between isearch, ace-jump-mode, avy, helm-swoop and swiper" single ((:commit . "a24bfc626100f183dbad016bd7723eb12e238534") (:authors ("Akira Tamamori")) (:maintainer "Akira Tamamori") (:url . "https://github.com/tam17aki/ace-isearch"))]) (ace-jump-buffer . [(20171031 1550) ((avy (0 4 0)) (dash (2 4 0))) "fast buffer switching extension to `avy'" single ((:commit . "0d335064230caf3efdd5a732e8fbd67e3948ed6a") (:authors ("Justin Talbott" . "justin@waymondo.com")) (:maintainer "Justin Talbott" . "justin@waymondo.com") (:url . "https://github.com/waymondo/ace-jump-buffer"))]) (ace-jump-helm-line . [(20160918 1836) ((avy (0 4 0)) (helm (1 6 3))) "Ace-jump to a candidate in helm window" single ((:commit . "1483055255df3f8ae349f7520f05b1e43ea3ed37") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions") (:url . "https://github.com/cute-jumper/ace-jump-helm-line"))]) (ace-jump-mode . [(20140616 815) nil "a quick cursor location minor mode for emacs" single ((:commit . "8351e2df4fbbeb2a4003f2fb39f46d33803f3dac") (:authors ("winterTTr" . "winterTTr@gmail.com")) (:maintainer "winterTTr" . "winterTTr@gmail.com") (:keywords "motion" "location" "cursor") (:url . "https://github.com/winterTTr/ace-jump-mode/"))]) @@ -69,43 +69,43 @@ (add-node-modules-path . [(20220315 340) ((s (1 12 0))) "Add node_modules to your exec-path" single ((:commit . "63f047fd84b825876152743f66de7ee6f9ed203b") (:authors ("Neri Marschik" . "marschik_neri@cyberagent.co.jp")) (:maintainer "Neri Marschik" . "marschik_neri@cyberagent.co.jp") (:keywords "javascript" "node" "node_modules" "eslint") (:url . "https://github.com/codesuki/add-node-modules-path"))]) (addressbook-bookmark . [(20190612 1638) ((emacs (24))) "An address book based on Standard Emacs bookmarks." single ((:commit . "d8e502fc2f3d3ab1508ce9e50ebf8a9addc6e5b3") (:authors ("Thierry Volpiatto" . "thierry.volpiatto@gmail.com")) (:maintainer "Thierry Volpiatto" . "thierry.volpiatto@gmail.com") (:url . "https://github.com/thierryvolpiatto/addressbook-bookmark"))]) (ado-mode . [(20220415 1647) ((emacs (25 1))) "Major mode for editing Stata-related files" tar ((:commit . "695ea71cf4d6ae5f0afbc37b6fd08458e5c584c4") (:authors ("Bill Rising" . "brising@alum.mit.edu")) (:maintainer "Bill Rising" . "brising@alum.mit.edu") (:keywords "tools" "languages" "files" "convenience" "stata" "mata" "ado") (:url . "https://github.com/louabill/ado-mode"))]) - (adoc-mode . [(20220720 622) ((emacs (26)) (markup-faces (1 0 0))) "a major-mode for editing AsciiDoc files in Emacs" single ((:commit . "e0d08ee6d29289a475796e22b2298b028c02b408") (:authors ("Florian Kaufmann" . "sensorflo@gmail.com")) (:maintainer "Florian Kaufmann" . "sensorflo@gmail.com") (:keywords "asciidoc") (:url . "https://github.com/emacsorphanage/adoc-mode"))]) + (adoc-mode . [(20220720 622) ((emacs (26)) (markup-faces (1 0 0))) "a major-mode for editing AsciiDoc files in Emacs" single ((:commit . "00c59c8d952c92c547cb18ba57dff0f673866670") (:authors ("Florian Kaufmann" . "sensorflo@gmail.com")) (:maintainer "Florian Kaufmann" . "sensorflo@gmail.com") (:keywords "asciidoc") (:url . "https://github.com/emacsorphanage/adoc-mode"))]) (aes . [(20211204 2348) ((emacs (26 1))) "Implementation of AES" single ((:commit . "c9cd12d6c1dbc18603eb4703276132cea59d5c78") (:authors ("Markus Sauermann" . "emacs-aes@sauermann-consulting.de")) (:maintainer "Markus Sauermann" . "emacs-aes@sauermann-consulting.de") (:keywords "data" "tools") (:url . "https://github.com/Sauermann/emacs-aes"))]) (affe . [(20220603 548) ((emacs (27 1)) (consult (0 16))) "Asynchronous Fuzzy Finder for Emacs" tar ((:commit . "0162da0aab3b30412eee5ebf68427361fc8fc26d") (:authors ("Daniel Mendler")) (:maintainer "Daniel Mendler") (:url . "https://github.com/minad/affe"))]) (afternoon-theme . [(20140104 1859) ((emacs (24 1))) "Dark color theme with a deep blue background" single ((:commit . "89b1d778a1f8b385775c122f2bd1c62f0fbf931a") (:authors ("Ozan Sener" . "ozan@ozansener.com")) (:maintainer "Ozan Sener" . "ozan@ozansener.com") (:keywords "themes") (:url . "http://github.com/osener/emacs-afternoon-theme"))]) (ag . [(20201031 2202) ((dash (2 8 0)) (s (1 9 0)) (cl-lib (0 5))) "A front-end for ag ('the silver searcher'), the C ack replacement." single ((:commit . "ed7e32064f92f1315cecbfc43f120bbc7508672c") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:url . "https://github.com/Wilfred/ag.el"))]) (agda-editor-tactics . [(20211024 2357) ((s (1 12 0)) (dash (2 16 0)) (emacs (27 1)) (org (9 1))) "An editor tactic to produce Σ-types from Agda records" single ((:commit . "c401c0c1ec0ad38bb5ee1636504e0e531b9e34b9") (:authors ("Musa Al-hassy" . "alhassy@gmail.com")) (:maintainer "Musa Al-hassy" . "alhassy@gmail.com") (:keywords "abbrev" "convenience" "languages" "agda" "tools") (:url . "https://github.com/alhassy/next-700-module-systems"))]) (aggressive-fill-paragraph . [(20180910 816) ((dash (2 10 0))) "A mode to automatically keep paragraphs filled" single ((:commit . "4a620e62b5e645a48b0a818bf4eb19daea4977df") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:keywords "fill-paragraph" "automatic" "comments") (:url . "https://github.com/davidshepherd7/aggressive-fill-paragraph-mode"))]) - (aggressive-indent . [(20210701 2224) ((emacs (24 3))) "Minor mode to aggressively keep your code always indented" single ((:commit . "cb416faf61c46977c06cf9d99525b04dc109a33c") (:authors ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "emacs@endlessparentheses.com") (:keywords "indent" "lisp" "maint" "tools") (:url . "https://github.com/Malabarba/aggressive-indent-mode"))]) + (aggressive-indent . [(20220817 9) ((emacs (24 3))) "Minor mode to aggressively keep your code always indented" single ((:commit . "70b3f0add29faff41e480e82930a231d88ee9ca7") (:authors ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "emacs@endlessparentheses.com") (:keywords "indent" "lisp" "maint" "tools") (:url . "https://github.com/Malabarba/aggressive-indent-mode"))]) (agtags . [(20200730 116) ((emacs (25))) "A frontend to GNU Global" tar ((:commit . "d80c6f61dee74040c07b7010d48cab1df13a3abf") (:authors ("Vietor Liu" . "vietor.liu@gmail.com")) (:maintainer "Vietor Liu" . "vietor.liu@gmail.com") (:keywords "tools" "convenience") (:url . "https://github.com/vietor/agtags"))]) (ah . [(20220730 1058) ((emacs (25 1))) "Additional hooks" single ((:commit . "8e12223f0f423e7fa882cc049a25af6db755902d") (:authors ("Takaaki ISHIKAWA ")) (:maintainer "Takaaki ISHIKAWA ") (:keywords "convenience") (:url . "https://github.com/takaxp/ah"))]) - (ahg . [(20220529 1200) nil "Alberto's Emacs interface for Mercurial (Hg)" single ((:commit . "d93cc73f79f6c29d533c468112181cd4c7b11935") (:authors ("Alberto Griggio" . "agriggio@users.sourceforge.net")) (:maintainer "Alberto Griggio" . "agriggio@users.sourceforge.net") (:url . "https://bitbucket.org/agriggio/ahg"))]) + (ahg . [(20220825 1006) nil "Alberto's Emacs interface for Mercurial (Hg)" single ((:commit . "0e1bb428e1501155dba3f1cbcd789267263ba264") (:authors ("Alberto Griggio" . "agriggio@users.sourceforge.net")) (:maintainer "Alberto Griggio" . "agriggio@users.sourceforge.net") (:url . "https://bitbucket.org/agriggio/ahg"))]) (ahk-mode . [(20200412 1832) ((emacs (24 3))) "Major mode for editing AHK (AutoHotkey and AutoHotkey_L)" single ((:commit . "729007b5f22a49f5187ff47fca18c0d674e73047") (:authors ("Rich Alesi")) (:maintainer "Rich Alesi") (:keywords "ahk" "autohotkey" "hotkey" "keyboard shortcut" "automation") (:url . "https://github.com/ralesi/ahk-mode"))]) (ahungry-theme . [(20180131 328) ((emacs (24))) "Ahungry color theme for Emacs. Make sure to (load-theme 'ahungry)." single ((:commit . "7d18c85c014671573628686012f3952fcd72c97b") (:authors ("Matthew Carter" . "m@ahungry.com")) (:maintainer "Matthew Carter" . "m@ahungry.com") (:keywords "ahungry" "palette" "color" "theme" "emacs" "color-theme" "deftheme") (:url . "https://github.com/ahungry/color-theme-ahungry"))]) (aio . [(20200610 1904) ((emacs (26 1))) "async/await for Emacs Lisp" tar ((:commit . "da93523e235529fa97d6f251319d9e1d6fc24a41") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/emacs-aio"))]) (airline-themes . [(20211214 1749) ((powerline (2 3))) "vim-airline themes for emacs powerline" tar ((:commit . "6bd102e49a7d87af1a72eb86e953991ff7bc954e") (:authors ("Anthony DiGirolamo" . "anthony.digirolamo@gmail.com")) (:maintainer "Anthony DiGirolamo" . "anthony.digirolamo@gmail.com") (:keywords "evil" "mode-line" "powerline" "airline" "themes") (:url . "http://github.com/AnthonyDiGirolamo/airline-themes"))]) (airplay . [(20130212 1226) ((request (20130110 2144)) (simple-httpd (1 4 1)) (deferred (0 3 1))) "Airplay bindings to Emacs" tar ((:commit . "bd690aafcae3a887946e1bba8327597932d964ad") (:authors ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainer "Wataru MIYAGUNI" . "gonngo@gmail.com") (:keywords "appletv" "airplay") (:url . "https://github.com/gongo/airplay-el"))]) (alan-mode . [(20220106 727) ((flycheck (32)) (emacs (25 1)) (s (1 12))) "Major mode for editing Alan files" single ((:commit . "e96b06115f53ef81097f585f627855d97b35b89f") (:authors ("Paul van Dam" . "pvandam@kjerner.com")) (:maintainer "Paul van Dam" . "pvandam@kjerner.com") (:keywords "alan" "languages") (:url . "https://github.com/Kjerner/AlanForEmacs"))]) - (alarm-clock . [(20191204 716) ((emacs (24 4)) (f (0 17 0))) "Alarm Clock" tar ((:commit . "644f331071f8b09a898fae490541908b5054d2e6") (:authors ("Steve Lemuel" . "wlemuel@hotmail.com")) (:maintainer "Steve Lemuel" . "wlemuel@hotmail.com") (:keywords "calendar" "tools" "convenience") (:url . "https://github.com/wlemuel/alarm-clock"))]) + (alarm-clock . [(20220819 1538) ((emacs (24 4)) (f (0 17 0))) "Alarm Clock" tar ((:commit . "2ca4e5177d2ca9a21fa52f4b4b75711617d5424c") (:authors ("Steve Lemuel" . "wlemuel@hotmail.com")) (:maintainer "Steve Lemuel" . "wlemuel@hotmail.com") (:keywords "calendar" "tools" "convenience") (:url . "https://github.com/wlemuel/alarm-clock"))]) (alchemist . [(20180312 1304) ((elixir-mode (2 2 5)) (dash (2 11 0)) (emacs (24 4)) (company (0 8 0)) (pkg-info (0 4)) (s (1 11 0))) "Elixir tooling integration into Emacs" tar ((:commit . "6f99367511ae209f8fe2c990779764bbb4ccb6ed") (:authors ("Samuel Tonini" . "tonini.samuel@gmail.com")) (:maintainer "Samuel Tonini" . "tonini.samuel@gmail.com") (:keywords "languages" "elixir" "elixirc" "mix" "hex" "alchemist") (:url . "http://www.github.com/tonini/alchemist.el"))]) (alda-mode . [(20210705 654) ((emacs (24 0))) "An Alda major mode" single ((:commit . "4de011d572e958a377fb16daae05a1b411f0c8ad") (:authors ("Jay Kamat" . "jaygkamat@gmail.com")) (:maintainer "Jay Kamat" . "jaygkamat@gmail.com") (:keywords "alda" "highlight") (:url . "http://gitlab.com/jgkamat/alda-mode"))]) (alect-themes . [(20211022 1651) ((emacs (24 0))) "Configurable light, dark and black themes for Emacs 24 or later" tar ((:commit . "89560047934c236d05ea6b911c0c63702a8e06f3") (:authors ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "color" "theme") (:url . "https://github.com/alezost/alect-themes"))]) (alectryon . [(20211018 321) ((flycheck (31)) (emacs (25 1))) "Toggle between Coq and reStructuredText" tar ((:commit . "739b46da22d272e748f60f3efcd2989d696fba71") (:authors ("Clément Pit-Claudel" . "clement.pitclaudel@live.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/cpitclaudel/alectryon"))]) - (alert . [(20200303 2118) ((gntp (0 1)) (log4e (0 3 0)) (cl-lib (0 5))) "Growl-style notification system for Emacs" single ((:commit . "7046393272686c7a1a9b3e7f7b1d825d2e5250a6") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainer "John Wiegley" . "jwiegley@gmail.com") (:keywords "notification" "emacs" "message") (:url . "https://github.com/jwiegley/alert"))]) + (alert . [(20220818 1606) ((gntp (0 1)) (log4e (0 3 0)) (cl-lib (0 5))) "Growl-style notification system for Emacs" single ((:commit . "c39ce68ed8d80fb996fb78eafc06481a54bf27ce") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainer "John Wiegley" . "jwiegley@gmail.com") (:keywords "notification" "emacs" "message") (:url . "https://github.com/jwiegley/alert"))]) (alert-termux . [(20181119 951) ((emacs (24 4))) "alert.el notifications on Termux" single ((:commit . "47c414285c2f5971f3be52aaf0a4066ea6989238") (:authors ("Gergely Polonkai" . "gergely@polonkai.eu")) (:maintainer "Gergely Polonkai" . "gergely@polonkai.eu") (:keywords "terminals") (:url . "https://github.com/gergelypolonkai/alert-termux"))]) (alert-toast . [(20220312 229) ((emacs (25 1)) (alert (1 2)) (f (0 20 0)) (s (1 12 0))) "Windows 10 toast notifications" single ((:commit . "ba931529a266537783cfec2a28c2b8c058364ff2") (:authors ("Grzegorz Kowzan" . "grzegorz@kowzan.eu")) (:maintainer "Grzegorz Kowzan" . "grzegorz@kowzan.eu") (:url . "https://github.com/gkowzan/alert-toast"))]) (align-cljlet . [(20160112 2101) ((clojure-mode (1 11 5))) "Space align various Clojure forms" single ((:commit . "602d72a7ad52788a0265e3c6da519464a98166b8") (:url . "https://github.com/gstamp/align-cljlet"))]) (all-ext . [(20200315 1443) ((emacs (24 4)) (all (1 0))) "M-x all with helm-swoop/anything/multiple-cursors/line-number" single ((:commit . "c865c62506af2c9edc7705a7c24dc8b70d5d4de2") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "matching" "all" "search" "replace" "anything" "helm" "helm-swoop" "occur") (:url . "https://github.com/rubikitch/all-ext"))]) - (all-the-icons . [(20220801 1541) ((emacs (24 3))) "A library for inserting Developer icons" tar ((:commit . "6f876fa11ef64af20d9b2a44fdabac6446de51ba") (:authors ("Dominic Charlesworth" . "dgc336@gmail.com")) (:maintainer "Dominic Charlesworth" . "dgc336@gmail.com") (:keywords "convenient" "lisp") (:url . "https://github.com/domtronn/all-the-icons.el"))]) + (all-the-icons . [(20220823 1719) ((emacs (24 3))) "A library for inserting Developer icons" tar ((:commit . "4a4d6269b8b85b0b15954f063e6ce378630d80c0") (:authors ("Dominic Charlesworth" . "dgc336@gmail.com")) (:maintainer "Dominic Charlesworth" . "dgc336@gmail.com") (:keywords "convenient" "lisp") (:url . "https://github.com/domtronn/all-the-icons.el"))]) (all-the-icons-completion . [(20220409 1204) ((emacs (26 1)) (all-the-icons (5 0))) "Add icons to completion candidates" single ((:commit . "286e2c064a1298be0d8d4100dc91d7a7a554d04a") (:authors ("Itai Y. Efrat ")) (:maintainer "Itai Y. Efrat" . "itai3397@gmail.com") (:keywords "convenient" "lisp") (:url . "https://github.com/iyefrat/all-the-icons-completion"))]) (all-the-icons-dired . [(20220620 1939) ((emacs (24 4)) (all-the-icons (2 2 0))) "Shows icons for each file in dired mode" single ((:commit . "b5d3af1e47de09e6ac80d4d7fba516e6a3c38e26") (:authors ("jtbm37")) (:maintainer "Jimmy Yuen Ho Wong" . "wyuenho@gmail.com") (:keywords "files" "icons" "dired") (:url . "https://github.com/wyuenho/all-the-icons-dired"))]) (all-the-icons-gnus . [(20180511 654) ((emacs (24 4)) (dash (2 12 0)) (all-the-icons (3 1 0))) "Shows icons for in Gnus" single ((:commit . "27f78996da0725943bcfb2d18038e6f7bddfa9c7") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "mail" "tools"))]) (all-the-icons-ibuffer . [(20220424 1027) ((emacs (24 4)) (all-the-icons (2 2 0))) "Display icons for all buffers in ibuffer" single ((:commit . "370fff61b52b9d918da8af6c72edbc21e766c499") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "convenience" "icons" "ibuffer") (:url . "https://github.com/seagle0128/all-the-icons-ibuffer"))]) (all-the-icons-ivy . [(20190508 1803) ((emacs (24 4)) (all-the-icons (2 4 0)) (ivy (0 8 0))) "Shows icons while using ivy and counsel" single ((:commit . "a70cbfa1effe36efc946a823a580cec686d5e88d") (:authors ("asok")) (:maintainer "asok") (:keywords "faces"))]) - (all-the-icons-ivy-rich . [(20220625 1332) ((emacs (25 1)) (ivy-rich (0 1 0)) (all-the-icons (2 2 0))) "Better experience with icons for ivy" single ((:commit . "616a39f71484b15d21d89ca40cd174e0ca15b8f4") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "convenience" "icons" "ivy") (:url . "https://github.com/seagle0128/all-the-icons-ivy-rich"))]) + (all-the-icons-ivy-rich . [(20220810 2027) ((emacs (25 1)) (ivy-rich (0 1 0)) (all-the-icons (2 2 0))) "Better experience with icons for ivy" single ((:commit . "5b20fd336f248104a9ab1f8cb37c21d636ea2974") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "convenience" "icons" "ivy") (:url . "https://github.com/seagle0128/all-the-icons-ivy-rich"))]) (almost-mono-themes . [(20220422 1714) ((emacs (24))) "Almost monochromatic color themes" tar ((:commit . "0641bf565c113caef8d5c2a93f38cff32ebb62b7") (:authors ("John Olsson" . "john@cryon.se")) (:maintainer "John Olsson" . "john@cryon.se") (:keywords "faces") (:url . "https://github.com/cryon/almost-mono-themes"))]) (alsamixer . [(20191002 1133) nil "Functions to call out to amixer." single ((:commit . "1bdb99e433acd38685f05408562746cfbf2bc820") (:authors ("R.W. van 't Veer")) (:maintainer "R.W. van 't Veer") (:keywords "convenience") (:url . "https://github.com/remvee/alsamixer-el"))]) - (alt-codes . [(20220704 644) ((emacs (26 1))) "Insert alt codes using meta key" single ((:commit . "cd007b1627afc450a30183da82e50d611afe6b8e") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "alt" "codes" "insertion" "meta") (:url . "https://github.com/jcs-elpa/alt-codes"))]) + (alt-codes . [(20220704 644) ((emacs (26 1))) "Insert alt codes using meta key" single ((:commit . "6ba4b3cc6c7891adff408e58e9ed399d9effbea9") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "alt" "codes" "insertion" "meta") (:url . "https://github.com/jcs-elpa/alt-codes"))]) (amd-mode . [(20180111 1402) ((emacs (25)) (projectile (20161008 47)) (s (1 9 0)) (f (0 16 2)) (seq (2 16)) (makey (0 3)) (js2-mode (20140114)) (js2-refactor (0 6 1))) "Minor mode for handling JavaScript AMD module requirements." single ((:commit . "01fd19e0d635ccaf8e812364d8720733f2e84126") (:authors ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainer "Nicolas Petton" . "petton.nicolas@gmail.com") (:keywords "javascript" "amd" "projectile"))]) (ameba . [(20200103 1454) ((emacs (24 4))) "An interface to Crystal Ameba linter" single ((:commit . "0c4925ae0e998818326adcb47ed27ddf9761c7dc") (:authors ("Vitalii Elenhaupt")) (:maintainer "Vitalii Elenhaupt") (:keywords "convenience") (:url . "https://github.com/crystal-ameba/ameba.el"))]) (ample-regexps . [(20200508 1021) nil "ample regular expressions for Emacs" tar ((:commit . "153969ce547afe410b8986f01c9ed4087c9cd20b") (:authors ("immerrr" . "immerrr@gmail.com")) (:maintainer "immerrr" . "immerrr@gmail.com") (:keywords "regexps" "extensions" "tools"))]) @@ -118,7 +118,7 @@ (anakondo . [(20210221 1727) ((emacs (26 3))) "Adds clj-kondo based Clojure[Script] editing facilities" single ((:commit . "16b0ba14d94a5d7e55655efc9e1d6d069a9306f2") (:authors ("Didier A." . "didibus@users.noreply.github.com")) (:maintainer "Didier A." . "didibus@users.noreply.github.com") (:keywords "clojure" "clojurescript" "cljc" "clj-kondo" "completion" "languages" "tools") (:url . "https://github.com/didibus/anakondo"))]) (anaphora . [(20180618 2200) nil "anaphoric macros providing implicit temp variables" single ((:commit . "3b2da3f759b244975852e79721c4a2dbad3905cf") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "extensions") (:url . "http://github.com/rolandwalker/anaphora"))]) (ancient-one-dark-theme . [(20211030 1358) ((emacs (24 1))) "A color theme based off uetchy's Ancient One Dark Theme" single ((:commit . "db79f86842c10874ce18c1a1e4496e9d0e28bed9") (:authors ("Daniils Petrovs")) (:maintainer "Daniils Petrovs") (:url . "https://github.com/DaniruKun/ancient-one-dark-emacs-theme"))]) - (android-env . [(20200722 1403) ((emacs (24 3))) "Helper functions for working in android" single ((:commit . "5c6a6d9449f300eec4f374a5410edc1cbab02e40") (:authors ("Fernando Jascovich")) (:maintainer "Fernando Jascovich") (:keywords "android" "gradle" "java" "tools" "convenience") (:url . "https://github.com/fernando-jascovich/android-env.el"))]) + (android-env . [(20220810 1449) ((emacs (24 3)) (s (1 12 0))) "Helper functions for working in android" single ((:commit . "d2890f1156ed184314adbfcf01cdceb6ea79b10d") (:authors ("Fernando Jascovich")) (:maintainer "Fernando Jascovich") (:keywords "android" "gradle" "java" "tools" "convenience") (:url . "https://github.com/fernando-jascovich/android-env.el"))]) (android-mode . [(20190903 811) nil "Minor mode for Android application development" single ((:commit . "d5332e339a1f5e30559a53feffb8442ca79265d6") (:authors ("R.W. van 't Veer")) (:maintainer "R.W. van 't Veer") (:keywords "tools" "processes") (:url . "https://github.com/remvee/android-mode"))]) (angry-police-captain . [(20120829 1252) nil "Show quote from http://theangrypolicecaptain.com in the minibuffer" single ((:commit . "d11931c5cb63368dcc4a48797962428cca6d3e9d") (:authors ("Rolando Pereira" . "rolando_pereira@sapo.pt")) (:maintainer "Rolando Pereira" . "rolando_pereira@sapo.pt") (:keywords "games" "web" "fun"))]) (angular-mode . [(20151201 2127) nil "Major mode for Angular.js" tar ((:commit . "8720cde86af0f1859ccc8580571e8d0ad1c52cff") (:authors ("Rudolf Olah" . "omouse@gmail.com")) (:maintainer "Rudolf Olah" . "omouse@gmail.com") (:keywords "languages" "javascript") (:url . "https://github.com/omouse/angularjs-mode"))]) @@ -128,9 +128,9 @@ (anki-mode . [(20201223 719) ((emacs (24 4)) (dash (2 12 0)) (markdown-mode (2 2)) (s (1 11 0)) (request (0 3 0))) "A major mode for creating anki cards" single ((:commit . "d9b84028cd6a1ae040fb5604080a8b5fa8138562") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:keywords "tools") (:url . "https://github.com/davidshepherd7/anki-mode"))]) (anki-vocabulary . [(20200103 325) ((emacs (24 4)) (s (1 0)) (youdao-dictionary (0 4)) (anki-connect (1 0)) (s (1 10))) "Help you to create vocabulary cards in Anki" single ((:commit . "863fe0219577f996ab126f1b7902db3c2cc59b2b") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "lisp" "anki" "translator" "chinese") (:url . "https://github.com/lujun9972/anki-vocabulary.el"))]) (annalist . [(20190929 207) ((emacs (24 4)) (cl-lib (0 5))) "Record and display information such as keybindings" tar ((:commit . "134fa3f0fb91a636a1c005c483516d4b64905a6d") (:authors ("Fox Kiester" . "noct@posteo.net")) (:maintainer "Fox Kiester" . "noct@posteo.net") (:keywords "convenience" "tools" "keybindings" "org") (:url . "https://github.com/noctuid/annalist.el"))]) - (annotate . [(20220707 1207) nil "annotate files without changing them" single ((:commit . "4c697e70e882657aaf9072d9703c6142e931b265") (:authors ("Bastian Bechtold")) (:maintainer "Bastian Bechtold , cage" . "cage-dev@twistfold.it") (:url . "https://github.com/bastibe/annotate.el"))]) + (annotate . [(20220809 846) nil "annotate files without changing them" single ((:commit . "0f4ffd5c1c9f92fea6961699287f3dd124b88aad") (:authors ("Bastian Bechtold")) (:maintainer "Bastian Bechtold , cage" . "cage-dev@twistfold.it") (:url . "https://github.com/bastibe/annotate.el"))]) (annotate-depth . [(20160520 2040) nil "Annotate buffer if indentation depth is beyond threshold." single ((:commit . "fcb24fa36287250e40d195590c4ca4a8a696277b") (:authors ("Morten Slot Kristensen ")) (:maintainer "Morten Slot Kristensen ") (:keywords "convenience") (:url . "https://github.com/netromdk/annotate-depth"))]) - (annotation . [(20200914 644) nil "Functions for annotating text with faces and help bubbles" single ((:commit . "5d2d77abbc0c97f5b23d7089797c3ef8796508dc") (:url . "https://github.com/agda/agda"))]) + (annotation . [(20200914 644) nil "Functions for annotating text with faces and help bubbles" single ((:commit . "b382d7d376fad3429940de2fa0d399519e172cfc") (:url . "https://github.com/agda/agda"))]) (annoying-arrows-mode . [(20161024 646) ((cl-lib (0 5))) "Ring the bell if using arrows too much" single ((:commit . "3c42e9807d7696da2da2a21b63beebf9cdb3f5dc") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com"))]) (ansi . [(20211104 1420) ((emacs (24 1)) (cl-lib (0 6))) "Turn string into ansi strings" single ((:commit . "2367fba7b3b2340364a30cd6de7f3eb6bb9898a3") (:authors ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Johan Andersson" . "johan.rejeep@gmail.com") (:keywords "terminals" "color" "ansi") (:url . "http://github.com/rejeep/ansi"))]) (ansible . [(20220114 45) ((s (1 9 0)) (f (0 16 2))) "Ansible minor mode" tar ((:commit . "d89ac0ee57742cca0f0e0a3453d9dcc521575690") (:authors ("k1LoW (Kenichirou Oyama), ")) (:maintainer "k1LoW (Kenichirou Oyama), ") (:url . "https://github.com/k1LoW/emacs-ansible"))]) @@ -147,46 +147,46 @@ (apache-mode . [(20210519 1931) nil "Major mode for editing Apache httpd configuration files" single ((:commit . "f2c11aac2f5fc598123e04f4604bea248689a117") (:authors ("Karl Chen" . "quarl@nospam.quarl.org")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "languages" "faces") (:url . "https://github.com/emacs-php/apache-mode"))]) (apdl-mode . [(20211023 1831) ((emacs (25 1))) "Major mode for the APDL programming language." tar ((:commit . "c55c6468526100ba0da00bff05cfb17cdf8839cf") (:authors ("H. Dieter Wilhelm" . "dieter@duenenhof-wilhelm.de")) (:maintainer "H. Dieter Wilhelm") (:keywords "languages" "convenience" "tools" "ansys" "apdl") (:url . "https://github.com/dieter-wilhelm/apdl-mode"))]) (apel . [(20220720 1308) ((emacs (24 5))) "A Portable Emacs Library provides support for portable Emacs Lisp programs" tar ((:commit . "82eb2325bd149dc57b43a9ce9402c6c6183e4052"))]) - (apheleia . [(20220620 2151) ((emacs (26))) "Reformat buffer stably" single ((:commit . "12804a50206c708570104637472411288d6133f5") (:authors ("Radian LLC" . "contact+apheleia@radian.codes")) (:maintainer "Radian LLC" . "contact+apheleia@radian.codes") (:keywords "tools") (:url . "https://github.com/raxod502/apheleia"))]) + (apheleia . [(20220821 2221) ((emacs (26))) "Reformat buffer stably" single ((:commit . "04366a90dfc1063c4a22ea2e4ccaa48303a457e0") (:authors ("Radian LLC" . "contact+apheleia@radian.codes")) (:maintainer "Radian LLC" . "contact+apheleia@radian.codes") (:keywords "tools") (:url . "https://github.com/raxod502/apheleia"))]) (apib-mode . [(20200101 1017) ((markdown-mode (2 1))) "Major mode for API Blueprint files" single ((:commit . "c6dd05201f6eb9295736d8668a79a7510d11159e") (:authors ("Vilibald Wanča" . "vilibald@wvi.cz")) (:maintainer "Vilibald Wanča" . "vilibald@wvi.cz") (:keywords "tools" "api-blueprint") (:url . "http://github.com/w-vi/apib-mode"))]) (apiwrap . [(20180602 2231) ((emacs (25))) "api-wrapping macros" single ((:commit . "a4fb21d96027369307b22439a4a6c765ee272f44") (:authors ("Sean Allred" . "code@seanallred.com")) (:maintainer "Sean Allred" . "code@seanallred.com") (:keywords "tools" "maint" "convenience") (:url . "https://github.com/vermiculus/apiwrap.el"))]) (apparmor-mode . [(20220411 648) ((emacs (24 4))) "Major mode for editing AppArmor policy files" single ((:commit . "abc2a6adf563b89daee9f8fa07a71d78957defdb") (:authors ("Alex Murray" . "murray.alex@gmail.com")) (:maintainer "Alex Murray" . "murray.alex@gmail.com") (:url . "https://github.com/alexmurray/apparmor-mode"))]) (apples-mode . [(20110121 418) nil "Major mode for editing and executing AppleScript code" tar ((:commit . "83a9ab0d6ba82496e2f7df386909b1a55701fccb") (:authors ("tequilasunset" . "tequilasunset.mac@gmail.com")) (:maintainer "tequilasunset" . "tequilasunset.mac@gmail.com") (:keywords "applescript" "languages"))]) (applescript-mode . [(20210802 1715) ((emacs (24 3))) "major mode for editing AppleScript source" single ((:commit . "9b84a7cb74d687745df37ba15113933fc6256274") (:authors ("sakito" . "sakito@users.sourceforge.jp")) (:maintainer "sakito" . "sakito@users.sourceforge.jp") (:keywords "languages" "tools") (:url . "https://github.com/emacsorphanage/applescript-mode"))]) (aproject . [(20220410 541) nil "Basic project framework for Emacs" tar ((:commit . "13e176ee69851403bec6471c5cceed17b7912b6f") (:authors ("Vietor Liu" . "vietor.liu@gmail.com")) (:maintainer "Vietor Liu" . "vietor.liu@gmail.com") (:keywords "environment" "project") (:url . "https://github.com/vietor/aproject"))]) - (apropospriate-theme . [(20220612 1555) nil "A colorful, low-contrast, light & dark theme set for Emacs with a fun name." tar ((:commit . "17ec6fc0f43f7c0bcb970e2b71b8674891a464c7"))]) + (apropospriate-theme . [(20220817 235) nil "A colorful, low-contrast, light & dark theme set for Emacs with a fun name." tar ((:commit . "07265cc0122d3bde62615e8f5671e271598d95da"))]) (apt-sources-list . [(20180527 1241) ((emacs (24 4))) "Mode for editing APT source.list files" single ((:commit . "5289443ceff230dfc8a2c1c6b524c90560eb08a5") (:authors ("Dr. Rafael Sepúlveda" . "drs@gnulinux.org.mx")) (:maintainer "Joe Wreschnig" . "joe.wreschnig@gmail.com") (:url . "https://git.korewanetadesu.com/apt-sources-list.git"))]) (aqi . [(20200215 1334) ((emacs (25 1)) (request (0 3)) (let-alist (0 0))) "Air quality data from the World Air Quality Index" single ((:commit . "c107a2e21cd1ac6008d8baaeeedb3fab26583d45") (:authors ("nik gaffney" . "nik@fo.am")) (:maintainer "nik gaffney" . "nik@fo.am") (:keywords "air quality" "aqi" "pollution" "weather" "data") (:url . "https://github.com/zzkt/aqi"))]) (arc-dark-theme . [(20190314 1632) ((emacs (24))) "Arc dark theme" single ((:commit . "ee17dcca35dd0304145efc468b3f25af6907a59d") (:authors ("Christopher Fraser" . "cfraz89@gmail.com")) (:maintainer "Christopher Fraser" . "cfraz89@gmail.com") (:keywords "faces" "theme") (:url . "https://github.com/cfraz89/arc-dark-theme"))]) (arch-packer . [(20170730 1321) ((emacs (25 1)) (s (1 11 0)) (async (1 9 2)) (dash (2 12 0))) "Arch Linux package management frontend" single ((:commit . "940e96f7d357c6570b675a0f942181c787f1bfd7") (:authors ("Fritz Stelzer" . "brotzeitmacher@gmail.com")) (:maintainer "Fritz Stelzer" . "brotzeitmacher@gmail.com") (:url . "https://github.com/brotzeitmacher/arch-packer"))]) (archive-region . [(20200316 1425) ((emacs (24 4))) "Move region to archive file instead of killing" single ((:commit . "53cd2d96ea7c33f320353982b36854f25c900c2e") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "languages") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/archive-region.el"))]) (archive-rpm . [(20220527 632) ((emacs (24 4))) "RPM and CPIO support for archive-mode" tar ((:commit . "4116be5ddab61d7f2366d5efcd23baa7519e6e84") (:authors ("Magnus Henoch" . "magnus.henoch@gmail.com")) (:maintainer "Magnus Henoch" . "magnus.henoch@gmail.com") (:keywords "files"))]) - (arduino-cli-mode . [(20210511 653) ((emacs (25 1))) "Arduino-CLI command wrapper" single ((:commit . "a93de7e8fef202163df4657f2ab522b57f70f202") (:authors ("Love Lagerkvist")) (:maintainer "Love Lagerkvist") (:keywords "processes" "tools") (:url . "https://github.com/motform/arduino-cli-mode"))]) + (arduino-cli-mode . [(20210511 653) ((emacs (25 1))) "Arduino-CLI command wrapper" single ((:commit . "603f9f4d7d9cb37b005b8f10e5105636dc559217") (:authors ("Love Lagerkvist")) (:maintainer "Love Lagerkvist") (:keywords "processes" "tools") (:url . "https://github.com/motform/arduino-cli-mode"))]) (arduino-mode . [(20220210 1355) ((emacs (25 1)) (spinner (1 7 3))) "Major mode for editing Arduino code" tar ((:commit . "652c6a328fa8f2db06534d5f231c6b6933be3edc") (:maintainer "stardiviner" . "numbchild@gmail.com") (:keywords "languages" "arduino") (:url . "https://repo.or.cz/arduino-mode.git"))]) (aria2 . [(20190816 25) ((emacs (24 4))) "Control aria2c commandline tool from Emacs" single ((:commit . "32e08d5a8ad2f305578e0f783e087c1d312238c7") (:authors ("Łukasz Gruner" . "lukasz@gruner.lu")) (:maintainer "Łukasz Gruner" . "lukasz@gruner.lu") (:keywords "download" "bittorrent" "aria2") (:url . "https://bitbucket.org/ukaszg/aria2-mode"))]) (ariadne . [(20131117 1711) ((bert (0 1))) "Ariadne plugin for Emacs" single ((:commit . "6fe401c7f996bcbc2f685e7971324c6f5e5eaf15") (:authors ("Oleksandr Manzyuk" . "manzyuk@gmail.com")) (:maintainer "Oleksandr Manzyuk" . "manzyuk@gmail.com") (:keywords "comm" "convenience" "processes"))]) (arjen-grey-theme . [(20170522 2047) nil "A soothing dark grey theme" single ((:commit . "4cd0be72b65d42390e2105cfdaa408a1ead8d8d1") (:authors ("Arjen Wiersma" . "arjen@wiersma.org")) (:maintainer "Arjen Wiersma" . "arjen@wiersma.org") (:keywords "faces") (:url . "https://github.com/credmp/arjen-grey"))]) (artbollocks-mode . [(20170524 422) nil "Improve your writing (especially about art)" single ((:commit . "33a41ca4f8206f57e5498a526d3b0ea18d08bb93") (:authors ("Rob Myers , Sacha Chua" . "sacha@sachachua.com")) (:maintainer "Rob Myers , Sacha Chua" . "sacha@sachachua.com") (:url . "https://github.com/sachac/artbollocks-mode"))]) (arview . [(20160419 2109) nil "extract and view archives in the temporary directory" single ((:commit . "5437b4221b64b238c273a651d4792c577dba6d45") (:authors ("Andrey Fainer" . "fandrey@gmx.com")) (:maintainer "Andrey Fainer" . "fandrey@gmx.com") (:keywords "files") (:url . "https://github.com/afainer/arview"))]) - (arxiv-citation . [(20220706 706) ((emacs (25 1)) (dash (2 19 1)) (s (1 12 0))) "Utility functions for dealing with arXiv papers" single ((:commit . "a10af07419b66559569362ea57d897d988c17760") (:authors ("Tony Zorman" . "soliditsallgood@mailbox.org")) (:maintainer "Tony Zorman" . "soliditsallgood@mailbox.org") (:keywords "convenience") (:url . "https://gitlab.com/slotThe/arXiv-citation"))]) + (arxiv-citation . [(20220816 542) ((emacs (25 1)) (dash (2 19 1)) (s (1 12 0))) "Utility functions for dealing with arXiv papers" single ((:commit . "e41d5b90a00b79849cd2fd405b2af75a53b15abe") (:authors ("Tony Zorman" . "soliditsallgood@mailbox.org")) (:maintainer "Tony Zorman" . "soliditsallgood@mailbox.org") (:keywords "convenience") (:url . "https://gitlab.com/slotThe/arXiv-citation"))]) (arxiv-mode . [(20220128 920) ((emacs (27 1)) (hydra (0))) "Read and search for articles on arXiv.org" tar ((:commit . "f550583d2da8bd9600bd26bb4028fe22a9744da2") (:authors ("Alex Chen (fizban007)" . "fizban007@gmail.com") ("Simon Lin (Simon-Lin)" . "n.sibetz@gmail.com")) (:maintainer "Alex Chen (fizban007)" . "fizban007@gmail.com") (:keywords "bib" "convenience" "hypermedia") (:url . "https://github.com/fizban007/arxiv-mode"))]) (ascii-table . [(20201019 700) ((emacs (24 3)) (cl-lib (0 5))) "Interactive ASCII table" single ((:commit . "4f68ad0b36c365c0652756691ab1703d0d46b4b4") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "help" "tools") (:url . "https://github.com/lassik/emacs-ascii-table"))]) (asilea . [(20150105 1525) ((emacs (24)) (cl-lib (0 5))) "Find best compiler options using simulated annealing" single ((:commit . "2aab1cc63b64ef08d12e84fd7ba5c94065f6039f") (:authors ("Fanael Linithien" . "fanael4@gmail.com")) (:maintainer "Fanael Linithien" . "fanael4@gmail.com") (:url . "https://github.com/Fanael/asilea"))]) - (asm-blox . [(20220802 258) ((emacs (26 1)) (yaml (0 5 1))) "Programming game involving WAT" tar ((:commit . "dd43e800096066a8472db630a2af411de770e3ac") (:authors ("Zachary Romero")) (:maintainer "Zachary Romero") (:keywords "games") (:url . "https://github.com/zkry/asm-blox"))]) + (asm-blox . [(20220808 128) ((emacs (26 1)) (yaml (0 5 1))) "Programming game involving WAT" tar ((:commit . "d511ec0e24a081f1aa691c19cd38c8e0a90cb87e") (:authors ("Zachary Romero")) (:maintainer "Zachary Romero") (:keywords "games") (:url . "https://github.com/zkry/asm-blox"))]) (asn1-mode . [(20170729 226) ((emacs (24 3)) (s (1 10 0))) "ASN.1/GDMO mode for GNU Emacs" single ((:commit . "d5d4a8259daf708411699bcea85d322f18beb972") (:authors ("Taichi Kawabata ")) (:maintainer "Taichi Kawabata ") (:keywords "languages" "processes" "tools") (:url . "https://github.com/kawabata/asn1-mode/"))]) (assess . [(20220719 1904) ((emacs (24 4)) (m-buffer (0 15))) "Test support functions" tar ((:commit . "44083d94feb45d3636f7ee6c55e0ef6bbb32b938") (:authors ("Phillip Lord" . "phillip.lord@russet.org.uk")) (:maintainer "Phillip Lord" . "phillip.lord@russet.org.uk"))]) (astyle . [(20200328 616) ((emacs (24 4)) (reformatter (0 3))) "Astyle formatter functions" single ((:commit . "04ff2941f08c4b731fe6a18ee1697436d1ca1cc0") (:authors ("Petter Storvik")) (:maintainer "Petter Storvik") (:keywords "astyle" "c" "c++" "cpp" "reformatter") (:url . "https://github.com/storvik/emacs-astyle"))]) (asx . [(20191024 1100) ((emacs (26 1))) "Ask StackExchange/StackOverflow" single ((:commit . "ec4bf74de602b97df1f306d51acf4cda45184aac") (:authors ("Alex Ragone" . "ragonedk@gmail.com")) (:maintainer "Alex Ragone" . "ragonedk@gmail.com") (:keywords "convenience") (:url . "https://github.com/ragone/asx"))]) - (async . [(20220630 57) ((emacs (24 4))) "Asynchronous processing in Emacs" tar ((:commit . "7f4ed1e8b44e0b88eadb2efeeaf97f32c38f14c4") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:keywords "async") (:url . "https://github.com/jwiegley/emacs-async"))]) - (async-await . [(20200117 828) ((emacs (25 1)) (promise (1 1)) (iter2 (0 9 10))) "Async/Await" single ((:commit . "deef2bb343463f5196545f1dd8c2a32d0cb3b146") (:authors ("chuntaro" . "chuntaro@sakura-games.jp")) (:maintainer "chuntaro" . "chuntaro@sakura-games.jp") (:keywords "async" "await" "convenience") (:url . "https://github.com/chuntaro/emacs-async-await"))]) + (async . [(20220820 530) ((emacs (24 4))) "Asynchronous processing in Emacs" tar ((:commit . "73f825fd739875e4b8e10d47d497df402bbb61e7") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:keywords "async") (:url . "https://github.com/jwiegley/emacs-async"))]) + (async-await . [(20220825 946) ((emacs (25 1)) (promise (1 1)) (iter2 (0 9 10))) "Async/Await" single ((:commit . "46cd65bdac59c85ce54e3bf3ce6ea2849a0331d4") (:authors ("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 . [(20220131 1438) ((emacs (24 4))) "Backup on each save without freezing Emacs" single ((:commit . "6ddb39fe77d66cdef48b87cb0d0554ad7d132308") (:authors ("contrapunctus" . "xmpp:contrapunctus@jabjab.de")) (:maintainer "contrapunctus" . "xmpp:contrapunctus@jabjab.de") (:keywords "files") (:url . "https://tildegit.org/contrapunctus/async-backup"))]) (atcoder-tools . [(20200109 1236) ((emacs (26)) (f (0 20)) (s (1 12))) "An atcoder-tools client" single ((:commit . "cfe61ed18ea9b3b1bfb6f9e7d80a47599680cd1f") (:authors ("Seong Yong-ju" . "sei40kr@gmail.com")) (:maintainer "Seong Yong-ju" . "sei40kr@gmail.com") (:keywords "extensions" "tools") (:url . "https://github.com/sei40kr/atcoder-tools"))]) - (atl-long-lines . [(20220704 644) ((emacs (24 3))) "Turn off truncate-lines when the line is long" single ((:commit . "074183f5d4fe77c9b5f1d0c90287a41aaa3e5eff") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "truncate" "lines" "auto" "long") (:url . "https://github.com/jcs-elpa/atl-long-lines"))]) - (atl-markup . [(20220704 644) ((emacs (24 3))) "Automatically truncate lines for markup languages" single ((:commit . "14639839f5e206b315c800ab4dc09d2d1693fa0b") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "automatic" "truncate" "visual" "lines") (:url . "https://github.com/jcs-elpa/atl-markup"))]) + (atl-long-lines . [(20220704 644) ((emacs (24 3))) "Turn off truncate-lines when the line is long" single ((:commit . "5ddd52c752fc60096ed3cf939d773b60f781e325") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "truncate" "lines" "auto" "long") (:url . "https://github.com/jcs-elpa/atl-long-lines"))]) + (atl-markup . [(20220704 644) ((emacs (24 3))) "Automatically truncate lines for markup languages" single ((:commit . "e5047b5fd68b540e2653aa5915da80c70201544d") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "automatic" "truncate" "visual" "lines") (:url . "https://github.com/jcs-elpa/atl-markup"))]) (atom-dark-theme . [(20220114 1902) nil "An Emacs port of the Atom Dark theme from Atom.io." single ((:commit . "2b3c7ad42bbcab3214a131f8957b92e717b36ad3") (:authors ("Jeremy Whitlock" . "jwhitlock@apache.org")) (:maintainer "Jeremy Whitlock" . "jwhitlock@apache.org") (:keywords "themes" "atom" "dark") (:url . "https://github.com/whitlockjc/atom-dark-theme-emacs"))]) (atom-one-dark-theme . [(20210128 1640) nil "Atom One Dark color theme" single ((:commit . "b34b62e85593812b55ee552a1cb0eecfb04767bb") (:authors ("Jonathan Chu" . "me@jonathanchu.is")) (:maintainer "Jonathan Chu" . "me@jonathanchu.is") (:url . "https://github.com/jonathanchu/atom-one-dark-theme"))]) (atomic-chrome . [(20220723 113) ((emacs (24 4)) (let-alist (1 0 4)) (websocket (1 4))) "Edit Chrome text area with Emacs using Atomic Chrome" single ((:commit . "061958ab96c31085b5daf449b1d826b052777b59") (:authors ("alpha22jp" . "alpha22jp@gmail.com")) (:maintainer "alpha22jp" . "alpha22jp@gmail.com") (:keywords "chrome" "edit" "textarea") (:url . "https://github.com/alpha22jp/atomic-chrome"))]) - (attrap . [(20220124 1253) ((dash (2 12 0)) (emacs (25 1)) (f (0 19 0)) (flycheck (0 30)) (s (1 11 0))) "ATtempt To Repair At Point" single ((:commit . "19a520ecb99529790906a1fb5599acdf2b4f005f") (:authors ("Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com")) (:maintainer "Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com") (:keywords "programming" "tools") (:url . "https://github.com/jyp/attrap"))]) + (attrap . [(20220822 1348) ((dash (2 12 0)) (emacs (25 1)) (f (0 19 0)) (flycheck (0 30)) (s (1 11 0))) "ATtempt To Repair At Point" single ((:commit . "62fbd5f2665f0001f9c6a2dd0622edda7f4431da") (:authors ("Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com")) (:maintainer "Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com") (:keywords "programming" "tools") (:url . "https://github.com/jyp/attrap"))]) (auctex-cluttex . [(20220730 1100) ((emacs (24 4)) (auctex (13 1))) "ClutTeX support for AUCTeX" single ((:commit . "f4012ac86e612eac7662c62afd946e59b3b405bd") (:authors ("Masahiro Nakamura" . "tsuucat@icloud.com")) (:maintainer "Masahiro Nakamura" . "tsuucat@icloud.com") (:keywords "tex") (:url . "https://github.com/tsuu32/auctex-cluttex"))]) (auctex-latexmk . [(20170618 1636) ((auctex (11 87))) "Add LatexMk support to AUCTeX" single ((:commit . "4d353522650d7685acbf1d38f7dbc504f734bd84") (:authors ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainer "Tomoya Tanjo" . "ttanjo@gmail.com") (:keywords "tex") (:url . "https://github.com/tom-tan/auctex-latexmk/"))]) (auctex-lua . [(20151121 1610) ((auctex (11 86)) (lua-mode (20130419))) "Lua editing support for AUCTeX" single ((:commit . "799cd8ac10c96991bb63d9aa60528ae5d8c786b5") (:authors ("Sean Allred" . "seallred@smcm.edu")) (:maintainer "Sean Allred" . "seallred@smcm.edu") (:keywords "latex" "lua") (:url . "http://github.com/vermiculus/auctex-lua"))]) @@ -194,13 +194,13 @@ (audio-notes-mode . [(20170611 2159) nil "Play audio notes synced from somewhere else." single ((:commit . "fa38350829c7e97257efc746a010471d33748a68") (:authors ("Artur Malabarba" . "bruce.connor.am@gmail.com")) (:maintainer "Artur Malabarba" . "bruce.connor.am@gmail.com") (:keywords "hypermedia" "convenience") (:url . "http://github.com/Bruce-Connor/audio-notes-mode"))]) (aurel . [(20170114 937) ((emacs (24 3)) (bui (1 1 0)) (dash (2 11 0))) "Search, get info, vote for and download AUR packages" single ((:commit . "fc7ad208f43f8525f84a18941c9b55f956df8961") (:authors ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "tools") (:url . "https://github.com/alezost/aurel"))]) (aurora-config-mode . [(20180216 2302) nil "Major mode for Apache Aurora configuration files" single ((:commit . "8273ec7937a21b469b9dbb6c11714255b890f410") (:authors ("Berk D. Demir" . "bdd@mindcast.org")) (:maintainer "Berk D. Demir" . "bdd@mindcast.org") (:keywords "languages" "configuration") (:url . "https://github.com/bdd/aurora-config.el"))]) - (auth-source-keytar . [(20220704 626) ((emacs (24 4)) (keytar (0 1 2)) (s (1 12 0))) "Integrate auth-source with keytar" single ((:commit . "c69176b476c9424a42d19ef8c200fcf2dc850749") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "keytar" "password" "credential" "secret" "security") (:url . "https://github.com/emacs-grammarly/auth-source-keytar"))]) + (auth-source-keytar . [(20220704 626) ((emacs (24 4)) (keytar (0 1 2)) (s (1 12 0))) "Integrate auth-source with keytar" single ((:commit . "1f38686e5818eba4d570ce3dec1e2130adc2a648") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "keytar" "password" "credential" "secret" "security") (:url . "https://github.com/emacs-grammarly/auth-source-keytar"))]) (auth-source-kwallet . [(20210605 1032) ((emacs (24 4))) "KWallet integration for auth-source" single ((:commit . "053ed5e964acaf6f16a1708c36d812eeb7c1817d") (:authors ("Ekaterina Vaartis" . "vaartis@kotobank.ch")) (:maintainer "Ekaterina Vaartis" . "vaartis@kotobank.ch") (:url . "https://github.com/vaartis/auth-source-kwallet"))]) - (auth-source-xoauth2 . [(20220628 2232) ((emacs (26 1))) "Integrate auth-source with XOAUTH2" single ((:commit . "8bbfd9395a2dc397639ec265299ccaadb71aeebc") (:authors ("Cesar Crusius" . "ccrusius@google.com")) (:maintainer "Cesar Crusius" . "ccrusius@google.com") (:url . "https://github.com/ccrusius/auth-source-xoauth2"))]) + (auth-source-xoauth2 . [(20220804 2219) ((emacs (26 1))) "Integrate auth-source with XOAUTH2" single ((:commit . "5d1adfa649bb5a9df20a2fa89f235a55a64b52e4") (:authors ("Cesar Crusius" . "ccrusius@google.com")) (:maintainer "Cesar Crusius" . "ccrusius@google.com") (:url . "https://github.com/ccrusius/auth-source-xoauth2"))]) (auto-async-byte-compile . [(20160916 454) nil "Automatically byte-compile when saved" single ((:commit . "8681e74ddb8481789c5dbb3cafabb327db4c4484") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "lisp" "convenience") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/auto-async-byte-compile.el"))]) (auto-auto-indent . [(20131106 1903) ((es-lib (0 1)) (cl-lib (1 0))) "Indents code as you type" single ((:commit . "0139378577f936d34b20276af6f022fb457af490") (:authors ("sabof")) (:maintainer "sabof") (:url . "https://github.com/sabof/auto-auto-indent"))]) (auto-compile . [(20220517 1501) ((emacs (25 1)) (compat (28 1 1 0)) (packed (3 0 3))) "Automatically compile Emacs Lisp libraries" single ((:commit . "b204e2f85aaa4d41af4eb1819633c9613f5172bf") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "compile" "convenience" "lisp") (:url . "https://github.com/emacscollective/auto-compile"))]) - (auto-complete . [(20220105 439) ((popup (0 5 0)) (cl-lib (0 5))) "Auto Completion for GNU Emacs" tar ((:commit . "8f33bc4fecb653d48d70a4902078f9d493eb882f") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "completion" "convenience") (:url . "https://github.com/auto-complete/auto-complete"))]) + (auto-complete . [(20220105 439) ((popup (0 5 0)) (cl-lib (0 5))) "Auto Completion for GNU Emacs" tar ((:commit . "561e010b6b5e7df60e59309464d2793ef2c8cd40") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "completion" "convenience") (:url . "https://github.com/auto-complete/auto-complete"))]) (auto-complete-auctex . [(20140223 1758) ((yasnippet (0 6 1)) (auto-complete (1 4))) "auto-completion for auctex" single ((:commit . "855633f668bcc4b9408396742a7cb84e0c4a2f77") (:authors ("Christopher Monsanto" . "chris@monsan.to")) (:maintainer "Christopher Monsanto" . "chris@monsan.to"))]) (auto-complete-c-headers . [(20150912 323) ((auto-complete (1 4))) "An auto-complete source for C/C++ header files" single ((:commit . "52fef720c6f274ad8de52bef39a343421006c511") (:authors ("Masafumi Oyamada" . "stillpedant@gmail.com")) (:maintainer "Masafumi Oyamada" . "stillpedant@gmail.com") (:keywords "c"))]) (auto-complete-chunk . [(20140225 946) ((auto-complete (1 4))) "Auto-completion for dot.separated.words." single ((:commit . "a9aa77ffb84a1037984a7ce4dda25074272f13fe") (:authors ("ARAKAKI, Takafumi")) (:maintainer "ARAKAKI, Takafumi") (:url . "https://github.com/tkf/auto-complete-chunk"))]) @@ -215,14 +215,14 @@ (auto-dark . [(20220320 1703) ((emacs (24 4))) "Automatically set the dark-mode theme based on MacOS status" single ((:commit . "c5dd3afa6771f4777db9e427f21bfcbe4883abaf") (:authors ("Rahul M. Juliato") ("Tim Harper ")) (:maintainer "Rahul M. Juliato") (:keywords "tools" "unix" "faces") (:url . "https://github.com/LionyxML/auto-dark-emacs"))]) (auto-dictionary . [(20150410 1610) nil "automatic dictionary switcher for flyspell" single ((:commit . "b364e08009fe0062cf0927d8a0582fad5a12b8e7") (:authors ("Nikolaj Schumacher ")) (:maintainer "Nikolaj Schumacher ") (:keywords "wp") (:url . "http://nschum.de/src/emacs/auto-dictionary/"))]) (auto-dim-other-buffers . [(20220209 2101) nil "Makes windows without focus less prominent" single ((:commit . "33b5f88b799a17947c266b04ad59462c5aeb4ed7") (:authors ("Michal Nazarewicz" . "mina86@mina86.com")) (:maintainer "Michal Nazarewicz" . "mina86@mina86.com") (:url . "https://github.com/mina86/auto-dim-other-buffers.el"))]) - (auto-highlight-symbol . [(20220505 505) ((emacs (26 1)) (ht (2 3))) "Automatic highlighting current symbol minor mode" single ((:commit . "616c1391646da49aa3b5a6034d0f8d6a987d5e8c") (:authors ("Mitsuo Saito" . "arch320@NOSPAM.gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "highlight" "face" "match" "convenience") (:url . "http://github.com/jcs-elpa/auto-highlight-symbol"))]) + (auto-highlight-symbol . [(20220505 505) ((emacs (26 1)) (ht (2 3))) "Automatic highlighting current symbol minor mode" single ((:commit . "b0cf431f3020c91988cc807a7088b44354935539") (:authors ("Mitsuo Saito" . "arch320@NOSPAM.gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "highlight" "face" "match" "convenience") (:url . "http://github.com/jcs-elpa/auto-highlight-symbol"))]) (auto-indent-mode . [(20211029 11) nil "Auto indent Minor mode" tar ((:commit . "664006b67329a8e27330541547f8c2187dab947c") (:authors ("Matthew L. Fidler, Le Wang & Others")) (:maintainer "Matthew L. Fidler") (:keywords "auto" "indentation") (:url . "https://github.com/mlf176f2/auto-indent-mode.el/"))]) (auto-minor-mode . [(20180527 1123) ((emacs (24 4))) "Enable minor modes by file name and contents" single ((:commit . "17cfa1b54800fdef2975c0c0531dad34846a5065") (:authors ("Joe Wreschnig" . "joe.wreschnig@gmail.com")) (:maintainer "Joe Wreschnig" . "joe.wreschnig@gmail.com") (:keywords "convenience") (:url . "https://github.com/joewreschnig/auto-minor-mode"))]) (auto-org-md . [(20180213 2343) ((emacs (24 4))) "export a markdown file automatically when you save an org-file" single ((:commit . "9318338bdb7fe8bd698d88f3af89b2d6413efdd2") (:authors ("jamcha" . "jamcha.aa@gmail.com")) (:maintainer "jamcha" . "jamcha.aa@gmail.com") (:keywords "org" "markdown") (:url . "https://github.com/jamcha-aa/auto-org-md"))]) (auto-package-update . [(20211108 2025) ((emacs (24 4)) (dash (2 1 0))) "Automatically update Emacs packages." single ((:commit . "ad95435fefe2bb501d1d787b08272f9c1b7df488") (:authors ("Renan Ranelli")) (:maintainer "Renan Ranelli") (:keywords "package" "update") (:url . "http://github.com/rranelli/auto-package-update.el"))]) (auto-pause . [(20160426 1216) ((emacs (24 4))) "Run processes which will be paused when Emacs is idle" single ((:commit . "a4d778de774ca3895542cb559a953e0d98657338") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "menu") (:url . "https://github.com/lujun9972/auto-pause"))]) (auto-read-only . [(20200827 1754) ((emacs (25 1)) (cl-lib (0 5))) "Automatically make the buffer to read-only" single ((:commit . "db209bf5b7f76f4c3dc4d0892fc6a24430779f29") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "files" "convenience") (:url . "https://github.com/zonuexe/auto-read-only.el"))]) - (auto-rename-tag . [(20220704 639) ((emacs (24 4))) "Automatically rename paired HTML/XML tag" single ((:commit . "955a25bf371fe7edc7c1c5fdda0578731273c108") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "auto-complete" "html" "rename" "tag" "xml") (:url . "https://github.com/jcs-elpa/auto-rename-tag"))]) + (auto-rename-tag . [(20220704 639) ((emacs (24 4))) "Automatically rename paired HTML/XML tag" single ((:commit . "7e47763d127ccebcf58aa5fce5daf14260b5d55b") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "auto-complete" "html" "rename" "tag" "xml") (:url . "https://github.com/jcs-elpa/auto-rename-tag"))]) (auto-save-buffers-enhanced . [(20161109 710) nil "Automatically save buffers in a decent way" single ((:commit . "461e8c816c1b7c650be5f209078b381fe55da8c6") (:authors ("Kentaro Kuribayashi" . "kentarok@gmail.com")) (:maintainer "Kentaro Kuribayashi" . "kentarok@gmail.com"))]) (auto-shell-command . [(20180817 1502) ((deferred (20130312)) (popwin (20130329))) "Run the shell command asynchronously that you specified when you save the file." single ((:commit . "a8f9213e3c773b5687b81881240e6e648f2f56ba") (:authors ("ongaeshi")) (:maintainer "ongaeshi") (:keywords "shell" "save" "async" "deferred" "auto"))]) (auto-sudoedit . [(20220421 1147) ((emacs (26 1)) (f (0 19 0))) "Auto sudo edit by tramp" single ((:commit . "1d9dd74cb1adaf615500208dbc0158630dd92933") (:authors ("ncaq" . "ncaq@ncaq.net")) (:maintainer "ncaq" . "ncaq@ncaq.net") (:url . "https://github.com/ncaq/auto-sudoedit"))]) @@ -236,7 +236,7 @@ (autodisass-llvm-bitcode . [(20150411 125) nil "Automatically disassemble LLVM bitcode" tar ((:commit . "d2579e3a1427af2dc947c343e49eb3434078bf04") (:authors ("George Balatsouras ")) (:maintainer "George Balatsouras ") (:keywords "convenience" "data" "files"))]) (autotest . [(20190331 2230) nil "ZenTest's autotest integration with emacs." single ((:commit . "7725c08f00a463ba7210efcb759c934223c85b00") (:authors ("Ryan Davis" . "ryand-ruby@zenspider.com")) (:maintainer "Ryan Davis" . "ryand-ruby@zenspider.com") (:keywords "testing" "ruby" "convenience") (:url . "https://github.com/zenspider/elisp/blob/master/autotest.el"))]) (autotetris-mode . [(20141114 1646) ((cl-lib (0 5))) "automatically play tetris" single ((:commit . "0c3a746dcc304a67d2a6e7ad4ef93f512486343a") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/autotetris-mode"))]) - (autothemer . [(20220106 416) ((dash (2 10 0)) (emacs (24)) (cl-lib (0 5))) "Conveniently define themes." single ((:commit . "1dbc06ad430c51b5ec1a602a808ee46b9bd4bafa") (:authors ("Sebastian Sturm")) (:maintainer "Sebastian Sturm") (:url . "https://github.com/sebastiansturm/autothemer"))]) + (autothemer . [(20220824 2009) ((dash (2 10 0)) (emacs (26 1))) "Conveniently define themes" single ((:commit . "378f8707bdd944f19b6fd4544359e993c9113752") (:authors ("Sebastian Sturm")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "https://github.com/jasonm23/autothemer"))]) (autumn-light-theme . [(20150515 1447) nil "A light color theme with muted, autumnal colors." single ((:commit . "1e3b2a43a3001e4a97a5ff073ba3f0d2ea3888f9") (:authors ("Adam Alpern" . "adam.alpern@gmail.com")) (:maintainer "Adam Alpern" . "adam.alpern@gmail.com") (:keywords "color" "theme") (:url . "http://github.com/aalpern/emacs-color-theme-autumn-light"))]) (avandu . [(20170101 1903) nil "Gateway to Tiny Tiny RSS" tar ((:commit . "f44588d8e747fa880411cb4542cc39962252b90a") (:authors ("Tom Willemse" . "tom@ryuslash.org")) (:maintainer "Tom Willemse" . "tom@ryuslash.org") (:keywords "net"))]) (avk-emacs-themes . [(20210521 1051) nil "Collection of avk themes" tar ((:commit . "7b9b6517873c4d4d73e6e34ca56c54062db60759") (:authors ("Alex V. Koval" . "alex@koval.kharkov.ua")) (:maintainer "Alex V. Koval" . "alex@koval.kharkov.ua") (:keywords "theme") (:url . "https://github.com/avkoval/avk-emacs-themes"))]) @@ -269,11 +269,11 @@ (bap-mode . [(20200128 1354) nil "Major-mode for BAP's IR" single ((:commit . "8969679f60db0aa918d35f40d959c0a9c723b111") (:authors ("Thomas Barabosch ")) (:maintainer "Thomas Barabosch" . "thomas.barabosch@fkie.fraunhofer.de") (:keywords "languages") (:url . "https://github.com/fkie-cad/bap-mode"))]) (bar-cursor . [(20201204 2244) nil "package used to switch block cursor to a bar" single ((:commit . "78f195b6db63459033c4f1c7e7add5d82f3ce424") (:authors ("Joe Casadonte" . "emacs@northbound-train.com")) (:maintainer "Andrew Johnson" . "andrew@andrewjamesjohnson.com") (:keywords "files") (:url . "https://github.com/ajsquared/bar-cursor"))]) (bart-mode . [(20190601 1004) ((emacs (24 3))) "Real time BART departures info." single ((:commit . "f70b6c42452e47c0c6b3ebd4c90e555a9bedeec7") (:authors ("Michael Schuldt" . "mbschuldt@gmail.com")) (:maintainer "Michael Schuldt" . "mbschuldt@gmail.com") (:keywords "convenience" "transit") (:url . "https://github.com/mschuldt/bart-mode"))]) - (base16-theme . [(20220725 353) nil "Collection of themes built on combinations of 16 base colors" tar ((:commit . "36cd6ed044ee37e399fc5d0db8070ae79fb06800") (:authors ("Kaleb Elwert" . "belak@coded.io") ("Neil Bhakta")) (:maintainer "Kaleb Elwert" . "belak@coded.io") (:url . "https://github.com/base16-project/base16-emacs"))]) + (base16-theme . [(20220821 127) nil "Collection of themes built on combinations of 16 base colors" tar ((:commit . "d70a96152a3d2d66776012ec7e578f93d7c6aa2c") (:authors ("Kaleb Elwert" . "belak@coded.io") ("Neil Bhakta")) (:maintainer "Kaleb Elwert" . "belak@coded.io") (:url . "https://github.com/base16-project/base16-emacs"))]) (bash-completion . [(20220531 1104) ((emacs (24 3))) "BASH completion for the shell buffer" single ((:commit . "8e9c20dbfe01d8bf6c61db231593623a201c75c6") (:authors ("Stephane Zermatten" . "szermatt@gmx.net")) (:maintainer "Stephane Zermatten" . "szermatt@gmail.com") (:keywords "shell" "bash" "bash-completion") (:url . "http://github.com/szermatt/emacs-bash-completion"))]) (basic-c-compile . [(20170302 1112) ((cl-lib (0 5)) (f (0 19 0))) "Quickly create a Makefile, compile and run C." single ((:commit . "0129786aeee50d7bb0020d9fc2b7508875d403e8") (:authors ("Nick Spain" . "nicholas.spain96@gmail.com")) (:maintainer "Nick Spain" . "nicholas.spain96@gmail.com") (:keywords "c" "makefile" "compilation" "convenience") (:url . "https://github.com/nick96/basic-c-compile"))]) (basic-ide . [(20200429 1104) ((emacs (25)) (basic-mode (0 4 2)) (company (0 9 12)) (flycheck (0 22)) (dash (2 12 0)) (f (0 17 0))) "BASIC IDE c64" single ((:commit . "1d026b6ae70db9cde36596dcf46b101058a2e004") (:authors ("Fermin MF" . "fmfs@posteo.net")) (:maintainer "Fermin MF" . "fmfs@posteo.net") (:keywords "languages" "basic") (:url . "https://gitlab.com/sasanidas/emacs-c64-basic-ide"))]) - (basic-mode . [(20210316 1253) ((seq (2 20)) (emacs (24 3))) "major mode for editing BASIC code" single ((:commit . "eaa5f24d2fb303d9e5d7de2a28c7c18b01532ab6") (:authors ("Johan Dykstrom")) (:maintainer "Johan Dykstrom") (:keywords "basic" "languages") (:url . "https://github.com/dykstrom/basic-mode"))]) + (basic-mode . [(20220823 1148) ((seq (2 20)) (emacs (25 1))) "Major mode for editing BASIC code" single ((:commit . "8d7f66ba38d003d7ee6ff271482edfabf146963c") (:authors ("Johan Dykstrom")) (:maintainer "Johan Dykstrom") (:keywords "basic" "languages") (:url . "https://github.com/dykstrom/basic-mode"))]) (basic-theme . [(20160817 827) ((emacs (24))) "Minimalistic light color theme" single ((:commit . "e2a855bd39f4b78296228d4b790f9123156f7d7e") (:authors ("Felix Geller" . "fgeller@gmail.com")) (:maintainer "Felix Geller" . "fgeller@gmail.com") (:keywords "theme" "basic" "minimal" "colors") (:url . "http://github.com/fgeller/basic-theme.el"))]) (bats-mode . [(20160514 615) nil "Emacs mode for editing and running Bats tests" single ((:commit . "d519f7c89f5ae17dfc33400596df4564b478315f") (:authors ("Doug MacEachern")) (:maintainer "Doug MacEachern") (:keywords "bats" "tests") (:url . "https://github.com/dougm/bats-mode"))]) (battery-notifier . [(20220705 2030) ((alert (1 3))) "Notify when battery capacity is low" single ((:commit . "b7301d3633afff78609afd45dcf78268f98d52d3") (:authors ("Jason Johnson" . "jason@fullsteamlabs.com")) (:maintainer "Jason Johnson" . "jason@fullsteamlabs.com") (:keywords "hardware" "battery") (:url . "https://github.com/jasonmj/battery-notifier"))]) @@ -288,9 +288,9 @@ (bbdb2erc . [(20190822 907) ((bbdb (3 0))) "make bbdb show if pal is online with ERC, click i to chat" single ((:commit . "40b89e961762af3e7ade3a1844a9fbcd4084ac65") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "irc" "contacts" "chat" "client" "internet"))]) (bbyac . [(20180206 1441) ((browse-kill-ring (1 3)) (cl-lib (0 5))) "Type a little Bit, and Bang! You Are Completed." tar ((:commit . "9f0de9cad13801891ffb590dc09f51ff9a7cb225") (:authors ("Bao Haojun" . "baohaojun@gmail.com")) (:maintainer "Bao Haojun" . "baohaojun@gmail.com") (:keywords "abbrev") (:url . "https://github.com/baohaojun/bbyac"))]) (beacon . [(20220730 100) ((emacs (25 1))) "Highlight the cursor whenever the window scrolls" single ((:commit . "85261a928ae0ec3b41e639f05291ffd6bf7c231c") (:authors ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "emacs@endlessparentheses.com") (:keywords "convenience") (:url . "https://github.com/Malabarba/beacon"))]) - (bech32 . [(20220718 1440) ((emacs (25 1)) (dash (2 19 0))) "Bech32 library" single ((:commit . "40d04a0baf5c3d1087b18cc03595c573a1b5891d") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) + (bech32 . [(20220810 1529) ((emacs (26 1))) "Bech32 library" single ((:commit . "5e1bf8b8ffa4c75bece7a93feab9858f0e7d676e") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) (beeminder . [(20201227 1533) ((emacs (24 3)) (seq (2 16)) (org (7))) "Emacs interface for Beeminder" tar ((:commit . "161d9c94c594614a01cb08219693d9e000af4f69") (:authors ("Phil Newton" . "phil@sodaware.net")) (:maintainer "Phil Newton" . "phil@sodaware.net") (:keywords "tools" "beeminder") (:url . "http://www.philnewton.net/code/beeminder-el/"))]) - (beginend . [(20220803 1431) ((emacs (25 3))) "Redefine M-< and M-> for some modes" single ((:commit . "eb77d82dc88846c5715353b7fd99c9030a2e2ee7") (:url . "https://github.com/DamienCassou/beginend"))]) + (beginend . [(20220824 1605) ((emacs (25 3))) "Redefine M-< and M-> for some modes" single ((:commit . "d0aec04c05911a0d47b34625959e1950ead4e4bd") (:url . "https://github.com/DamienCassou/beginend"))]) (belarus-holidays . [(20190102 1343) nil "Belarus holidays whith transfers" single ((:commit . "35a18273e19edc3b4c761030ffbd11116483b83e") (:authors ("Yauhen Makei" . "yauhen.makei@gmail.com")) (:maintainer "Yauhen Makei" . "yauhen.makei@gmail.com") (:url . "http://bitbucket.org/EugeneMakei/belarus-holidays.el"))]) (benchmark-init . [(20220414 1612) ((emacs (24 3))) "Benchmarks for require and load calls" tar ((:commit . "02435560415bbadbcf5051fb7042880549170e7e") (:authors ("Steve Purcell")) (:maintainer "David Holm" . "dholmster@gmail.com") (:keywords "convenience" "benchmark") (:url . "https://github.com/dholm/benchmark-init-el"))]) (benchstat . [(20171014 312) nil "proper benchmarking made simple" single ((:commit . "a5b67cf7972ca2bbc9f5bc6a0f521ab02b76d4f0") (:authors ("Iskander Sharipov" . "quasilyte@gmail.com")) (:maintainer "Iskander Sharipov" . "quasilyte@gmail.com") (:keywords "lisp") (:url . "https://github.com/Quasilyte/benchstat.el"))]) @@ -299,7 +299,7 @@ (bert . [(20131117 1014) nil "BERT serialization library for Emacs" single ((:commit . "a3eec6980a725aa4abd2019e4c00246450260490") (:authors ("Oleksandr Manzyuk" . "manzyuk@gmail.com")) (:maintainer "Oleksandr Manzyuk" . "manzyuk@gmail.com") (:keywords "comm" "data"))]) (better-defaults . [(20220116 2220) ((emacs (25 1))) "Fixing weird quirks and poor defaults" single ((:commit . "20ac176ccdc18ff8cb4a6b37cf1fe90fa7f88335") (:authors ("Phil Hagelberg")) (:maintainer "Phil Hagelberg") (:keywords "convenience") (:url . "https://github.com/technomancy/better-defaults"))]) (better-jumper . [(20220110 118) ((emacs (25 1))) "configurable jump list" single ((:commit . "47622213783ece37d5337dc28d33b530540fc319") (:authors ("Bryan Gilbert ")) (:maintainer "Bryan Gilbert" . "bryan@bryan.sh") (:keywords "convenience" "jump" "history" "evil") (:url . "https://github.com/gilbertw1/better-jumper"))]) - (better-scroll . [(20220704 645) ((emacs (24 3))) "Improve user experience when scrolling window" single ((:commit . "9ec8cb24751784cc114263728d7565fd473facd7") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "scrolling" "scroll" "window" "better" "improvement") (:url . "https://github.com/jcs-elpa/better-scroll"))]) + (better-scroll . [(20220704 645) ((emacs (24 3))) "Improve user experience when scrolling window" single ((:commit . "6ca5c402757710eba2c20c382ea532aa10f054a2") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "scrolling" "scroll" "window" "better" "improvement") (:url . "https://github.com/jcs-elpa/better-scroll"))]) (better-shell . [(20191025 1737) ((emacs (24 4))) "Better shell management" single ((:commit . "70c787b981caeef8c5f8012b170eb7b9f167cd13") (:authors ("Russell Black" . "killdash9@github")) (:maintainer "Russell Black" . "killdash9@github") (:keywords "convenience") (:url . "https://github.com/killdash9/better-shell"))]) (bf-mode . [(20130403 1442) nil "Browse file persistently on dired" single ((:commit . "7cc4d09aed64d9db6be95646f5f5067de68f8895") (:authors ("isojin")) (:maintainer "myuhe ") (:keywords "convenience") (:url . "https://github.com/emacs-jp/bf-mode"))]) (bfbuilder . [(20210228 1740) ((cl-lib (0 3)) (emacs (24 4))) "A brainfuck development environment with interactive debugger" single ((:commit . "689f320a9a1326cdeff43b8538e0d739f8519c4b") (:authors ("zk_phi")) (:maintainer "zk_phi") (:url . "http://zk-phi.gitub.io/"))]) @@ -315,8 +315,8 @@ (bicycle . [(20220422 1600) ((emacs (25 1)) (compat (28 1 1 0))) "Cycle outline and code visibility" single ((:commit . "e6d8ca47f77e0579fcb5a1dcb88218087102c355") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "outlines") (:url . "https://github.com/tarsius/bicycle"))]) (bifocal . [(20200325 539) ((emacs (24 4))) "Split-screen scrolling for comint-mode buffers" single ((:commit . "de8d09b08b0b30714c4f9b98c97e9577d47b9be6") (: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." single ((:commit . "87042230d7f3fe3e9a77fae0dbab7d8f7e7794ad") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "games" "time" "display") (:url . "https://github.com/davep/binclock.el"))]) - (bind-chord . [(20171204 2010) ((bind-key (1 0)) (key-chord (0 6))) "key-chord binding helper for use-package-chords" single ((:commit . "0ad5d9d5d8a61517a207ab04bf69e71c081149eb") (:authors ("Justin Talbott" . "justin@waymondo.com")) (:maintainer "Justin Talbott" . "justin@waymondo.com") (:keywords "convenience" "tools" "extensions") (:url . "https://github.com/waymondo/use-package-chords"))]) - (bind-key . [(20210210 1609) nil "A simple way to manage personal keybindings" single ((:commit . "0ad5d9d5d8a61517a207ab04bf69e71c081149eb") (:authors ("John Wiegley" . "johnw@newartisans.com")) (:maintainer "John Wiegley" . "johnw@newartisans.com") (:keywords "keys" "keybinding" "config" "dotemacs") (:url . "https://github.com/jwiegley/use-package"))]) + (bind-chord . [(20220807 1556) ((bind-key (1 0)) (key-chord (0 6))) "key-chord binding helper for use-package-chords" single ((:commit . "e2d173b1200865a9efd5c2066831a230497582c0") (:authors ("Justin Talbott" . "justin@waymondo.com")) (:maintainer "Justin Talbott" . "justin@waymondo.com") (:keywords "convenience" "tools" "extensions") (:url . "https://github.com/jwiegley/use-package"))]) + (bind-key . [(20220815 1925) nil "A simple way to manage personal keybindings" single ((:commit . "e2d173b1200865a9efd5c2066831a230497582c0") (:authors ("John Wiegley" . "johnw@newartisans.com")) (:maintainer "John Wiegley" . "johnw@newartisans.com") (:keywords "keys" "keybinding" "config" "dotemacs") (:url . "https://github.com/jwiegley/use-package"))]) (bind-map . [(20220108 228) ((emacs (24 3))) "Bind personal keymaps in multiple locations" single ((:commit . "510a24138d8de3b8df0783f1ac493a551fc9bd74") (:authors ("Justin Burkett" . "justin@burkett.cc")) (:maintainer "Justin Burkett" . "justin@burkett.cc") (:url . "https://github.com/justbur/emacs-bind-map"))]) (binder . [(20220429 2055) ((emacs (24 4)) (seq (2 20))) "Global minor mode to facilitate multi-file writing projects" tar ((:commit . "127463a7cb8cc2fa9904d3feb3fca95d2244ddcc") (:authors ("Paul W. Rankin" . "pwr@bydasein.com")) (:maintainer "Paul W. Rankin" . "pwr@bydasein.com") (:keywords "files" "outlines" "wp" "text") (:url . "https://github.com/rnkn/binder"))]) (bing-dict . [(20200216 110) nil "Minimalists' English-Chinese Bing dictionary" tar ((:commit . "1d581aaa9622b34f8fb83af5579fa252aa24cfef") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions") (:url . "https://github.com/cute-jumper/bing-dict.el"))]) @@ -344,16 +344,16 @@ (bmx-mode . [(20210319 620) ((emacs (25 1)) (cl-lib (0 5)) (company (0 9 4)) (dash (2 13 0)) (s (1 12 0))) "Batch Mode eXtras" single ((:commit . "6f008707efe0bb5646f0c1b0d6f57f0a8800e200") (:authors ("Jostein Kjønigsen" . "jostein@gmail.com")) (:maintainer "Jostein Kjønigsen" . "jostein@gmail.com") (:keywords "c" "convenience" "tools") (:url . "http://github.com/josteink/bmx-mode"))]) (bnf-mode . [(20200323 1348) ((cl-lib (0 5)) (emacs (24 3))) "Major mode for editing BNF grammars." tar ((:commit . "d9329dd90e5d4f629295e85898362d9682047898") (:authors ("Serghei Iakovlev" . "egrep@protonmail.ch")) (:maintainer "Serghei Iakovlev" . "egrep@protonmail.ch") (:keywords "languages") (:url . "https://github.com/sergeyklay/bnf-mode"))]) (bnfc . [(20160605 1927) ((emacs (24 3))) "Define context-free grammars for the BNFC tool" single ((:commit . "1b58df1dd0cb9b81900632fb2843a03b94f56fdb") (:authors ("Jacob Mitchell" . "jmitchell@member.fsf.org")) (:maintainer "Jacob Mitchell" . "jmitchell@member.fsf.org") (:keywords "languages" "tools") (:url . "https://github.com/jmitchell/bnfc-mode"))]) - (bog . [(20201030 357) ((cl-lib (0 5))) "Extensions for research notes in Org mode" single ((:commit . "af929c164c4ffaee0c33ba97c06733f0ce9431d4") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "bib" "outlines") (:url . "https://github.com/kyleam/bog"))]) + (bog . [(20201030 357) ((cl-lib (0 5))) "Extensions for research notes in Org mode" single ((:commit . "449c17b5f25513398173f265b272fc1f9178a6e2") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "bib" "outlines") (:url . "https://github.com/kyleam/bog"))]) (bolt-mode . [(20180310 810) ((emacs (24 3))) "Editing support for Bolt language" single ((:commit . "85a5a752bfbebb4aed884326c25db64c000e9934") (:authors ("Mikhail Pontus" . "mpontus@gmail.com")) (:maintainer "Mikhail Pontus" . "mpontus@gmail.com") (:keywords "languages") (:url . "https://github.com/mpontus/bolt-mode"))]) (bongo . [(20201002 1020) ((cl-lib (0 5)) (emacs (24 1))) "play music with Emacs" tar ((:commit . "9e9629090262bba6d0003dabe5a375e47a4477f1"))]) (bonjourmadame . [(20170919 1134) nil "Say \"Hello ma'am!\"" single ((:commit . "d3df185fce78aefa689fded8e56a654f0fde4ac0"))]) - (boogie-friends . [(20220726 1637) ((cl-lib (0 5)) (dash (2 10 0)) (flycheck (0 23)) (yasnippet (0 9 0 1)) (company (0 8 12))) "A collection of programming modes for Boogie, Dafny, and Z3 (SMTLIB v2)." tar ((:commit . "0a736ab6e9669e755141db5afefbfe8cd5df6961") (:authors ("Clément Pit-Claudel" . "clement.pitclaudel@live.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "convenience" "languages") (:url . "https://github.com/boogie-org/boogie-friends/"))]) + (boogie-friends . [(20220815 1808) ((cl-lib (0 5)) (dash (2 10 0)) (flycheck (0 23)) (yasnippet (0 9 0 1)) (company (0 8 12))) "A collection of programming modes for Boogie, Dafny, and Z3 (SMTLIB v2)." tar ((:commit . "986458f66a4f306e4697b71806c9a797940cf92c") (:authors ("Clément Pit-Claudel" . "clement.pitclaudel@live.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "convenience" "languages") (:url . "https://github.com/boogie-org/boogie-friends/"))]) (bookmark-in-project . [(20220708 211) ((emacs (27 1))) "Bookmark access within a project" single ((:commit . "ccffde03b57d9b16fff59188d8e4090bf64d46d6") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "convenience") (:url . "https://codeberg.org/ideasman42/emacs-bookmark-in-project"))]) (bookmark-view . [(20220403 2204) ((emacs (27 1))) "Bookmark views" single ((:commit . "0d40ac67f53b7fa75fe65c38a5ef65701ce4c3da") (:authors ("Daniel Mendler")) (:maintainer "Daniel Mendler") (:url . "https://github.com/minad/bookmark-view"))]) (bool-flip . [(20161215 1539) ((emacs (24 3))) "flip the boolean under the point" single ((:commit . "f58a9a7b9ab875bcfbd57c8262697ae404eb4485") (:authors ("Michael Brandt" . "michaelbrandt5@gmail.com")) (:maintainer "Michael Brandt" . "michaelbrandt5@gmail.com") (:keywords "boolean" "convenience" "usability") (:url . "http://github.com/michaeljb/bool-flip/"))]) - (boon . [(20220502 1850) ((emacs (26 1)) (dash (2 12 0)) (expand-region (0 10 0)) (multiple-cursors (1 3 0))) "Ergonomic Command Mode for Emacs." tar ((:commit . "db7b6083d390e3febf82f9af5782e1a36d30093c"))]) - (borg . [(20220803 1958) ((emacs (26)) (epkg (3 3 3)) (magit (3 3 0))) "Assimilate Emacs packages as Git submodules" tar ((:commit . "47122b6559f4990c03e0b26d6e123e7f9f46525b") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/emacscollective/borg"))]) + (boon . [(20220822 1348) ((emacs (26 1)) (dash (2 12 0)) (expand-region (0 10 0)) (multiple-cursors (1 3 0))) "Ergonomic Command Mode for Emacs." tar ((:commit . "ccf3e5c0c92f0f73d20f14cad0337e4cb0907a9b"))]) + (borg . [(20220812 1139) ((emacs (26)) (epkg (3 3 3)) (magit (3 3 0))) "Assimilate Emacs packages as Git submodules" tar ((:commit . "d3cdc176ee72e3e3feac6dd6b527cff5dcf788eb") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/emacscollective/borg"))]) (borland-blue-theme . [(20160117 1321) ((emacs (24 1))) "Blue/yellow theme based on old DOS Borland/Turbo C IDE" single ((:commit . "db74eefebbc89d3c62575f8f50b319e87b4a3470") (:authors ("Alexey Veretennikov ")) (:maintainer "Alexey Veretennikov ") (:keywords "themes") (:url . "http://github.com/fourier/borland-blue-theme"))]) (boron-theme . [(20170808 1308) ((emacs (24 0))) "an Emacs 24 theme based on Boron (tmTheme)" single ((:commit . "87ae1a765e07429fec25d2f29b004f84b52d2e0a") (:authors ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) (boxquote . [(20220105 1515) ((cl-lib (0 5))) "Quote text with a semi-box." single ((:commit . "67775ce80886b776efedceb31cdbacec1e26678e") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "quoting") (:url . "https://github.com/davep/boxquote.el"))]) @@ -363,7 +363,7 @@ (bracketed-paste . [(20160407 2348) ((emacs (24 3))) "bracketed paste mode support within emacs -nw" single ((:commit . "843ce3bbb63d560face889e13a57a2f7543957d5") (:authors ("Takeshi Banse" . "takebi@laafc.net")) (:maintainer "Takeshi Banse" . "takebi@laafc.net") (:keywords "terminals"))]) (brainfuck-mode . [(20150113 842) ((langdoc (20130601 1450))) "Brainfuck mode for Emacs" single ((:commit . "36e69552bb3b97a4f888d362c59845651bd0d492") (:authors ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainer "Tomoya Tanjo" . "ttanjo@gmail.com") (:keywords "brainfuck" "langdoc") (:url . "https://github.com/tom-tan/brainfuck-mode/"))]) (brazilian-holidays . [(20210302 107) ((emacs (26))) "Brazilian holidays" single ((:commit . "68811fd5f3e9d9c0572995c3ca46ead2c35eb421") (:authors ("Jaguaraquem A. Reinaldo" . "jaguar.adler@gmail.com")) (:maintainer "Jaguaraquem A. Reinaldo" . "jaguar.adler@gmail.com") (:keywords "calendar" "holidays" "brazilian") (:url . "https://github.com/jadler/brazilian-holidays"))]) - (brf . [(20220710 2116) ((fringe-helper (0 1 1)) (emacs (24 3))) "Brf-mode provides features from the legendary editor Brief" tar ((:commit . "1aaf5b237a2bd550dceff6d76c56e4bf3365ca25") (:authors ("Mike Woolley" . "mike@bulsara.com")) (:maintainer "Mike Woolley" . "mike@bulsara.com") (:keywords "brief" "crisp" "emulations") (:url . "https://bitbucket.org/MikeWoolley/brf-mode"))]) + (brf . [(20220807 1438) ((fringe-helper (0 1 1)) (emacs (24 3))) "Brf-mode provides features from the legendary editor Brief" tar ((:commit . "8f86b980d450e44cd29d24e66c30ff079c6b9982") (:authors ("Mike Woolley" . "mike@bulsara.com")) (:maintainer "Mike Woolley" . "mike@bulsara.com") (:keywords "brief" "crisp" "emulations") (:url . "https://bitbucket.org/MikeWoolley/brf-mode"))]) (brightscript-mode . [(20200321 2126) ((emacs (26 3))) "Major mode for editing Brightscript files" single ((:commit . "71c555c2e254629c365e6fc44c2fc4d5b6d0ae8b") (:authors ("Daniel Mircea" . "daniel@viseztrance.com")) (:maintainer nil . "daniel@viseztrance.com") (:keywords "languages") (:url . "https://github.com/viseztrance/brightscript-mode"))]) (broadcast . [(20151205 212) ((emacs (24 4))) "Links buffers together for simultaneous editing." single ((:commit . "f6f9cd2e0e3f8c31d6b8e7446c27eb0e50b25f16") (:authors ("Russell Black" . "killdash9@github")) (:maintainer "Russell Black" . "killdash9@github") (:keywords "convenience" "frames" "link" "cursors") (:url . "https://github.com/killdash9/broadcast.el"))]) (browse-at-remote . [(20210603 802) ((f (0 17 2)) (s (1 9 0)) (cl-lib (0 5))) "Open github/gitlab/bitbucket/stash/gist/phab/sourcehut page from Emacs" single ((:commit . "cef26f2c063f2473af42d0e126c8613fe2f709e4") (:authors ("Rustem Muslimov" . "r.muslimov@gmail.com")) (:maintainer "Rustem Muslimov" . "r.muslimov@gmail.com") (:keywords "github" "gitlab" "bitbucket" "gist" "stash" "phabricator" "sourcehut" "pagure") (:url . "https://github.com/rmuslimov/browse-at-remote"))]) @@ -377,15 +377,15 @@ (bubbleberry-theme . [(20141017 944) ((emacs (24 1))) "A theme based on LightTable for Emacs24" single ((:commit . "22e9adf4586414024e4592972022ec297321b320") (:authors ("Jason Milkins" . "jasonm23@gmail.com") ("Gaurav Giri github.com/grvgr")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "https://github.com/jasonm23/emacs-bubbleberry-theme"))]) (buckwalter . [(20191119 1950) nil "Write arabic using Buckwalter transliteration" single ((:commit . "b8c0c2170c7113b515477b1bb39c58d22aad67e1") (:authors ("Joe HAKIM RAHME" . "joehakimrahme@gmail.com")) (:maintainer "Joe HAKIM RAHME" . "joehakimrahme@gmail.com") (:keywords "arabic" "transliteration" "i18n") (:url . "https://github.com/joehakimrahme/buckwalter-arabic"))]) (buffer-buttons . [(20150106 1439) nil "Define, save, and load code-safe buttons in files for emacs" single ((:commit . "2feb8494fa7863b98256bc85da670d74a3a8a975") (:authors ("Ryan Pavlik" . "rpavlik@gmail.com")) (:maintainer "Ryan Pavlik" . "rpavlik@gmail.com") (:url . "https://github.com/rpav/buffer-buttons"))]) - (buffer-env . [(20220728 1835) ((emacs (27 1)) (compat (28 1))) "Buffer-local process environments" single ((:commit . "7c176d043445ea94fe924a715158c25b91ec4776") (:authors ("Augusto Stoffel" . "arstoffel@gmail.com")) (:maintainer "Augusto Stoffel" . "arstoffel@gmail.com") (:keywords "processes" "tools") (:url . "https://github.com/astoff/buffer-env"))]) + (buffer-env . [(20220811 1159) ((emacs (27 1)) (compat (28 1))) "Buffer-local process environments" single ((:commit . "9ccfbd07c4b2e2af38fe315ce7e3d905298d2fdd") (:authors ("Augusto Stoffel" . "arstoffel@gmail.com")) (:maintainer "Augusto Stoffel" . "arstoffel@gmail.com") (:keywords "processes" "tools") (:url . "https://github.com/astoff/buffer-env"))]) (buffer-flip . [(20220718 10) ((cl-lib (0 5))) "Cycle through buffers like Alt-Tab in Windows" single ((:commit . "dda0cbcd202cdadf322942f9637a11ed92525756") (:authors ("Russell Black" . "killdash9@github")) (:maintainer "Russell Black" . "killdash9@github") (:keywords "convenience") (:url . "https://github.com/killdash9/buffer-flip.el"))]) (buffer-manage . [(20211122 1957) ((emacs (26 1)) (choice-program (0 13)) (dash (2 17 0))) "Manage buffers" tar ((:commit . "819bbfd9ae2f028361f484bc3b60d751623a2df5") (:authors ("Paul Landes")) (:maintainer "Paul Landes") (:keywords "internal" "maint") (:url . "https://github.com/plandes/buffer-manage"))]) (buffer-move . [(20220512 755) ((emacs (24 1))) "easily swap buffers" single ((:commit . "e7800b3ab1bd76ee475ef35507ec51ecd5a3f065") (:keywords "convenience") (:url . "https://github.com/lukhas/buffer-move/"))]) (buffer-ring . [(20220120 124) ((emacs (25 1)) (dynaring (0 3)) (s (1 12 0)) (ht (2 0))) "Rings and tori for buffer navigation" single ((:commit . "177d67238c4d126a0270585e21c0f03ae750ca2a") (:authors ("Mike Mattie" . "codermattie@gmail.com") ("Sid Kasivajhula" . "sid@countvajhula.com")) (:maintainer "Sid Kasivajhula" . "sid@countvajhula.com") (:url . "https://github.com/countvajhula/buffer-ring"))]) - (buffer-sets . [(20170718 340) ((cl-lib (0 5))) "Sets of Buffers for Buffer Management" single ((:commit . "bc84c2f79a33609cccf3c996101125859b2e26ab") (:authors ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Samuel W. Flint" . "swflint@flintfam.org") (:keywords "buffer-management") (:url . "http://github.com/swflint/buffer-sets"))]) + (buffer-sets . [(20220821 1413) ((cl-lib (0 5))) "Sets of Buffers for Buffer Management" single ((:commit . "d0e4c86960d0a36cabdc34d860722fd304646a88") (:authors ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Samuel W. Flint" . "swflint@flintfam.org") (:keywords "buffer-management") (:url . "http://github.com/swflint/buffer-sets"))]) (buffer-utils . [(20140512 1400) nil "Buffer-manipulation utility functions" single ((:commit . "685b13457e3a2085b7584e41365d2aa0779a1b6f") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "extensions") (:url . "http://github.com/rolandwalker/buffer-utils"))]) (buffer-watcher . [(20170913 839) ((f (0 16 2)) (cl-lib (0 5))) "Easily run shell scripts per filetype/directory when a buffer is saved" single ((:commit . "b32c67c8a5d724257d759f4c903d0dedc32246ef") (:authors ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr"))]) - (buffer-wrap . [(20220704 646) ((emacs (24 4))) "Wrap the beginning and the end of buffer" single ((:commit . "8be8508ad5570b1487d9d2fc1b5aa71558f2d0f5") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "buffer" "tool" "wrap") (:url . "https://github.com/jcs-elpa/buffer-wrap"))]) + (buffer-wrap . [(20220704 646) ((emacs (24 4))) "Wrap the beginning and the end of buffer" single ((:commit . "b0c18216904581d6223e76626821a266355830da") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "buffer" "tool" "wrap") (:url . "https://github.com/jcs-elpa/buffer-wrap"))]) (bufler . [(20220726 1658) ((emacs (26 3)) (dash (2 18)) (f (0 17)) (pretty-hydra (0 2 2)) (magit-section (0 1)) (map (2 1))) "Group buffers into workspaces with programmable rules" tar ((:commit . "5e8f02c3a454d6d43c18851023d6ac6ae470c31f") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "convenience") (:url . "https://github.com/alphapapa/bufler.el"))]) (bufshow . [(20130726 1838) ((emacs (24 1))) "A simple presentation tool for Emacs." single ((:commit . "d60a554e7239e6f7520d9c3436d5ecdbc9cf6957") (:authors ("Peter Jones" . "pjones@pmade.com")) (:maintainer "Peter Jones" . "pjones@pmade.com") (:url . "https://github.com/pjones/bufshow"))]) (bug-reference-github . [(20200206 2158) nil "Set `bug-reference-url-format' in Github repos" tar ((:commit . "c9512a010f19633e69f1d4b1597eff7048b21112") (:authors ("Arne Jørgensen" . "arne@arnested.dk")) (:maintainer "Arne Jørgensen" . "arne@arnested.dk") (:keywords "programming" "tools") (:url . "https://github.com/arnested/bug-reference-github"))]) @@ -401,7 +401,7 @@ (buster-snippets . [(20151125 1010) ((yasnippet (0 8 0))) "Yasnippets for the Buster javascript testing framework" tar ((:commit . "bb8769dae132659858e74d52f3f4e8790399423a") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "snippets"))]) (busybee-theme . [(20170719 928) nil "port of vim's mustang theme" single ((:commit . "66b2315b030582d0ebee605cf455d386d8c30fcd") (:authors ("martin haesler")) (:maintainer "martin haesler") (:url . "http://github.com/mswift42/busybee-theme"))]) (butler . [(20210928 230) ((deferred (0 3 2)) (json (1 2)) (emacs (24))) "Emacs client for Jenkins" tar ((:commit . "10943ccdf2030187b2f7bd97337d78acb7fd31c9") (:authors ("Ashton Kemerling" . "ashtonkemerling@gmail.com")) (:maintainer "Ashton Kemerling" . "ashtonkemerling@gmail.com") (:keywords "jenkins" "hudson" "ci") (:url . "http://www.github.com/AshtonKem/Butler.git"))]) - (buttercup . [(20220619 2214) ((emacs (24 3))) "Behavior-Driven Emacs Lisp Testing" tar ((:commit . "42df1faa653f2765941f478167dafac059dc3a57") (:authors ("Jorgen Schaefer" . "contact@jorgenschaefer.de")) (:maintainer "Ola Nilsson" . "ola.nilsson@gmail.com") (:url . "https://github.com/jorgenschaefer/emacs-buttercup"))]) + (buttercup . [(20220814 2150) ((emacs (24 3))) "Behavior-Driven Emacs Lisp Testing" tar ((:commit . "e2b77ac49cc61c32566f22b84ba304a5703ff7b3") (:authors ("Jorgen Schaefer" . "contact@jorgenschaefer.de")) (:maintainer "Ola Nilsson" . "ola.nilsson@gmail.com") (:url . "https://github.com/jorgenschaefer/emacs-buttercup"))]) (buttercup-junit . [(20190802 2258) ((emacs (24 3)) (buttercup (1 15))) "JUnit reporting for Buttercup" single ((:commit . "3ae4f84813c9e04e03a6e703990ca998b62b6deb") (:authors ("Ola Nilsson" . "ola.nilsson@gmail.com")) (:maintainer "Ola Nilsson" . "ola.nilsson@gmail.com") (:keywords "tools" "test" "unittest" "buttercup" "ci") (:url . "https://bitbucket.org/olanilsson/buttercup-junit"))]) (button-lock . [(20200309 1323) nil "Clickable text defined by regular expression" single ((:commit . "9afe0f4d05910b0cccc94cb6d4d880119f3b0528") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "mouse" "button" "hypermedia" "extensions") (:url . "http://github.com/rolandwalker/button-lock"))]) (buttons . [(20201123 2333) ((cl-lib (0 3))) "Define and visualize hierarchies of keymaps" single ((:commit . "de41b48244574a13000c4289fdb4216a2b0490ff") (:authors ("Ernesto Alfonso")) (:maintainer nil . "(concat \"erjoalgo\" \"@\" \"gmail\" \".com\")") (:keywords "keymap" "template" "snippet") (:url . "http://github.com/erjoalgo/emacs-buttons"))]) @@ -418,6 +418,7 @@ (cakecrumbs . [(20180929 139) ((emacs (24 4))) "Show parents on header for HTML/Jade/Sass/Stylus" single ((:commit . "cf8c1df885eee004602f73c4f841301e200e5850") (:authors ("ono hiroko ")) (:maintainer "ono hiroko ") (:keywords "languages" "html" "jade" "pug" "sass" "scss" "stylus") (:url . "https://github.com/kuanyui/cakecrumbs.el"))]) (cal-china-x . [(20200924 1837) ((cl-lib (0 5))) "Chinese localization, lunar/horoscope/zodiac info and more..." tar ((:commit . "94005e678a1d2522b7a00299779f40c5c77286b8") (:authors ("William Xu" . "william.xwl@gmail.com")) (:maintainer "William Xu" . "william.xwl@gmail.com") (:url . "https://github.com/xwl/cal-china-x"))]) (calc-at-point . [(20210219 1252) ((emacs (26)) (dash (2 18 0))) "Perform calculations at point or over selection" single ((:commit . "0c1a9e94b519b0edb0abcbacdf6101eea2f2a524") (:authors ("Sebastian Wålinder" . "s.walinder@gmail.com")) (:maintainer "Sebastian Wålinder" . "s.walinder@gmail.com") (:keywords "convenience") (:url . "https://github.com/walseb/calc-at-point"))]) + (calc-prog-utils . [(20220820 1855) ((emacs (24 1))) "Calc programmers utilities" single ((:commit . "190acfda56660a2d75df2d9eac5b14edaccccd80") (:authors ("Jesse Millwood")) (:maintainer "Jesse Millwood") (:keywords "tools" "convenience") (:url . "https://github.com/Jesse-Millwood/calc-prog"))]) (calendar-norway . [(20220211 1129) nil "Norwegian calendar" single ((:commit . "0db0ea63365f4ff5f7d18fb8335fa88af194a2cc") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "calendar" "norwegian" "localization"))]) (calfw . [(20180118 45) nil "Calendar view framework on Emacs" single ((:commit . "03abce97620a4a7f7ec5f911e669da9031ab9088") (:authors ("SAKURAI Masashi ")) (:maintainer "SAKURAI Masashi ") (:keywords "calendar") (:url . "https://github.com/kiwanami/emacs-calfw"))]) (calfw-cal . [(20170320 1206) nil "calendar view for emacs diary" single ((:commit . "03abce97620a4a7f7ec5f911e669da9031ab9088") (:authors ("SAKURAI Masashi ")) (:maintainer "SAKURAI Masashi ") (:keywords "calendar"))]) @@ -431,12 +432,12 @@ (camcorder . [(20190317 2138) ((emacs (24)) (names (20150000)) (cl-lib (0 5))) "Record screencasts in gif or other formats." single ((:commit . "b11ca61491a27681bb3131b72b51c105fd996bed") (:authors ("Artur Malabarba" . "bruce.connor.am@gmail.com")) (:maintainer "Artur Malabarba" . "bruce.connor.am@gmail.com") (:keywords "multimedia" "screencast") (:url . "http://github.com/Bruce-Connor/camcorder.el"))]) (caml . [(20220503 1742) ((emacs (24 3))) "Caml mode for GNU Emacs" tar ((:commit . "f2f170f46b758341d96385986e8a93b9b4d248f1") (:authors ("Jacques Garrigue" . "garrigue@kurims.kyoto-u.ac.jp") ("Ian T Zimmerman" . "itz@rahul.net") ("Damien Doligez" . "damien.doligez@inria.fr")) (:maintainer "Christophe Troestler" . "Christophe.Troestler@umons.ac.be") (:keywords "ocaml") (:url . "https://github.com/ocaml/caml-mode"))]) (cangjie . [(20211201 2307) ((emacs (24 4)) (s (1 12 0)) (dash (2 14 1)) (f (0 2 0))) "Retrieve cangjie code for han characters" tar ((:commit . "87408d79b73a69194842a8848de6d7708e98c3a4") (:keywords "convenience" "writing") (:url . "https://github.com/kisaragi-hiu/cangjie.el"))]) - (cape . [(20220719 646) ((emacs (27 1))) "Completion At Point Extensions" tar ((:commit . "6cfc9574358c98f24c9f58e7004d6753ddb27a89") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:url . "https://github.com/minad/cape"))]) - (capnp-mode . [(20210707 2310) nil "Major mode for editing Capn' Proto Files" single ((:commit . "52028fe3e0dd83e82857069bce48ea890f5aad4c") (:authors ("Brian Taylor" . "el.wubo@gmail.com")) (:maintainer "Brian Taylor" . "el.wubo@gmail.com") (:url . "https://github.com/capnproto/capnproto"))]) + (cape . [(20220819 1710) ((emacs (27 1))) "Completion At Point Extensions" tar ((:commit . "f360464007386b36952b4c3ba0a977f056d0ee19") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:url . "https://github.com/minad/cape"))]) + (capnp-mode . [(20210707 2310) nil "Major mode for editing Capn' Proto Files" single ((:commit . "577e258e754426ffb731af139de745bce8b44312") (:authors ("Brian Taylor" . "el.wubo@gmail.com")) (:maintainer "Brian Taylor" . "el.wubo@gmail.com") (:url . "https://github.com/capnproto/capnproto"))]) (capture . [(20130828 1644) nil "screencasting with \"avconv\" or \"ffmpeg\"" tar ((:commit . "1bb26060311da76767f70096218313fc93b0c806") (:authors ("Sergey Pashinin ")) (:maintainer "Sergey Pashinin "))]) (carbon-now-sh . [(20220701 332) ((emacs (24 4))) "https://carbon.now.sh integration." single ((:commit . "e66f2e43e288f35ad9075f5fc84d59ad348efc88") (:authors ("Vitalii Elenhaupt")) (:maintainer "Vitalii Elenhaupt") (:keywords "convenience") (:url . "https://github.com/veelenga/carbon-now-sh.el"))]) - (cardano-tx . [(20220718 1440) ((emacs (27 1)) (f (0 20 0)) (yasnippet (0 14 0)) (yaml-mode (0 0 15)) (yaml (0 1 0)) (helm (3 6 2)) (cbor (0 2 0)) (bech32 (0 1 1)) (readable-numbers (0 1 0)) (emacsql (3 0 0)) (emacsql-sqlite3 (1 0 2))) "Cardano transaction editor" tar ((:commit . "40d04a0baf5c3d1087b18cc03595c573a1b5891d") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) - (cardano-wallet . [(20220718 1434) ((emacs (27 1)) (yaml (0 1 0)) (dash (2 19 0)) (yaml-mode (0 0 15)) (readable-numbers (0 1 0)) (cardano-tx (0 1 0))) "Interact with cardano wallet" single ((:commit . "40d04a0baf5c3d1087b18cc03595c573a1b5891d") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) + (cardano-tx . [(20220718 1440) ((emacs (27 1)) (f (0 20 0)) (yasnippet (0 14 0)) (yaml-mode (0 0 15)) (yaml (0 1 0)) (helm (3 6 2)) (cbor (0 2 0)) (bech32 (0 1 1)) (readable-numbers (0 1 0)) (emacsql (3 0 0)) (emacsql-sqlite3 (1 0 2))) "Cardano transaction editor" tar ((:commit . "5e1bf8b8ffa4c75bece7a93feab9858f0e7d676e") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) + (cardano-wallet . [(20220718 1434) ((emacs (27 1)) (yaml (0 1 0)) (dash (2 19 0)) (yaml-mode (0 0 15)) (readable-numbers (0 1 0)) (cardano-tx (0 1 0))) "Interact with cardano wallet" single ((:commit . "5e1bf8b8ffa4c75bece7a93feab9858f0e7d676e") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) (cargo . [(20220717 1129) ((emacs (24 3)) (markdown-mode (2 4))) "Emacs Minor Mode for Cargo, Rust's Package Manager." tar ((:commit . "7bd3682456bcd666ae550224487e63c0106a7cf9") (:authors ("Kevin W. van Rooijen")) (:maintainer "Kevin W. van Rooijen") (:keywords "tools"))]) (cargo-mode . [(20210605 1003) ((emacs (25 1))) "Cargo Major Mode. Cargo is the Rust package manager" single ((:commit . "b98ea60ddec30eac174012671ee09e125748a193") (:authors ("Ayrat Badykov" . "ayratin555@gmail.com")) (:maintainer "Ayrat Badykov" . "ayratin555@gmail.com") (:keywords "tools") (:url . "https://github.com/ayrat555/cargo-mode"))]) (cargo-transient . [(20220730 154) ((emacs (28 1))) "A transient UI for Cargo, Rust's package manager" single ((:commit . "1755da9c1cedde6026a0a9e7fd0fe98ed2d30dbb") (:authors ("Peter Stuart" . "peter@peterstuart.org")) (:maintainer "Peter Stuart" . "peter@peterstuart.org") (:url . "https://github.com/peterstuart/cargo-transient"))]) @@ -450,13 +451,13 @@ (catmacs . [(20170826 1157) ((emacs (24))) "Simple CAT interface for Yaesu Transceivers." single ((:commit . "65d3e0563abe6ff9577202cf2278074d4130fbdd") (:authors ("Frank Singleton" . "b17flyboy@gmail.com")) (:maintainer "Frank Singleton" . "b17flyboy@gmail.com") (:keywords "comm" "hardware") (:url . "https://bitbucket.org/pymaximus/catmacs"))]) (catppuccin-theme . [(20220515 435) ((emacs (25 1))) "Catppuccin Theme" single ((:commit . "715935a78d3acef9da52b3a178d5703c162829c3") (:authors ("pspiagicw")) (:maintainer "pspiagicw" . "pspiagicw@gmail.com") (:url . "https://github.com/catppuccin/emacs"))]) (cbm . [(20171116 1240) ((cl-lib (0 5))) "Switch to similar buffers." single ((:commit . "5b41c936ba9f6d170309a85ffebc9939c1050b31") (:authors ("Lukas Fürmetz" . "fuermetz@mailbox.org")) (:maintainer "Lukas Fürmetz" . "fuermetz@mailbox.org") (:keywords "buffers") (:url . "http://github.com/akermu/cbm.el"))]) - (cbor . [(20220802 1008) ((emacs (25 1)) (dash (2 19 0))) "CBOR utilities" single ((:commit . "40d04a0baf5c3d1087b18cc03595c573a1b5891d") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) + (cbor . [(20220810 1600) ((emacs (25 1)) (dash (2 19 0))) "CBOR utilities" single ((:commit . "5e1bf8b8ffa4c75bece7a93feab9858f0e7d676e") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) (cc-cedict . [(20210814 819) ((emacs (26 1))) "Interface to CC-CEDICT (a Chinese-English dictionary)" single ((:commit . "03fbe7d1589d36f627ef9fe7b86f9fe6f623cbb3") (:authors ("Xu Chunyang")) (:maintainer "Xu Chunyang") (:url . "https://github.com/xuchunyang/cc-cedict.el"))]) (ccc . [(20210501 820) nil "buffer local cursor color control library" single ((:commit . "3ed86d42717ab2a54ec8de6ab32d552dc0a4c3b0") (:authors ("Masatake YAMATO" . "masata-y@is.aist-nara.ac.jp")) (:maintainer "SKK Development Team") (:keywords "cursor") (:url . "https://github.com/skk-dev/ddskk"))]) (ccls . [(20200820 308) ((emacs (25 1)) (lsp-mode (6 3 1)) (dash (2 14 1))) "ccls client for lsp-mode" tar ((:commit . "ae74a39303457a5e6976dd1c6816cde97d357a0d") (:authors ("Tobias Pisani, Fangrui Song")) (:maintainer "Tobias Pisani, Fangrui Song") (:keywords "languages" "lsp" "c++") (:url . "https://github.com/MaskRay/emacs-ccls"))]) (cd-compile . [(20141108 1957) nil "run compile in a specific directory" single ((:commit . "10284ccae86afda4a37b09ba90acd1e2efedec9f") (:authors ("Jamie Nicol" . "jamie@thenicols.net")) (:maintainer "Jamie Nicol" . "jamie@thenicols.net"))]) (cdb . [(20200904 1431) nil "constant database (cdb) reader for Emacs Lisp" single ((:commit . "3ed86d42717ab2a54ec8de6ab32d552dc0a4c3b0") (:authors ("Yusuke Shinyama ")) (:maintainer "SKK Development Team") (:keywords "cdb") (:url . "https://github.com/skk-dev/ddskk"))]) - (cdlatex . [(20220701 1236) ((auctex (9 9))) "Fast input methods for LaTeX environments and math" single ((:commit . "4c392765e123f9c5481e7d113486a6acf720ab13") (:authors ("Carsten Dominik" . "carsten.dominik@gmail.com")) (:maintainer "Carsten Dominik" . "carsten.dominik@gmail.com") (:keywords "tex"))]) + (cdlatex . [(20220823 747) nil "Fast input methods for LaTeX environments and math" single ((:commit . "6f734db8ab87f11dffb77c33a445140baeb723c4") (:authors ("Carsten Dominik" . "carsten.dominik@gmail.com")) (:maintainer "Carsten Dominik" . "carsten.dominik@gmail.com") (:keywords "tex"))]) (cdnjs . [(20161031 1522) ((dash (2 13 0)) (deferred (0 4)) (f (0 17 2)) (pkg-info (0 5))) "A front end for http://cdnjs.com" single ((:commit . "ce19880d3ec3d81e6c665d0b1dfea99cc7a3f908") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainer "Yasuyuki Oka" . "yasuyk@gmail.com") (:keywords "tools") (:url . "https://github.com/yasuyk/cdnjs.el"))]) (cedit . [(20200816 526) nil "paredit-like commands for c-like languages" single ((:commit . "cb38316903e6cfa8b8c978defa7e1dafcd4e0c12") (:authors ("zk_phi")) (:maintainer "zk_phi") (: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?" single ((:commit . "51197d74f5eaa8ae09144af7663a2f4277f07d16") (:authors ("ardumont" . "eniotna.t@gmail.com")) (:maintainer "ardumont" . "eniotna.t@gmail.com") (:keywords "celery" "convenience") (:url . "https://github.com/ardumont/emacs-celery"))]) @@ -468,13 +469,13 @@ (cerbere . [(20181113 1641) ((pkg-info (0 5))) "Unit testing in Emacs for several programming languages" tar ((:commit . "c667c165d9c1657f13d2d46f09ba21b61f9402cc") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "python" "go" "php" "phpunit" "elisp" "ert" "tests" "tdd") (:url . "https://github.com/nlamirault/cerbere"))]) (cern-root-mode . [(20220531 1304) ((emacs (26 1))) "Major-mode for running C++ code with ROOT" single ((:commit . "0227ab66f67276d3142a95cfc858bf621f501346") (:authors ("Jay Morgan" . "jay@morganwastaken.com")) (:maintainer "Jay Morgan" . "jay@morganwastaken.com") (:keywords "languages" "tools") (:url . "https://github.com/jaypmorgan/cern-root-mode"))]) (ceylon-mode . [(20180606 1324) ((emacs (25))) "Major mode for editing Ceylon source code" single ((:commit . "948515672bc596dc118e8e3ede3ede5ec6a3c95a") (:authors ("Lucas Werkmeister" . "mail@lucaswerkmeister.de")) (:maintainer "Lucas Werkmeister" . "mail@lucaswerkmeister.de") (:keywords "languages" "ceylon") (:url . "https://github.com/lucaswerkmeister/ceylon-mode"))]) - (cfengine-code-style . [(20171115 2108) nil "C code style for CFEngine project." single ((:commit . "fce72aa0bd7f98522ab08ae8ee92f08fc470eae4") (:authors ("Mikhail Gusarov" . "mikhail.gusarov@cfengine.com")) (:maintainer "Mikhail Gusarov" . "mikhail.gusarov@cfengine.com") (:url . "https://github.com/cfengine/core"))]) + (cfengine-code-style . [(20171115 2108) nil "C code style for CFEngine project." single ((:commit . "c854fca2bef6929487f5dc828d6a08529f69f47a") (:authors ("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" single ((:commit . "b6ab2a28e64ef06f281ec74cfe3114e450644dfa") (:authors ("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" single ((:commit . "2de315abddb6af088a2346e142cc305889dcd775") (:authors ("Andrew Myers" . "am2605@gmail.com")) (:maintainer "Andrew Myers" . "am2605@gmail.com") (:url . "https://github.com/am2605/cfml-mode"))]) (cfn-mode . [(20220221 1029) ((emacs (26 0)) (f (0 20 0)) (s (1 12 0)) (yaml-mode (0 0 13))) "AWS cloudformation mode" tar ((:commit . "4cf56affe3035fda364109836e26499431095185") (:authors ("William Orr" . "will@worrbase.com")) (:maintainer "William Orr" . "will@worrbase.com") (:keywords "convenience" "languages" "tools") (:url . "https://gitlab.com/worr/cfn-mode"))]) (cframe . [(20201222 1930) ((emacs (26)) (buffer-manage (0 11)) (dash (2 17 0))) "Customize a frame and fast switch size and positions" single ((:commit . "38544521e82befc06e397123a118dd96dda2c6b6") (:authors ("Paul Landes")) (:maintainer "Paul Landes") (: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" single ((:commit . "f3a21f237b2a54e6b9f8a420a9da42b4f0a63121") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/cfrs"))]) - (cg . [(20220318 1007) ((emacs (26 1))) "Major mode for editing Constraint Grammar files" single ((:commit . "2b930905c23f1cd2fcf3ecad558fde5b708ea1a8") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "languages") (:url . "https://visl.sdu.dk/constraint_grammar.html"))]) + (cg . [(20220318 1007) ((emacs (26 1))) "Major mode for editing Constraint Grammar files" single ((:commit . "86e04b7182b1205769b03281e56d4f2d53e9fee9") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "languages") (:url . "https://visl.sdu.dk/constraint_grammar.html"))]) (challenger-deep-theme . [(20210120 941) ((emacs (24))) "challenger-deep Theme" single ((:commit . "2a799259406a8b96a688873093ffab6630a3ad3b") (:authors ("MaxSt")) (:maintainer "MaxSt") (:url . "https://github.com/challenger-deep-theme/emacs"))]) (change-inner . [(20210126 1456) ((expand-region (0 7))) "Change contents based on semantic units" single ((:commit . "42cad58aed2caec260f8e8ff61f78a7d3db72d1b") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "convenience" "extensions"))]) (chapel-mode . [(20210513 457) ((emacs (25 1)) (hydra (0 15 0))) "A major mode for the Chapel programming language" single ((:commit . "39fd24bb7cf44808200354ac0496be4fc4fddd9a") (:keywords "chapel" "chpl" "programming" "languages") (:url . "https://github.com/damon-kwok/chapel-mode"))]) @@ -506,21 +507,22 @@ (chronos . [(20150602 1529) nil "multiple simultaneous countdown / countup timers" tar ((:commit . "b360d9dae57aa553cf2a14ffa0756a51ad71de09") (:authors ("David Knight" . "dxknight@opmbx.org")) (:maintainer "David Knight" . "dxknight@opmbx.org") (:keywords "calendar") (:url . "http://github.com/dxknight/chronos"))]) (chruby . [(20180114 1652) ((cl-lib (0 5))) "Emacs integration for chruby" single ((:commit . "42bc6d521f832eca8e2ba210f30d03ad5529788f") (:authors ("Arne Brasseur" . "arne@arnebrasseur.net")) (:maintainer "Arne Brasseur" . "arne@arnebrasseur.net") (:keywords "languages") (:url . "https://github.com/plexus/chruby.el"))]) (chyla-theme . [(20180302 1658) nil "chyla.org - green color theme." single ((:commit . "ae5e7ecace2ab474151eb0ac5ef07fba2dc32f8a") (:authors ("Adam Chyła" . "adam@chyla.org")) (:maintainer "Adam Chyła" . "adam@chyla.org") (:url . "https://github.com/chyla/ChylaThemeForEmacs"))]) - (cider . [(20220731 522) ((emacs (26)) (clojure-mode (5 15 1)) (parseedn (1 0 6)) (queue (0 2)) (spinner (1 7)) (seq (2 22)) (sesman (0 3 2))) "Clojure Interactive Development Environment that Rocks" tar ((:commit . "21ccc40d8e50b9b74ac09d8d31374211c92b27e1") (: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")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "cider") (:url . "http://www.github.com/clojure-emacs/cider"))]) + (cider . [(20220824 759) ((emacs (26)) (clojure-mode (5 15 1)) (parseedn (1 0 6)) (queue (0 2)) (spinner (1 7)) (seq (2 22)) (sesman (0 3 2))) "Clojure Interactive Development Environment that Rocks" tar ((:commit . "5bba4856fb42636bd52d1a1ebb4997a8806df759") (: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")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "cider") (:url . "http://www.github.com/clojure-emacs/cider"))]) (cider-decompile . [(20151122 537) ((cider (0 3 0)) (javap-mode (9))) "decompilation extension for cider" single ((:commit . "5d87035f3c3c14025e8f01c0c53d0ce2c8f56651") (:authors ("Dmitry Bushenko")) (:maintainer "Dmitry Bushenko") (:keywords "languages" "clojure" "cider") (:url . "http://www.github.com/clojure-emacs/cider-decompile"))]) (cider-eval-sexp-fu . [(20190311 2152) ((emacs (24)) (eval-sexp-fu (0 5 0))) "Briefly highlights an evaluated sexp." single ((:commit . "7fd229f1441356866aedba611fd0cf4e89b50921") (:authors ("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." single ((:commit . "c3b8a15d72dddfbc390ab6a454bd7e4c765a2c95") (:authors ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainer "Tianxiang Xiong" . "tianxiang.xiong@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/clojure-emacs/cider-hydra"))]) (ciel . [(20180914 815) ((emacs (24))) "A command that is clone of \"ci\" in vim." single ((:commit . "429773a3c551691a463ecfddd634b8bae2f48503") (:authors ("Takuma Matsushita" . "cs14095@gmail.com")) (:maintainer "Takuma Matsushita" . "cs14095@gmail.com") (:keywords "convinience") (:url . "https://github.com/cs14095/ciel.el"))]) (cil-mode . [(20160622 1430) nil "Common Intermediate Language mode" single ((:commit . "a78a88ca9a66a82f069329a96e34b67478ae2d9b") (:authors ("Friedrich von Never" . "friedrich@fornever.me")) (:maintainer "Friedrich von Never" . "friedrich@fornever.me") (:keywords "languages") (:url . "https://github.com/ForNeVeR/cil-mode"))]) - (cilk-mode . [(20220411 1342) ((emacs (25 1)) (flycheck (32 -4))) "Minor mode for Cilk code editing" single ((:commit . "2f33f991c726d5214d6a17bbbd19836302a8e423") (:authors ("Alexandros-Stavros Iliopoulos ")) (:maintainer "Alexandros-Stavros Iliopoulos" . "1577182+ailiop@users.noreply.github.com") (:keywords "c" "convenience" "faces" "languages") (:url . "https://github.com/ailiop/cilk-mode"))]) + (cilk-mode . [(20220807 1629) ((emacs (25 1)) (flycheck (32 -4))) "Minor mode for Cilk code editing" single ((:commit . "d5ba732a5a313a97a96085943cd7840b8e2d9c7c") (:authors ("Alexandros-Stavros Iliopoulos ")) (:maintainer "Alexandros-Stavros Iliopoulos" . "1577182+ailiop@users.noreply.github.com") (:keywords "c" "convenience" "faces" "languages") (:url . "https://github.com/ailiop/cilk-mode"))]) (cinspect . [(20150716 233) ((emacs (24)) (cl-lib (0 5)) (deferred (0 3 1)) (python-environment (0 0 2))) "Use cinspect to look at the CPython source of builtins and other C objects!" single ((:commit . "4e199a90f89b335cccda1518aa0963e0a1d4fbab") (:authors ("Ben Yelsey" . "ben.yelsey@gmail.com")) (:maintainer "Ben Yelsey" . "ben.yelsey@gmail.com") (:keywords "python") (:url . "https://github.com/inlinestyle/cinspect-mode"))]) (circadian . [(20181024 1256) ((emacs (24 4))) "Theme-switching based on daytime" single ((:commit . "bf5a00ea45c14dfdcda72c5d9f61bcd230c48159") (:authors ("Guido Schmidt")) (:maintainer "Guido Schmidt" . "git@guidoschmidt.cc") (:keywords "themes") (:url . "https://github.com/GuidoSchmidt/circadian"))]) (circe . [(20220526 1206) ((emacs (24 5)) (cl-lib (0 5))) "Client for IRC in Emacs" tar ((:commit . "41cdc116b09818d33f5cb0fc1d72c025c23aa2f1") (:authors ("Jorgen Schaefer" . "forcer@forcix.cx")) (:maintainer "Jorgen Schaefer" . "forcer@forcix.cx") (:keywords "irc" "chat" "comm") (:url . "https://github.com/emacs-circe/circe"))]) (circe-notifications . [(20180102 2318) ((emacs (24 4)) (circe (2 3)) (alert (1 2))) "Add desktop notifications to Circe." single ((:commit . "291149ac12877bbd062da993479d3533a26862b0") (:authors ("Ruben Maher" . "r@rkm.id.au")) (:maintainer "Ruben Maher" . "r@rkm.id.au") (:url . "https://github.com/eqyiel/circe-notifications"))]) (circleci-api . [(20210227 1607) ((emacs (27)) (request (0 3 2))) "Bindings for the CircleCI API" single ((:commit . "2e39c5896819bb2063f9d7795c4299f419cf5542") (:authors ("Robin Schroer")) (:maintainer "Robin Schroer") (:url . "https://github.com/sulami/circleci-api"))]) - (citar . [(20220803 1554) ((emacs (27 1)) (parsebib (3 0)) (org (9 5)) (citeproc (0 9))) "Citation-related commands for org, latex, markdown" tar ((:commit . "146f2cb5a31d4968ec17f39f189e4ea131ccaf56") (:authors ("Bruce D'Arcus ")) (:maintainer "Bruce D'Arcus ") (:url . "https://github.com/emacs-citar/citar"))]) - (citar-embark . [(20220724 2250) ((emacs (27 1)) (embark (0 17)) (citar (0 9 7))) "Citar/Embark integration" single ((:commit . "146f2cb5a31d4968ec17f39f189e4ea131ccaf56") (:authors ("Bruce D'Arcus" . "bdarcus@gmail.com")) (:maintainer "Bruce D'Arcus" . "bdarcus@gmail.com") (:keywords "bib" "extensions") (:url . "https://github.com/emacs-citar/citar-embark"))]) - (citeproc . [(20220717 729) ((emacs (26)) (dash (2 13 0)) (s (1 12 0)) (f (0 18 0)) (queue (0 2)) (string-inflection (1 0)) (org (9)) (parsebib (2 4))) "A CSL 1.0.2 Citation Processor" tar ((:commit . "406bd9964f1ce531fc45beddcf9ccc44d3456129") (:authors ("András Simonyi" . "andras.simonyi@gmail.com")) (:maintainer "András Simonyi" . "andras.simonyi@gmail.com") (:keywords "bib") (:url . "https://github.com/andras-simonyi/citeproc-el"))]) + (citar . [(20220820 1409) ((emacs (27 1)) (parsebib (4 2)) (org (9 5)) (citeproc (0 9))) "Citation-related commands for org, latex, markdown" tar ((:commit . "731c0ae81aecca94f7dcc4f2dad51a0cd1c3de68") (:authors ("Bruce D'Arcus ")) (:maintainer "Bruce D'Arcus ") (:url . "https://github.com/emacs-citar/citar"))]) + (citar-embark . [(20220724 2250) ((emacs (27 1)) (embark (0 17)) (citar (0 9 7))) "Citar/Embark integration" single ((:commit . "731c0ae81aecca94f7dcc4f2dad51a0cd1c3de68") (:authors ("Bruce D'Arcus" . "bdarcus@gmail.com")) (:maintainer "Bruce D'Arcus" . "bdarcus@gmail.com") (:keywords "bib" "extensions") (:url . "https://github.com/emacs-citar/citar-embark"))]) + (citar-org-roam . [(20220815 114) ((emacs (27 1)) (org-roam (2 2)) (citar (1 0))) "Citar/org-roam integration" single ((:commit . "4325e648c228269e519c322d696846a6c1f42f99") (:authors ("Bruce D'Arcus" . "bdarcus@gmail.com")) (:maintainer "Bruce D'Arcus" . "bdarcus@gmail.com") (:url . "https://github.com/emacs-citar/citar-org-roam"))]) + (citeproc . [(20220816 1732) ((emacs (26)) (dash (2 13 0)) (s (1 12 0)) (f (0 18 0)) (queue (0 2)) (string-inflection (1 0)) (org (9)) (parsebib (2 4))) "A CSL 1.0.2 Citation Processor" tar ((:commit . "36c4ecdc485a2f264297bb60f82c0afe1a1d64d3") (:authors ("András Simonyi" . "andras.simonyi@gmail.com")) (:maintainer "András Simonyi" . "andras.simonyi@gmail.com") (:keywords "bib") (:url . "https://github.com/andras-simonyi/citeproc-el"))]) (citeproc-org . [(20200915 2009) ((emacs (25 1)) (dash (2 12 0)) (org (9)) (f (0 18 0)) (citeproc (0 1)) (org-ref (1 1 1))) "Render org-mode references in CSL styles" tar ((:commit . "840e352855704921e2e68065e9abfdba769ccbd1") (:authors ("András Simonyi" . "andras.simonyi@gmail.com")) (:maintainer "András Simonyi" . "andras.simonyi@gmail.com") (:keywords "org-ref" "org-mode" "cite" "bib") (:url . "https://github.com/andras-simonyi/citeproc-org"))]) (citre . [(20220523 745) ((emacs (26 1))) "Ctags IDE on the True Editor" tar ((:commit . "1c0ca637c7993559a0175e3001941457b8c71211") (:authors ("Hao Wang" . "amaikinono@gmail.com")) (:maintainer "Hao Wang" . "amaikinono@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/universal-ctags/citre"))]) (cl-format . [(20210831 530) nil "CL format routine." tar ((:commit . "ad1a4fb6bc91e65ea90bcf6792cc5a1be5380f9d") (:authors ("Andreas Politz" . "politza@fh-trier.de")) (:maintainer "akater" . "nuclearspace@gmail.com") (:keywords "extensions") (:url . "https://gitlab.com/akater/elisp-cl-format"))]) @@ -542,7 +544,7 @@ (clips-mode . [(20170909 823) nil "Major mode for editing CLIPS code and REPL" tar ((:commit . "dd38e2822640a38f7d8bfec4f69d8dd24be27074") (:authors ("David E. Young" . "david.young@fnc.fujitsu.com") ("Andrey Kotlarski" . "m00naticus@gmail.com") ("Grant Rettke" . "grettke@acm.org")) (:maintainer "Grant Rettke" . "grettke@acm.org") (:keywords "clips"))]) (clj-decompiler . [(20220103 1746) ((emacs (26 1)) (clojure-mode (5 12)) (cider (1 2 0))) "Clojure Java decompiler expansion" single ((:commit . "8c0c53f87e6e33f2be7e7aff6095eb586b50be1a") (:authors ("Ben Sless" . "ben.sless@gmail.com")) (:maintainer "Ben Sless" . "ben.sless@gmail.com") (:keywords "languages" "clojure" "cider" "java" "decompiler") (:url . "https://www.github.com/bsless/clj-decompiler.el"))]) (clj-deps-new . [(20220221 2235) ((emacs (25 1)) (transient (0 3 7))) "Create clojure projects from templates" single ((:commit . "183089e6d4ded90efff491916e1c87411ead0461") (:authors ("jpe90" . "eskinjp@gmail.com")) (:maintainer "jpe90" . "eskinjp@gmail.com") (:url . "https://github.com/jpe90/emacs-deps-new"))]) - (clj-refactor . [(20220729 543) ((emacs (26 1)) (seq (2 19)) (yasnippet (0 6 1)) (paredit (24)) (multiple-cursors (1 2 2)) (clojure-mode (5 14)) (cider (1 4 1)) (parseedn (1 1 0)) (inflections (2 6)) (hydra (0 13 2))) "A collection of commands for refactoring Clojure code" tar ((:commit . "1bd7b09976c05ebd1ff1ee3d5c882af303780b67") (:authors ("Magnar Sveen" . "magnars@gmail.com") ("Lars Andersen" . "expez@expez.com") ("Benedek Fazekas" . "benedek.fazekas@gmail.com") ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "convenience" "clojure" "cider"))]) + (clj-refactor . [(20220821 1956) ((emacs (26 1)) (seq (2 19)) (yasnippet (0 6 1)) (paredit (24)) (multiple-cursors (1 2 2)) (clojure-mode (5 14)) (cider (1 4 1)) (parseedn (1 1 0)) (inflections (2 6)) (hydra (0 13 2))) "A collection of commands for refactoring Clojure code" tar ((:commit . "cd97626fd952f824564808a2f21b40bfcb59ff0f") (:authors ("Magnar Sveen" . "magnars@gmail.com") ("Lars Andersen" . "expez@expez.com") ("Benedek Fazekas" . "benedek.fazekas@gmail.com") ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "convenience" "clojure" "cider"))]) (cljr-helm . [(20220721 824) ((clj-refactor (0 13 0)) (helm-core (3 6 0)) (cl-lib (0 5))) "Wraps clojure refactor commands with helm" single ((:commit . "2c1f9cbd892ec03335f671ea3f974ee2ff6078dc") (:authors ("Phil Jackson" . "phil@shellarchive.co.uk")) (:maintainer "Phil Jackson" . "phil@shellarchive.co.uk") (:keywords "helm" "clojure" "refactor") (:url . "https://github.com/philjackson/cljr-helm"))]) (cljr-ivy . [(20200602 1607) ((clj-refactor (2 5 0)) (ivy (0 13 0)) (emacs (24 3)) (cl-lib (0 6 1))) "Access clojure refactor with ivy completion" single ((:commit . "921ba65d0db7cda4edcd690c708946125b874a70") (:authors ("Wanderson Ferreira" . "iagwanderson@gmail.com")) (:maintainer "Wanderson Ferreira" . "iagwanderson@gmail.com") (:keywords "convenience" "matching") (:url . "https://github.com/wandersoncferreira/cljr-ivy"))]) (cljsbuild-mode . [(20160402 1700) nil "A minor mode for the ClojureScript 'lein cljsbuild' command" single ((:commit . "fa2315660cb3ce944b5e16c679dcf5afd6a97f4c") (:keywords "clojure" "clojurescript" "leiningen" "compilation") (:url . "http://github.com/kototama/cljsbuild-mode"))]) @@ -554,12 +556,12 @@ (clojars . [(20180825 1951) ((request-deferred (0 2 0))) "clojars.org search interface" single ((:commit . "696c5b056e45067512a7d6dcce2515f3c639f61b") (:authors ("Joshua Miller" . "josh@joshmiller.io")) (:maintainer "Joshua Miller" . "josh@joshmiller.io") (:keywords "docs" "help" "tools") (:url . "https://github.com/joshuamiller/clojars.el"))]) (clojure-essential-ref . [(20200619 1653) ((emacs (24)) (cider (0 24 0))) "Cider-doc to \"Clojure, The Essential Reference\"" single ((:commit . "13ac560c25f7355fba00d9ca8c9f4ca03e7fd189") (:url . "https://github.com/p3r7/clojure-essential-ref"))]) (clojure-essential-ref-nov . [(20200719 608) ((emacs (24)) (dash (2 16 0)) (nov (0 3 1)) (clojure-essential-ref (0 1 0))) "Cider-doc to \"Clojure, The Essential Reference\" (EPUB)" single ((:commit . "13ac560c25f7355fba00d9ca8c9f4ca03e7fd189") (:url . "https://github.com/p3r7/clojure-essential-ref"))]) - (clojure-mode . [(20220729 2246) ((emacs (25 1))) "Major mode for Clojure code" single ((:commit . "ad322e989e56c10c05bb286e5b55a82b1e031d62") (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "clojurescript" "lisp") (:url . "http://github.com/clojure-emacs/clojure-mode"))]) - (clojure-mode-extra-font-locking . [(20220715 1509) ((clojure-mode (3 0))) "Extra font-locking for Clojure mode" single ((:commit . "ad322e989e56c10c05bb286e5b55a82b1e031d62") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "lisp") (:url . "http://github.com/clojure-emacs/clojure-mode"))]) + (clojure-mode . [(20220824 1801) ((emacs (25 1))) "Major mode for Clojure code" single ((:commit . "52f3e1f981af2adb007ddbf178c9e5625fdc63b6") (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "clojurescript" "lisp") (:url . "http://github.com/clojure-emacs/clojure-mode"))]) + (clojure-mode-extra-font-locking . [(20220715 1509) ((clojure-mode (3 0))) "Extra font-locking for Clojure mode" single ((:commit . "52f3e1f981af2adb007ddbf178c9e5625fdc63b6") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "lisp") (:url . "http://github.com/clojure-emacs/clojure-mode"))]) (clojure-quick-repls . [(20150814 736) ((cider (0 8 1)) (dash (2 9 0))) "Quickly create Clojure and ClojureScript repls for a project." single ((:commit . "730311dd3ac4e0aceb0204f818b422017873467f") (:keywords "languages" "clojure" "cider" "clojurescript") (:url . "https://github.com/symfrog/clojure-quick-repls"))]) (clojure-snippets . [(20180314 1308) ((yasnippet (0 10 0))) "Yasnippets for clojure" tar ((:commit . "6068dca90467a0f4ebc2cd39338a173d6f5ddc04") (:authors ("Max Penet" . "m@qbits.cc")) (:maintainer "Max Penet" . "m@qbits.cc") (:keywords "snippets"))]) (clomacs . [(20220415 1035) ((emacs (24 3)) (cider (0 22 1)) (s (1 12 0)) (simple-httpd (1 4 6)) (dash (2 19 1))) "Simplifies Emacs Lisp interaction with Clojure." single ((:commit . "9cd7c9fd86bc7bc627a31275d1ef131378b90a49") (:authors ("Kostafey" . "kostafey@gmail.com")) (:maintainer "Kostafey" . "kostafey@gmail.com") (:keywords "clojure" "interaction") (:url . "https://github.com/clojure-emacs/clomacs"))]) - (closql . [(20220422 1601) ((emacs (25 1)) (compat (28 1 1 0)) (emacsql-sqlite (3 0 0))) "Store EIEIO objects using EmacSQL" single ((:commit . "87d2edae8bc3d390bcfc5e909e9c13ff9fce994a") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "extensions") (:url . "https://github.com/emacscollective/closql"))]) + (closql . [(20220821 1814) ((emacs (25 1)) (compat (28 1 1 0)) (emacsql-sqlite (3 0 0))) "Store EIEIO objects using EmacSQL" single ((:commit . "46b3020acf6655fd8abb4ef60e090629ee33e8c3") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "extensions") (:url . "https://github.com/emacscollective/closql"))]) (closure-lint-mode . [(20101118 2124) nil "minor mode for the Closure Linter" single ((:commit . "bc3d2fd5c35580bf1b8af43b12484c95a343b4b5") (:authors ("Roman Scherer" . "roman@burningswell.com")) (:maintainer "Roman Scherer" . "roman@burningswell.com") (:keywords "tools" "closure" "javascript" "lint" "flymake") (:url . "https://github.com/r0man/closure-lint-mode"))]) (cloud-theme . [(20220205 1336) ((emacs (24))) "A light colored theme" single ((:commit . "16372ea1f527917102ac302afaec3ef09e289d24") (:authors ("Valerii Lysenko" . "vallyscode@gmail.com")) (:maintainer "Valerii Lysenko" . "vallyscode@gmail.com") (:keywords "color" "theme") (:url . "https://github.com/vallyscode/cloud-theme"))]) (cloud-to-butt-erc . [(20130627 2308) nil "Replace 'the cloud' with 'my butt'" single ((:commit . "6710c03d1bc91736435cbfe845924940cae34e5c") (:authors ("David Leatherman" . "leathekd@gmail.com")) (:maintainer "David Leatherman" . "leathekd@gmail.com") (:url . "http://www.github.com/leathekd/cloud-to-butt-erc"))]) @@ -567,7 +569,7 @@ (cm-mode . [(20170203 2107) ((cl-lib (0 5))) "Minor mode for CriticMarkup" single ((:commit . "276d49c859822265070ae5dfbb403fd7d8d06436") (:authors ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainer "Joost Kremers" . "joostkremers@fastmail.fm") (:keywords "text" "markdown"))]) (cmake-font-lock . [(20211224 2006) ((cmake-mode (0 0))) "Advanced, type aware, highlight support for CMake" single ((:commit . "0d6111b36a66013aa9b452e664c93308df3b07e1") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "faces" "languages") (:url . "https://github.com/Lindydancer/cmake-font-lock"))]) (cmake-ide . [(20210610 1525) ((emacs (24 4)) (cl-lib (0 5)) (seq (1 11)) (levenshtein (0)) (s (1 11 0))) "Calls CMake to find out include paths and other compiler flags" single ((:commit . "28dc4ab5bd01d99553901b4efeb7234280928b18") (:authors ("Atila Neves" . "atila.neves@gmail.com")) (:maintainer "Atila Neves" . "atila.neves@gmail.com") (:keywords "languages") (:url . "http://github.com/atilaneves/cmake-ide"))]) - (cmake-mode . [(20220617 1532) ((emacs (24 1))) "major-mode for editing CMake sources" single ((:commit . "e95059c867c1ec25f23aa48f9fe4dc8330e25879"))]) + (cmake-mode . [(20220823 1201) ((emacs (24 1))) "major-mode for editing CMake sources" single ((:commit . "9d1ecd72fb45af722da7668d0c7482b7a0b1876f"))]) (cmake-project . [(20171121 1115) nil "Integrates CMake build process with Emacs" single ((:commit . "a7cf9e4c01c4683e14b6942cc5cc5e8cddc98721") (:authors ("Alexander Lamaison" . "alexander.lamaison@gmail")) (:maintainer "Alexander Lamaison" . "alexander.lamaison@gmail") (:keywords "c" "cmake" "languages" "tools") (:url . "http://github.com/alamaison/emacs-cmake-project"))]) (cmd-to-echo . [(20161203 2133) ((emacs (24 4)) (s (1 11 0)) (shell-split-string (20151224 208))) "Show the output of long-running commands in the echo area" single ((:commit . "e0e874fc0e1ad6d291e39ed76023445297ad438a") (:authors ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainer "Tijs Mallaerts" . "tijs.mallaerts@gmail.com"))]) (cmm-mode . [(20150225 746) nil "Major mode for C-- source code" single ((:commit . "c3ad514dff3eb30434f6b20d953276d4c00de1ee"))]) @@ -582,6 +584,7 @@ (code-stats . [(20201209 2135) ((emacs (25)) (request (0 3 0))) "Code::Stats plugin" single ((:commit . "9a467dfd6a3cef849468623e1c085cbf59dac154") (:authors ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainer "Xu Chunyang" . "mail@xuchunyang.me") (:url . "https://github.com/xuchunyang/code-stats-emacs"))]) (codebug . [(20140929 2137) nil "Interact with codebug" single ((:commit . "ac0e4331ba94ccb5203fa492570e1ca6b90c3d52") (:authors ("Shane Dowling")) (:maintainer "Shane Dowling") (:url . "http://www.shanedowling.com/"))]) (codesearch . [(20181006 1431) ((log4e (0 3 1))) "Core support for managing codesearch tools" tar ((:commit . "f6eb96f034a925444412cfa03e45e0ccbbafe3f2") (:authors ("Austin Bingham" . "austin.bingham@gmail.com") ("Youngjoo Lee" . "youngker@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:keywords "tools" "development" "search") (:url . "https://github.com/abingham/emacs-codesearch"))]) + (codespaces . [(20220824 1816) ((emacs (28 1))) "Connect to GitHub Codespaces via TRAMP" single ((:commit . "43cf115189b27c7b348b8bb22ef74160d2c942e7") (:authors ("Patrick Thomson" . "patrickt@github.com")) (:maintainer "Patrick Thomson" . "patrickt@github.com") (:keywords "comm") (:url . "https://github.com/patrickt/codespaces.el"))]) (codic . [(20150926 1127) ((emacs (24)) (cl-lib (0 5))) "Search Codic (codic.jp) naming dictionaries" tar ((:commit . "52bbb6997ef4ab9fb7fea43bbfff7f04671aa557") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-codic"))]) (coffee-fof . [(20131012 1230) ((coffee-mode (0 4 1))) "A coffee-mode configuration for `ff-find-other-file'." single ((:commit . "211529594bc074721c6cbc4edb73a63cc05f89ac") (:authors ("Yasuyki Oka" . "yasuyk@gmail.com")) (:maintainer "Yasuyki Oka" . "yasuyk@gmail.com") (:keywords "coffee-mode") (:url . "http://github.com/yasuyk/coffee-fof"))]) (coffee-mode . [(20200315 1133) ((emacs (24 3))) "Major mode for CoffeeScript code" single ((:commit . "35a41c7d8233eac0b267d9593e67fb8b6235e134") (:authors ("Chris Wanstrath" . "chris@ozmm.org")) (:maintainer "Chris Wanstrath" . "chris@ozmm.org") (:keywords "coffeescript" "major" "mode") (:url . "http://github.com/defunkt/coffee-mode"))]) @@ -599,7 +602,7 @@ (colorless-themes . [(20210102 1035) ((emacs (24 1))) "A macro to generate mostly colorless themes" single ((:commit . "c1ed1e12541cf05cc6c558d23c089c07e10b54d7") (:authors ("Thomas Letan" . "contact@thomasletan.fr")) (:maintainer "Thomas Letan" . "contact@thomasletan.fr") (:keywords "faces themes" "faces") (:url . "https://git.sr.ht/~lthms/colorless-themes.el"))]) (colormaps . [(20171008 2224) ((emacs (25))) "Hex colormaps" single ((:commit . "19fbb64a6288d505b9cf45c9b5a3eed0bfb135e2") (:authors ("Abhinav Tushar" . "lepisma@fastmail.com")) (:maintainer "Abhinav Tushar" . "lepisma@fastmail.com") (:keywords "tools") (:url . "https://github.com/lepisma/colormaps.el"))]) (column-enforce-mode . [(20200605 1933) nil "Highlight text that extends beyond a column" single ((:commit . "14a7622f2268890e33536ccd29510024d51ee96f") (:authors ("Jordon Biondo")) (:maintainer "Jordon Biondo") (:url . "www.github.com/jordonbiondo/column-enforce-mode"))]) - (com-css-sort . [(20220704 647) ((emacs (25 1)) (s (1 12 0))) "Common way of sorting the CSS attributes" single ((:commit . "bda336544e61b45e24927d8850fdb28d4b7c267e") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "matching" "css" "sort") (:url . "https://github.com/jcs-elpa/com-css-sort"))]) + (com-css-sort . [(20220704 647) ((emacs (25 1)) (s (1 12 0))) "Common way of sorting the CSS attributes" single ((:commit . "f54c2ba98d4e73d5504152ae8d61071ed467e39f") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "matching" "css" "sort") (:url . "https://github.com/jcs-elpa/com-css-sort"))]) (comb . [(20201010 1147) ((emacs (25 1))) "Interactive code auditing and grep tool" tar ((:commit . "31f3e94afb2a7f7d18d30c2468a0c683700f7a66") (:authors ("Andrea Cardaci" . "cyrus.and@gmail.com")) (:maintainer "Andrea Cardaci" . "cyrus.and@gmail.com") (:keywords "matching") (:url . "https://github.com/cyrus-and/comb"))]) (comby . [(20200629 140) ((emacs (25 1))) "Emacs comby integration" single ((:commit . "928b8b8959a2556aba5526f2a25801341eb59dc3") (:authors ("Sergey Kostyaev" . "feo.me@ya.ru")) (:maintainer "Sergey Kostyaev" . "feo.me@ya.ru") (:keywords "languages") (:url . "https://github.com/s-kostyaev/comby.el"))]) (comint-hyperlink . [(20211026 100) ((emacs (24 3))) "Create hyperlinks in comint for SGR URL control sequences" single ((:commit . "905f2db1f95950899301b9f71faed9e9362cf5dc") (:authors ("Matthew Bauer" . "mjbauer95@gmail.com")) (:maintainer "Matthew Bauer" . "mjbauer95@gmail.com") (:keywords "comint" "shell" "processes" "hypermedia" "terminals") (:url . "https://github.com/matthewbauer/comint-hyperlink"))]) @@ -614,7 +617,7 @@ (commenter . [(20160219 1627) ((emacs (24 4)) (let-alist (1 0 4))) "multiline-comment support package" single ((:commit . "6d1885419434ba779270c6fda0e30d390bb074bd") (:authors ("Yuta Yamada ")) (:maintainer "Yuta Yamada ") (:keywords "comment") (:url . "https://github.com/yuutayamada/commenter"))]) (commify . [(20220531 1301) ((s (1 9 0))) "Toggle grouping commas in numbers" single ((:commit . "c4aeccae5b4a073fc3f4e8bd780a2ebbb7d5e533") (:authors ("Daniel E. Doherty" . "ded-commify@ddoherty.net")) (:maintainer "Daniel E. Doherty" . "ded-commify@ddoherty.net") (:keywords "convenience" "editing" "numbers" "grouping" "commas") (:url . "https://github.com/ddoherty03/commify"))]) (common-lisp-snippets . [(20180226 1523) ((yasnippet (0 8 0))) "Yasnippets for Common Lisp" tar ((:commit . "c82ebf18f4ad49f390dd96ffcc59f8683c1a868b") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "snippets") (:url . "https://github.com/mrkkrp/common-lisp-snippets"))]) - (company . [(20220731 2333) ((emacs (25 1))) "Modular text completion framework" tar ((:commit . "f1877a370ca76d5cd6614c527635b2d6983188af") (:authors ("Nikolaj Schumacher")) (:maintainer "Dmitry Gutov" . "dgutov@yandex.ru") (:keywords "abbrev" "convenience" "matching") (:url . "http://company-mode.github.io/"))]) + (company . [(20220825 1044) ((emacs (25 1))) "Modular text completion framework" tar ((:commit . "35408c0ac3a730908fbce0810f8d159eb353c1d5") (:authors ("Nikolaj Schumacher")) (:maintainer "Dmitry Gutov" . "dgutov@yandex.ru") (:keywords "abbrev" "convenience" "matching") (:url . "http://company-mode.github.io/"))]) (company-anaconda . [(20200404 1859) ((company (0 8 0)) (anaconda-mode (0 1 1)) (cl-lib (0 5 0)) (dash (2 6 0)) (s (1 9))) "Anaconda backend for company-mode" single ((:commit . "da1566db41a68809ef7f91ebf2de28118067c89b") (:authors ("Artem Malyshev" . "proofit404@gmail.com")) (:maintainer "Artem Malyshev" . "proofit404@gmail.com") (:url . "https://github.com/proofit404/anaconda-mode"))]) (company-ansible . [(20200306 1441) ((emacs (24 4)) (company (0 8 12))) "A company back-end for ansible" tar ((:commit . "79dd421b161efa49fbdffad57fa40edb41f484a3") (:authors ("Krzysztof Magosa" . "krzysztof@magosa.pl")) (:maintainer "Krzysztof Magosa" . "krzysztof@magosa.pl") (:keywords "ansible") (:url . "https://github.com/krzysztof-magosa/company-ansible"))]) (company-arduino . [(20160306 1739) ((emacs (24 1)) (company (0 8 0)) (irony (0 1 0)) (cl-lib (0 5)) (company-irony (0 1 0)) (company-c-headers (20140930)) (arduino-mode (1 0))) "company-mode for Arduino" single ((:commit . "d7e369702b8eee63e6dfdeba645ce28b6dc66fb1") (:authors ("Yuta Yamada" . "sleepboy.zzz@gmail.com")) (:maintainer "Yuta Yamada" . "sleepboy.zzz@gmail.com") (:keywords "convenience" "development" "company") (:url . "https://github.com/yuutayamada/company-arduino"))]) @@ -631,11 +634,11 @@ (company-distel . [(20180827 1344) ((distel-completion-lib (1 0 0))) "Erlang/distel completion backend for company-mode" single ((:commit . "acc4c0a5521904203d797fe96b08e5fae4233c7e") (:authors ("Sebastian Weddmark Olsson")) (:maintainer "Sebastian Weddmark Olsson") (:keywords "erlang" "distel" "company") (:url . "github.com/sebastiw/distel-completion"))]) (company-emacs-eclim . [(20180911 1121) ((eclim (0 3)) (company (0 7)) (cl-lib (0 5))) "Eclim company backend" single ((:commit . "222ddd48fcf0ee01592dec77c58e0cf3f2ea1100"))]) (company-emoji . [(20210427 2151) ((cl-lib (0 5)) (company (0 8 0))) "company-mode backend for emoji" tar ((:commit . "90594eb58b20fb937cfd4e946efcc446ee630e6f") (:authors ("Alex Dunn" . "dunn.alex@gmail.com")) (:maintainer "Alex Dunn" . "dunn.alex@gmail.com") (:keywords "emoji" "company") (:url . "https://github.com/dunn/company-emoji.git"))]) - (company-emojify . [(20220727 1740) ((emacs (26 1)) (company (0 8 0)) (emojify (1 2 1)) (ht (2 0))) "Company completion for Emojify" single ((:commit . "cc3ae96fbafa51d71fde802fa3c1e5fad9402158") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "emoji" "company" "emojify") (:url . "https://github.com/jcs-elpa/company-emojify"))]) + (company-emojify . [(20220727 1740) ((emacs (26 1)) (company (0 8 0)) (emojify (1 2 1)) (ht (2 0))) "Company completion for Emojify" single ((:commit . "f877614d3395ba3f2a678b50d79bac6d392c2d2b") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "emoji" "company" "emojify") (:url . "https://github.com/jcs-elpa/company-emojify"))]) (company-erlang . [(20170123 538) ((emacs (24 4)) (ivy-erlang-complete (0 1)) (company (0 9 2))) "company backend based on ivy-erlang-complete" single ((:commit . "bc0524a16f17b66c7397690e4ca0e004f09ea6c5") (:authors ("Sergey Kostyaev" . "feo.me@ya.ru")) (:maintainer "Sergey Kostyaev" . "feo.me@ya.ru") (:keywords "tools"))]) (company-flow . [(20180225 2159) ((company (0 8 0)) (dash (2 13 0))) "Flow backend for company-mode" single ((:commit . "76ef585c70d2a3206c2eadf24ba61e59124c3a16") (:authors ("Aaron Jensen" . "aaronjensen@gmail.com")) (:maintainer "Aaron Jensen" . "aaronjensen@gmail.com") (:url . "https://github.com/aaronjensen/company-flow"))]) (company-flx . [(20180103 518) ((emacs (24)) (company (0 8 12)) (flx (0 5))) "flx based fuzzy matching for company" single ((:commit . "16ca0d2f84e8e768bf2db8c5cfe421230a00bded") (:authors ("PythonNut" . "pythonnut@pythonnut.com")) (:maintainer "PythonNut" . "pythonnut@pythonnut.com") (:keywords "convenience" "company" "fuzzy" "flx") (:url . "https://github.com/PythonNut/company-flx"))]) - (company-fuzzy . [(20220721 1609) ((emacs (26 1)) (company (0 8 12)) (s (1 12 0)) (ht (2 0))) "Fuzzy matching for `company-mode'" single ((:commit . "91b76fc475f7eb17a0ae8b94a42625dfb546cc01") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "matching" "auto-complete" "complete" "fuzzy") (:url . "https://github.com/jcs-elpa/company-fuzzy"))]) + (company-fuzzy . [(20220721 1609) ((emacs (26 1)) (company (0 8 12)) (s (1 12 0)) (ht (2 0))) "Fuzzy matching for `company-mode'" single ((:commit . "7b54f88d2241073986b0e3d109012ee396cfdb3f") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "matching" "auto-complete" "complete" "fuzzy") (:url . "https://github.com/jcs-elpa/company-fuzzy"))]) (company-ghci . [(20190707 311) ((company (0 8 11)) (haskell-mode (13))) "company backend which uses the current ghci process." single ((:commit . "a1d25652583ab4666c5a78cac18cd8039776b50d") (:authors ("Hector Orellana" . "hofm92@gmail.com")) (:maintainer "Hector Orellana" . "hofm92@gmail.com"))]) (company-glsl . [(20210109 1403) ((company (0 9 4)) (glsl-mode (2 4)) (emacs (24 4))) "Support glsl in company-mode" single ((:commit . "3a40501ba831a30a7fd3e8529b20d1305d0454aa") (:authors ("Guido Schmidt" . "git@guidoschmidt.cc")) (:maintainer "Guido Schmidt" . "git@guidoschmidt.cc") (:url . "https://github.com/guidoschmidt/company-glsl"))]) (company-go . [(20170825 1643) ((company (0 8 0)) (go-mode (1 0 0))) "company-mode backend for Go (using gocode)" single ((:commit . "31948b463f2fc18f8801e5a8fe511fef300eb3dd") (:authors ("nsf" . "no.smile.face@gmail.com")) (:maintainer "nsf" . "no.smile.face@gmail.com") (:keywords "languages"))]) @@ -649,13 +652,13 @@ (company-lua . [(20171108 2306) ((company (0 8 12)) (s (1 10 0)) (f (0 17 0)) (lua-mode (20151025))) "Company backend for Lua" tar ((:commit . "29f6819de4d691e5fd0b62893a9f4fbc1c6fcb52") (:authors ("Peter Vasil" . "mail@petervasil.net")) (:maintainer "Peter Vasil" . "mail@petervasil.net"))]) (company-manually . [(20200709 913) ((emacs (24 3)) (company (0 9 0)) (ivy (0 13 0))) "A company backend that lets you manually build candidates" single ((:commit . "44c7a655e5f2a462835a96d1f0ed2ce434848416") (:authors ("Yanghao Xie")) (:maintainer "Yanghao Xie" . "yhaoxie@gmail.com") (:keywords "convenience" "company-mode" "manually build candidates") (:url . "https://github.com/yanghaoxie/company-manually"))]) (company-math . [(20210731 2019) ((company (0 8 0)) (math-symbol-lists (1 3))) "Completion backends for unicode math symbols and latex tags" single ((:commit . "45778f5731c97a21a83e3b965cbde42018709afd") (:authors ("Vitalie Spinu" . "spinuvit@gmail.com")) (:maintainer "Vitalie Spinu" . "spinuvit@gmail.com") (:keywords "unicode" "symbols" "completion") (:url . "https://github.com/vspinu/company-math"))]) - (company-maxima . [(20210520 2034) ((emacs (25 1)) (maxima (0 6 1)) (seq (2 20)) (company (0 9 13))) "Maxima company integration" single ((:commit . "1334f44725bd80a265de858d652f3fde4ae401fa") (:authors ("Fermin Munoz")) (:maintainer "Fermin Munoz" . "fmfs@posteo.net") (:keywords "languages" "tools" "convenience") (:url . "https://gitlab.com/sasanidas/maxima"))]) + (company-maxima . [(20210520 2034) ((emacs (25 1)) (maxima (0 6 1)) (seq (2 20)) (company (0 9 13))) "Maxima company integration" single ((:commit . "1913ee496bb09430e85f76dfadf8ba4d4f95420f") (:authors ("Fermin Munoz")) (:maintainer "Fermin Munoz" . "fmfs@posteo.net") (:keywords "languages" "tools" "convenience") (:url . "https://gitlab.com/sasanidas/maxima"))]) (company-nand2tetris . [(20171201 1813) ((nand2tetris (1 1 0)) (company (0 5)) (cl-lib (0 5 0))) "Company backend for nand2tetris major mode" single ((:commit . "33acee34d24b1c6a87db833b7d23449cf858f64f") (:authors ("Diego Berrocal" . "cestdiego@gmail.com")) (:maintainer "Diego Berrocal" . "cestdiego@gmail.com") (:keywords "nand2tetris" "hdl" "company") (:url . "http://www.github.com/CestDiego/nand2tetris.el/"))]) (company-native-complete . [(20220103 1622) ((emacs (26 1)) (company (0 9 0)) (native-complete (0 1 0))) "Company completion using native-complete" single ((:commit . "9dbfc842b3af803f636df61dec6129e1d8593ee4") (:authors ("Troy Hinckley" . "troy.hinckley@gmail.com")) (:maintainer "Troy Hinckley" . "troy.hinckley@gmail.com") (:url . "https://github.com/CeleritasCelery/emacs-native-shell-complete"))]) (company-nginx . [(20220210 1411) ((emacs (24)) (cl-lib (0)) (company (0))) "company-mode keywords support for nginx-mode" single ((:commit . "8a9f1a5653fe2d9a5042bfb9377d54f37fcc64c8") (:keywords "company" "nginx") (:url . "https://repo.or.cz/company-nginx.git"))]) (company-ngram . [(20170129 1913) ((cl-lib (0 5)) (company (0 8 0))) "N-gram based completion" tar ((:commit . "09a68b802e64799e95f205b438d469bbd78cd2e6") (:authors ("kshramt")) (:maintainer "kshramt") (:url . "https://github.com/kshramt/company-ngram"))]) (company-nixos-options . [(20160215 857) ((company (0 8 0)) (nixos-options (0 0 1)) (cl-lib (0 5 0))) "Company Backend for nixos-options" single ((:commit . "053a2d5110ce05b7f99bcc2ac4804b70cbe87916") (:authors ("Diego Berrocal" . "cestdiego@gmail.com") ("Travis B. Hartwell" . "nafai@travishartwell.net")) (:maintainer "Diego Berrocal" . "cestdiego@gmail.com") (:keywords "unix") (:url . "http://www.github.com/travisbhartwell/nix-emacs/"))]) - (company-org-block . [(20210825 2107) ((emacs (25 1)) (company (0 8 0)) (org (9 2 0))) "Org blocks company backend" single ((:commit . "115af0a3625f4669358eca568466d468cacc78bd") (:authors ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/company-org-block"))]) + (company-org-block . [(20220809 2027) ((emacs (25 1)) (company (0 8 0)) (org (9 2 0))) "Org blocks company backend" single ((:commit . "4d96750beba7f90478dbf5f4d52eb9c21d538f4a") (:authors ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/company-org-block"))]) (company-php . [(20211204 558) ((cl-lib (0 5)) (ac-php-core (2 0)) (company (0 9))) "A company back-end for PHP." single ((:commit . "dc563f4b1efeac8ae75f217532f4c99b4ba417de") (:authors ("jim" . "xcwenn@qq.com")) (:maintainer "jim") (:keywords "completion" "convenience" "intellisense") (:url . "https://github.com/xcwen/ac-php"))]) (company-phpactor . [(20200121 1218) ((emacs (24 3)) (company (0 9 6)) (phpactor (0 1 0))) "company-mode backend for Phpactor" single ((:commit . "34195f1533209e2ffd0f898a69c7db2bffd1eabe") (:authors ("Martin Tang" . "martin.tang365@gmail.com") ("Mikael Kermorgant" . "mikael@kgtech.fi")) (:maintainer "Martin Tang" . "martin.tang365@gmail.com") (:keywords "tools" "php") (:url . "https://github.com/emacs-php/phpactor.el"))]) (company-plisp . [(20200531 1927) ((emacs (25)) (s (1 2 0)) (company (0 8 12)) (dash (2 12 0)) (cl-lib (0 5))) "Company mode backend for PicoLisp language" tar ((:commit . "fc0b56d2a711340ca3e63119bfe692bb3e8620fb") (:authors ("Fermin MF" . "fmfs@posteo.net")) (:maintainer "Fermin MF" . "fmfs@posteo.net") (:keywords "company" "plisp" "convenience" "auto-completion") (:url . "https://gitlab.com/sasanidas/company-plisp"))]) @@ -664,13 +667,13 @@ (company-posframe . [(20220331 2141) ((emacs (26 0)) (company (0 9 0)) (posframe (0 9 0))) "Use a posframe as company candidate menu" single ((:commit . "df0e34f69dc8e9aaa1a6c5e88783898f4ae3f2df") (:authors ("Clément Pit-Claudel, Feng Shu, Lars Andersen" . "expez@expez.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "abbrev" "convenience" "matching") (:url . "https://github.com/tumashu/company-posframe"))]) (company-prescient . [(20220601 1652) ((emacs (25 1)) (prescient (5 2 1)) (company (0 9 6))) "prescient.el + Company" single ((:commit . "07d61b7779c4cca3009390383e7f98a55de7e17e") (:authors ("Radian LLC" . "contact+prescient@radian.codes")) (:maintainer "Radian LLC" . "contact+prescient@radian.codes") (:keywords "extensions") (:url . "https://github.com/raxod502/prescient.el"))]) (company-qml . [(20170428 1708) ((qml-mode (0 1)) (company (0 8 12))) "Company backend for QML files" tar ((:commit . "4af4f32a7ad86d86bb9293fb0b675aec513b5736") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) - (company-quickhelp . [(20211115 1335) ((emacs (24 3)) (company (0 8 9)) (pos-tip (0 4 6))) "Popup documentation for completion candidates" single ((:commit . "3ca2708b4e5190205aca01d65fe1b391963a53f9") (:authors ("Lars Andersen" . "expez@expez.com")) (:maintainer "Lars Andersen" . "expez@expez.com") (:keywords "company" "popup" "documentation" "quickhelp") (:url . "https://www.github.com/expez/company-quickhelp"))]) - (company-quickhelp-terminal . [(20220704 647) ((emacs (24 4)) (company-quickhelp (2 2 0)) (popup (0 5 3))) "Terminal support for `company-quickhelp'" single ((:commit . "70c8a979d82e436dca8595e3ec049c733246f338") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "terminal" "extends" "support" "tip" "help") (:url . "https://github.com/jcs-elpa/company-quickhelp-terminal"))]) + (company-quickhelp . [(20220825 1037) ((emacs (24 3)) (company (0 8 9)) (pos-tip (0 4 6))) "Popup documentation for completion candidates" single ((:commit . "6660a1b380b9599af9ef1cc617168db83f24b9b7") (:authors ("Lars Andersen" . "expez@expez.com")) (:maintainer "Lars Andersen" . "expez@expez.com") (:keywords "company" "popup" "documentation" "quickhelp") (:url . "https://www.github.com/expez/company-quickhelp"))]) + (company-quickhelp-terminal . [(20220704 647) ((emacs (24 4)) (company-quickhelp (2 2 0)) (popup (0 5 3))) "Terminal support for `company-quickhelp'" single ((:commit . "4968de67d0db8ddae29a03719b31f1be0434cb9f") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "terminal" "extends" "support" "tip" "help") (:url . "https://github.com/jcs-elpa/company-quickhelp-terminal"))]) (company-racer . [(20171205 310) ((emacs (24 4)) (cl-lib (0 5)) (company (0 8 0)) (deferred (0 3 1))) "Company integration for racer" single ((:commit . "a00381c9d416f375f783fcb6ae8d40669ce1f567") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "convenience") (:url . "https://github.com/emacs-pe/company-racer"))]) (company-reftex . [(20210418 1316) ((emacs (25 1)) (s (1 12)) (company (0 8))) "Company backend based on RefTeX." single ((:commit . "42eb98c6504e65989635d95ab81b65b9d5798e76") (:authors ("Eivind Fonn" . "evfonn@gmail.com")) (:maintainer "Eivind Fonn" . "evfonn@gmail.com") (:keywords "bib" "tex" "company" "latex" "reftex" "references" "labels" "citations") (:url . "https://github.com/TheBB/company-reftex"))]) (company-restclient . [(20190426 1312) ((cl-lib (0 5)) (company (0 8 0)) (emacs (24)) (know-your-http-well (0 2 0)) (restclient (0 0 0))) "company-mode completion back-end for restclient-mode" single ((:commit . "e5a3ec54edb44776738c13e13e34c85b3085277b") (:authors ("Iku Iwasa" . "iku.iwasa@gmail.com")) (:maintainer "Iku Iwasa" . "iku.iwasa@gmail.com") (:url . "https://github.com/iquiw/company-restclient"))]) - (company-rtags . [(20191222 920) ((emacs (24 3)) (company (0 8 1)) (rtags (2 10))) "RTags back-end for company" single ((:commit . "c628efc9b485470a48aec2692d79f7c140bc5b92") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) - (company-shell . [(20211013 1725) ((emacs (24 4)) (company (0 8 12)) (dash (2 12 0)) (cl-lib (0 5))) "Company mode backend for shell functions" single ((:commit . "a77f4de75912aa87314cde92c603b831d5050246") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:keywords "company" "shell" "auto-completion") (:url . "https://github.com/Alexander-Miller/company-shell"))]) + (company-rtags . [(20191222 920) ((emacs (24 3)) (company (0 8 1)) (rtags (2 10))) "RTags back-end for company" single ((:commit . "b9c680e7ca003c103687e790f740d86daa6b4b17") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) + (company-shell . [(20220822 2014) ((emacs (24 4)) (company (0 8 12)) (dash (2 12 0)) (cl-lib (0 5))) "Company mode backend for shell functions" single ((:commit . "024c2172a0cc8f1b35f173e42fff5f2d938920fc") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:keywords "company" "shell" "auto-completion") (:url . "https://github.com/Alexander-Miller/company-shell"))]) (company-solidity . [(20181117 1518) ((company (0 9 0)) (cl-lib (0 5 0)) (solidity-mode (0 1 9))) "Company-mode back-end for solidity-mode" single ((:commit . "211dbdf0dfab1139681156e6f9621a5bbe0f74a1") (:authors ("Samuel Smolkin" . "sam@future-precedent.org")) (:maintainer "Samuel Smolkin" . "sam@future-precedent.org") (:keywords "solidity" "completion" "company") (:url . "https://github.com/ethereum/emacs-solidity"))]) (company-sourcekit . [(20210430 2155) ((emacs (24 3)) (company (0 8 12)) (dash (2 18 0)) (sourcekit (0 2 0))) "company-mode completion backend for SourceKit" single ((:commit . "a1860ad4dd3a542acd2fa0dfac2a388cbdf4af0c") (:authors ("Nathan Kot" . "nk@nathankot.com")) (:maintainer "Nathan Kot" . "nk@nathankot.com") (:keywords "abbrev") (:url . "https://github.com/nathankot/company-sourcekit"))]) (company-stan . [(20211129 2051) ((emacs (24 3)) (company (0 9 10)) (stan-mode (10 3 0))) "A company-mode completion backend for stan" single ((:commit . "150bbbe5fd3ad2b5a3dbfba9d291e66eeea1a581") (:authors ("Kazuki Yoshida" . "kazukiyoshida@mail.harvard.edu")) (:maintainer "Kazuki Yoshida" . "kazukiyoshida@mail.harvard.edu") (:keywords "languages") (:url . "https://github.com/stan-dev/stan-mode/tree/master/company-stan"))]) @@ -685,35 +688,36 @@ (company-ycmd . [(20180520 1053) ((ycmd (1 3)) (company (0 9 3)) (deferred (0 5 1)) (s (1 11 0)) (dash (2 13 0)) (let-alist (1 0 5)) (f (0 19 0))) "company-mode backend for ycmd" single ((:commit . "c17ff9e0250a9b39d23af37015a2b300e2f36fed") (:url . "https://github.com/abingham/emacs-ycmd"))]) (compdef . [(20200304 611) ((emacs (24 4))) "A local completion definer" single ((:commit . "30fb5846ed851efee641ce8c5d8879ad36cd7ac6") (:authors ("Uros Perisic")) (:maintainer "Uros Perisic") (:keywords "convenience") (:url . "https://gitlab.com/jjzmajic/compdef"))]) (competitive-programming-snippets . [(20201115 1702) ((emacs (26)) (yasnippet (0 8 0))) "Competitive Programming snippets for yasnippet" tar ((:commit . "3b43c1aeaa6676d1d3d0c47e78790db9bee150b6") (:authors ("Seong Yong-ju" . "sei40kr@gmail.com")) (:maintainer "Seong Yong-ju" . "sei40kr@gmail.com") (:keywords "tools") (:url . "https://github.com/sei40kr/competitive-programming-snippets"))]) - (compiler-explorer . [(20210916 1316) ((emacs (26 1)) (request (0 3 0))) "Compiler explorer client (godbolt.org)" single ((:commit . "9ea0cc78ac40f667dfaf9277758a22b9058ca434") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "c" "tools") (:url . "https://github.com/mkcms/compiler-explorer.el"))]) + (compiler-explorer . [(20220807 1136) ((emacs (26 1)) (request (0 3 0))) "Compiler explorer client (godbolt.org)" single ((:commit . "04da0fd822d7e9eca82c993c99f6318df824b652") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "c" "tools") (:url . "https://github.com/mkcms/compiler-explorer.el"))]) (completions-frame . [(20210430 640) ((emacs (26 1))) "Show completions in child frame" single ((:commit . "860e5b97730df7ef5c34584ad164bc69c561db84") (:authors ("Andrii Kolomoiets" . "andreyk.mad@gmail.com")) (:maintainer "Andrii Kolomoiets" . "andreyk.mad@gmail.com") (:keywords "frames") (:url . "https://github.com/muffinmad/emacs-completions-frame"))]) (composable . [(20220608 1148) ((emacs (25 1))) "composable editing" tar ((:commit . "205a69c64ea95ef67070423c31ed70ec44ec980c") (:authors ("Simon Friis Vindum" . "simon@vindum.io")) (:maintainer "Simon Friis Vindum" . "simon@vindum.io") (:keywords "lisp"))]) (composer . [(20200616 1717) ((emacs (24 3)) (s (1 9 0)) (f (0 17)) (seq (1 9)) (php-runtime (0 1 0))) "Interface to PHP Composer" single ((:commit . "7c7f89df226cac69664d7eca5e913b544dc475c5") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "tools" "php" "dependency" "manager") (:url . "https://github.com/zonuexe/composer.el"))]) (comware-router-mode . [(20220108 2111) ((dash (2 16 0)) (emacs (24 3))) "Major mode for editing Comware configuration files" single ((:commit . "cd8c74653c0e221e3dd1ca540496c4b4c7ee4617") (:authors ("Davide Restivo" . "davide.restivo@yahoo.it")) (:maintainer "Davide Restivo" . "davide.restivo@yahoo.it") (:keywords "convenience" "faces") (:url . "https://github.com/daviderestivo/comware-router-mode"))]) (concurrent . [(20161229 330) ((emacs (24 3)) (deferred (0 5 0))) "Concurrent utility functions for emacs lisp" single ((:commit . "2239671d94b38d92e9b28d4e12fd79814cfb9c16") (:authors ("SAKURAI Masashi ")) (:maintainer "SAKURAI Masashi ") (:keywords "deferred" "async" "concurrent") (:url . "https://github.com/kiwanami/emacs-deferred/blob/master/README-concurrent.markdown"))]) - (conda . [(20220724 1857) ((emacs (25 1)) (pythonic (0 1 0)) (dash (2 13 0)) (s (1 11 0)) (f (0 18 2))) "Work with your conda environments" single ((:commit . "a65ed0084876366fd1b238bb2b8b36ee75ac98d8") (:authors ("Rami Chowdhury" . "rami.chowdhury@gmail.com")) (:maintainer "Rami Chowdhury" . "rami.chowdhury@gmail.com") (:keywords "languages" "local" "tools" "python" "environment" "conda") (:url . "http://github.com/necaris/conda.el"))]) + (conda . [(20220814 1919) ((emacs (25 1)) (pythonic (0 1 0)) (dash (2 13 0)) (s (1 11 0)) (f (0 18 2))) "Work with your conda environments" single ((:commit . "e7f7e72cc058318b9c11499c38c3b0125322e6d6") (:authors ("Rami Chowdhury" . "rami.chowdhury@gmail.com")) (:maintainer "Rami Chowdhury" . "rami.chowdhury@gmail.com") (:keywords "languages" "local" "tools" "python" "environment" "conda") (:url . "http://github.com/necaris/conda.el"))]) (config-general-mode . [(20171024 1840) nil "Config::General config file mode" single ((:commit . "b4a8e6ba0bb027a77e4a0f701409f3e57bb2e4c0") (:authors ("T.v.Dein" . "tlinden@cpan.org")) (:maintainer "T.v.Dein" . "tlinden@cpan.org") (:keywords "files") (:url . "https://github.com/tlinden/config-general-mode"))]) (config-parser . [(20160426 1219) ((emacs (24 4))) "a library for parsing config file" single ((:commit . "85d559e7889d8f5b98b8794b79426ae25ec3caa5") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "config") (:url . "https://github.com/lujun9972/el-config-parser"))]) (conkeror-minor-mode . [(20150114 1604) nil "Mode for editing conkeror javascript files." single ((:commit . "476e81c27b056e21c192391fe674a2bf875466b0") (:authors ("Artur Malabarba" . "bruce.connor.am@gmail.com>")) (:maintainer "Artur Malabarba" . "bruce.connor.am@gmail.com>") (:keywords "programming" "tools") (:url . "http://github.com/Bruce-Connor/conkeror-minor-mode"))]) (conllu-mode . [(20200501 2328) ((emacs (25)) (cl-lib (0 5)) (flycheck (30)) (hydra (0 13 0)) (s (1 0))) "editing mode for CoNLL-U files" tar ((:commit . "0db3063572b0de08874822e20570bb153747e6ed") (:authors ("bruno cuconato" . "bcclaro+emacs@gmail.com")) (:maintainer "bruno cuconato" . "bcclaro+emacs@gmail.com") (:keywords "extensions") (:url . "https://github.com/odanoburu/conllu-mode"))]) (connection . [(20191111 446) nil "TCP-based client connection" single ((:commit . "bdf0aa7761d1c1a3bc0652b2fdc4a54b3acdb06a") (:authors ("Torsten Hilbrich" . "torsten.hilbrich@gmx.net")) (:maintainer "Torsten Hilbrich" . "torsten.hilbrich@gmx.net") (:keywords "network"))]) (constant-theme . [(20180921 1012) ((emacs (24 1))) "A calm, dark, almost monochrome color theme." tar ((:commit . "23543a09729569b566175abe1efbe774048d3fa8") (:authors ("Jannis Pohlmann" . "contact@jannispohlmann.de")) (:maintainer "Jannis Pohlmann" . "contact@jannispohlmann.de") (:keywords "themes") (:url . "https://github.com/jannis/emacs-constant-theme"))]) - (consult . [(20220804 821) ((emacs (27 1)) (compat (28 1))) "Consulting completing-read" tar ((:commit . "aaba2b0260a522cea3c733850dd13a9aae803917") (:authors ("Daniel Mendler and Consult contributors")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:url . "https://github.com/minad/consult"))]) + (consult . [(20220807 1302) ((emacs (27 1)) (compat (28 1))) "Consulting completing-read" tar ((:commit . "90536f39db3bd9085971998308ee42176613d68f") (:authors ("Daniel Mendler and Consult contributors")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:url . "https://github.com/minad/consult"))]) (consult-ag . [(20220419 1721) ((emacs (27 1)) (consult (0 16))) "The silver searcher integration using Consult" single ((:commit . "2460ae6829e86c9f1186a852304d919526838cb8") (:authors ("Kanon Kakuno" . "yadex205@outlook.jp")) (:maintainer "Kanon Kakuno" . "yadex205@outlook.jp") (:url . "https://github.com/yadex205/consult-ag"))]) (consult-company . [(20211021 1152) ((emacs (27 1)) (company (0 9)) (consult (0 9))) "Consult frontend for company" single ((:commit . "ef1c553b4a72b23297b55708bf6f6dd1b27cc68e") (:authors ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainer "mohsin kaleem" . "mohkale@kisara.moe") (:url . "https://github.com/mohkale/consult-company"))]) (consult-dash . [(20220621 226) ((emacs (27 2)) (dash-docs (1 4 0)) (consult (0 16))) "Consult front-end for dash-docs" single ((:commit . "0eb8e133a12570f482efcf367dcc7887c15def32") (:authors ("Ravi R Kiran" . "lists.ravi@gmail.com")) (:maintainer "Ravi R Kiran" . "lists.ravi@gmail.com") (:keywords "consult" "dash" "docs") (:url . "https://codeberg.org/ravi/consult-dash"))]) - (consult-dir . [(20220706 1256) ((emacs (26 1)) (consult (0 9)) (project (0 6 0))) "Insert paths into the minibuffer prompt" single ((:commit . "88f1d7cce614fabbfb41dff5338399971835c48c") (:authors ("Karthik Chikmagalur")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "convenience") (:url . "https://github.com/karthink/consult-dir"))]) + (consult-dir . [(20220808 141) ((emacs (26 1)) (consult (0 9)) (project (0 6 0))) "Insert paths into the minibuffer prompt" single ((:commit . "8abf62df088de87175e98adf8f6f5fb93515004c") (:authors ("Karthik Chikmagalur")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "convenience") (:url . "https://github.com/karthink/consult-dir"))]) (consult-eglot . [(20220409 1238) ((emacs (27 1)) (eglot (1 7)) (consult (0 16)) (project (0 3 0))) "A consulting-read interface for eglot" single ((:commit . "0da8801dd8435160ce1f62ad8066bd52e38f5cbd") (:authors ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainer "Mohsin Kaleem") (:keywords "tools" "completion" "lsp") (:url . "https://github.com/mohkale/consult-eglot"))]) (consult-flycheck . [(20220403 1810) ((consult (0 16)) (flycheck (31)) (emacs (27 1))) "Provides the command `consult-flycheck'" single ((:commit . "9b40f136c017fadf6239d7602d16bf73b4ad5198") (:authors ("Daniel Mendler and Consult contributors")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:url . "https://github.com/minad/consult"))]) (consult-flyspell . [(20220419 2044) ((emacs (25 1)) (consult (0 12))) "Consult integration for flyspell" single ((:commit . "396def174495cc77413e2065ef79658a02490dad") (:authors ("Marco Pawłowski")) (:maintainer "Marco Pawłowski") (:keywords "convenience") (:url . "https://gitlab.com/OlMon/consult-flyspell"))]) (consult-ghq . [(20210606 2047) ((emacs (26 1)) (consult (0 8)) (affe (0 1))) "Ghq interface using consult" single ((:commit . "c8619d66bd8f8728e43ed15096078b89eb4d2083") (:authors ("Tomoya Otake" . "tomoya.ton@gmail.com")) (:maintainer "Tomoya Otake" . "tomoya.ton@gmail.com") (:keywords "convenience" "usability" "consult" "ghq") (:url . "https://github.com/tomoya/consult-ghq"))]) (consult-ls-git . [(20220501 1823) ((emacs (27 1)) (consult (0 16))) "Consult integration for git" single ((:commit . "f2398b354994e583ad22af324a129cf94d06009e") (:authors ("Robin Joy")) (:maintainer "Robin Joy") (:keywords "convenience") (:url . "https://github.com/rcj/consult-ls-git"))]) (consult-lsp . [(20220507 856) ((emacs (27 1)) (lsp-mode (5 0)) (consult (0 16)) (f (0 20 0))) "LSP-mode Consult integration" single ((:commit . "19606a03cf854e1b0930c4526ed92c4560dccdc2") (:authors ("Gerry Agbobada")) (:maintainer "Gerry Agbobada") (:keywords "tools" "completion" "lsp") (:url . "https://github.com/gagbo/consult-lsp"))]) + (consult-notes . [(20220822 158) ((emacs (27 1)) (consult (0 17)) (s (1 12 0)) (dash (2 19))) "Manage notes with consult" tar ((:commit . "fd4091dd3655fdbbbb3fa15cfa96babe17c8b8b5") (:authors ("Colin McLear" . "mclear@fastmail.com")) (:maintainer "Colin McLear") (:keywords "convenience") (:url . "https://github.com/mclear-tools/consult-notes"))]) (consult-notmuch . [(20220513 1647) ((emacs (26 1)) (consult (0 9)) (notmuch (0 31))) "Notmuch search using consult" single ((:commit . "4138855cddee0ef126cff6a5fc5ca9c49fd2682d") (:authors ("Jose A Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose A Ortega Ruiz") (:keywords "mail") (:url . "https://codeberg.org/jao/consult-notmuch"))]) (consult-org-roam . [(20220706 627) ((emacs (27 1)) (org-roam (2 2 0)) (consult (0 16))) "Consult integration for org-roam" single ((:commit . "9b51aed939054c54934a6969290ad78587051cde") (:authors ("jgru ")) (:maintainer "jgru ") (:url . "https://github.com/jgru/consult-org-roam"))]) (consult-project-extra . [(20220424 1815) ((emacs (27 1)) (consult (0 17)) (project (0 8 1))) "Consult integration for project.el" single ((:commit . "fa882a0bf9b697ebb59d0dfa2ffd81ea6daabf41") (:authors ("Enrique Kessler Martínez")) (:maintainer "Enrique Kessler Martínez") (:keywords "convenience" "project" "management") (:url . "https://github.com/Qkessler/consult-project-extra"))]) (consult-projectile . [(20220617 1042) ((emacs (25 1)) (consult (0 12)) (projectile (2 5 0))) "Consult integration for projectile" single ((:commit . "5ef1ada3be767ea766255801050210f5d796deec") (:authors ("Marco Pawłowski")) (:maintainer "Marco Pawłowski") (:keywords "convenience") (:url . "https://gitlab.com/OlMon/consult-projectile"))]) - (consult-recoll . [(20220804 1353) ((emacs (26 1)) (consult (0 18))) "Recoll queries using consult" single ((:commit . "a2ee98e0577cae0314b433ffacfc8b9f2ab023d7") (:authors ("Jose A Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose A Ortega Ruiz") (:keywords "docs" "convenience") (:url . "https://codeberg.org/jao/consult-recoll"))]) + (consult-recoll . [(20220823 1338) ((emacs (26 1)) (consult (0 18))) "Recoll queries using consult" single ((:commit . "b3724c704b0d7d942c095dedd9e0ce064a8847c9") (:authors ("Jose A Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose A Ortega Ruiz" . "jao@gnu.org") (:keywords "docs" "convenience") (:url . "https://codeberg.org/jao/consult-recoll"))]) (consult-spotify . [(20211114 2258) ((emacs (26 1)) (consult (0 8)) (espotify (0 1))) "Spotify queries using consult" single ((:commit . "ea6d6021e5acc550560325db2f09198839ee702f") (:authors ("Jose A Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose A Ortega Ruiz") (:keywords "multimedia") (:url . "https://codeberg.org/jao/espotify"))]) (consult-yasnippet . [(20220724 1338) ((emacs (27 1)) (yasnippet (0 14)) (consult (0 16))) "A consulting-read interface for yasnippet" single ((:commit . "ae0450889484f23dc4ec37518852a2c61b89f184") (:authors ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainer "mohsin kaleem" . "mohkale@kisara.moe") (:url . "https://github.com/mohkale/consult-yasnippet"))]) (contextual . [(20180726 800) ((emacs (24)) (dash (2 12 1)) (cl-lib (0 5))) "Contextual profile management system" single ((:commit . "e3c0de4a2e06757a0e8407c3c6e75930026191e3") (:authors ("Alexander Kahl" . "ak@sodosopa.io")) (:maintainer "Alexander Kahl" . "ak@sodosopa.io") (:keywords "convenience" "tools") (:url . "https://github.com/lshift-de/contextual"))]) @@ -721,7 +725,7 @@ (contrast-color . [(20160903 1807) ((emacs (24 3)) (cl-lib (0 5))) "Pick best contrast color for you" single ((:commit . "c5fb77a211ebbef3185ada37bea7420534c33f94") (:authors ("Yuta Yamada ")) (:maintainer "Yuta Yamada ") (:keywords "color" "convenience") (:url . "https://github.com/yuutayamada/contrast-color-el"))]) (control-mode . [(20160624 1710) nil "A \"control\" mode, similar to vim's \"normal\" mode" single ((:commit . "72d6179b60adc438aada74083b2bf4264b575de3") (:authors ("Stephen Marsh" . "stephen.david.marsh@gmail.com")) (:maintainer "Stephen Marsh" . "stephen.david.marsh@gmail.com") (:keywords "convenience" "emulations") (:url . "https://github.com/stephendavidmarsh/control-mode"))]) (conventional-changelog . [(20211212 1158) ((emacs (27)) (transient (0 3 6))) "Conventional Changelog Generator" single ((:commit . "40c2ee58364422b776e81dc153918205bfbeda86") (:authors ("liuyinz" . "liuyinz95@gmail.com")) (:maintainer "liuyinz" . "liuyinz95@gmail.com") (:keywords "tools") (:url . "https://github.com/liuyinz/emacs-conventional-changelog"))]) - (cool-mode . [(20220612 1904) ((emacs (25))) "Major mode for cool compiler language" tar ((:commit . "961e66956412a1dd63f79473a8273da8853f7179") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:url . "https://github.com/nverno/cool-mode"))]) + (cool-mode . [(20220612 1904) ((emacs (25))) "Major mode for cool compiler language" tar ((:commit . "8b2f5d0f7d821599ef4ab99c5ba42d55cd04168d") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:url . "https://github.com/nverno/cool-mode"))]) (copy-as-format . [(20190523 258) ((cl-lib (0 5))) "Copy buffer locations as GitHub/Slack/JIRA etc... formatted code" single ((:commit . "a0962b670e26b723ce304b14e3397da453aef84e") (:authors ("Skye Shaw" . "skye.shaw@gmail.com")) (:maintainer "Skye Shaw" . "skye.shaw@gmail.com") (:keywords "github" "slack" "jira" "hipchat" "gitlab" "bitbucket" "org-mode" "pod" "rst" "asciidoc" "tools" "convenience") (:url . "https://github.com/sshaw/copy-as-format"))]) (copy-file-on-save . [(20200616 518) ((emacs (24 3)) (cl-lib (0 5)) (f (0 17)) (s (1 7 0))) "Copy file on save, automatic deployment it." single ((:commit . "811c8fe638c5616b6471525421e61a4470be3b52") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "files" "comm" "deploy") (:url . "https://github.com/emacs-php/emacs-auto-deployment"))]) (copyit . [(20190919 1258) ((emacs (24 3)) (s (1 9 0))) "Copy it, yank anything!" single ((:commit . "c4f2c28e5b6270e8e3364341619f1154bb4e682e") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "convenience" "yank" "clipboard") (:url . "https://github.com/zonuexe/emacs-copyit"))]) @@ -740,7 +744,7 @@ (counsel-css . [(20211115 1755) ((emacs (24 4)) (counsel (0 7 0)) (cl-lib (0 5))) "stylesheet-selector-aware swiper" single ((:commit . "8e9c0515fc952452eee786d8ebb43d48ea86c9f8") (:authors ("Henrik Lissner ")) (:maintainer "Henrik Lissner" . "contact@henrik.io") (:keywords "convenience" "tools" "counsel" "swiper" "selector" "css" "less" "scss") (:url . "https://github.com/hlissner/emacs-counsel-css"))]) (counsel-dash . [(20200103 1411) ((emacs (24 4)) (dash-docs (1 4 0)) (counsel (0 8 0)) (cl-lib (0 5))) "Browse dash docsets using Ivy" single ((:commit . "370d5f6f14b5294d0eb717f7b2a6a8e93df1ed24") (:authors ("Nathan Kot" . "nk@nathankot.com")) (:maintainer "Nathan Kot" . "nk@nathankot.com") (:keywords "dash" "ivy" "counsel") (:url . "https://github.com/nathankot/counsel-dash"))]) (counsel-edit-mode . [(20210824 1504) ((emacs (26 1)) (ht (2 3)) (s (1 12 0)) (counsel (0 10 0))) "Edit results of counsel commands in-place" single ((:commit . "378803ac0040c04762ff001ab1aca7d4325ecf22") (:authors ("Tyler Dodge")) (:maintainer "Tyler Dodge") (:keywords "convenience" "matching") (:url . "https://github.com/tyler-dodge/counsel-edit-mode"))]) - (counsel-etags . [(20220728 1351) ((emacs (26 1)) (counsel (0 13 4))) "Fast and complete Ctags/Etags solution using ivy" single ((:commit . "b109dff190db6706e32ad08deb64b77fc8ad75c7") (:authors ("Chen Bin ")) (:maintainer "Chen Bin ") (:keywords "tools" "convenience") (:url . "http://github.com/redguardtoo/counsel-etags"))]) + (counsel-etags . [(20220825 259) ((emacs (26 1)) (counsel (0 13 4))) "Fast and complete Ctags/Etags solution using ivy" single ((:commit . "e38b12771183fd43fb94c9fa562b20b5efeefc1b") (:authors ("Chen Bin ")) (:maintainer "Chen Bin ") (:keywords "tools" "convenience") (:url . "http://github.com/redguardtoo/counsel-etags"))]) (counsel-fd . [(20220514 2227) ((counsel (0 12 0))) "counsel interface for fd" single ((:commit . "c1ba2e36fe69111e7f6f42ea1b0e3b7a45d96de0") (:keywords "tools") (:url . "https://github.com/CsBigDataHub/counsel-fd"))]) (counsel-ffdata . [(20191017 1237) ((emacs (25 1)) (counsel (0 11 0)) (emacsql (3 0 0))) "Use ivy to access firefox data" single ((:commit . "88c2348c4039d9e562bd3d9a364708b01037c283") (:authors ("Zhu Zihao" . "all_but_last@163.com")) (:maintainer "Zhu Zihao" . "all_but_last@163.com") (:keywords "convenience" "tools" "matching") (:url . "https://github.com/cireu/counsel-ffdata"))]) (counsel-gtags . [(20210222 1803) ((emacs (25 1)) (counsel (0 8 0)) (seq (1 0))) "ivy for GNU global" single ((:commit . "1d52eaeffeb60266434d4f7416a108ca058fde91") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com") ("Felipe Lema" . "felipelema@mortemale.org") ("Jimmy Aguilar Mena" . "spacibba@aol.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/FelipeLema/emacs-counsel-gtags"))]) @@ -770,7 +774,7 @@ (cql-mode . [(20190315 225) ((emacs (24))) "Major mode for editting CQLs" single ((:commit . "d400c046850d3cf404778b2c47d6be4ff84ca04b") (:authors ("Yuki Inoue ")) (:maintainer "Yuki Inoue ") (:keywords "cql" "cassandra") (:url . "https://github.com/Yuki-Inoue/cql-mode"))]) (cquery . [(20190118 542) ((emacs (25 1)) (lsp-mode (3 4)) (dash (0 13))) "cquery client for lsp-mode" tar ((:commit . "555e50984ebda177421fdcdc8c76cb29235d9694") (:authors ("Tobias Pisani")) (:maintainer "Tobias Pisani") (:keywords "languages" "lsp" "c++") (:url . "https://github.com/jacobdufault/cquery"))]) (crappy-jsp-mode . [(20140311 931) nil "A pretty crappy major-mode for jsp." single ((:commit . "6c45ab92b452411cc0fab9bcee2f456276b4fc40") (:keywords "jsp" "major" "mode"))]) - (creamsody-theme . [(20220616 119) ((autothemer (0 2))) "Straight from the soda fountain." single ((:commit . "21add9e946e2d00c15b609e75d65aa4c292bc7a2") (:url . "http://github.com/emacsfodder/emacs-theme-creamsody"))]) + (creamsody-theme . [(20220819 754) ((autothemer (0 2)) (emacs (24))) "Straight from the soda fountain" tar ((:commit . "0823101e9886cc31cc48f2d79f354a4fd2c2b9f3") (:url . "http://github.com/emacsfodder/emacs-theme-creamsody"))]) (create-link . [(20220621 1440) ((emacs (25 1))) "Smart format link generator" single ((:commit . "276fafcc6fb568ede256c8d459c3beb408ad9b46") (:authors ("Kijima Daigo" . "norimaking777@gmail.com")) (:maintainer "Kijima Daigo" . "norimaking777@gmail.com") (:keywords "link" "format" "browser" "convenience") (:url . "https://github.com/kijimaD/create-link"))]) (creds . [(20140510 1706) ((s (1 9 0)) (dash (2 5 0))) "A parser credentials file library (not limited to credentials entries)" tar ((:commit . "b059397a7d59481f05fbb1bb9c8d3c2c69226482") (:authors ("Antoine R. Dumont ")) (:maintainer "Antoine R. Dumont ") (:keywords "credentials") (:url . "https://github.com/ardumont/emacs-creds"))]) (creole . [(20140924 1500) ((noflet (0 0 3)) (kv (0 0 17))) "A parser for the Creole Wiki language" single ((:commit . "7d5cffe93857f6c75ca09ac79c0e47b8d4410e53") (:authors ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp" "creole" "wiki"))]) @@ -784,7 +788,7 @@ (crystal-mode . [(20220104 2146) ((emacs (24 4))) "Major mode for editing Crystal files" single ((:commit . "96a8058205b24b513d0b9307db32f05e30f9570b") (:keywords "languages" "crystal") (:url . "https://github.com/crystal-lang-tools/emacs-crystal-mode"))]) (crystal-playground . [(20180830 501) ((emacs (25)) (crystal-mode (0 1 2))) "Local crystal playground for short code snippets." single ((:commit . "fb3691b1281207b459c5be50015a626f356dc40d") (:authors ("Jason Howell")) (:maintainer "Jason Howell") (:keywords "tools" "crystal") (:url . "https://github.com/jasonrobot/crystal-playground"))]) (csgo-conf-mode . [(20161209 1619) nil "CS:GO Configuration files syntax highlighting" single ((:commit . "57e7224f87a3ccc76b5564cc95fa0ff43bb6807c") (:authors ("Guillermo Robles" . "guillerobles1995@gmail.com")) (:maintainer "Guillermo Robles" . "guillerobles1995@gmail.com") (:keywords "languages") (:url . "https://github.com/wynro/emacs-csgo-conf-mode"))]) - (csharp-mode . [(20220704 1242) ((emacs (26 1))) "C# mode derived mode" tar ((:commit . "396b5e9b32bb58c9f984a03f5075455af87f7307") (:authors ("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"))]) + (csharp-mode . [(20220819 1351) ((emacs (26 1))) "C# mode derived mode" tar ((:commit . "6e9d1f6b9900d3b8aa4106abf84a89311dfed85f") (:authors ("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 . [(20211215 1925) ((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 . "44c49e5a9262ede4b4477bafb13b42b1ba047b9c") (:authors ("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")) (: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" single ((:commit . "386a5defc8543a3b87820f1761c075c7d1d93b38") (:authors (nil . "Kyung Mo Kweon and contributors")) (:maintainer nil . "Kyung Mo Kweon and contributors") (:keywords "convenience" "usability" "css") (:url . "https://github.com/kkweon/emacs-css-autoprefixer"))]) @@ -804,7 +808,7 @@ (cubicle-mode . [(20171009 1957) nil "Major mode for the Cubicle model checker" single ((:commit . "00f09bb2d4bb496549775e770d7ada08bc1e4866") (:authors ("Alain Mebsout")) (:maintainer "Alain Mebsout"))]) (cucumber-goto-step . [(20131210 519) ((pcre2el (1 5))) "Jump to cucumber step definition" single ((:commit . "f2713ffb26ebe1b757d1f2ea80e900b55e5895aa") (:authors ("Glen Stampoultzis" . "gstamp@gmail.com")) (:maintainer "Glen Stampoultzis" . "gstamp@gmail.com") (:url . "http://orthogonal.me"))]) (cuda-mode . [(20201013 2230) nil "NVIDIA CUDA Major Mode" single ((:commit . "7f593518fd135fc6af994024bcb47986dfa502d2") (:authors ("Jack Morrison" . "jackmorrison1@gmail.com")) (:maintainer "Jack Morrison" . "jackmorrison1@gmail.com") (:keywords "c" "languages"))]) - (cue-mode . [(20220512 2104) ((emacs (25 1))) "Major mode for CUE language files" single ((:commit . "f98b9f9088fcb66c97f9200f6c8a0cd16c11caae") (:authors ("Russell Sim" . "russell.sim@gmail.com")) (:maintainer "Russell Sim" . "russell.sim@gmail.com") (:keywords "data" "languages") (:url . "https://github.com/russell/cue-mode"))]) + (cue-mode . [(20220811 1938) ((emacs (25 1))) "Major mode for CUE language files" single ((:commit . "31c671d56e7884fa87ad0f1d27d0bb439dc65380") (:authors ("Russell Sim" . "russell.sim@gmail.com")) (:maintainer "Russell Sim" . "russell.sim@gmail.com") (:keywords "data" "languages") (:url . "https://github.com/russell/cue-mode"))]) (curl-to-elisp . [(20201124 1012) ((emacs (25 1))) "Convert cURL command to Emacs Lisp code" single ((:commit . "63d8d9c6d5efb8af8aa88042bfc0690ba699ef64") (:authors ("Xu Chunyang")) (:maintainer "Xu Chunyang") (:keywords "lisp") (:url . "https://github.com/xuchunyang/curl-to-elisp"))]) (currency-convert . [(20210427 2032) ((emacs (24 4))) "Currency converter" single ((:commit . "12805ea66aa8421de5eedda39d23f709de634460") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "comm" "convenience" "i18n") (:url . "https://github.com/lassik/emacs-currency-convert"))]) (current-word-highlight . [(20210323 1401) nil "Highlight the current word minor mode" single ((:commit . "d860f4e170ffa4cef840da93647f458cc409d554") (:authors ("Kijima Daigo" . "norimaking777@gmail.com")) (:maintainer "Kijima Daigo" . "norimaking777@gmail.com") (:keywords "highlight" "face" "convenience" "word") (:url . "https://github.com/kijimaD/current-word-highlight"))]) @@ -812,6 +816,7 @@ (cursor-flash . [(20210722 445) ((emacs (24 3))) "Highlight the cursor on buffer/window-switch" single ((:commit . "6bb54a1e2e1bf9df80926718b1b8b9ee49080484") (:keywords "convenience" "faces" "maint") (:url . "https://github.com/Boruch-Baum/emacs-cursor-flash"))]) (cursor-test . [(20131207 1732) ((emacs (24))) "testing library for cursor position in emacs." single ((:commit . "e09956e048b88fd2ee8dd90b5678baed8b04d31b") (:authors ("ainame")) (:maintainer "ainame") (:url . "https://github.com/ainame/cursor-test.el"))]) (cwl-mode . [(20210510 1150) ((yaml-mode (0 0 13)) (emacs (24 4))) "A major mode for editing CWL" single ((:commit . "23a333119efaac78453cba95d316109805bd6aec") (:authors ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainer "Tomoya Tanjo" . "ttanjo@gmail.com") (:keywords "languages" "cwl" "common workflow language") (:url . "https://github.com/tom-tan/cwl-mode"))]) + (cyanometric-theme . [(20220822 301) ((autothemer (0 2)) (emacs (24))) "A Theme with overwhelming bias towards cyan" single ((:commit . "9b20e33a8cc2c76bfe6ad45916be6881386707f5") (:url . "http://github.com/emacsfodder/emacs-theme-cyanometric"))]) (cyberpunk-2019-theme . [(20191008 1133) ((emacs (24 1))) "A retina-scorching cyberpunk theme" single ((:commit . "7e40c37210c363b2819fd9bb98a73101d7a3c206") (:authors ("Alex Lynham" . "alex@lynh.am")) (:maintainer "Alex Lynham" . "alex@lynh.am") (:keywords "cyberpunk" "theme" "themes") (:url . "https://github.com/the-frey/cyberpunk-2019"))]) (cyberpunk-theme . [(20200601 1632) nil "Cyberpunk Color Theme" single ((:commit . "cbd0d7193e69ff9e98262eb06aee3d27667ff5f5") (:authors ("Nicholas M. Van Horn" . "nvanhorn@protonmail.com")) (:maintainer "Nicholas M. Van Horn" . "nvanhorn@protonmail.com") (:keywords "color" "theme" "cyberpunk") (:url . "https://github.com/n3mo/cyberpunk-theme.el"))]) (cycbuf . [(20131203 2037) nil "Cycle buffers, inspired by swbuff.el, swbuff-x.el, and bs.el" single ((:commit . "1079b41c3eb27d65b66d4399959bb6253f84858e") (:authors ("Martin Pohlack martinp (at) gmx.de")) (:maintainer "Martin Pohlack martinp (at) gmx.de") (:keywords "files" "convenience" "buffer switching") (:url . "https://github.com/martinp26/cycbuf"))]) @@ -820,7 +825,7 @@ (cycle-themes . [(20150403 309) ((cl-lib (0 5))) "A global minor mode to make switching themes easier" single ((:commit . "6e125d11fdbc6b78fc9f219eb2609a5e29815898") (:keywords "themes" "utility" "global minor mode") (:url . "http://github.com/toroidal-code/cycle-themes.el"))]) (cyphejor . [(20210816 1607) ((emacs (24 4))) "Shorten major mode names using user-defined rules" single ((:commit . "576d237a46be79449a22e3a7912a3464d7b0c233") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "mode-line" "major-mode") (:url . "https://github.com/mrkkrp/cyphejor"))]) (cypher-mode . [(20151110 1142) nil "major mode for editing cypher scripts" single ((:commit . "ce8543d7877c736c574a17b49874c9dcdc7a06d6") (:authors ("François-Xavier Bois ")) (:maintainer "François-Xavier Bois") (:keywords "cypher" "graph") (:url . "http://github.com/fxbois/cypher-mode"))]) - (cython-mode . [(20211111 1407) nil "Major mode for editing Cython files" single ((:commit . "8afd932c28d08428d45bba03d6b642093e4c973b"))]) + (cython-mode . [(20211111 1407) nil "Major mode for editing Cython files" single ((:commit . "3424926e9c8f03061b55516d2516a9f98999399e"))]) (czech-holidays . [(20160113 1752) nil "Adds a list of Czech public holidays to Emacs calendar" single ((:commit . "d136fa09a152b3cd80db6d55c7b4ddfe07b90fbf") (:authors ("David Chkhikvadze" . "david.chk@outlook.com")) (:maintainer "David Chkhikvadze" . "david.chk@outlook.com") (:keywords "calendar"))]) (d-mode . [(20220601 1949) ((emacs (25 1))) "D Programming Language major mode for (X)Emacs" single ((:commit . "024aca97d07e72bf3500fb6bf0cdf50c4992a741") (:authors ("William Baxter")) (:maintainer "Russel Winder" . "russel@winder.org.uk") (:keywords "d" "programming" "language" "emacs" "cc-mode"))]) (dactyl-mode . [(20140906 1725) nil "Major mode for editing Pentadactyl config files" single ((:commit . "cc55fe6b987271d9647492b8df4c812d884f661f") (:keywords "languages" "vim") (:url . "https://github.com/luxbock/dactyl-mode"))]) @@ -830,7 +835,7 @@ (dakrone-theme . [(20170801 1933) nil "dakrone's custom dark theme" single ((:commit . "232ad1be5f3572dcbdf528f1655109aa355a6937") (:authors ("Lee Hinman ")) (:maintainer "Lee Hinman ") (:keywords "color" "themes") (:url . "https://github.com/dakrone/dakrone-theme"))]) (danneskjold-theme . [(20220316 1101) nil "Beautiful high-contrast Emacs theme." tar ((:commit . "054c0b9bc9cefb53a4065096e66707d20885c461") (:authors ("Dmitry Akatov" . "akatovda@yandex.com")) (:maintainer "Dmitry Akatov" . "akatovda@yandex.com") (:url . "https://github.com/rails-to-cosmos/"))]) (dante . [(20220429 1454) ((dash (2 12 0)) (emacs (25 1)) (f (0 19 0)) (flycheck (0 30)) (company (0 9)) (haskell-mode (13 14)) (s (1 11 0)) (lcr (1 0))) "Development mode for Haskell" single ((:commit . "b81081c2eb8dcbd7e67e05cf5e1991df6cf3e57c") (:authors ("Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com")) (:maintainer "Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com") (:keywords "haskell" "tools") (:url . "https://github.com/jyp/dante"))]) - (dap-mode . [(20220802 617) ((emacs (26 1)) (dash (2 18 0)) (lsp-mode (6 0)) (bui (1 1 0)) (f (0 20 0)) (s (1 12 0)) (lsp-treemacs (0 1)) (posframe (0 7 0)) (ht (2 3)) (lsp-docker (1 0 0))) "Debug Adapter Protocol mode" tar ((:commit . "e2a37cc0a4ec2da858022badd33ccc086b283075") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "languages" "debug") (:url . "https://github.com/emacs-lsp/dap-mode"))]) + (dap-mode . [(20220824 623) ((emacs (26 1)) (dash (2 18 0)) (lsp-mode (6 0)) (bui (1 1 0)) (f (0 20 0)) (s (1 12 0)) (lsp-treemacs (0 1)) (posframe (0 7 0)) (ht (2 3)) (lsp-docker (1 0 0))) "Debug Adapter Protocol mode" tar ((:commit . "b5a617ff916ea6c50d6d7a6b5584d51a2ea197a0") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "languages" "debug") (:url . "https://github.com/emacs-lsp/dap-mode"))]) (darcsum . [(20190316 2215) nil "a pcl-cvs like interface for managing darcs patches" single ((:commit . "6a8b690539d133c5e3d17cb23fe4365fbb6fb493") (:authors ("John Wiegley" . "johnw@gnu.org")) (:maintainer "John Wiegley" . "johnw@gnu.org") (:keywords "completion" "convenience" "tools" "vc"))]) (darcula-theme . [(20171227 1845) nil "Inspired by IntelliJ's Darcula theme" single ((:commit . "d9b82b58ded9014985be6658f4ab17e26ed9e93e") (:authors ("Sam Halliday" . "Sam.Halliday@gmail.com")) (:maintainer "Sam Halliday" . "Sam.Halliday@gmail.com") (:keywords "faces") (:url . "https://gitlab.com/fommil/emacs-darcula-theme"))]) (dark-krystal-theme . [(20170808 1300) ((emacs (24 0))) "an Emacs 24 theme based on Dark Krystal (tmTheme)" single ((:commit . "79084b99665dc9ffb0ec62cc092349a5ecebebbc") (:authors ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) @@ -839,7 +844,7 @@ (darkburn-theme . [(20170423 1652) nil "A not-so-low contrast color theme for Emacs." single ((:commit . "0af794ff7fac19778ac8a7efb92455c6f6c2158f") (:authors ("Jonas Gorauskas" . "jgorauskas@gmail.com")) (:maintainer "Jonas Gorauskas" . "jgorauskas@gmail.com") (:url . "http://github.com/gorauskas/darkburn-theme"))]) (darkmine-theme . [(20160406 624) nil "Yet another emacs dark color theme." single ((:commit . "7f7e82ca03bcad52911fa41fb3e204e32d6ee63e") (:authors ("Pierre Lecocq" . "pierre.lecocq@gmail.com")) (:maintainer "Pierre Lecocq" . "pierre.lecocq@gmail.com") (:url . "https://github.com/pierre-lecocq/darkmine-theme"))]) (darkokai-theme . [(20200614 1452) nil "A darker variant on Monokai." single ((:commit . "5820aeddfc8c869ba840cc534eba776936656a66") (:url . "http://github.com/sjrmanning/darkokai"))]) - (darktooth-theme . [(20201215 822) ((autothemer (0 2))) "From the darkness... it watches" single ((:commit . "ec03b30ee7f43f89ca4c382bb3fe4ee560c028a8") (:url . "http://github.com/emacsfodder/emacs-theme-darktooth"))]) + (darktooth-theme . [(20220819 754) ((emacs (27 1)) (autothemer (0 2))) "From the darkness... it watches" tar ((:commit . "d6f3876d686b9af5a5eb80bc55ae3d17f8ec42b8") (:url . "http://github.com/emacsfodder/emacs-theme-darktooth"))]) (dart-mode . [(20220401 0) ((emacs (24 3))) "Major mode for editing Dart files" single ((:commit . "9c846769abd37f7fdc7ba8388d1f3a2b844b75e3") (:authors ("https://github.com/bradyt/dart-mode/issues")) (:maintainer "https://github.com/bradyt/dart-mode/issues") (:keywords "languages") (:url . "https://github.com/bradyt/dart-mode"))]) (dart-server . [(20210501 1445) ((emacs (24 5)) (cl-lib (0 5)) (dash (2 10 0)) (flycheck (0 23)) (s (1 10))) "Minor mode for editing Dart files" single ((:commit . "75562baf9a89b7e314bc2f795f6ecdc5d1f2cc8c") (:authors ("Natalie Weizenbaum") ("Brady Trainor" . "mail@bradyt.com")) (:maintainer "Brady Trainor" . "mail@bradyt.com") (:keywords "languages") (:url . "https://github.com/bradyt/dart-server"))]) (dash . [(20220608 1931) ((emacs (24))) "A modern list library for Emacs" tar ((:commit . "0ac1ecf6b56eb67bb81a3cf70f8d4354b5782341") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "extensions" "lisp") (:url . "https://github.com/magnars/dash.el"))]) @@ -847,9 +852,9 @@ (dash-at-point . [(20211023 104) nil "Search the word at point with Dash" single ((:commit . "fba1a6f42ea51d05110e12c62bdced664059eb55") (:authors ("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")) (:maintainer "Raimon Grau" . "raimonster@gmail.com") (:keywords "docs") (:url . "http://github.com/areina/helm-dash"))]) (dash-functional . [(20210210 1449) ((dash (2 18 0))) "Collection of useful combinators for Emacs Lisp" single ((:commit . "0ac1ecf6b56eb67bb81a3cf70f8d4354b5782341") (:authors ("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 . [(20220717 905) ((emacs (26 1))) "A startup screen extracted from Spacemacs" tar ((:commit . "36c8da41bca977707c42bd9b13cdf39380b957d7") (:authors ("Rakan Al-Hneiti" . "rakan.alhneiti@gmail.com")) (:maintainer "Jesús Martínez" . "jesusmartinez93@gmail.com") (:keywords "startup" "screen" "tools" "dashboard") (:url . "https://github.com/emacs-dashboard/emacs-dashboard"))]) + (dashboard . [(20220809 1358) ((emacs (26 1))) "A startup screen extracted from Spacemacs" tar ((:commit . "f6ac4a2ba87bd669d196bd4f53ac59191b789dcd") (:authors ("Rakan Al-Hneiti" . "rakan.alhneiti@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" single ((:commit . "dd5f8ec998d7b7bf162b4eb72474b683b8aa0a14") (:authors ("Hayato KAJIYAMA" . "kaji1216@gmail.com")) (:maintainer "Hayato KAJIYAMA" . "kaji1216@gmail.com") (:url . "https://github.com/hyakt/emacs-dashboard-hackernews"))]) - (dashboard-ls . [(20220704 633) ((emacs (24 3)) (dashboard (1 2 5))) "Display files/directories in current directory on Dashboard" single ((:commit . "62cc1aa84c9f5a6657fcd5032574a7670eac5aa0") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "directory" "file" "show") (:url . "https://github.com/emacs-dashboard/dashboard-ls"))]) + (dashboard-ls . [(20220704 633) ((emacs (24 3)) (dashboard (1 2 5))) "Display files/directories in current directory on Dashboard" single ((:commit . "1f8c66c7d05c3d350f6986841adaa3294f1699d2") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, 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." single ((:commit . "7675c138e9df8fe2c626e7ba9bbb8b6717671a41") (:authors ("Jason Duncan" . "jasond496@msn.com")) (:maintainer "Jason Duncan" . "jasond496@msn.com") (:url . "https://github.com/functionreturnfunction/dashboard-project-status"))]) (date-at-point . [(20150308 1243) nil "Add `date' to `thing-at-point' function" single ((:commit . "38df823d05df08ec0748a4185113fae5f99090e9") (:authors ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "convenience") (:url . "https://github.com/alezost/date-at-point.el"))]) (date-field . [(20141129 105) ((dash (2 9 0)) (log4e (0 2 0)) (yaxception (0 3 2))) "Date widget" single ((:commit . "11c9170d1f7b343233f7716d4c0a62be024c1654") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "widgets") (:url . "https://github.com/aki2o/emacs-date-field"))]) @@ -875,7 +880,7 @@ (default-font-presets . [(20220731 2219) ((emacs (26 1))) "Support selecting fonts from a list of presets" single ((:commit . "80380aa053c78b7126275e269e80d8988ba3f1e3") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-default-font-presets"))]) (default-text-scale . [(20191226 2234) ((emacs (24))) "Easily adjust the font size in all frames" single ((:commit . "bfc0987c37e93742255d3b23d86c17096fda8e7e") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "frames" "faces") (:url . "https://github.com/purcell/default-text-scale"))]) (deferred . [(20170901 1330) ((emacs (24 4))) "Simple asynchronous functions for emacs lisp" single ((:commit . "2239671d94b38d92e9b28d4e12fd79814cfb9c16") (:authors ("SAKURAI Masashi ")) (:maintainer "SAKURAI Masashi ") (:keywords "deferred" "async") (:url . "https://github.com/kiwanami/emacs-deferred"))]) - (define-it . [(20220713 744) ((emacs (25 1)) (s (1 12 0)) (popup (0 5 3)) (pos-tip (0 4 6)) (posframe (1 1 7)) (define-word (0 1 0)) (google-translate (0 11 18)) (wiki-summary (0 1))) "Define, translate, wiki the word" single ((:commit . "de026f399d5b7fa9286f7733b2e3416c6f234372") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "dictionary" "explanation" "search" "wiki") (:url . "https://github.com/jcs-elpa/define-it"))]) + (define-it . [(20220713 744) ((emacs (25 1)) (s (1 12 0)) (popup (0 5 3)) (pos-tip (0 4 6)) (posframe (1 1 7)) (define-word (0 1 0)) (google-translate (0 11 18)) (wiki-summary (0 1))) "Define, translate, wiki the word" single ((:commit . "c2ca045c76a63ebea45da3808bf1c1df791beae8") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "dictionary" "explanation" "search" "wiki") (:url . "https://github.com/jcs-elpa/define-it"))]) (define-word . [(20220104 1848) ((emacs (24 3))) "display the definition of word at point." single ((:commit . "31a8c67405afa99d0e25e7c86a4ee7ef84a808fe") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "dictionary" "convenience") (:url . "https://github.com/abo-abo/define-word"))]) (defproject . [(20151201 2219) ((emacs (24))) "Manager dir-locals and project specific variables" single ((:commit . "674d48a5e34cb4bba76faa38ee901322ec649086") (:authors (nil . "")) (:maintainer nil . "") (:keywords "convenience") (:url . "https://github.com/kotfic/defproject"))]) (defrepeater . [(20180830 410) ((emacs (25 2)) (s (1 12 0))) "Easily make commands repeatable" single ((:commit . "9c027a2561fe141dcfb79f75fcaee36cd0386ec1") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "convenience") (:url . "http://github.com/alphapapa/defrepeater.el"))]) @@ -891,9 +896,9 @@ (desktop-environment . [(20220425 1834) ((emacs (25 1))) "Helps you control your GNU/Linux computer" single ((:commit . "2863dc3d66aed9052c8af39cc8c8c264be300560") (:authors ("Damien Cassou , Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Damien Cassou , Nicolas Petton" . "nicolas@petton.fr") (:url . "https://gitlab.petton.fr/DamienCassou/desktop-environment"))]) (desktop-mail-user-agent . [(20210519 1008) ((emacs (24 3))) "Call OS default mail program to compose mail" single ((:commit . "caac672ef7e4ddced960fa31cef3a6ba5d7ab451") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "mail") (:url . "https://github.com/lassik/emacs-desktop-mail-user-agent"))]) (desktop-registry . [(20140119 2143) nil "Keep a central registry of desktop files" single ((:commit . "244c2e7f9f0a1050aa8a47ad0b38f4e4584682dd") (:authors ("Tom Willemse" . "tom@ryuslash.org")) (:maintainer "Tom Willemse" . "tom@ryuslash.org") (:keywords "convenience") (:url . "http://projects.ryuslash.org/desktop-registry/"))]) - (detached . [(20220718 1501) ((emacs (27 1))) "A package to launch, and manage, detached processes" tar ((:commit . "d8e657cc569da2ad0f1916aa0b5dbc444f0675ff") (:authors ("Niklas Eklund" . "niklas.eklund@posteo.net")) (:maintainer "Niklas Eklund" . "niklas.eklund@posteo.net") (:keywords "convenience" "processes") (:url . "https://sr.ht/~niklaseklund/detached.el/"))]) + (detached . [(20220823 2025) ((emacs (27 1))) "A package to launch, and manage, detached processes" tar ((:commit . "8a76055290aae3e9b555a055498e71591032e995") (:authors ("Niklas Eklund" . "niklas.eklund@posteo.net")) (:maintainer "Niklas Eklund" . "niklas.eklund@posteo.net") (:keywords "convenience" "processes") (:url . "https://sr.ht/~niklaseklund/detached.el/"))]) (detour . [(20181122 2138) ((emacs (24 4))) "Take a quick detour and return" single ((:commit . "1ff23c236e18971ed1077840daf047cde79a45ee") (:authors ("Stefan Kamphausen ")) (:maintainer "Stefan Kamphausen ") (:keywords "convenience" "abbrev") (:url . "https://github.com/ska2342/detour/"))]) - (devdocs . [(20220729 923) ((emacs (27 1))) "Emacs viewer for DevDocs" single ((:commit . "60099be5fc5c90d5adc2795b3bfacb492a0adb88") (:authors ("Augusto Stoffel" . "arstoffel@gmail.com")) (:maintainer "Augusto Stoffel" . "arstoffel@gmail.com") (:keywords "help") (:url . "https://github.com/astoff/devdocs.el"))]) + (devdocs . [(20220811 703) ((emacs (27 1))) "Emacs viewer for DevDocs" single ((:commit . "61ce83b79dc64e2f99d7f016a09b97e14b331459") (:authors ("Augusto Stoffel" . "arstoffel@gmail.com")) (:maintainer "Augusto Stoffel" . "arstoffel@gmail.com") (:keywords "help") (:url . "https://github.com/astoff/devdocs.el"))]) (devdocs-browser . [(20211218 949) ((emacs (27 1))) "Browse devdocs.io documents using EWW" single ((:commit . "a46a2cdb83ed27869befe56fea04914a33252b3a") (:authors ("blahgeek" . "i@blahgeek.com")) (:maintainer "blahgeek" . "i@blahgeek.com") (:keywords "docs" "help" "tools") (:url . "https://github.com/blahgeek/emacs-devdocs-browser"))]) (dfmt . [(20170728 1023) nil "Emacs Interface to D indenting/formatting tool dfmt." single ((:commit . "21b9094e907b7ac53f5ecb4ff4539613a9d12434") (:authors ("Per Nordlöw")) (:maintainer "Kirill Babikhin ") (:keywords "tools" "convenience" "languages" "dlang") (:url . "https://github.com/qsimpleq/elisp-dfmt"))]) (dhall-mode . [(20220519 1115) ((emacs (24 4)) (reformatter (0 3))) "Major mode for the dhall configuration language" single ((:commit . "c77f1c1e75b6d2725019c5275fc102ae98d25628") (:authors ("Sibi Prabakaran" . "sibi@psibi.in")) (:maintainer "Sibi Prabakaran" . "sibi@psibi.in") (:keywords "languages") (:url . "https://github.com/psibi/dhall-mode"))]) @@ -905,7 +910,7 @@ (didyoumean . [(20200905 1843) ((emacs (24 4))) "Did you mean to open another file?" single ((:commit . "ce5edcce160b86e7f6480f0381be785d43f97e19") (:keywords "convenience") (:url . "https://gitlab.com/kisaragi-hiu/didyoumean.el"))]) (diff-ansi . [(20220731 2329) ((emacs (27 1))) "Display diff's using alternative diffing tools" single ((:commit . "c4f350da4302cd7d33343d83d5faaeae6795768f") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-diff-ansi"))]) (diff-at-point . [(20220708 211) ((emacs (26 2))) "Diff navigation" single ((:commit . "b32a741d5967b38749039ceafec85062a45e6bcd") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-diff-at-point"))]) - (diff-hl . [(20220620 1309) ((cl-lib (0 2)) (emacs (25 1))) "Highlight uncommitted changes using VC" tar ((:commit . "dabb7be6283488abd8d232ea8ce590d502713ed8") (:authors ("Dmitry Gutov" . "dgutov@yandex.ru")) (:maintainer "Dmitry Gutov" . "dgutov@yandex.ru") (:keywords "vc" "diff") (:url . "https://github.com/dgutov/diff-hl"))]) + (diff-hl . [(20220620 1309) ((cl-lib (0 2)) (emacs (25 1))) "Highlight uncommitted changes using VC" tar ((:commit . "283aa2b9df68ed125090d66a0ae664e1fc196b74") (:authors ("Dmitry Gutov" . "dgutov@yandex.ru")) (:maintainer "Dmitry Gutov" . "dgutov@yandex.ru") (:keywords "vc" "diff") (:url . "https://github.com/dgutov/diff-hl"))]) (difflib . [(20210224 2242) ((emacs (24 4)) (cl-generic (0 3)) (ht (2 2)) (s (1 12 0))) "Helpers for computing deltas between sequences." single ((:commit . "646fc4388274fe765bbf4661e17a24e4d081250c") (:authors ("Diego A. Mundo" . "dieggsy@pm.me")) (:maintainer "Diego A. Mundo" . "dieggsy@pm.me") (:keywords "matching" "tools" "string") (:url . "http://github.com/dieggsy/difflib.el"))]) (diffpdf . [(20210626 1447) ((emacs (25 1)) (transient (0 3 0))) "Transient diffpdf" single ((:commit . "a5b203b549e373cb9b0ef3f00c0010bd34dd644a") (:authors ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainer "Shuguang Sun" . "shuguang79@qq.com") (:keywords "tools") (:url . "https://github.com/ShuguangSun/diffpdf.el"))]) (diffscuss-mode . [(20141014 2357) nil "Major mode for diffscuss files." single ((:commit . "53f2d001bd3a5cb80c6ada16b4e570afd1989a09") (:authors ("Edmund Jorgensen" . "edmund@hut8labs.com")) (:maintainer "Edmund Jorgensen" . "edmund@hut8labs.com") (:keywords "tools"))]) @@ -920,8 +925,8 @@ (dim-autoload . [(20220422 1601) ((emacs (25 1)) (compat (28 1 1 0))) "Dim or hide autoload cookie lines" single ((:commit . "81c94b0707d5ddd9a3b9962ee441206db1d25967") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "https://github.com/tarsius/dim-autoload"))]) (dime . [(20210329 604) ((emacs (25 1)) (dylan (3 0))) "Dylan interaction mode" tar ((:commit . "9d2891e3e06405b75072d296f385fa795aeb9835") (:url . "https://opendylan.org/"))]) (diminish . [(20220104 1539) ((emacs (24 3))) "Diminished modes are minor modes with no modeline display" single ((:commit . "6b7e837b0cf0129e9d7d6abae48093cf599bb9e8") (:authors ("Will Mengarini" . "seldon@eskimo.com")) (:maintainer "Martin Yrjölä" . "martin.yrjola@gmail.com") (:keywords "extensions" "diminish" "minor" "codeprose") (:url . "https://github.com/myrjola/diminish.el"))]) - (diminish-buffer . [(20220704 648) ((emacs (24 4))) "Diminish (hide) buffers from buffer-menu" single ((:commit . "6521793eb1a33d56c78bb1e8e579cdeafd224ed7") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "diminish" "hide" "buffer" "menu") (:url . "https://github.com/jcs-elpa/diminish-buffer"))]) - (dimmer . [(20211123 1536) ((emacs (25 1))) "Visually highlight the selected buffer" single ((:commit . "2f915b100044e09dd647b22085e1696249c4b115") (:authors ("Neil Okamoto")) (:maintainer "Neil Okamoto") (:keywords "faces" "editing") (:url . "https://github.com/gonewest818/dimmer.el"))]) + (diminish-buffer . [(20220704 648) ((emacs (24 4))) "Diminish (hide) buffers from buffer-menu" single ((:commit . "ace41c25a6ec8bce755a0d83b3abef22db7ea641") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "diminish" "hide" "buffer" "menu") (:url . "https://github.com/jcs-elpa/diminish-buffer"))]) + (dimmer . [(20220817 122) ((emacs (25 1))) "Visually highlight the selected buffer" single ((:commit . "a5b697580e5aed6168b571ae3d925753428284f8") (:authors ("Neil Okamoto")) (:maintainer "Neil Okamoto") (:keywords "faces" "editing") (:url . "https://github.com/gonewest818/dimmer.el"))]) (dionysos . [(20160810 1056) ((libmpdee (2 1 0)) (alert (1 2)) (s (1 11 0)) (dash (2 12 1)) (pkg-info (0 5 0)) (cl-lib (0 5))) "Dionysos, a music player for Emacs" tar ((:commit . "0aac21caadabc5a7f09e18a9dcb02f3dec26588b") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "music") (:url . "https://github.com/nlamirault/dionysos"))]) (dir-treeview . [(20220505 27) ((emacs (24 4)) (treeview (1 1 0))) "A directory tree browser and simple file manager" tar ((:commit . "fa0b795b36740755ec37f5b41c3a734ad702e5a1") (:authors ("Tilman Rassy" . "tilman.rassy@googlemail.com")) (:maintainer "Tilman Rassy" . "tilman.rassy@googlemail.com") (:keywords "tools" "convenience" "files") (:url . "https://github.com/tilmanrassy/emacs-dir-treeview"))]) (dircmp . [(20141204 1756) nil "Compare and sync directories." tar ((:commit . "558ee0b601c2de9d247612085aafe2926f56a09f") (:authors ("Matt McClure -- http://matthewlmcclure.com")) (:maintainer "Matt McClure -- http://matthewlmcclure.com") (:keywords "unix" "tools") (:url . "https://github.com/matthewlmcclure/dircmp-mode"))]) @@ -941,7 +946,7 @@ (dired-icon . [(20170223 526) ((emacs (24 3))) "A minor mode to display a list of associated icons in dired buffers." tar ((:commit . "f60e10757a5011235b519231ad35974ff25963ed") (:authors ("Hong Xu" . "hong@topbug.net")) (:maintainer "Hong Xu" . "hong@topbug.net") (:keywords "dired" "files") (:url . "https://gitlab.com/xuhdev/dired-icon"))]) (dired-imenu . [(20140109 1610) nil "imenu binding for dired mode" single ((:commit . "610e21fe0988c85931d34894d3eee2442c79ab0a") (:authors ("Damien Cassou" . "damien.cassou@gmail.com")) (:maintainer "Damien Cassou" . "damien.cassou@gmail.com") (:keywords "dired" "imenu") (:url . "https://github.com/DamienCassou/dired-imenu"))]) (dired-k . [(20211002 2358) ((emacs (24 3))) "Highlight dired by size, date, git status" tar ((:commit . "1ddd8e0ea06f0e25cd5dedb2370cfa0cacfa8c9d") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Neil Okamoto" . "neil.okamoto+melpa@gmail.com") (:url . "https://github.com/emacsorphanage/dired-k"))]) - (dired-launch . [(20220317 1839) nil "Use dired as a launcher" single ((:commit . "72ebbe2b3d2e04dbfda636fa114d4f47835ce044") (:authors ("David Thompson")) (:maintainer "David Thompson") (:keywords "dired" "launch") (:url . "https://github.com/thomp/dired-launch"))]) + (dired-launch . [(20220805 1655) ((emacs (24 3))) "Use dired as a launcher" single ((:commit . "e7877700144cf4abb5f5482eea7fde9b749cfc57") (:authors ("David Thompson")) (:maintainer "David Thompson") (:keywords "dired" "launch") (:url . "https://github.com/thomp/dired-launch"))]) (dired-lsi . [(20200812 929) ((emacs (26 1))) "Add memo to directory and show it in dired" single ((:commit . "0f4038c8b47f6cfc70f82062800700c14c9912c2") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "convenience") (:url . "https://github.com/conao3/dired-lsi.el"))]) (dired-narrow . [(20181114 1723) ((dash (2 7 0)) (dired-hacks-utils (0 0 1))) "Live-narrowing of search results for dired" single ((:commit . "7c0ef09d57a80068a11edc74c3568e5ead5cc15a") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "files"))]) (dired-open . [(20180922 1113) ((dash (2 5 0)) (dired-hacks-utils (0 0 1))) "Open files from dired using using custom actions" single ((:commit . "7c0ef09d57a80068a11edc74c3568e5ead5cc15a") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "files"))]) @@ -962,14 +967,14 @@ (diredc . [(20220113 332) ((emacs (26 1)) (key-assist (1 0))) "Extensions for dired" single ((:commit . "7ee68f6b1c87f8ab86cf23416472747e88860717") (:keywords "files") (:url . "https://github.com/Boruch-Baum/emacs-diredc"))]) (diredfl . [(20220508 805) ((emacs (24))) "Extra font lock rules for a more colourful dired" single ((:commit . "62b559e1d6b69834a56a57eb1832ac6ad4d2e5d0") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "faces") (:url . "https://github.com/purcell/diredfl"))]) (diredful . [(20160529 2017) nil "colorful file names in dired buffers" single ((:commit . "ad328a15c5deffc1021af9b3f19a745dcd8f4415") (:authors ("Thamer Mahmoud" . "thamer.mahmoud@gmail.com")) (:maintainer "Thamer Mahmoud" . "thamer.mahmoud@gmail.com") (:keywords "dired" "colors" "extension" "widget") (:url . "https://github.com/thamer/diredful"))]) - (direnv . [(20220513 656) ((emacs (25 1)) (dash (2 12 0))) "direnv integration" single ((:commit . "416ed17efa93503b37eba196a14f967e0899bce4") (:authors ("wouter bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "wouter bolsterlee" . "wouter@bolsterl.ee") (:keywords "direnv" "environment" "processes" "unix" "tools") (:url . "https://github.com/wbolster/emacs-direnv"))]) + (direnv . [(20220812 956) ((emacs (25 1)) (dash (2 12 0))) "direnv integration" single ((:commit . "268536f564b7eba99264a89a9149268eb4bc67ac") (:authors ("wouter bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "wouter bolsterlee" . "wouter@bolsterl.ee") (:keywords "direnv" "environment" "processes" "unix" "tools") (:url . "https://github.com/wbolster/emacs-direnv"))]) (direx . [(20170422 1327) nil "Simple Directory Explorer" tar ((:commit . "a79bfdb5980cf6ed7bfb3b41ddc471a7b6c0ede4") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Tomohiro Matsuyama" . "m2ym.pub@gmail.com") (:keywords "convenience"))]) (direx-grep . [(20140515 1506) ((direx (0 1 -3))) "Grep node of direx.el using incremental search like anything.el/helm.el" single ((:commit . "1109a512a80b2673a70b18b8568514049017faad") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "convenience") (:url . "https://github.com/aki2o/direx-grep"))]) (dirtree . [(20140129 832) ((tree-mode (1 1 1 1)) (windata (0))) "Directory tree views" single ((:commit . "ba55f1e716e386fdd37cb8e7f48616e405dc7251") (:authors ("Ye Wenbin" . "wenbinye@gmail.com")) (:maintainer "Ye Wenbin" . "wenbinye@gmail.com"))]) (dirtree-prosjekt . [(20140129 904) ((prosjekt (0 3)) (dirtree (0 1))) "dirtree integration for prosjekt." single ((:commit . "a864a8be5842223043702395f311e3350c28e9db") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:url . "https://github.com/abingham/prosjekt"))]) - (dirvish . [(20220804 1521) ((emacs (27 1)) (transient (0 3 7))) "A modern file manager based on dired mode" tar ((:commit . "030457d148e7b32deffb5e0e92c0a03cf7e6657a") (:authors ("Alex Lu ")) (:maintainer "Alex Lu ") (:keywords "files" "convenience") (:url . "https://github.com/alexluigit/dirvish"))]) + (dirvish . [(20220824 1107) ((emacs (27 1)) (transient (0 3 7))) "A modern file manager based on dired mode" tar ((:commit . "880f426fb780acdd19f932ba99e581c2f276d744") (:authors ("Alex Lu ")) (:maintainer "Alex Lu ") (:keywords "files" "convenience") (:url . "https://github.com/alexluigit/dirvish"))]) (disable-mouse . [(20210512 2114) ((emacs (24 1))) "Disable mouse commands globally" single ((:commit . "cae3be9dd012727b40ad3b511731191f79cebe42") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "mouse") (:url . "https://github.com/purcell/disable-mouse"))]) - (disaster . [(20171016 2152) nil "Disassemble C/C++ code under cursor in Emacs" single ((:commit . "10a785facc60d89d78e0d5177985ab1af1741bb4") (:authors ("Justine Tunney" . "jtunney@gmail.com")) (:maintainer "Justine Tunney" . "jtunney@gmail.com") (:keywords "tools") (:url . "https://github.com/jart/disaster"))]) + (disaster . [(20220807 2215) ((emacs (27))) "Disassemble C, C++ or Fortran code under cursor" single ((:commit . "e261bb4cfdc9da95a3e6b9614d79e75437c85c8b") (:authors ("Justine Tunney" . "jtunney@gmail.com") ("Abdelhak Bougouffa" . "abougouffa@fedoraproject.org")) (:maintainer "Abdelhak Bougouffa" . "abougouffa@fedoraproject.org") (:keywords "tools" "c") (:url . "https://github.com/jart/disaster"))]) (discourse . [(20160911 819) ((cl-lib (0 5)) (request (0 2)) (s (1 11 0))) "discourse api" single ((:commit . "a86c7e608851e186fe12e892a573994f08c8e65e") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "lisp" "discourse") (:url . "https://github.com/lujun9972/discourse-api"))]) (discover . [(20140103 2139) ((makey (0 3))) "discover more of Emacs" single ((:commit . "7b0044bbb3b3bd5d811fdfb0f5ac6ec8de1239df") (:authors ("Mickey Petersen" . "mickey@fyeah.org")) (:maintainer "Mickey Petersen" . "mickey@fyeah.org"))]) (discover-clj-refactor . [(20150328 1459) ((clj-refactor (0 14 0)) (discover (0 3))) "Adds discover context menu for clj-refactor" single ((:commit . "3fbd5c1162739e606d7cf5d4f5d7426547d99647") (:authors ("Marian Schubert" . "marian.schubert@gmail.com")) (:maintainer "Marian Schubert" . "marian.schubert@gmail.com") (:keywords "clj-refactor" "discover" "convenience"))]) @@ -1009,19 +1014,18 @@ (docker-cli . [(20190524 1624) nil "Running various commands in docker containers" single ((:commit . "c4b02894466d8642ad3d49df4c4a80e023a672aa") (:authors ("Boško Ivanišević" . "bosko.ivanisevic@gmail.com")) (:maintainer "Boško Ivanišević" . "bosko.ivanisevic@gmail.com") (:keywords "processes") (:url . "https://github.com/bosko/docker-cli"))]) (docker-compose-mode . [(20200830 1336) ((emacs (24 3)) (dash (2 12 0)) (yaml-mode (0 0 12))) "Major mode for editing docker-compose files" single ((:commit . "abaa4f3aeb5c62d7d16e186dd7d77f4e846e126a") (:authors ("Ricardo Martins")) (:maintainer "Ricardo Martins") (:keywords "convenience") (:url . "https://github.com/meqif/docker-compose-mode"))]) (docker-tramp . [(20220219 420) ((emacs (24)) (cl-lib (0 5))) "TRAMP integration for docker containers" tar ((:commit . "930d7b46c180d8a13240a028c1b40af84f2a3219") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "docker" "convenience") (:url . "https://github.com/emacs-pe/docker-tramp.el"))]) - (dockerfile-mode . [(20220220 1439) ((emacs (24))) "Major mode for editing Docker's Dockerfiles" single ((:commit . "b63a3d12b7dea0cb9efc7f78d7ad5672ceab2a3f") (:keywords "docker") (:url . "https://github.com/spotify/dockerfile-mode"))]) - (docopt . [(20220319 1912) ((emacs (26 3)) (dash (2 17 0)) (emacs (26 1)) (f (0 20 0)) (parsec (0 1 3)) (s (1 12 0)) (transient (0 3 0))) "A Docopt implementation in Elisp" tar ((:commit . "a7f5b4a8b1a43552067ce27bce6080a509c92cff") (:authors ("r0man" . "roman@burningswell.com")) (:maintainer "r0man" . "roman@burningswell.com") (:keywords "docopt" "tools" "processes") (:url . "https://github.com/r0man/docopt.el"))]) + (dockerfile-mode . [(20220822 2021) ((emacs (24))) "Major mode for editing Docker's Dockerfiles" single ((:commit . "52c6c00da1d31c0b6c29c74335b3af63ed6bf06c") (:keywords "docker" "languages" "processes" "tools") (:url . "https://github.com/spotify/dockerfile-mode"))]) + (docopt . [(20220319 1912) ((emacs (26 3)) (dash (2 17 0)) (emacs (26 1)) (f (0 20 0)) (parsec (0 1 3)) (s (1 12 0)) (transient (0 3 0))) "A Docopt implementation in Elisp" tar ((:commit . "525b5d42c51995f52f0f82fb41ba8e1d246532d1") (:authors ("r0man" . "roman@burningswell.com")) (:maintainer "r0man" . "roman@burningswell.com") (:keywords "docopt" "tools" "processes") (:url . "https://github.com/r0man/docopt.el"))]) (docstr . [(20220704 630) ((emacs (27 1)) (s (1 9 0))) "A document string minor mode" tar ((:commit . "ae851e805c27c0ec2952c30bedd95ae84bc90010") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "document" "string") (:url . "https://github.com/emacs-vs/docstr"))]) - (doct . [(20220713 1659) ((emacs (25 1))) "DOCT: Declarative Org capture templates" single ((:commit . "15974ad8d4d7baa071b5ea33877e9dc117c4153e") (:authors ("Nicholas Vollmer" . "progfolio@protonmail.com")) (:maintainer "Nicholas Vollmer" . "progfolio@protonmail.com") (:keywords "org" "convenience") (:url . "https://github.com/progfolio/doct"))]) + (doct . [(20220812 443) ((emacs (25 1))) "DOCT: Declarative Org capture templates" single ((:commit . "506c22f365b75f5423810c4933856802554df464") (:authors ("Nicholas Vollmer" . "progfolio@protonmail.com")) (:maintainer "Nicholas Vollmer" . "progfolio@protonmail.com") (:keywords "org" "convenience") (:url . "https://github.com/progfolio/doct"))]) (dogears . [(20210913 1259) ((emacs (26 3)) (map (2 1))) "Never lose your place again" single ((:commit . "c05b69e504a538c9e00fbb0ea86934fafe191d0c") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "convenience") (:url . "https://github.com/alphapapa/dogears.el"))]) (dokuwiki . [(20180102 59) ((emacs (24 3)) (xml-rpc (1 6 8))) "Edit Remote DokuWiki Pages Using XML-RPC" single ((:commit . "594c4d4904dcc2796bbbd2c0845d9e7c09ccf6f7") (:authors ("Juan Karlo Licudine" . "accidentalrebel@gmail.com")) (:maintainer "Juan Karlo Licudine" . "accidentalrebel@gmail.com") (:keywords "convenience") (:url . "http://www.github.com/accidentalrebel/emacs-dokuwiki"))]) (dokuwiki-mode . [(20170223 1301) nil "Major mode for DokuWiki document" single ((:commit . "e4e116f6fcc373e3f5937c1a7daa5c2c9c6d3fa1") (:authors ("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" single ((:commit . "500127f0172ac7a1eec627e026b59136580a74ac") (:authors ("Alessandro Piras" . "laynor@gmail.com")) (:maintainer "Alessandro Piras" . "laynor@gmail.com") (:keywords "tools" "convenience"))]) - (doneburn-theme . [(20220720 1218) nil "A light theme based on Bozhidar Batsov's Zenburn" single ((:commit . "824eae7ecf1cce08fa41f6762a27670815b7f786") (:authors ("Manuel Uberti" . "manuel.uberti@inventati.org")) (:maintainer "Manuel Uberti" . "manuel.uberti@inventati.org") (:keywords "faces" "themes") (:url . "http://github.com/manuel-uberti/doneburn-emacs"))]) (doom . [(20180301 2308) ((cl-lib (0 5))) "DOM implementation and manipulation library" single ((: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")) (:maintainer "Alex Schroeder") (:keywords "xml" "dom") (:url . "http://www.github.com/kensanata/doom.el/"))]) - (doom-modeline . [(20220804 508) ((emacs (25 1)) (compat (28 1 1 1)) (shrink-path (0 2 0))) "A minimal and modern mode-line" tar ((:commit . "4c4b451fcf705f58e6ca41339f22365434b664cb") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "faces" "mode-line") (:url . "https://github.com/seagle0128/doom-modeline"))]) + (doom-modeline . [(20220816 1627) ((emacs (25 1)) (compat (28 1 1 1)) (shrink-path (0 2 0))) "A minimal and modern mode-line" tar ((:commit . "acac2409e2debfeabcc81a17b6ae67f9622d72ae") (:authors ("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 . [(20210831 1442) ((emacs (24 4)) (doom-modeline (3 0 0)) (async (1 9 3))) "Segment for Doom Modeline to show playerctl information" single ((:commit . "ef9158dfdf32e8eb789b69e7394d0bddaa68f42c") (:authors ("Ellis Kenyő" . "me@elken.dev")) (:maintainer "Ellis Kenyő" . "me@elken.dev") (:url . "https://github.com/elken/doom-modeline-now-playing"))]) - (doom-themes . [(20220727 853) ((emacs (25 1)) (cl-lib (0 5))) "an opinionated pack of modern color-themes" tar ((:commit . "5f96ee83e9734587bf3e8ec61d4f067b43d9c26e") (:authors ("Henrik Lissner" . "contact@henrik.io")) (:maintainer "Henrik Lissner" . "contact@henrik.io") (:keywords "themes" "faces") (:url . "https://github.com/doomemacs/themes"))]) + (doom-themes . [(20220727 853) ((emacs (25 1)) (cl-lib (0 5))) "an opinionated pack of modern color-themes" tar ((:commit . "aa13e7fc713d458f46f4ab7cce367fd30d801765") (:authors ("Henrik Lissner" . "contact@henrik.io")) (:maintainer "Henrik Lissner" . "contact@henrik.io") (:keywords "themes" "faces") (:url . "https://github.com/doomemacs/themes"))]) (dot-mode . [(20180312 2300) ((emacs (24 3))) "minor mode to repeat typing or commands" single ((:commit . "6ca22b73bcdae2363ee9641b822a60685df16a3e") (:authors ("Robert Wyrick" . "rob@wyrick.org")) (:maintainer "Robert Wyrick" . "rob@wyrick.org") (:keywords "convenience") (:url . "https://github.com/wyrickre/dot-mode"))]) (dotenv-mode . [(20191027 2129) ((emacs (24 3))) "Major mode for .env files" single ((:commit . "e3701bf739bde44f6484eb7753deadaf691b73fb") (:authors ("Preetpal S. Sohal")) (:maintainer "Preetpal S. Sohal") (:url . "https://github.com/preetpalS/emacs-dotenv-mode"))]) (dotnet . [(20200803 1032) nil "Interact with dotnet CLI tool" single ((:commit . "83ba1305d7895b03f3dffb2d3458b7ec75e6909f") (:authors ("Julien BLANCHARD" . "julien@sideburns.eu")) (:maintainer "Julien BLANCHARD" . "julien@sideburns.eu") (:keywords ".net" "tools") (:url . "https://github.com/julienXX/dotnet.el"))]) @@ -1032,8 +1036,8 @@ (dpaste . [(20160303 2112) nil "Emacs integration for dpaste.com" single ((:commit . "5ebabb466a6ae70882549855b6b2194fc32189f8") (:authors ("Greg Newman" . "greg@gregnewman.org") ("Guilherme Gondim" . "semente@taurinus.org")) (:maintainer "Greg Newman" . "greg@gregnewman.org") (:keywords "paste" "pastie" "pastebin" "dpaste" "python"))]) (dpaste_de . [(20131015 1225) ((web (0 3 7))) "Emacs mode to paste to dpaste.de" single ((:commit . "f0c39e8864299f735642f7d9fa490689398ce39d") (:authors ("Thejaswi Puthraya" . "thejaswi.puthraya@gmail.com")) (:maintainer "Thejaswi Puthraya" . "thejaswi.puthraya@gmail.com") (:keywords "pastebin"))]) (dpkg-dev-el . [(20190824 2314) ((debian-el (37))) "Emacs modes for debian packaging" tar ((:commit . "458f5230d02b15c94e94eca1af4eabaec30f45db") (:authors ("Peter S Galbraith" . "psg@debian.org")) (:maintainer "Peter S Galbraith" . "psg@debian.org"))]) - (dr-racket-like-unicode . [(20200513 1642) ((emacs (24 1))) "DrRacket-style unicode input" single ((:commit . "70bc1caea6b277e49e1cb29e1926a7b0c83c5ebc") (:authors ("David Christiansen" . "david@davidchristiansen.dk")) (:maintainer "David Christiansen" . "david@davidchristiansen.dk") (:keywords "i18n" "tools"))]) - (dracula-theme . [(20220804 618) ((emacs (24 3))) "Dracula Theme" single ((:commit . "37b3979b91b06a0520a0d326bc323b4dda280123") (:authors ("film42")) (:maintainer "Étienne Deparis" . "etienne@depar.is") (:url . "https://github.com/dracula/emacs"))]) + (dr-racket-like-unicode . [(20220810 2000) ((emacs (24 3))) "DrRacket-style unicode input" single ((:commit . "d09b9be289e91e25c941107be5e8f52e7c8f0065") (:authors ("David Christiansen" . "david@davidchristiansen.dk")) (:maintainer "David Christiansen" . "david@davidchristiansen.dk") (:keywords "i18n" "tools") (:url . "https://github.com/david-christiansen/dr-racket-like-unicode"))]) + (dracula-theme . [(20220821 1717) ((emacs (24 3))) "Dracula Theme" single ((:commit . "fdf057f5e80037814098dc8bb67888886c89a761") (:authors ("film42")) (:maintainer "Étienne Deparis" . "etienne@depar.is") (:url . "https://github.com/dracula/emacs"))]) (draft-mode . [(20140609 1456) nil "Rough drafting for Emacs." single ((:commit . "4779fb32daf53746459da2def7e08004492d4f18") (:authors ("Eeli Reilin" . "gaudecker@fea.st")) (:maintainer "Eeli Reilin" . "gaudecker@fea.st") (:keywords "draft" "drafting") (:url . "https://github.com/gaudecker/draft-mode"))]) (drag-stuff . [(20161108 749) nil "Drag stuff (lines, words, region, etc...) around" tar ((:commit . "6d06d846cd37c052d79acd0f372c13006aa7e7c8") (:authors ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Johan Andersson" . "johan.rejeep@gmail.com") (:keywords "speed" "convenience") (:url . "http://github.com/rejeep/drag-stuff"))]) (drawille . [(20160418 1838) ((cl-lib (0 5))) "Drawille implementation in elisp" tar ((:commit . "d914845725719d8293e2f0dea3c9c7e0a1e0e62a") (:authors ("Josuah Demangeon" . "josuah.demangeon@gmail.com")) (:maintainer "Josuah Demangeon" . "josuah.demangeon@gmail.com") (:keywords "graphics") (:url . "https://github.com/sshbio/elisp-drawille"))]) @@ -1045,7 +1049,7 @@ (drupal-spell . [(20130520 1655) nil "Aspell extra dictionary for Drupal" tar ((:commit . "4087c28c89a884ee050961c57166e6b09085f59d") (:authors ("Arne Jørgensen" . "arne@arnested.dk")) (:maintainer "Arne Jørgensen" . "arne@arnested.dk") (:keywords "wp") (:url . "https://github.com/arnested/drupal-spell"))]) (dsvn . [(20190316 2201) nil "Subversion interface" single ((:commit . "c37d2412ba92aad647bcf5aeb151e620e8069f8d") (:authors ("David Kågedal" . "davidk@lysator.liu.se") (" Mattias Engdegård" . "mattiase@acm.org")) (:maintainer "Mattias Engdegård" . "mattiase@acm.org") (:keywords "docs"))]) (dtb-mode . [(20210105 1132) ((emacs (25))) "Show device tree souce in dtbs" single ((:commit . "7f66de945a0be2be5a26b4619cae097288fb55cd") (:authors ("Schspa Shi" . "schspa@gmail.com")) (:maintainer "Schspa Shi" . "schspa@gmail.com") (:keywords "dtb" "dts" "convenience") (:url . "https://github.com/schspa/dtb-mode"))]) - (dtk . [(20220602 2129) ((emacs (24 4)) (cl-lib (0 6 1)) (dash (2 12 0)) (seq (1 9)) (s (1 9))) "access SWORD content via diatheke" single ((:commit . "a657a8c034746069a5b340ef7356cb7d38290ec9") (:authors ("David Thompson")) (:maintainer "David Thompson") (:keywords "hypermedia") (:url . "https://github.com/dtk01/dtk.el"))]) + (dtk . [(20220821 2228) ((emacs (24 4)) (cl-lib (0 6 1)) (dash (2 12 0)) (seq (1 9)) (s (1 9))) "access SWORD content via diatheke" single ((:commit . "a9dce9b7639c2b011f42c04820472a9c0717546b") (:authors ("David Thompson")) (:maintainer "David Thompson") (:keywords "hypermedia") (:url . "https://github.com/dtk01/dtk.el"))]) (dtrace-script-mode . [(20150214 623) nil "DTrace code editing commands for Emacs" single ((:commit . "801af1ef16075d31a19830ebb8404bbf3a322f10"))]) (dtrt-indent . [(20220725 849) nil "Adapt to foreign indentation offsets" tar ((:commit . "d4fd1b4977eb0d534844fddf01c3c51c70c57205") (:authors ("Julian Scheid" . "julians37@googlemail.com")) (:maintainer "Reuben Thomas" . "rrt@sc3d.org") (:keywords "convenience" "files" "languages" "c"))]) (dts-mode . [(20211202 18) nil "Major mode for Devicetree source code" single ((:commit . "32517e7eeeccc785b7c669fd5e93c5df45597ef1") (:authors ("Ben Gamari" . "ben@smart-cactus.org")) (:maintainer "Ben Gamari" . "ben@smart-cactus.org") (:keywords "languages"))]) @@ -1053,12 +1057,12 @@ (dumb-diff . [(20171211 2122) ((emacs (24 3))) "fast arbitrary diffs" single ((:commit . "1a2331d283049b71a07c1b06b1e0627a950d55f4") (:authors ("jack angers")) (:maintainer "jack angers") (:keywords "programming" "diff"))]) (dumb-jump . [(20220620 2325) ((emacs (24 3)) (s (1 11 0)) (dash (2 9 0)) (popup (0 5 3))) "Jump to definition for 50+ languages without configuration" single ((:commit . "0a783d1db610ff1dc4e1b7869589cff16ff92f7a") (:authors ("jack angers and contributors")) (:maintainer "jack angers and contributors") (:keywords "programming") (:url . "https://github.com/jacktasia/dumb-jump"))]) (dummyparens . [(20141009 1024) nil "parenthesis auto-pairing and wrapping" single ((:commit . "9798ef1d0eaa24e4fe66f8aa6022a8c62714cc89") (:authors ("Sergei Nosov ")) (:maintainer "Sergei Nosov ") (:keywords "dummyparens" "auto-pair" "wrapping") (:url . "https://github.com/snosov1/dummyparens"))]) - (dune . [(20210909 1010) nil "Integration with the dune build system" tar ((:commit . "af209eb382718388f231bbf2e3e22615eb7440c7") (:url . "https://github.com/ocaml/dune"))]) + (dune . [(20220805 1652) nil "Integration with the dune build system" tar ((:commit . "7ce9e8eaf7b65645879c8974dc4f2c5e06876ffa") (:url . "https://github.com/ocaml/dune"))]) (dune-format . [(20210505 108) ((reformatter (0 6)) (emacs (24 1))) "Reformat OCaml's dune files automatically" single ((:commit . "196f16a01f4c855de7becddbc4cfed2f6788693a") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "languages") (:url . "https://github.com/purcell/emacs-dune-format"))]) (duplicate-thing . [(20181031 1500) nil "Duplicate current line & selection" single ((:commit . "9d8fd05e3e5caa35d3f2a0c0032c92f0c0908e21") (:authors ("ongaeshi")) (:maintainer "ongaeshi") (:keywords "convenience" "command" "duplicate" "line" "selection") (:url . "https://github.com/ongaeshi/duplicate-thing"))]) (dut-mode . [(20170729 2111) ((emacs (24))) "Major mode for the Dut programming language" single ((:commit . "9235c7acaa6690942e9de8b7acd1e4be0c859dc1") (:authors ("The dut-mode Authors")) (:maintainer "The dut-mode Authors") (:keywords "languages" "gut") (:url . "https://github.com/dut-lang/dut-mode"))]) (dw . [(20210331 2246) ((emacs (25 1))) "Diceware passphrase generation commands" single ((:commit . "61c5718ba64ace4c9e29de18aa2690ecc3f0f258") (:authors ("D. Williams" . "d.williams@posteo.net")) (:maintainer "D. Williams" . "d.williams@posteo.net") (:keywords "convenience" "games") (:url . "https://github.com/integral-dw/dw-passphrase-generator"))]) - (dwim-shell-command . [(20220804 853) ((emacs (27 1))) "Shell commands with DWIM behaviour" tar ((:commit . "8627205f00f4e2eed9607cf80ba116d1c3efae08") (:authors ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/dwim-shell-command"))]) + (dwim-shell-command . [(20220820 1006) ((emacs (27 1))) "Shell commands with DWIM behaviour" tar ((:commit . "ed5aff8f1176de141cadbd1be6771ef1ce2795f4") (:authors ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/dwim-shell-command"))]) (dyalog-mode . [(20210413 810) ((cl-lib (0 2)) (emacs (24 3))) "Major mode for editing Dyalog APL source code" tar ((:commit . "697a84194766708d2607e8ba48a552e383c6523e") (:authors ("Joakim Hårsman" . "joakim.harsman@gmail.com")) (:maintainer "Joakim Hårsman" . "joakim.harsman@gmail.com") (:keywords "languages") (:url . "https://github.com/harsman/dyalog-mode.git"))]) (dylan . [(20220115 1804) ((emacs (25 1))) "Dylan editing modes" tar ((:commit . "9d2891e3e06405b75072d296f385fa795aeb9835") (:url . "https://opendylan.org/"))]) (dynamic-fonts . [(20140731 1226) ((font-utils (0 7 0)) (persistent-soft (0 8 8)) (pcache (0 2 3))) "Set faces based on available fonts" single ((:commit . "ab0c65accbdb59acaed5b263327e22ec019b3e82") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "faces" "frames") (:url . "http://github.com/rolandwalker/dynamic-fonts"))]) @@ -1086,8 +1090,8 @@ (easy-kill-extras . [(20210529 945) ((easy-kill (0 9 4))) "Extra functions for easy-kill." tar ((:commit . "74e9d0fcafc38d5f24e6209671a552bc1ba5a867") (:authors ("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" single ((:commit . "060f0e6801c82c40c06961dc0528a00e18947a8c") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "repeat" "convenience") (:url . "https://github.com/xuchunyang/easy-repeat.el"))]) (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")) (:maintainer "Alexey Kutepov" . "reximkut@gmail.com") (:url . "http://github.com/rexim/ebf"))]) - (ebib . [(20220711 1845) ((parsebib (4 0)) (emacs (26 1))) "a BibTeX database manager" tar ((:commit . "36b487b798cb432a52c2d5d14c3aec74fc7eece9") (:authors ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainer "Joost Kremers" . "joostkremers@fastmail.fm") (:keywords "text" "bibtex") (:url . "http://joostkremers.github.io/ebib/"))]) - (ebnf-mode . [(20220606 1846) ((emacs (25 1))) "Major mode for EBNF files" single ((:commit . "89a868226e7ddb303548e82a81f76fbb0e5b21c5") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:url . "https://github.com/nverno/ebnf-mode"))]) + (ebib . [(20220822 1725) ((parsebib (4 0)) (emacs (26 1))) "a BibTeX database manager" tar ((:commit . "e1a230b49cb84b2cc8518a8a287cb2291692e68f") (:authors ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainer "Joost Kremers" . "joostkremers@fastmail.fm") (:keywords "text" "bibtex") (:url . "http://joostkremers.github.io/ebib/"))]) + (ebnf-mode . [(20220606 1846) ((emacs (25 1))) "Major mode for EBNF files" single ((:commit . "89df6ca4215b3a325dc94a8f246f403cacc99745") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:url . "https://github.com/nverno/ebnf-mode"))]) (ebuku . [(20220725 832) ((emacs (25 1))) "Interface to the buku Web bookmark manager" single ((:commit . "5b8bf34b8ea5d05f0b8dfc12bfea825f9cffbeda") (:authors ("Alexis , Erik Sjöstrand" . "sjostrand.erik@gmail.com")) (:maintainer "Alexis" . "flexibeast@gmail.com") (:keywords "bookmarks" "buku" "data" "web" "www") (:url . "https://github.com/flexibeast/ebuku"))]) (ecb . [(20170728 1921) nil "a code browser for Emacs" tar ((:commit . "1330a44cf3c171781083b0b926ab7622f64e6e81") (:authors ("Jesper Nordenberg" . "mayhem@home.se") ("Klaus Berndl" . "klaus.berndl@sdm.de") ("Kevin A. Burton" . "burton@openprivacy.org")) (:maintainer "Klaus Berndl" . "klaus.berndl@sdm.de") (:keywords "browser" "code" "programming" "tools"))]) (echo-bar . [(20220705 2212) nil "Turn the echo area into a custom status bar" single ((:commit . "b00e80acbbb708a3528c7b376dfb216deb64e4fe") (:authors ("Adam Tillou" . "qaiviq@gmail.com")) (:maintainer "Adam Tillou" . "qaiviq@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/qaiviq/echo-bar.el"))]) @@ -1099,7 +1103,7 @@ (ede-php-autoload . [(20180901 1255) nil "Simple EDE PHP Project" tar ((:commit . "8a4eeeaa93b8d87b65a107c4ebcbeb14528d9449") (:authors ("Steven Rémot" . "steven.remot@gmail.com") ("original code for C++ by Eric M. Ludlam" . "eric@siege-engine.com")) (:maintainer "Steven Rémot" . "steven.remot@gmail.com") (:keywords "php" "project" "ede") (:url . "https://github.com/emacs-php/ede-php-autoload"))]) (ede-php-autoload-composer-installers . [(20170221 2026) ((ede-php-autoload (1 0 0)) (f (0 19 0)) (s (1 7 0))) "Composer installers support for ede-php-autoload" single ((:commit . "7840439802c7d11ee086bbf465657f3da12f9f66") (:authors ("Thomas Fini Hansen" . "xen@xen.dk")) (:maintainer "Thomas Fini Hansen" . "xen@xen.dk") (:keywords "programming" "php") (:url . "https://github.com/xendk/ede-php-autoload-composer-installers"))]) (ede-php-autoload-drupal . [(20170316 2158) ((ede-php-autoload (1 0 0)) (f (0 19 0)) (s (1 7 0))) "Drupal support for ede-php-autoload" single ((:commit . "54a04241d94fabc4f4d16ae4dc8ba4f0c6e3b435") (:authors ("Thomas Fini Hansen" . "xen@xen.dk")) (:maintainer "Thomas Fini Hansen" . "xen@xen.dk") (:keywords "programming" "php" "drupal"))]) - (edebug-inline-result . [(20220210 1357) ((emacs (25 1))) "Show Edebug result inline" single ((:commit . "9fb3c48434da24f800833a5ee3419452d5fb83cb") (:keywords "extensions" "lisp" "tools") (:url . "https://repo.or.cz/edebug-inline-result.git"))]) + (edebug-inline-result . [(20220820 2240) ((emacs (25 1))) "Show Edebug result inline" single ((:commit . "90e401ae3e7b3c85da8b24af940fd97f5e744625") (:keywords "extensions" "lisp" "tools") (:url . "https://repo.or.cz/edebug-inline-result.git"))]) (edebug-x . [(20130616 625) nil "Extensions for Edebug" single ((:commit . "a2c2c42553d3bcbd5ac11898554865acbed1bc46") (:authors ("Scott Barnett" . "scott.n.barnett@gmail.com")) (:maintainer "Scott Barnett" . "scott.n.barnett@gmail.com") (:keywords "extensions") (:url . "https://github.com/ScottyB/edebug-x"))]) (edit-as-format . [(20220221 1312) ((emacs (26 1)) (edit-indirect (0 1 5))) "Edit document as other format" tar ((:commit . "59c6f439683846d994a7a2110b9b00cc16c08c40") (:authors ("Xiaobing Jing" . "jingxiaobing@gmail.com")) (:maintainer "Xiaobing Jing" . "jingxiaobing@gmail.com") (:keywords "files" "outlines" "convenience") (:url . "https://github.com/etern/edit-as-format"))]) (edit-at-point . [(20191013 1218) nil "edit(copy,cut..) current things(word,symbol..) under cursor" single ((:commit . "28c85a65c9c61f2aff50bc5e93f61cde26a5d9c0") (:authors (nil . "")) (:maintainer nil . "") (:url . "http://github.com/enoson/edit-at-point.el"))]) @@ -1160,7 +1164,7 @@ (elcontext . [(20210109 1238) ((ht (2 3)) (hydra (0 14 0)) (emacs (24 3)) (f (0 20 0)) (osx-location (0 4)) (uuidgen (0 3))) "Create context specific actions" tar ((:commit . "2efd3dd8c5176c4f071bb048be6cb069b05d6e9e") (:authors ("Thomas Sojka")) (:maintainer "Thomas Sojka") (:keywords "calendar" "convenience") (:url . "https://github.com/rollacaster/elcontext"))]) (elcord . [(20220723 33) ((emacs (25 1))) "Allows you to integrate Rich Presence from Discord" tar ((:commit . "7aacd702335b4ab3344d3815c0fffed319effdf9") (:authors ("heatingdevice") ("Wilfredo Velázquez-Rodríguez" . "zulu.inuoe@gmail.com")) (:maintainer "heatingdevice") (:keywords "games") (:url . "https://github.com/Mstrodl/elcord"))]) (elcouch . [(20201108 955) ((emacs (25 1)) (json-mode (1 0 0)) (libelcouch (0 11 0)) (navigel (0 3 0))) "View and manipulate CouchDB databases" single ((:commit . "3d162dda14411349e12509029d2b621c5d1edea2") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:keywords "data" "tools") (:url . "https://gitlab.petton.fr/DamienCassou/elcouch"))]) - (eldev . [(20220730 1643) ((emacs (24 4))) "Elisp development tool" tar ((:commit . "545412826f199cacdd5b4e937a719cc428e479be") (:authors ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainer "Paul Pogonyshev" . "pogonyshev@gmail.com") (:keywords "maint" "tools") (:url . "https://github.com/doublep/eldev"))]) + (eldev . [(20220816 1746) ((emacs (24 4))) "Elisp development tool" tar ((:commit . "067134a9bfe6326de258f0af666a53e13a9b4a93") (:authors ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainer "Paul Pogonyshev" . "pogonyshev@gmail.com") (:keywords "maint" "tools") (:url . "https://github.com/doublep/eldev"))]) (eldoc-box . [(20220729 844) ((emacs (27 1))) "Display documentation in childframe" single ((:commit . "4a57d48115501c68665535877e58694281a50563") (:authors ("Sebastien Chapuis" . "sebastien@chapu.is")) (:maintainer "Yuan Fu" . "casouri@gmail.com") (:url . "https://github.com/casouri/eldoc-box"))]) (eldoc-cmake . [(20190419 2244) ((emacs (25 1))) "Eldoc support for CMake" single ((:commit . "4453c03b5c95ff32842f13db2fc317fb0fe2f79e") (:authors ("Kirill Ignatiev")) (:maintainer "Kirill Ignatiev") (:url . "https://github.com/ikirill/eldoc-cmake"))]) (eldoc-eval . [(20220106 1951) nil "Enable eldoc support when minibuffer is in use." single ((:commit . "e91800503c90cb75dc70abe42f1d6ae499346cc1") (:authors ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net"))]) @@ -1169,7 +1173,7 @@ (eldoc-toml . [(20211026 1122) ((emacs (24 4))) "TOML table name at point for ElDoc" single ((:commit . "61106be3c3f3a5b293c3f285eec8c6f400142b6d") (:authors ("Maor Kadosh" . "git@avocadosh.xyz")) (:maintainer "Maor Kadosh" . "git@avocadosh.xyz") (:keywords "data") (:url . "https://github.com/it-is-wednesday/eldoc-toml"))]) (electric-case . [(20150417 1112) nil "insert camelCase, snake_case words without \"Shift\"ing" single ((:commit . "bac64e772107e3dc721a9819f63b9ebdc28a81f7") (:authors ("zk_phi")) (:maintainer "zk_phi") (:url . "http://hins11.yu-yake.com/"))]) (electric-cursor . [(20220108 2052) ((emacs (25 1))) "Change cursor automatically depending on mode" single ((:commit . "92f77b05fec80c5440a8b800b33345dabca13872") (:authors ("Case Duckworth" . "acdw@acdw.net")) (:maintainer "Case Duckworth" . "acdw@acdw.net") (:keywords "terminals" "frames") (:url . "https://github.com/duckwork/electric-cursor"))]) - (electric-operator . [(20220417 809) ((dash (2 10 0)) (emacs (24 4))) "Automatically add spaces around operators" tar ((:commit . "f567f03da4a55d6eafa0e6e148ca4884d5370498") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:keywords "electric") (:url . "https://github.com/davidshepherd7/electric-operator"))]) + (electric-operator . [(20220814 1439) ((dash (2 10 0)) (emacs (24 4))) "Automatically add spaces around operators" tar ((:commit . "281abdae033559fa9a8878ed1dd05a7ade118cfd") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:keywords "electric") (:url . "https://github.com/davidshepherd7/electric-operator"))]) (electric-spacing . [(20220220 1540) nil "Insert operators with surrounding spaces smartly" tar ((:commit . "c37b2502512dd49a8311d7c34e9bfd1af3d4dbcd") (:authors ("William Xu" . "william.xwl@gmail.com")) (:maintainer "William Xu" . "william.xwl@gmail.com"))]) (elegant-agenda-mode . [(20210115 353) ((emacs (26 1))) "An elegant theme for your org-agenda" single ((:commit . "5cbc688584ba103ea3be7d7b30e5d94e52f59eb6") (:authors ("Justin Barclay" . "justinbarclay@gmail.com")) (:maintainer "Justin Barclay" . "justinbarclay@gmail.com") (:keywords "faces") (:url . "https://github.com/justinbarclay/elegant-agenda-mode"))]) (elein . [(20120120 1116) nil "running leiningen commands from emacs" single ((:commit . "d4c0c0491dbb7c90e953d7a16172107c37103605") (:authors ("R.W. van 't Veer")) (:maintainer "R.W. van 't Veer") (:keywords "tools" "processes") (:url . "https://github.com/remvee/elein"))]) @@ -1179,12 +1183,12 @@ (elfeed-autotag . [(20210607 637) ((emacs (27 1)) (elfeed (3 4 1)) (elfeed-protocol (0 8 0)) (org (8 2 7)) (dash (2 10 0)) (s (1 9 0))) "Easy auto-tagging for elfeed" single ((:commit . "bc62c37fb79b720ff8b6d67f04f2268841306dcd") (:authors ("Paul Elms ")) (:maintainer "Paul Elms" . "paul@elms.pro") (:keywords "news") (:url . "https://github.com/paulelms/elfeed-autotag"))]) (elfeed-dashboard . [(20210727 603) ((emacs (25 1)) (elfeed (3 3 0))) "An extensible frontend for elfeed using org-mode" single ((:commit . "26ff3573efce3cb66c8814854a2983a69683af09") (:authors ("Manoj Kumar Manikchand" . "manojm321@protonmail.com")) (:maintainer "Manoj Kumar Manikchand" . "manojm321@protonmail.com") (:keywords "convenience") (:url . "https://github.com/Manoj321/elfeed-dashboard"))]) (elfeed-goodies . [(20220614 49) ((popwin (1 0 0)) (powerline (2 2)) (elfeed (2 0 0)) (cl-lib (0 5)) (link-hint (0 1))) "Elfeed goodies" tar ((:commit . "c9d9cd196746add3010d74f43b5c9866562f39fb") (:authors ("Gergely Nagy")) (:maintainer "Gergely Nagy") (:url . "https://github.com/algernon/elfeed-goodies"))]) - (elfeed-org . [(20220529 1958) ((elfeed (1 1 1)) (org (8 2 7)) (dash (2 10 0)) (s (1 9 0)) (cl-lib (0 5))) "Configure elfeed with one or more org-mode files" single ((:commit . "d28c858303e60dcb3a6eb18ea85ee3cb9e3dd623") (:authors ("Remy Honig" . "remyhonig@gmail.com")) (:maintainer "Remy Honig" . "remyhonig@gmail.com") (:keywords "news") (:url . "https://github.com/remyhonig/elfeed-org"))]) + (elfeed-org . [(20220529 1958) ((elfeed (1 1 1)) (org (8 2 7)) (dash (2 10 0)) (s (1 9 0)) (cl-lib (0 5))) "Configure elfeed with one or more org-mode files" single ((:commit . "f8715f20fc5e51d1b284667951ca23638475fcba") (:authors ("Remy Honig" . "remyhonig@gmail.com")) (:maintainer "Remy Honig" . "remyhonig@gmail.com") (:keywords "news") (:url . "https://github.com/remyhonig/elfeed-org"))]) (elfeed-protocol . [(20220524 336) ((emacs (24 4)) (elfeed (2 1 1)) (cl-lib (0 5))) "Provide fever/newsblur/owncloud/ttrss protocols for elfeed" tar ((:commit . "b813574faefc1ac4825da19b40f620339b6badff") (:authors ("Xu Fasheng ")) (:maintainer "Xu Fasheng ") (:keywords "news") (:url . "https://github.com/fasheng/elfeed-protocol"))]) (elfeed-score . [(20220702 1548) ((emacs (26 1)) (elfeed (3 3 0))) "Gnus-style scoring for Elfeed" tar ((:commit . "ac938fdc617d489eb25f1d8c0bd800bb7693c588") (:authors ("Michael Herstine" . "sp1ff@pobox.com")) (:maintainer "Michael Herstine" . "sp1ff@pobox.com") (:keywords "news") (:url . "https://github.com/sp1ff/elfeed-score"))]) (elfeed-summary . [(20220702 906) ((emacs (27 1)) (magit-section (3 3 0)) (elfeed (3 4 1))) "Feed summary interface for elfeed" single ((:commit . "012f6fee58404205f65fb20877eeaa6f1f6c6270") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/elfeed-summary.el"))]) - (elfeed-tube . [(20220703 2128) ((emacs (27 1)) (elfeed (3 4 1)) (aio (1 0))) "YouTube integration for Elfeed" tar ((:commit . "5817c91f5b3b7159965aa73839d2a0a08fd952bd") (:authors ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "news" "hypermedia" "convenience") (:url . "https://github.com/karthink/elfeed-tube"))]) - (elfeed-tube-mpv . [(20220704 1952) ((emacs (27 1)) (elfeed-tube (0 10)) (mpv (0 2 0))) "Control mpv from Elfeed" single ((:commit . "5817c91f5b3b7159965aa73839d2a0a08fd952bd") (:authors ("Karthik Chikmagalur" . "karthikchikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthikchikmagalur@gmail.com") (:keywords "news" "hypermedia") (:url . "https://github.com/karthink/elfeed-tube"))]) + (elfeed-tube . [(20220823 238) ((emacs (27 1)) (elfeed (3 4 1)) (aio (1 0))) "YouTube integration for Elfeed" tar ((:commit . "18d89f19203423b9e2df59a556c1240746903d8f") (:authors ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "news" "hypermedia" "convenience") (:url . "https://github.com/karthink/elfeed-tube"))]) + (elfeed-tube-mpv . [(20220814 42) ((emacs (27 1)) (elfeed-tube (0 10)) (mpv (0 2 0))) "Control mpv from Elfeed" single ((:commit . "18d89f19203423b9e2df59a556c1240746903d8f") (:authors ("Karthik Chikmagalur" . "karthikchikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthikchikmagalur@gmail.com") (:keywords "news" "hypermedia") (:url . "https://github.com/karthink/elfeed-tube"))]) (elfeed-web . [(20210226 258) ((simple-httpd (1 5 1)) (elfeed (3 2 0)) (emacs (24 3))) "web interface to Elfeed" tar ((:commit . "162d7d545ed41c27967d108c04aa31f5a61c8e16") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/elfeed"))]) (elforth . [(20210522 928) ((emacs (26 1))) "Do you have what it takes to hack Emacs Lisp in Forth?" single ((:commit . "2d8540434a28e7edaa04a992c3c362832b2fd61e") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "games") (:url . "https://github.com/lassik/elforth"))]) (elgrep . [(20211221 852) ((emacs (26 2)) (async (1 5))) "Searching files for regular expressions" single ((:commit . "f8124c699b6a4abfb471269bc26afbcc8136f476") (:authors ("Tobias Zawada" . "i@tn-home.de")) (:maintainer "Tobias Zawada" . "i@tn-home.de") (:keywords "tools" "matching" "files" "unix") (:url . "https://github.com/TobiasZawada/elgrep"))]) @@ -1199,7 +1203,7 @@ (elisp-refs . [(20220704 2141) ((dash (2 12 0)) (s (1 11 0))) "find callers of elisp functions or macros" single ((:commit . "af73739084637c8ebadad337a8fe58ff4f1d2ec1") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "lisp"))]) (elisp-sandbox . [(20131116 1842) nil "Evaluate EmacsLisp expressions in a sandbox" single ((:commit . "d894d68934ef09c42f72ac4e1173a0bedc23f139") (:authors ("Joel McCracken , D. Goel" . "deego@gnufans.org")) (:maintainer "Joel McCracken , D. Goel" . "deego@gnufans.org") (:keywords "lisp") (:url . "https://github.com/joelmccracken/elisp-sandbox"))]) (elisp-slime-nav . [(20210510 528) ((emacs (24 1)) (cl-lib (0 2))) "Make M-. and M-, work in elisp like they do in slime" single ((:commit . "8588d80d414aee1fafce5b9da0e913612ee0bcdd") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "languages" "navigation" "slime" "elisp" "emacs-lisp") (:url . "https://github.com/purcell/elisp-slime-nav"))]) - (elixir-mode . [(20220314 1302) ((emacs (25))) "Major mode for editing Elixir files" tar ((:commit . "e0d0466d83ec80ddb412bb1473908a21baad1ec3") (:keywords "languages" "elixir") (:url . "https://github.com/elixir-editors/emacs-elixir"))]) + (elixir-mode . [(20220811 353) ((emacs (25))) "Major mode for editing Elixir files" tar ((:commit . "d495ed87a9c507f5939a51c740f119950c83e2ff") (:keywords "languages" "elixir") (:url . "https://github.com/elixir-editors/emacs-elixir"))]) (elixir-yasnippets . [(20150417 1239) ((yasnippet (0 8 0))) "Yasnippets for Elixir" tar ((:commit . "980ca7626c14ef0573bec0035ec7942796062783") (:authors ("Yinghai Zhao" . "zyinghai@gmail.com")) (:maintainer "Yinghai Zhao" . "zyinghai@gmail.com") (:keywords "snippets"))]) (ellocate . [(20200112 1931) ((emacs (25 1)) (s (1 12 0)) (f (0 20 0))) "The locate command reimplemented in Emacs Lisp" single ((:commit . "81405082f68f0577c9f176d3d4f034a7142aba59") (:authors ("Sebastian Wålinder" . "s.walinder@gmail.com")) (:maintainer "Sebastian Wålinder" . "s.walinder@gmail.com") (:keywords "matching") (:url . "https://github.com/walseb/ellocate"))]) (elm-mode . [(20220227 931) ((f (0 17)) (s (1 7 0)) (emacs (25 1)) (seq (2 23)) (reformatter (0 3))) "Major mode for Elm" tar ((:commit . "1e277684d8a6681a2410cce2dd589ee30a998369") (:authors ("Joseph Collard")) (:maintainer "Joseph Collard") (:url . "https://github.com/jcollard/elm-mode"))]) @@ -1216,7 +1220,7 @@ (elpa-clone . [(20211205 1237) ((emacs (24 4))) "Clone ELPA archive" single ((:commit . "03d8e2af55dfb34ab9da1f9385079a995383b2ea") (:authors ("ZHANG Weiyi" . "dochang@gmail.com")) (:maintainer "ZHANG Weiyi" . "dochang@gmail.com") (:keywords "comm" "elpa" "clone" "mirror") (:url . "https://github.com/dochang/elpa-clone"))]) (elpa-deploy . [(20191022 718) ((emacs (24 4)) (f (0 0))) "ELPA deployment library" single ((:commit . "f5126a2da1e0e52981fad9c12028814be80328c2") (:authors ("Bruno Félix Rezende Ribeiro" . "oitofelix@gnu.org")) (:maintainer "Bruno Félix Rezende Ribeiro" . "oitofelix@gnu.org") (:keywords "tools") (:url . "https://github.com/oitofelix/elpa-deploy"))]) (elpa-mirror . [(20220526 1512) ((emacs (25 1))) "Create local package repository from installed packages" single ((:commit . "2e2048bc335d170b5680e97c6a4cccd71eb85129") (:authors ("Chen Bin" . "chenbin.sh@gmail.com")) (:maintainer "Chen Bin" . "chenbin.sh@gmail.com") (:keywords "tools") (:url . "http://github.com/redguardtoo/elpa-mirror"))]) - (elpher . [(20220715 612) ((emacs (27 1))) "A friendly gopher and gemini client" tar ((:commit . "b42463a49eca1f319bc1830f70295e8f127fb40b") (:authors ("Tim Vaughan" . "plugd@thelambdalab.xyz")) (:maintainer "Tim Vaughan" . "plugd@thelambdalab.xyz") (:keywords "comm" "gopher") (:url . "https://thelambdalab.xyz/elpher"))]) + (elpher . [(20220809 253) ((emacs (27 1))) "A friendly gopher and gemini client" tar ((:commit . "b5269970249871a8889950a3e47bdff51eb0420c") (:authors ("Tim Vaughan" . "plugd@thelambdalab.xyz")) (:maintainer "Tim Vaughan" . "plugd@thelambdalab.xyz") (:keywords "comm" "gopher") (:url . "https://thelambdalab.xyz/elpher"))]) (elpl . [(20220328 316) ((emacs (24 4))) "Emacs Lisp REPL" single ((:commit . "501871ab543b9967bfe87a8a82f83ab96b7f909e") (:authors ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainer "Gong Qijian" . "gongqijian@gmail.com") (:keywords "lisp" "tool") (:url . "https://github.com/twlz0ne/elpl"))]) (elpy . [(20220627 1416) ((company (0 9 2)) (emacs (24 4)) (highlight-indentation (0 5 0)) (pyvenv (1 3)) (yasnippet (0 8 0)) (s (1 11 0))) "Emacs Python Development Environment" tar ((:commit . "de31d30003c515c25ff7bfd3a361c70c298f78bb") (:authors ("Jorgen Schaefer , Gaby Launay" . "gaby.launay@protonmail.com")) (:maintainer "Jorgen Schaefer , Gaby Launay" . "gaby.launay@protonmail.com") (:keywords "python" "ide" "languages" "tools") (:url . "https://github.com/jorgenschaefer/elpy"))]) (elpygen . [(20171225 1736) ((emacs (25)) (yasnippet (0 8 0))) "Generate a Python function/method using a symbol under point" single ((:commit . "21929c997a05968f9eefe52b85a76ceaab3b0d81") (:authors ("Vladimir Kazanov" . "vkazanov@inbox.ru")) (:maintainer "Vladimir Kazanov" . "vkazanov@inbox.ru") (:keywords "python" "languages" "tools") (:url . "https://github.com/vkazanov/elpygen"))]) @@ -1232,7 +1236,7 @@ (elvish-mode . [(20180809 1612) ((emacs (24 3))) "Defines a major mode for Elvish" single ((:commit . "a13fcaf209d803e2e450ca2bf80dea94b40a0141") (:authors ("Adam Schwalm" . "adamschwalm@gmail.com")) (:maintainer "Adam Schwalm" . "adamschwalm@gmail.com") (:url . "https://github.com/ALSchwalm/elvish-mode"))]) (elwm . [(20150817 1007) ((dash (1 1 0))) "Minimalistic window manager for emacs" single ((:commit . "c33b183f006ad476c3a44dab316f580f8b369930") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "docs") (:url . "https://github.com/Fuco1/elwm"))]) (elx . [(20220331 2252) ((emacs (25 1))) "extract information from Emacs Lisp libraries" single ((:commit . "ea0b10340b22e8dd0454fe37ba84ff2157fada4f") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "docs" "libraries" "packages") (:url . "https://github.com/emacscollective/elx"))]) - (emacs-everywhere . [(20220628 1501) ((emacs (26 3))) "System-wide popup windows for quick edits" single ((:commit . "cbe56e216df38756de11370535601b5324fdc63b") (:authors ("TEC ")) (:maintainer "TEC" . "tec@tecosaur.com") (:keywords "conenience" "frames") (:url . "https://github.com/tecosaur/emacs-everywhere"))]) + (emacs-everywhere . [(20220806 1536) ((emacs (26 3))) "System-wide popup windows for quick edits" single ((:commit . "a1b16b53c5211607fa0f76c80aebb3a72f645ae1") (:authors ("TEC ")) (:maintainer "TEC" . "tec@tecosaur.com") (:keywords "conenience" "frames") (:url . "https://github.com/tecosaur/emacs-everywhere"))]) (emacsc . [(20220420 1042) nil "helper for emacsc(1)" tar ((:commit . "199c08147ebe98da1004c478c92ba8866950b637") (:authors ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainer "Akinori MUSHA" . "knu@iDaemons.org") (:keywords "tools") (:url . "https://github.com/knu/emacsc"))]) (emacsist-view . [(20160426 1223) nil "Mode for viewing emacsist.com" single ((:commit . "f67761259ed779a9bc95c9a4e0474522990c5c6b") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "usability") (:url . "https://github.com/lujun9972/emacsist-view"))]) (emacsql . [(20220408 1614) ((emacs (25 1))) "high-level SQL database front-end" tar ((:commit . "373975cbccf7776af771e23f86043b236a330702") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/emacsql"))]) @@ -1266,7 +1270,7 @@ (emoji-cheat-sheet-plus . [(20200202 1404) ((emacs (24)) (helm (1 6 4))) "emoji-cheat-sheet for emacs" tar ((:commit . "ffcc84d7060dfa000148e7f8be4fd6701593a74f") (:authors ("Sylvain Benner (based on the work of Shingo Fukuyama)")) (:maintainer "Sylvain Benner (based on the work of Shingo Fukuyama)") (:keywords "emacs" "emoji") (:url . "https://github.com/syl20bnr/emacs-emoji-cheat-sheet-plus"))]) (emoji-display . [(20140117 1013) nil "emoji displaying module" single ((:commit . "bb4217f6400151a9cfa6d4524b8427f01feb5193") (:authors ("Kazuhiro Ito" . "kzhr@d1.dion.ne.jp")) (:maintainer "Kazuhiro Ito" . "kzhr@d1.dion.ne.jp") (:keywords "emoji") (:url . "https://github.com/ikazuhiro/emoji-display"))]) (emoji-fontset . [(20160726 1924) nil "Set font face for Emoji." single ((:commit . "75d8ee5b9d2117e30f02c6ce9c608a005aee5d86") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "emoji" "font" "config"))]) - (emoji-github . [(20220704 648) ((emacs (24 4)) (emojify (1 0)) (request (0 3 0))) "Display list of GitHub's emoji. (cheat sheet)" single ((:commit . "caaa3a10a9c44aba74f4964b04169b0adedeb2f9") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "list" "github" "emoji" "display") (:url . "https://github.com/jcs-elpa/emoji-github"))]) + (emoji-github . [(20220704 648) ((emacs (24 4)) (emojify (1 0)) (request (0 3 0))) "Display list of GitHub's emoji. (cheat sheet)" single ((:commit . "01d4a14f06d76b6d121be897dea8bc92dad50ee6") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "list" "github" "emoji" "display") (:url . "https://github.com/jcs-elpa/emoji-github"))]) (emoji-recall . [(20160723 2208) ((emacs (24))) "How many emoji can you recall from memory?" tar ((:commit . "d9122f8fb1467309260109a1985cd14f18fdf631") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "game") (:url . "https://github.com/lujun9972/emoji-recall.el"))]) (emojify . [(20210108 1111) ((seq (1 11)) (ht (2 0)) (emacs (24 3))) "Display emojis in Emacs" tar ((:commit . "1b726412f19896abf5e4857d4c32220e33400b55") (:authors ("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")) (:maintainer "mxgoldstein" . "m_goldstein@gmx.net") (:url . "https://github.com/mxgoldstein/emojify-logos"))]) @@ -1281,12 +1285,12 @@ (enlive . [(20170725 1417) nil "query html document with css selectors" single ((:commit . "604a8ca272b6889f114e2b5a13adb5b1dc4bae86") (:authors ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainer "ZHOU Feng" . "zf.pascal@gmail.com") (:keywords "css" "selector" "query") (:url . "http://github.com/zweifisch/enlive"))]) (eno . [(20191013 1239) ((dash (2 12 1)) (edit-at-point (1 0))) "Goto/copy/cut any word/symbol/line in view, similar to ace-jump/easymotion" single ((:commit . "c5c6193687c0bede1ddf507c430cf8b0a6d272d9") (:authors (nil . "")) (:maintainer nil . "") (:url . "http://github.com/enoson/eno.el"))]) (enotify . [(20130407 1348) nil "A networked notification system for emacs" tar ((:commit . "7fd2f48ef4ff32c8f013c634ea2dd6b1d1409f80") (:authors ("Alessandro Piras" . "laynor@gmail.com")) (:maintainer "Alessandro Piras" . "laynor@gmail.com") (:keywords "tools"))]) - (envrc . [(20220604 1519) ((seq (2)) (emacs (25 1)) (inheritenv (0 1))) "Support for `direnv' that operates buffer-locally" single ((:commit . "7f36664fc6d97a7ca77c6c3e0c6577b72fa0b70d") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "processes" "tools") (:url . "https://github.com/purcell/envrc"))]) + (envrc . [(20220809 1320) ((seq (2)) (emacs (25 1)) (inheritenv (0 1))) "Support for `direnv' that operates buffer-locally" single ((:commit . "2a6257b5fdf51e77e41038c934fe81015c85fbde") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "processes" "tools") (:url . "https://github.com/purcell/envrc"))]) (eopengrok . [(20200205 624) ((s (1 9 0)) (dash (2 10 0)) (magit (2 1 0)) (cl-lib (0 5))) "opengrok interface for emacs" single ((:commit . "6fa16c4ccaaebaef64dca0d3d29904c45fd6597d") (:authors ("Youngjoo Lee" . "youngker@gmail.com")) (:maintainer "Youngjoo Lee" . "youngker@gmail.com") (:keywords "tools"))]) (epc . [(20140610 534) ((concurrent (0 3 1)) (ctable (0 1 2))) "A RPC stack for the Emacs Lisp" tar ((:commit . "e1bfa5ca163273859336e3cc89b4b6460f7f8cda") (:authors ("SAKURAI Masashi ")) (:maintainer "SAKURAI Masashi ") (:keywords "lisp" "rpc") (:url . "https://github.com/kiwanami/emacs-epc"))]) (epic . [(20170210 23) ((htmlize (1 47))) "Evernote Picker for Cocoa Emacs" single ((:commit . "a41826c330eb0ea061d58a08cc861b0c4ac8ec4e") (:authors ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainer "Yoshinari Nomura" . "nom@quickhack.net") (:keywords "evernote" "applescript") (:url . "https://github.com/yoshinari-nomura/epic"))]) (eping . [(20201027 2149) ((emacs (25 1))) "Ping websites to check internet connectivity" tar ((:commit . "99d3a4b6973d5b09864e0af7425a61f99c19b90a") (:authors ("Sean Hutchings" . "seanhut@yandex.com")) (:maintainer "Sean Hutchings" . "seanhut@yandex.com") (:keywords "comm" "processes" "terminals" "unix") (:url . "https://github.com/sean-hut/eping"))]) - (epkg . [(20220510 2036) ((emacs (25 1)) (compat (28 1 1 0)) (closql (20210927))) "Browse the Emacsmirror package database" tar ((:commit . "4524f9a8f19717c4afb84a184db7841b4ffcbf56") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/emacscollective/epkg"))]) + (epkg . [(20220822 1221) ((emacs (25 1)) (compat (28 1 1 0)) (closql (20210927))) "Browse the Emacsmirror package database" tar ((:commit . "f4dc72d130eae2ad4f479671339d1cdebda62811") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/emacscollective/epkg"))]) (epkg-marginalia . [(20220511 1942) ((emacs (27 1)) (compat (28 1 1 0)) (epkg (3 3 1)) (marginalia (0 12))) "Show Epkg information in completion annotations" single ((:commit . "ee784211346c7c374accec2bda78788cc68fa641") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/emacscollective/epkg-marginalia"))]) (epl . [(20180205 2049) ((cl-lib (0 3))) "Emacs Package Library" single ((:commit . "78ab7a85c08222cd15582a298a364774e3282ce6") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Johan Andersson" . "johan.rejeep@gmail.com") (:keywords "convenience") (:url . "http://github.com/cask/epl"))]) (epm . [(20190509 443) ((emacs (24 3)) (epl (0 8))) "Emacs Package Manager" tar ((:commit . "6375ddbf93c5f25647f6ebb25b54045b3c93a5be") (:authors ("Chunyang Xu" . "xuchunyang.me@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang.me@gmail.com") (:url . "https://github.com/xuchunyang/epm"))]) @@ -1316,8 +1320,8 @@ (erefactor . [(20200513 1252) ((cl-lib (0 3))) "Emacs-Lisp refactoring utilities" single ((:commit . "bfe27a1b8c7cac0fe054e76113e941efa3775fe8") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "extensions" "tools" "maint") (:url . "https://github.com/mhayashi1120/Emacs-erefactor"))]) (ergoemacs-mode . [(20220411 338) ((emacs (24 1)) (cl-lib (0 5))) "Emacs mode based on common modern interface and ergonomics." tar ((:commit . "9cd89eef490f6c9f4af273bb3dd2c68d5ed2de61") (:authors ("Xah Lee" . "xah@xahlee.org") ("David Capello" . "davidcapello@gmail.com") ("Matthew L. Fidler" . "matthew.fidler@gmail.com") ("Kim F. Storm" . "storm@cua.dk")) (:maintainer "Matthew L. Fidler" . "matthew.fidler@gmail.com") (:keywords "convenience") (:url . "https://github.com/ergoemacs/ergoemacs-mode"))]) (ergoemacs-status . [(20160318 538) ((powerline (2 3)) (mode-icons (0 1 0))) "Adaptive Status Bar / Mode Line" single ((:commit . "d952cc2361adf6eb4d6af60950ad4ab699c81320") (:authors ("Matthew Fidler")) (:maintainer "Matthew Fidler"))]) - (eri . [(20200914 644) nil "Enhanced relative indentation (eri)" single ((:commit . "5d2d77abbc0c97f5b23d7089797c3ef8796508dc") (:url . "https://github.com/agda/agda"))]) - (erlang . [(20220215 1844) ((emacs (24 1))) "Erlang major mode" tar ((:commit . "f97de7a99692cb08b5e27d17e86d92707ad16eed") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "erlang" "languages" "processes"))]) + (eri . [(20200914 644) nil "Enhanced relative indentation (eri)" single ((:commit . "b382d7d376fad3429940de2fa0d399519e172cfc") (:url . "https://github.com/agda/agda"))]) + (erlang . [(20220215 1844) ((emacs (24 1))) "Erlang major mode" tar ((:commit . "95fe8c4fcf670d6b2aff8a23cd7dad034d2c9b67") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "erlang" "languages" "processes"))]) (erlstack-mode . [(20220617 2049) ((emacs (25 1)) (dash (2 12 0))) "Minor mode for analysing Erlang stacktraces" single ((:commit . "a4a30f74e48894ccfdefc073a9e1b005ee632017") (:authors ("k32")) (:maintainer "k32") (:keywords "tools" "erlang") (:url . "https://github.com/k32/erlstack-mode"))]) (eros . [(20180415 618) ((emacs (24 4))) "Evaluation Result OverlayS for Emacs Lisp" single ((:commit . "dd8910279226259e100dab798b073a52f9b4233a") (:authors ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainer "Tianxiang Xiong" . "tianxiang.xiong@gmail.com") (:keywords "convenience" "lisp") (:url . "https://github.com/xiongtx/eros"))]) (ert-async . [(20200105 1031) ((emacs (24 1))) "Async support for ERT" single ((:commit . "948cf2faa10e085bda3739034ca5ea1912893433") (:authors ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Johan Andersson" . "johan.rejeep@gmail.com") (:keywords "lisp" "test") (:url . "http://github.com/rejeep/ert-async.el"))]) @@ -1341,12 +1345,12 @@ (eshell-info-banner . [(20220728 1006) ((emacs (25 1)) (s (1))) "System information as your Eshell banner" single ((:commit . "2f4e59ea7ac2129b175af2b6acf353b29687fb9f") (:authors ("Lucien Cartier-Tilet" . "lucien@phundrak.com")) (:maintainer "Lucien Cartier-Tilet" . "lucien@phundrak.com") (:url . "https://github.com/Phundrak/eshell-info-banner.el"))]) (eshell-outline . [(20201121 620) ((emacs (25 1))) "Enhanced outline-mode for Eshell" single ((:commit . "6f917afa5b3d36764d76d7864589094647d8c3b4") (:authors ("Jamie Beardslee" . "jdb@jamzattack.xyz")) (:maintainer "Jamie Beardslee" . "jdb@jamzattack.xyz") (:keywords "unix" "eshell" "outline" "convenience") (:url . "https://git.jamzattack.xyz/eshell-outline"))]) (eshell-prompt-extras . [(20210925 110) ((emacs (25))) "Display extra information for your eshell prompt." single ((:commit . "c2078093323206b91a1b1f5786d79faa00b76be7") (:authors ("zwild" . "judezhao@outlook.com")) (:maintainer "Chunyang Xu" . "mail@xuchunyang.me") (:keywords "eshell" "prompt") (:url . "https://github.com/zwild/eshell-prompt-extras"))]) - (eshell-syntax-highlighting . [(20210429 413) ((emacs (25 1))) "Highlight eshell commands" single ((:commit . "8e3a685fc6d97af79e1046e5b24385786d8e92f6") (:authors ("Alex Kreisher" . "akreisher18@gmail.com")) (:maintainer "Alex Kreisher" . "akreisher18@gmail.com") (:keywords "convenience") (:url . "https://github.com/akreisher/eshell-syntax-highlighting"))]) + (eshell-syntax-highlighting . [(20220816 2017) ((emacs (25 1))) "Highlight eshell commands" single ((:commit . "1ba39a9ffb2298cd716a4314cf3f369028c7bafe") (:authors ("Alex Kreisher" . "akreisher18@gmail.com")) (:maintainer "Alex Kreisher" . "akreisher18@gmail.com") (:keywords "convenience") (:url . "https://github.com/akreisher/eshell-syntax-highlighting"))]) (eshell-toggle . [(20220718 729) ((emacs (25 1)) (dash (2 11 0))) "Show/hide eshell under active window." single ((:commit . "d4e884624f02e68b267b0044322ad17baa6780f8") (:authors ("Dmitry Cherkassov" . "dcherkassov@gmail.com")) (:maintainer "Dmitry Cherkassov" . "dcherkassov@gmail.com") (:keywords "processes") (:url . "https://github.com/4da/eshell-toggle"))]) (eshell-up . [(20170425 1737) ((emacs (24))) "Quickly go to a specific parent directory in eshell" single ((:commit . "ff84e6069b98f2ed00857a0f78bff19d96e4955c") (:authors ("Peter W. V. Tran-Jørgensen" . "peter.w.v.jorgensen@gmail.com")) (:maintainer "Peter W. V. Tran-Jørgensen" . "peter.w.v.jorgensen@gmail.com") (:keywords "eshell") (:url . "https://github.com/peterwvj/eshell-up"))]) (eshell-vterm . [(20220506 1212) ((emacs (27 1)) (vterm (0 0 1))) "Vterm for visual commands in eshell" single ((:commit . "4e8589fcaf6243011a76b4816e7689d913927aab") (:authors ("Illia Ostapyshyn" . "ilya.ostapyshyn@gmail.com")) (:maintainer "Illia Ostapyshyn" . "ilya.ostapyshyn@gmail.com") (:keywords "eshell" "vterm" "terminals" "shell" "visual" "tools" "processes") (:url . "https://github.com/iostapyshyn/eshell-vterm"))]) (eshell-z . [(20191116 333) ((cl-lib (0 5))) "cd to frequent directory in eshell" single ((:commit . "337cb241e17bd472bd3677ff166a0800f684213c") (:authors ("Chunyang Xu" . "mail@xuchunyang.me")) (:maintainer "Chunyang Xu" . "mail@xuchunyang.me") (:keywords "convenience") (:url . "https://github.com/xuchunyang/eshell-z"))]) - (eslint-disable-rule . [(20220328 354) ((emacs (27 2))) "Commands to add JS comments disabling eslint rules" tar ((:commit . "7d4cc05d336fbc465f91a87b38bf360efaf76fcf") (:url . "https://github.com/DamienCassou/eslint-disable-rule"))]) + (eslint-disable-rule . [(20220811 1006) ((emacs (27 2))) "Commands to add JS comments disabling eslint rules" tar ((:commit . "642ead124172dd470e8ab59fd0645597dc9d8e66") (:url . "https://github.com/DamienCassou/eslint-disable-rule"))]) (eslint-fix . [(20211005 221) nil "Fix JavaScript files using ESLint" single ((:commit . "0435d8e2864bb4f1be59ae548d0068c69fa31c7a") (:authors ("Neri Marschik" . "marschik_neri@cyberagent.co.jp")) (:maintainer "Neri Marschik" . "marschik_neri@cyberagent.co.jp") (:keywords "tools" "javascript" "eslint" "lint" "formatting" "style") (:url . "https://github.com/codesuki/eslint-fix"))]) (eslint-rc . [(20220328 800) ((emacs (24 3)) (eslint-fix (0 1 0))) "Use local rc rules with ESLint" single ((:commit . "eb6f3e715792952bc957d5dc8ab1a607f3dbbd55") (:authors ("Joel Bryan Juliano ")) (:maintainer "Joel Bryan Juliano ") (:keywords "convenience" "edit" "js" "ts" "rc" "eslintrc" "eslint-rc" "eslint" "eslint-fix") (:url . "https://github.com/jjuliano/eslint-rc-emacs"))]) (eslintd-fix . [(20210731 1649) ((dash (2 12 0)) (emacs (26 3))) "use eslint_d to automatically fix js files" single ((:commit . "3897d8a679a6e98e3f5054aaefe07f6b55f8f128") (:authors ("Aaron Jensen" . "aaronjensen@gmail.com")) (:maintainer "Aaron Jensen" . "aaronjensen@gmail.com") (:url . "https://github.com/aaronjensen/eslintd-fix"))]) @@ -1357,7 +1361,7 @@ (espy . [(20200317 2333) ((emacs (24))) "Emacs Simple Password Yielder" single ((:commit . "2c01be937a5e5bde62921684a0b27300705fb4e0") (:authors ("Sebastian Wålinder" . "s.walinder@gmail.com")) (:maintainer "Sebastian Wålinder" . "s.walinder@gmail.com") (:keywords "convenience") (:url . "https://github.com/walseb/espy"))]) (esqlite . [(20151206 1206) ((pcsv (1 3 3))) "Manipulate sqlite file from Emacs" single ((:commit . "08a779a821f8d32c1a1985d8d9eb6cf21646ce2e") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "data") (:url . "https://github.com/mhayashi1120/Emacs-esqlite"))]) (esqlite-helm . [(20151116 850) ((esqlite (0 2 0)) (helm (20131207 845))) "Define helm source for sqlite database" single ((:commit . "08a779a821f8d32c1a1985d8d9eb6cf21646ce2e") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "data") (:url . "https://github.com/mhayashi1120/Emacs-esqlite"))]) - (ess . [(20220727 1131) ((emacs (25 1))) "Emacs Speaks Statistics" tar ((:commit . "b8167727d3c51f7adee6ebcad698ab566b68a55e") (:authors ("David Smith" . "dsmith@stats.adelaide.edu.au") ("A.J. Rossini" . "blindglobe@gmail.com") ("Richard M. Heiberger" . "rmh@temple.edu") ("Kurt Hornik" . "Kurt.Hornik@R-project.org") ("Martin Maechler" . "maechler@stat.math.ethz.ch") ("Rodney A. Sparapani" . "rsparapa@mcw.edu") ("Stephen Eglen" . "stephen@gnu.org") ("Sebastian P. Luque" . "spluque@gmail.com") ("Henning Redestig" . "henning.red@googlemail.com") ("Vitalie Spinu" . "spinuvit@gmail.com") ("Lionel Henry" . "lionel.hry@gmail.com") ("J. Alexander Branham" . "alex.branham@gmail.com")) (:maintainer "ESS Core Team" . "ESS-core@r-project.org") (:url . "https://ess.r-project.org/"))]) + (ess . [(20220815 2020) ((emacs (25 1))) "Emacs Speaks Statistics" tar ((:commit . "9675bf58bd7237e2762e24501ccc5a6b4e031333") (:authors ("David Smith" . "dsmith@stats.adelaide.edu.au") ("A.J. Rossini" . "blindglobe@gmail.com") ("Richard M. Heiberger" . "rmh@temple.edu") ("Kurt Hornik" . "Kurt.Hornik@R-project.org") ("Martin Maechler" . "maechler@stat.math.ethz.ch") ("Rodney A. Sparapani" . "rsparapa@mcw.edu") ("Stephen Eglen" . "stephen@gnu.org") ("Sebastian P. Luque" . "spluque@gmail.com") ("Henning Redestig" . "henning.red@googlemail.com") ("Vitalie Spinu" . "spinuvit@gmail.com") ("Lionel Henry" . "lionel.hry@gmail.com") ("J. Alexander Branham" . "alex.branham@gmail.com")) (:maintainer "ESS Core Team" . "ESS-core@r-project.org") (:url . "https://ess.r-project.org/"))]) (ess-R-data-view . [(20130509 1158) ((ctable (20130313 1743)) (popup (20130324 1305)) (ess (20130225 1754))) "Data viewer for GNU R" single ((:commit . "d6e98d3ae1e2a2ea39a56eebcdb73e99d29562e9") (:authors ("myuhe ")) (:maintainer "myuhe") (:keywords "convenience") (:url . "https://github.com/myuhe/ess-R-data-view.el"))]) (ess-r-insert-obj . [(20220610 1406) ((emacs (26 1)) (ess (18 10 1))) "Insert objects in ESS-R" single ((:commit . "2ded9c23d0af2a7f6c0e02f9ea4af0e5b3cb7fb4") (:authors ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainer "Shuguang Sun" . "shuguang79@qq.com") (:keywords "tools") (:url . "https://github.com/ShuguangSun/ess-r-insert-obj"))]) (ess-smart-equals . [(20210411 1333) ((emacs (25 1)) (ess (18 10))) "flexible, context-sensitive assignment key for R/S" single ((:commit . "fea9eea4b59c3e9559b379508e3500076ca99ef1") (:authors ("Christopher R. Genovese" . "genovese@cmu.edu")) (:maintainer "Christopher R. Genovese" . "genovese@cmu.edu") (:keywords "r" "s" "ess" "convenience") (:url . "https://github.com/genovese/ess-smart-equals"))]) @@ -1381,7 +1385,7 @@ (evalator-clojure . [(20160208 2148) ((cider (0 10 0)) (evalator (1 0 0))) "Clojure evaluation context for evalator via CIDER." tar ((:commit . "caa4e0a137bdfada86593128a654e16aa617ad50") (:authors ("Sean Irby")) (:maintainer "Sean Irby" . "sean.t.irby@gmail.com") (:keywords "languages" "clojure" "cider" "helm") (:url . "http://www.github.com/seanirby/evalator-clojure"))]) (eve-mode . [(20170822 2231) ((emacs (25)) (polymode (1 0)) (markdown-mode (2 0))) "Major mode for editing Eve documents." single ((:commit . "a4661114d9c18725691b76321d72167ca5a9070a") (:authors ("Joshua Cole" . "joshuafcole@gmail.com")) (:maintainer "Joshua Cole" . "joshuafcole@gmail.com") (:keywords "languages" "wp" "tools") (:url . "https://github.com/witheve/emacs-eve-mode"))]) (everlasting-scratch . [(20220412 921) ((emacs (25 1))) "The *scratch* that lasts forever" single ((:commit . "8706c55f3b7c267c15b8f10170ecec9998b3cc3d") (:authors ("Huming Chen" . "chenhuming@gmail.com")) (:maintainer "Huming Chen" . "chenhuming@gmail.com") (:keywords "convenience" "tool") (:url . "https://github.com/beacoder/everlasting-scratch"))]) - (evil . [(20220802 1212) ((emacs (24 1)) (goto-chg (1 6)) (cl-lib (0 5))) "Extensible Vi layer for Emacs." tar ((:commit . "5e562ae0a5775a6928fddb6f97a76d770a59dfa6") (:maintainer "Tom Dalziel" . "tom.dalziel@gmail.com") (:keywords "emulation" "vim") (:url . "https://github.com/emacs-evil/evil"))]) + (evil . [(20220825 745) ((emacs (24 1)) (goto-chg (1 6)) (cl-lib (0 5))) "Extensible Vi layer for Emacs." tar ((:commit . "691d0cd36e6c09ecbc18537b7721dcb6d775e873") (:maintainer "Tom Dalziel" . "tom.dalziel@gmail.com") (:keywords "emulation" "vim") (:url . "https://github.com/emacs-evil/evil"))]) (evil-anzu . [(20200514 1902) ((evil (1 0 0)) (anzu (0 46))) "anzu for evil-mode" single ((:commit . "d3f6ed4773b48767bd5f4708c7f083336a8a8a86") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com") ("Fredrik Bergroth" . "fbergroth@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-evil-anzu"))]) (evil-args . [(20220125 1626) ((evil (1 0 8))) "Motions and text objects for delimited arguments in Evil." single ((:commit . "2671071a4a57eaee7cc8c27b9e4b6fc60fd2ccd3") (:authors ("Connor Smith" . "wconnorsmith@gmail.com")) (:maintainer "Connor Smith" . "wconnorsmith@gmail.com") (:keywords "evil" "vim-emulation") (:url . "http://github.com/wcsmith/evil-args"))]) (evil-avy . [(20150908 748) ((emacs (24 1)) (cl-lib (0 5)) (avy (0 3 0)) (evil (1 2 3))) "set-based completion" single ((:commit . "2dd955cc3ecaa7ddeb67b295298abdc6d16dd3a5") (:authors ("Yufan Lou" . "loganlyf@gmail.com")) (:maintainer "Yufan Lou" . "loganlyf@gmail.com") (:keywords "point" "location" "evil" "vim") (:url . "https://github.com/louy2/evil-avy"))]) @@ -1389,7 +1393,7 @@ (evil-cleverparens . [(20170718 413) ((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 . "8c45879d49bfa6d4e414b6c1df700a4a51cbb869") (:authors ("Olli Piepponen" . "opieppo@gmail.com")) (:maintainer "Olli Piepponen" . "opieppo@gmail.com") (:keywords "cleverparens" "parentheses" "evil" "paredit" "smartparens") (:url . "https://github.com/luxbock/evil-cleverparens"))]) (evil-colemak-basics . [(20220222 1856) ((emacs (24 3)) (evil (1 2 12)) (evil-snipe (2 0 3))) "Basic Colemak key bindings for evil-mode" single ((:commit . "66648de206a7368013f28c0d053b1b32c3efe6c6") (:authors ("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" single ((:commit . "6d98b6da60f414524a0d718f76024c26dce742b3") (:authors ("Bryan Allred" . "bryan@revolvingcow.com")) (:maintainer "Bryan Allred" . "bryan@revolvingcow.com") (:keywords "colemak" "evil") (:url . "https://github.com/bmallred/evil-colemak-minimal"))]) - (evil-collection . [(20220730 1602) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "8cb38434801b5e364abc1e160021c6f1d1a0a006") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "evil" "tools") (:url . "https://github.com/emacs-evil/evil-collection"))]) + (evil-collection . [(20220810 1901) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "665d5c99e216c7b18856f7ceda7c91ea5669f904") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "evil" "tools") (:url . "https://github.com/emacs-evil/evil-collection"))]) (evil-commentary . [(20210210 1702) ((evil (1 0 0))) "Comment stuff out. A port of vim-commentary." tar ((:commit . "2dab6ac34d1617971768ad219d73af48f7473fec") (:authors ("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 . "824f7c56980d72a0ff04c662223540cd66f13754") (:authors ("Joshua Branson")) (:maintainer "Joshua Branson") (:keywords "dvorak" "evil" "vim"))]) (evil-easymotion . [(20200424 135) ((emacs (24)) (avy (0 3 0)) (cl-lib (0 5))) "A port of vim's easymotion to emacs" single ((:commit . "f96c2ed38ddc07908db7c3c11bcd6285a3e8c2e9") (:authors ("PythonNut" . "pythonnut@pythonnut.com")) (:maintainer "PythonNut" . "pythonnut@pythonnut.com") (:keywords "convenience" "evil") (:url . "https://github.com/pythonnut/evil-easymotion"))]) @@ -1443,7 +1447,7 @@ (evil-swap-keys . [(20191105 1426) ((emacs (24 4))) "Intelligently swap keys on text input with evil" single ((:commit . "b5ef105499f998b5667da40da30c073229a213ea") (:authors ("Wouter Bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "Wouter Bolsterlee" . "wouter@bolsterl.ee") (:keywords "convenience" "data" "languages" "tools") (:url . "https://github.com/wbolster/evil-swap-keys"))]) (evil-tabs . [(20160217 1520) ((evil (0 0 0)) (elscreen (0 0 0))) "Integrating Vim-style tabs for Evil mode users." single ((:commit . "53d3314a810017b6056ab6796aef671f5ea1c063") (:authors ("Kris Jenkins" . "krisajenkins@gmail.com")) (:maintainer "Kris Jenkins" . "krisajenkins@gmail.com") (:keywords "evil" "tab" "tabs" "vim") (:url . "https://github.com/krisajenkins/evil-tabs"))]) (evil-terminal-cursor-changer . [(20220628 1831) nil "Change cursor shape and color by evil state in terminal" single ((:commit . "12ea9c0438c67e560b3866dc78b5c7d1d93f8cc5") (:authors ("7696122")) (:maintainer "7696122") (:keywords "evil" "terminal" "cursor") (:url . "https://github.com/7696122/evil-terminal-cursor-changer"))]) - (evil-test-helpers . [(20220425 2132) ((evil (1 15 0))) "unit test helpers for Evil" single ((:commit . "5e562ae0a5775a6928fddb6f97a76d770a59dfa6") (:authors ("Vegard Øye ")) (:maintainer "Vegard Øye "))]) + (evil-test-helpers . [(20220425 2132) ((evil (1 15 0))) "unit test helpers for Evil" single ((:commit . "691d0cd36e6c09ecbc18537b7721dcb6d775e873") (:authors ("Vegard Øye ")) (:maintainer "Vegard Øye "))]) (evil-tex . [(20220415 842) ((emacs (26 1)) (evil (1 0)) (auctex (11 88))) "Useful features for editing LaTeX in evil-mode" single ((:commit . "26035ec9a09f8b38ce0d495ff788e83ec8b195d5") (:keywords "tex" "emulation" "vi" "evil" "wp") (:url . "https://github.com/iyefrat/evil-tex"))]) (evil-text-object-python . [(20191010 1328) ((emacs (25)) (evil (1 2 14)) (dash (2 16 0))) "Python specific evil text objects" single ((:commit . "39d22fc524f0413763f291267eaab7f4e7984318") (:authors ("Wouter Bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "Wouter Bolsterlee" . "wouter@bolsterl.ee") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/wbolster/evil-text-object-python"))]) (evil-textobj-anyblock . [(20170905 1907) ((cl-lib (0 5)) (evil (1 1 0))) "Textobject for the closest user-defined blocks." single ((:commit . "ff00980f0634f95bf2ad9956b615a155ea8743be") (:authors ("Fox Kiester" . "noct@openmailbox.org")) (:maintainer "Fox Kiester" . "noct@openmailbox.org") (:keywords "evil") (:url . "https://github.com/noctuid/evil-textobj-anyblock"))]) @@ -1451,7 +1455,7 @@ (evil-textobj-entire . [(20150422 1254) ((emacs (24)) (evil (1 0 0))) "text object for entire lines of buffer for evil" single ((:commit . "5b3a98f3a69edc3a788f539f6ffef4a0ef5e853d") (:authors ("supermomonga")) (:maintainer "supermomonga") (:keywords "convenience" "emulations") (:url . "https://github.com/supermomonga/evil-textobj-entire"))]) (evil-textobj-line . [(20211101 1429) ((evil (1 0 0))) "Line text object for Evil" single ((:commit . "9eaf9a5485c2b5c05e16552b34632ca520cd681d") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com"))]) (evil-textobj-syntax . [(20181210 1213) ((names (0 5)) (emacs (24)) (evil (0))) "Provides syntax text objects." single ((:commit . "2d9ba8c75c754b409aea7469f46a5cfa52a872f3") (:keywords "evil" "syntax" "highlight" "text-object") (:url . "https://github.com/laishulu/evil-textobj-syntax"))]) - (evil-textobj-tree-sitter . [(20220715 1530) ((emacs (25 1)) (evil (1 0 0)) (tree-sitter (0 15 0))) "Provides evil textobjects using tree-sitter" tar ((:commit . "9dce8dab68c954ae32095328cf898eb856fc341a") (:keywords "evil" "tree-sitter" "text-object" "convenience") (:url . "https://github.com/meain/evil-textobj-tree-sitter"))]) + (evil-textobj-tree-sitter . [(20220819 1408) ((emacs (25 1)) (evil (1 0 0)) (tree-sitter (0 15 0))) "Provides evil textobjects using tree-sitter" tar ((:commit . "f0b887faac1eb1f964e11745e63c1ef53398a7a8") (:keywords "evil" "tree-sitter" "text-object" "convenience") (:url . "https://github.com/meain/evil-textobj-tree-sitter"))]) (evil-traces . [(20191214 558) ((emacs (25 1)) (evil (1 2 13))) "Visual hints for `evil-ex'" single ((:commit . "290b5323542c46af364ec485c8ec9000040acf90") (:authors ("Daniel Phan" . "daniel.phan36@gmail.com")) (:maintainer "Daniel Phan" . "daniel.phan36@gmail.com") (:keywords "emulations" "evil" "visual") (:url . "https://github.com/mamapanda/evil-traces"))]) (evil-tree-edit . [(20220425 2355) ((emacs (27 1)) (tree-edit (0 1 0)) (tree-sitter (0 15 0)) (evil (1 0 0)) (avy (0 5 0)) (s (0 0 0))) "Evil structural editing for any language!" tar ((:commit . "eafee31ca4f532a9dbee326d3ec3bdd1e997223b") (:authors ("Ethan Leba" . "ethanleba5@gmail.com")) (:maintainer "Ethan Leba" . "ethanleba5@gmail.com") (:url . "https://github.com/ethan-leba/tree-edit"))]) (evil-tutor . [(20150103 650) ((evil (1 0 9))) "Vimtutor adapted to Evil and wrapped in a major-mode" tar ((:commit . "4e124cd3911dc0d1b6817ad2c9e59b4753638f28") (:authors ("Sylvain Benner" . "sylvain.benner@gmail.com")) (:maintainer "Sylvain Benner" . "sylvain.benner@gmail.com") (:keywords "convenience" "editing" "evil") (:url . "https://github.com/syl20bnr/evil-tutor"))]) @@ -1480,7 +1484,7 @@ (exsqlaim-mode . [(20170607 1003) ((s (1 10 0))) "Use variables inside sql queries" single ((:commit . "a2e0a62ec8b87193d8eaa695774bfd689324b06c") (:authors ("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" single ((:commit . "92e0fff482a0a4dc2971c39581c5ea9e84ae5e1c") (:authors ("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") (:authors ("Matthew L. Fidler")) (:maintainer "Matthew L. Fidler") (:keywords "extend" "drag and drop") (:url . "https://github.com/mlf176f2/extend-dnd"))]) - (external-dict . [(20220801 2349) ((emacs (25 1))) "Query external dictionary like goldendict, Bob.app etc" single ((:commit . "aa051548ca850d15f939acfa82b358f756a761f6") (:keywords "wp" "processes") (:url . "https://repo.or.cz/external-dict.el.git"))]) + (external-dict . [(20220816 2136) ((emacs (25 1))) "Query external dictionary like goldendict, Bob.app etc" single ((:commit . "680c37f01e5205805dffeef05be00e2196886a8c") (:keywords "wp" "processes") (:url . "https://repo.or.cz/external-dict.el.git"))]) (extmap . [(20211023 1904) ((emacs (24 1))) "Externally-stored constant mapping for Elisp" single ((:commit . "5875a4ab27831eb81af6246b12a174c765d52a78") (:authors ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainer "Paul Pogonyshev" . "pogonyshev@gmail.com") (:keywords "lisp") (:url . "https://github.com/doublep/extmap"))]) (exunit . [(20211209 1012) ((s (1 11 0)) (emacs (24 3)) (f (0 20 0)) (transient (0 3 6))) "ExUnit test runner" single ((:commit . "0715c2dc2dca0b56c61330eda0690f90cca5f98b") (:authors ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainer "Anantha kumaran" . "ananthakumaran@gmail.com") (:keywords "processes" "elixir" "exunit") (:url . "http://github.com/ananthakumaran/exunit.el"))]) (exwm-edit . [(20220414 106) ((emacs (25 1))) "Edit mode for EXWM" single ((:commit . "b5b7e950f57e30befd68d51df34540b70e6ac28f") (:authors ("Ag Ibragimov")) (:maintainer "Ag Ibragimov") (:keywords "convenience") (:url . "https://github.com/agzam/exwm-edit"))]) @@ -1495,7 +1499,7 @@ (eyuml . [(20141028 2227) ((request (0 2 0)) (s (1 8 0))) "Write textual uml diagram from emacs using yuml.me" single ((:commit . "eb29c37316e44a14741f16e894fbcfcb7537dc80") (:authors ("Anthony HAMON" . "hamon.anth@gmail.com")) (:maintainer "Anthony HAMON" . "hamon.anth@gmail.com") (:keywords "uml") (:url . "http://github.com/antham/eyuml"))]) (ez-query-replace . [(20210724 2247) ((dash (1 2 0)) (s (1 11 0))) "a smarter context-sensitive query-replace that can be reapplied" single ((:commit . "2b68472f4007a73908c3b242e83ac5a7587967ff") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk"))]) (eziam-theme . [(20200327 1810) nil "A mostly monochrome theme, inspired by Tao and Leuven, with dark and light versions." tar ((:commit . "d7e517f8e626035df3b63ec6fc07b85d48a996c5"))]) - (f . [(20220608 943) ((emacs (24 1)) (s (1 7 0)) (dash (2 2 0))) "Modern API for working with files and directories" tar ((:commit . "d2019d4f9625bcc44349c69fe46d6645fd9ff4ff") (:authors ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Lucien Cartier-Tilet" . "lucien@phundrak.com") (:keywords "files" "directories") (:url . "http://github.com/rejeep/f.el"))]) + (f . [(20220814 1054) ((emacs (24 1)) (s (1 7 0)) (dash (2 2 0))) "Modern API for working with files and directories" tar ((:commit . "85c91f95f8b98e153fd959ae467b46bf79622c5d") (:authors ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Lucien Cartier-Tilet" . "lucien@phundrak.com") (:keywords "files" "directories") (:url . "http://github.com/rejeep/f.el"))]) (f3 . [(20180130 1158) ((emacs (24 3)) (helm (2 8 8)) (cl-lib (0 5))) "a helm interface to find" tar ((:commit . "000009ce4adf7a57eae80512f29c4ec2a1391ce5") (:authors ("Danny McClanahan")) (:maintainer "Danny McClanahan") (:keywords "find" "file" "files" "helm" "fast" "finder") (:url . "https://github.com/cosmicexplorer/f3"))]) (fabric . [(20171116 656) nil "Launch Fabric using Emacs" tar ((:commit . "df79be341d0b34ed23850f9894136092fa5fea8c") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@chmouel.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@chmouel.com") (:keywords "python" "fabric") (:url . "https://github.com/nlamirault/fabric.el"))]) (face-explorer . [(20190517 1857) nil "Library and tools for faces and text properties" single ((:commit . "ad1300e13e5643e4c246cabfd91f833d39113052") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "faces") (:url . "https://github.com/Lindydancer/face-explorer"))]) @@ -1509,7 +1513,7 @@ (fancy-dabbrev . [(20220211 633) ((emacs (25 1)) (popup (0 5 3))) "Like dabbrev-expand with preview and popup menu" single ((:commit . "cf4a2f7e3e43e07ab9aa9db16532a21010e9fc8c") (:authors ("Joel Rosdahl" . "joel@rosdahl.net")) (:maintainer "Joel Rosdahl" . "joel@rosdahl.net") (:url . "https://github.com/jrosdahl/fancy-dabbrev"))]) (fancy-narrow . [(20171031 16) nil "narrow-to-region with more eye candy." single ((:commit . "c9b3363752c09045b8ce7a2635afae42d2ae63c7") (:authors ("Artur Malabarba" . "bruce.connor.am@gmail.com")) (:maintainer "Artur Malabarba" . "bruce.connor.am@gmail.com") (:keywords "faces" "convenience") (:url . "http://github.com/Bruce-Connor/fancy-narrow"))]) (fantom-theme . [(20200328 604) ((emacs (24 1))) "Dark theme based on Phantom Code for VSCode" single ((:commit . "2c1c7fd53086c2ff86ee0961642c3b58e2343c08") (:authors ("Adam Svanberg")) (:maintainer "Adam Svanberg") (:url . "https://github.com/adsva/fantom-emacs-theme"))]) - (fanyi . [(20220702 812) ((emacs (27 1)) (s (1 12 0))) "Not only English-Chinese translator" tar ((:commit . "07815b29decc08994e7b6ae24be188047531b1b9") (:authors ("Zhiwei Chen" . "condy0919@gmail.com")) (:maintainer "Zhiwei Chen" . "condy0919@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/condy0919/fanyi.el"))]) + (fanyi . [(20220805 216) ((emacs (27 1)) (s (1 12 0))) "Not only English-Chinese translator" tar ((:commit . "031c7ab7a16113bdca2b351781dc95aff9658c9a") (:authors ("Zhiwei Chen" . "condy0919@gmail.com")) (:maintainer "Zhiwei Chen" . "condy0919@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/condy0919/fanyi.el"))]) (farmhouse-theme . [(20160713 2244) nil "Farmhouse Theme, Emacs edition" tar ((:commit . "7ddc1ff13b4a3d5466bd0d33ecb86100352e83a7"))]) (fasd . [(20210104 738) nil "Emacs integration for the command-line productivity booster `fasd'" single ((:commit . "c1d92553f33ebb018135c698db1a6d7f86731a26") (:authors ("steckerhalter")) (:maintainer "steckerhalter") (:keywords "cli" "bash" "zsh" "autojump") (:url . "https://framagit.org/steckerhalter/emacs-fasd"))]) (fast-scroll . [(20191016 327) ((emacs (25 1)) (cl-lib (0 6 1))) "Some utilities for faster scrolling over large buffers." single ((:commit . "3f6ca0d5556fe9795b74714304564f2295dcfa24") (:authors ("Matthew Carter" . "m@ahungry.com")) (:maintainer "Matthew Carter" . "m@ahungry.com") (:keywords "ahungry" "convenience" "fast" "scroll" "scrolling") (:url . "https://github.com/ahungry/fast-scroll"))]) @@ -1517,7 +1521,7 @@ (fastnav . [(20120211 1457) nil "Fast navigation and editing routines." single ((:commit . "1019ba2b61d1a070204099b23da347278a61bc89") (:authors ("Zsolt Terek" . "zsolt@google.com")) (:maintainer "Zsolt Terek" . "zsolt@google.com") (:keywords "nav" "fast" "fastnav" "navigation"))]) (faust-mode . [(20201004 1353) nil "Faust syntax colorizer for Emacs." single ((:commit . "2a56cda14b152d5471f21a5d82f23c141dc7134c") (:authors ("rukano" . "rukano@gmail.com")) (:maintainer "Yassin Philip" . "xaccrocheur@gmail.com") (:keywords "languages" "faust") (:url . "https://github.com/rukano/emacs-faust-mode"))]) (faustine . [(20171122 1202) ((emacs (24 3)) (faust-mode (0 3))) "Edit, visualize, build and run Faust code" single ((:commit . "07a38963111518f86123802f9d477be0d4689a3f") (:authors ("Yassin Philip" . "xaccrocheur@gmail.com")) (:maintainer "Yassin Philip" . "xaccrocheur@gmail.com") (:keywords "languages" "faust") (:url . "https://bitbucket.org/yphil/faustine"))]) - (fb2-reader . [(20211214 954) ((emacs (26 2)) (f (0 17)) (s (1 11 0)) (dash (2 12 0)) (visual-fill-column (2 2)) (async (1 9 4))) "Read FB2 and FB2.ZIP documents" single ((:commit . "9dcc0801a7dd302ee0620781ea17868895d3f082") (:authors ("Dmitriy Pshonko" . "jumper047@gmail.com")) (:maintainer "Dmitriy Pshonko" . "jumper047@gmail.com") (:keywords "multimedia" "ebook" "fb2") (:url . "https://github.com/jumper047/fb2-reader"))]) + (fb2-reader . [(20220820 1607) ((emacs (26 2)) (f (0 17)) (s (1 11 0)) (dash (2 12 0)) (visual-fill-column (2 2)) (async (1 9 4))) "Read FB2 and FB2.ZIP documents" single ((:commit . "8138cc3b674c911e92d280b97a5fdbbdf6e3182d") (:authors ("Dmitriy Pshonko" . "jumper047@gmail.com")) (:maintainer "Dmitriy Pshonko" . "jumper047@gmail.com") (:keywords "multimedia" "ebook" "fb2") (:url . "https://github.com/jumper047/fb2-reader"))]) (fcitx . [(20190806 1923) nil "Make fcitx better in Emacs" single ((:commit . "12dc2638ddd15c8f6cfaecb20e1f428ab2bb5624") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions") (:url . "https://github.com/cute-jumper/fcitx.el"))]) (fcopy . [(20150304 1403) nil "Funny Copy, set past point HERE then search copy text" single ((:commit . "e355f6ec889d8ecbdb096019c2dc660b1cec4941") (:authors ("Masayuki Ataka" . "masayuki.ataka@gmail.com")) (:maintainer "Masayuki Ataka" . "masayuki.ataka@gmail.com") (:keywords "convenience") (:url . "https://github.com/ataka/fcopy"))]) (fd-dired . [(20210723 549) ((emacs (25))) "find-dired alternative using fd" single ((:commit . "458464771bb220b6eb87ccfd4c985c436e57dc7e") (:authors ("Rashawn Zhang" . "namy.19@gmail.com")) (:maintainer "Rashawn Zhang" . "namy.19@gmail.com") (:keywords "tools" "fd" "find" "dired") (:url . "https://github.com/yqrashawn/fd-dired"))]) @@ -1527,7 +1531,7 @@ (feed-discovery . [(20200714 1118) ((emacs (25 1)) (dash (2 16 0))) "Discover feed url by RSS/Atom autodiscovery" single ((:commit . "12fcd1a28fe7c8c46c74e32f395ec631d45ec739") (:authors ("Hiroki YAMAKAWA" . "s06139@gmail.com")) (:maintainer "Hiroki YAMAKAWA" . "s06139@gmail.com") (:url . "https://github.com/HKey/feed-discovery"))]) (fennel-mode . [(20220701 1956) ((emacs (26 1))) "A major-mode for editing Fennel code" tar ((:commit . "5664357349462d0564c0bb55cb289a6722f0ecbc") (:authors ("Phil Hagelberg")) (:maintainer "Phil Hagelberg") (:keywords "languages" "tools") (:url . "https://git.sr.ht/~technomancy/fennel-mode"))]) (fetch . [(20131201 730) nil "Fetch and unpack resources" single ((:commit . "3f2793afcbbc32f320e572453166f9354ecc6d06") (:authors ("Christian 'crshd' Brassat" . "christian.brassat@gmail.com")) (:maintainer "Christian 'crshd' Brassat" . "christian.brassat@gmail.com") (:url . "https://github.com/crshd/fetch.el"))]) - (ffmpeg-player . [(20220704 641) ((emacs (24 4)) (s (1 12 0)) (f (0 20 0))) "Play video using ffmpeg" single ((:commit . "fad2f5ce83800b7ddcd5927bd84d377990446981") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "multimedia" "video" "ffmpeg" "buffering" "images") (:url . "https://github.com/jcs-elpa/ffmpeg-player"))]) + (ffmpeg-player . [(20220704 641) ((emacs (24 4)) (s (1 12 0)) (f (0 20 0))) "Play video using ffmpeg" single ((:commit . "24fc776c9647859031fdc44d737aee5b96d2bf51") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "multimedia" "video" "ffmpeg" "buffering" "images") (:url . "https://github.com/jcs-elpa/ffmpeg-player"))]) (fic-mode . [(20180603 2035) nil "Show FIXME/TODO/BUG(...) in special face only in comments and strings" single ((:commit . "a05fc36ed54ba0c6dc22ac216a6a72cf191ca13d") (:url . "https://github.com/lewang/fic-mode"))]) (fifo-class . [(20160425 558) nil "First in first out abstract class" single ((:commit . "8fe4cf690727f4ac7b67f29c55f845df023c3f21") (:authors ("Mola-T" . "Mola@molamola.xyz")) (:maintainer "Mola-T" . "Mola@molamola.xyz") (:keywords "lisp") (:url . "https://github.com/mola-T/fifo-class"))]) (figlet . [(20160218 2237) nil "Annoy people with big, ascii art text" single ((:commit . "19a38783a90e151faf047ff233a21a729db0cea9") (:authors ("Philip Jackson" . "phil@shellarchive.co.uk")) (:maintainer "Philip Jackson" . "phil@shellarchive.co.uk"))]) @@ -1536,7 +1540,7 @@ (filetree . [(20220312 1650) ((dash (2 12 0)) (helm (3 7 0)) (seq (2 23)) (transient (0 3 6))) "File tree view/manipulatation package" single ((:commit . "9125e5b7ebbb99b8c007018fcfd5034e7ac6630d") (:authors ("Ketan Patel" . "knpatel401@gmail.com")) (:maintainer "Ketan Patel" . "knpatel401@gmail.com") (:url . "https://github.com/knpatel401/filetree"))]) (fill-column-indicator . [(20200806 2239) nil "Graphically indicate the fill column" single ((:commit . "c35f9de072c241699b57bcb46da84bed5af29cfe") (:authors ("Alp Aker" . "alp.tekin.aker@gmail.com")) (:maintainer "Alp Aker" . "alp.tekin.aker@gmail.com") (:keywords "convenience"))]) (fill-function-arguments . [(20201223 819) ((emacs (24 4))) "Convert function arguments to/from single line" single ((:commit . "a0a2f8538c80ac08e497dea784fcb90c93ab465b") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:keywords "convenience") (:url . "https://github.com/davidshepherd7/fill-function-arguments"))]) - (fill-page . [(20220704 650) ((emacs (24 4))) "Fill buffer so you don't see empty lines at the end" single ((:commit . "7da1dc1259c1b15e28736d27d87f4cef40a3a18d") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "fill" "page" "buffer") (:url . "https://github.com/jcs-elpa/fill-page"))]) + (fill-page . [(20220704 650) ((emacs (24 4))) "Fill buffer so you don't see empty lines at the end" single ((:commit . "84f3ba47d7864e9a5ba9fa83770e3f3919751380") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "fill" "page" "buffer") (:url . "https://github.com/jcs-elpa/fill-page"))]) (fillcode . [(20200524 2226) nil "Fill (wrap) function calls and expressions in source code" single ((:commit . "501468082e46bd0975ef4d8765363fd564338099") (:authors ("Ryan Barrett" . "fillcode@ryanb.org")) (:maintainer "Ryan Barrett" . "fillcode@ryanb.org") (:url . "https://snarfed.org/fillcode"))]) (filldent . [(20220423 2216) ((emacs (24 1))) "Fill or indent" single ((:commit . "2f32e0cf5e27c613f962fa41bf3427bbdc04e6c0") (:authors ("Case Duckworth" . "acdw@acdw.net")) (:maintainer "Case Duckworth" . "acdw@acdw.net") (:url . "https://github.com/duckwork/filldent.el"))]) (finalize . [(20170418 1945) ((emacs (24 1)) (cl-generic (0 3)) (cl-lib (0 3)) (eieio (1 4))) "finalizers for Emacs Lisp" tar ((:commit . "846731531e7d1d80451787992e07bfe7dedbe9ff") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/elisp-finalize"))]) @@ -1590,7 +1594,7 @@ (flx . [(20211101 146) ((cl-lib (0 3))) "fuzzy matching with good sorting" single ((:commit . "e3b3f0533e44c5250ce73d728b59a7e96c692b5d") (:authors ("Le Wang")) (:maintainer "Le Wang") (:url . "https://github.com/lewang/flx"))]) (flx-ido . [(20180117 1519) ((flx (0 1)) (cl-lib (0 3))) "flx integration for ido" single ((:commit . "e3b3f0533e44c5250ce73d728b59a7e96c692b5d") (:authors ("Le Wang")) (:maintainer "Le Wang") (:url . "https://github.com/lewang/flx"))]) (flx-isearch . [(20191119 515) ((emacs (24)) (flx (20140821)) (cl-lib (0 5))) "Fuzzy incremental searching for emacs" single ((:commit . "a44097fb8f539a193c2f09a37ea52a68f2c51839") (:authors ("PythonNut" . "pythonnut@pythonnut.com")) (:maintainer "PythonNut" . "pythonnut@pythonnut.com") (:keywords "convenience" "search" "flx") (:url . "https://github.com/pythonnut/flx-isearch"))]) - (flycheck . [(20220729 1039) ((dash (2 12 1)) (pkg-info (0 4)) (let-alist (1 0 4)) (seq (1 11)) (emacs (24 3))) "On-the-fly syntax checking" tar ((:commit . "8541a61053bba1f2f31d0791e597cd3c78a21456") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "convenience" "languages" "tools") (:url . "http://www.flycheck.org"))]) + (flycheck . [(20220823 1254) ((dash (2 12 1)) (pkg-info (0 4)) (let-alist (1 0 4)) (seq (1 11)) (emacs (24 3))) "On-the-fly syntax checking" tar ((:commit . "600b3bffda3862121d96bbc5c1f8990fa9033a82") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "convenience" "languages" "tools") (:url . "http://www.flycheck.org"))]) (flycheck-ameba . [(20191226 1011) ((emacs (24 4)) (flycheck (30))) "Add support for Ameba to Flycheck" single ((:commit . "0c4925ae0e998818326adcb47ed27ddf9761c7dc") (:keywords "tools" "crystal" "ameba") (:url . "https://github.com/crystal-ameba/ameba.el"))]) (flycheck-apertium . [(20181211 1038) ((flycheck (0 25))) "Apertium checkers in flycheck" tar ((:commit . "22b60a17836477ac1edd15dc85b14f88ca871ba9") (:authors ("Kevin Brubeck Unhammer" . "unhammer+apertium@mm.st")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer+apertium@mm.st") (:keywords "convenience" "tools" "xml") (:url . "http://wiki.apertium.org/wiki/Emacs"))]) (flycheck-aspell . [(20220411 826) ((flycheck (28 0)) (emacs (25 1))) "Aspell checker for flycheck" single ((:commit . "dcf7e6543e4d94d58375e00e4a10db615ef06941") (:authors ("Leo Gaskin" . "leo.gaskin@le0.gs")) (:maintainer "Leo Gaskin" . "leo.gaskin@le0.gs") (:keywords "wp" "flycheck" "spell" "aspell") (:url . "https://github.com/leotaku/flycheck-aspell"))]) @@ -1630,10 +1634,10 @@ (flycheck-ghcmod . [(20150114 632) ((flycheck (0 21 -4 1)) (dash (2 0))) "A flycheck checker for Haskell using ghcmod" single ((:commit . "6bb7b7d879f05bbae54e99eb04806c877adf3ccc") (:authors ("Shen Chao" . "scturtle@gmail.com")) (:maintainer "Shen Chao" . "scturtle@gmail.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/scturtle/flycheck-ghcmod"))]) (flycheck-golangci-lint . [(20190330 1412) ((emacs (24)) (flycheck (0 22))) "Flycheck checker for golangci-lint" single ((:commit . "8e446c68311048f0b87febf8ef0379e29d358851") (:authors ("Wei Jian Gan" . "weijiangan@outlook.com")) (:maintainer "Wei Jian Gan" . "weijiangan@outlook.com") (:keywords "convenience" "tools" "go") (:url . "https://github.com/weijiangan/flycheck-golangci-lint"))]) (flycheck-gometalinter . [(20180424 941) ((emacs (24)) (flycheck (0 22))) "flycheck checker for gometalinter" single ((:commit . "1e3eede14da405b914a7d8b00300846e4393cb83") (:authors ("Diep Pham" . "me@favadi.com")) (:maintainer "Diep Pham" . "me@favadi.com") (:keywords "convenience" "tools" "go") (:url . "https://github.com/favadi/flycheck-gometalinter"))]) - (flycheck-google-cpplint . [(20220616 1802) ((flycheck (0 20 -4 1))) "Help to comply with the Google C++ Style Guide" single ((:commit . "93c5b26ec55d0df54bc230e2de0890fe074f35d3") (:authors ("Akiha Senda" . "senda.akiha@gmail.com")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "flycheck" "c" "c++") (:url . "https://github.com/flycheck/flycheck-google-cpplint/"))]) + (flycheck-google-cpplint . [(20220616 1802) ((flycheck (0 20 -4 1))) "Help to comply with the Google C++ Style Guide" single ((:commit . "8a7293dedc6245830580260d2dc70e1001a0ff29") (:authors ("Akiha Senda" . "senda.akiha@gmail.com")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "flycheck" "c" "c++") (:url . "https://github.com/flycheck/flycheck-google-cpplint/"))]) (flycheck-gradle . [(20190315 234) ((emacs (25 1)) (flycheck (0 25))) "Flycheck extension for Gradle." single ((:commit . "1ca08bbc343362a923cbdc2010f66e41655e92ab") (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "languages" "gradle") (:url . "https://github.com/jojojames/flycheck-gradle"))]) - (flycheck-grammalecte . [(20210705 1656) ((emacs (26 1)) (flycheck (26))) "Integrate Grammalecte with Flycheck" tar ((:commit . "59b37e09923290da1c8458e507da43f403f555d2") (:authors ("Guilhem Doulcier" . "guilhem.doulcier@espci.fr") ("Étienne Deparis" . "etienne@depar.is")) (:maintainer "Étienne Deparis" . "etienne@depar.is") (:keywords "i18n" "text") (:url . "https://git.umaneti.net/flycheck-grammalecte/"))]) - (flycheck-grammarly . [(20220704 625) ((emacs (25 1)) (flycheck (0 14)) (grammarly (0 3 0)) (s (1 12 0))) "Grammarly support for Flycheck" single ((:commit . "fccdba6a6bfb043031a43c62b3c5b635d52bf723") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-grammarly/flycheck-grammarly"))]) + (flycheck-grammalecte . [(20220822 2307) ((emacs (26 1)) (flycheck (26))) "Integrate Grammalecte with Flycheck" tar ((:commit . "314de13247710410f11d060a214ac4f400c02a71") (:authors ("Guilhem Doulcier" . "guilhem.doulcier@espci.fr") ("Étienne Deparis" . "etienne@depar.is")) (:maintainer "Étienne Deparis" . "etienne@depar.is") (:keywords "i18n" "text") (:url . "https://git.umaneti.net/flycheck-grammalecte/"))]) + (flycheck-grammarly . [(20220704 625) ((emacs (25 1)) (flycheck (0 14)) (grammarly (0 3 0)) (s (1 12 0))) "Grammarly support for Flycheck" single ((:commit . "73308f79d98dbb12ae68e2823970edc84a7149ca") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-grammarly/flycheck-grammarly"))]) (flycheck-guile . [(20201202 509) ((emacs (24 1)) (flycheck (0 22)) (geiser (0 11))) "A Flycheck checker for GNU Guile" single ((:commit . "e46d6e5453dd7471309fae6549445c48e6d8f340") (:authors ("Ricardo Wurmus" . "rekado@elephly.net")) (:maintainer "Andrew Whatson" . "whatson@gmail.com") (:url . "https://github.com/flatwhatson/flycheck-guile"))]) (flycheck-haskell . [(20220426 2358) ((emacs (24 3)) (flycheck (0 25)) (haskell-mode (13 7)) (dash (2 4 0)) (seq (1 11)) (let-alist (1 0 1))) "Flycheck: Automatic Haskell configuration" tar ((:commit . "d92dea78fb8638f7c27a3eb925d84c669fb257dd") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Sebastian Wiesner" . "swiesner@lunaryorn.com") (:keywords "tools" "convenience") (:url . "https://github.com/flycheck/flycheck-haskell"))]) (flycheck-hdevtools . [(20160926 702) ((flycheck (0 21 -4 1)) (dash (2 0))) "A flycheck checker for Haskell using hdevtools" single ((:commit . "8248ebaf8376ee5e37ff47c814a291550a7bdcf2") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/flycheck/flycheck-hdevtools"))]) @@ -1648,7 +1652,7 @@ (flycheck-julia . [(20170729 2141) ((emacs (24)) (flycheck (0 22))) "Julia support for Flycheck" single ((:commit . "213b60a5a9a1cb7887260e1d159b5bb27167cbb6") (:authors ("Guido Kraemer" . "guido.kraemer@gmx.de")) (:maintainer "Guido Kraemer" . "guido.kraemer@gmx.de") (:keywords "convenience" "tools" "languages") (:url . "https://github.com/gdkrmr/flycheck-julia"))]) (flycheck-keg . [(20200726 218) ((emacs (24 3)) (keg (0 1)) (flycheck (0 1))) "Flycheck for Keg projects" single ((:commit . "944e36144d92a798e1fd0f3d83fc6347d57a976e") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "convenience") (:url . "https://github.com/conao3/keg.el"))]) (flycheck-kotlin . [(20210406 1148) ((flycheck (0 20))) "Support kotlin in flycheck" single ((:commit . "bf1b398bdde128806a0a7479ebbe369bbaa40dae") (:authors ("Elric Milon" . "whirm_REMOVETHIS__@gmx.com")) (:maintainer "Elric Milon" . "whirm_REMOVETHIS__@gmx.com"))]) - (flycheck-languagetool . [(20220731 2301) ((emacs (25 1)) (flycheck (0 14))) "Flycheck support for LanguageTool" single ((:commit . "53b3e46d47a0e70fd2e5c49fea9134ee9aa41793") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com") ("Peter Oliver" . "git@mavit.org.uk")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-languagetool/flycheck-languagetool"))]) + (flycheck-languagetool . [(20220731 2301) ((emacs (25 1)) (flycheck (0 14))) "Flycheck support for LanguageTool" single ((:commit . "f267f3075298ef715406f16efc1aba23f53a37ad") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com") ("Peter Oliver" . "git@mavit.org.uk")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-languagetool/flycheck-languagetool"))]) (flycheck-ledger . [(20200304 2204) ((emacs (24 1)) (flycheck (0 15))) "Flycheck integration for ledger files" single ((:commit . "628e25ba66604946085571652a94a54f4d1ad96f") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/purcell/flycheck-ledger"))]) (flycheck-lilypond . [(20211006 2102) ((emacs (24 3)) (flycheck (0 22))) "LilyPond support in Flycheck" single ((:commit . "78f8c16cd67f9f6d3f1806e1fd403222723ba400") (:authors ("Hinrik Örn Sigurðsson" . "hinrik.sig@gmail.com")) (:maintainer "Hinrik Örn Sigurðsson" . "hinrik.sig@gmail.com") (:keywords "tools" "convenience") (:url . "https://github.com/hinrik/flycheck-lilypond"))]) (flycheck-liquidhs . [(20170412 2326) ((flycheck (0 15))) "A flycheck checker for Haskell using liquid (i.e. liquidhaskell)" single ((:commit . "c27252ac24d77f4b6eec76a4ba9cd61761a3fba9") (:authors ("Ranjit Jhala" . "jhala@cs.ucsd.edu")) (:maintainer "Ranjit Jhala" . "jhala@cs.ucsd.edu") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/ucsd-progsys/liquidhaskell/flycheck-liquid.el"))]) @@ -1657,7 +1661,7 @@ (flycheck-mypy . [(20200113 1336) ((flycheck (0 18))) "Support mypy in flycheck" single ((:commit . "5b4e14ab0cbce2ff35fee7e69b5b95eafd609c80") (:authors ("Lorenzo Bolla" . "lbolla@gmail.com")) (:maintainer "Lorenzo Bolla" . "lbolla@gmail.com"))]) (flycheck-nim . [(20190927 1514) ((dash (2 4 0)) (flycheck (0 20))) "Defines a flycheck syntax checker for nim" single ((:commit . "ddfade51001571c2399f78bcc509e0aa8eb752a4") (:authors ("Adam Schwalm" . "adamschwalm@gmail.com")) (:maintainer "Adam Schwalm" . "adamschwalm@gmail.com") (:url . "https://github.com/ALSchwalm/flycheck-nim"))]) (flycheck-nimsuggest . [(20171027 2208) ((flycheck (0 23)) (emacs (24 3))) "flycheck backend for Nim using nimsuggest" single ((:commit . "dc9a5de1cb3ee05db5794d824610959a1f603bc9") (:authors ("Yuta Yamada ")) (:maintainer "Yuta Yamada ") (:url . "https://github.com/yuutayamada/flycheck-nimsuggest"))]) - (flycheck-objc-clang . [(20210911 1023) ((emacs (24 4)) (flycheck (26))) "Flycheck: Objective-C support using Clang" single ((:commit . "0a86156fad0d6f02e8e6b4c5594f7173c96d6481") (:authors ("Goichi Hirakawa" . "gooichi@gyazsquare.com")) (:maintainer "Goichi Hirakawa" . "gooichi@gyazsquare.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/GyazSquare/flycheck-objc-clang"))]) + (flycheck-objc-clang . [(20210911 1023) ((emacs (24 4)) (flycheck (26))) "Flycheck: Objective-C support using Clang" single ((:commit . "3b08af9f502d64db26c7e6fea3510155d7e6d185") (:authors ("Goichi Hirakawa" . "gooichi@gyazsquare.com")) (:maintainer "Goichi Hirakawa" . "gooichi@gyazsquare.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/GyazSquare/flycheck-objc-clang"))]) (flycheck-ocaml . [(20220730 542) ((emacs (24 3)) (flycheck (32)) (merlin (3 0 1)) (let-alist (1 0 3))) "Flycheck: OCaml support" single ((:commit . "77f8ddbd9bfc3a11957ac7ec7e45d5fa9179b192") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "convenience" "tools" "languages" "ocaml") (:url . "https://github.com/flycheck/flycheck-ocaml"))]) (flycheck-package . [(20210509 2323) ((emacs (24 1)) (flycheck (0 22)) (package-lint (0 2))) "A Flycheck checker for elisp package authors" single ((:commit . "615c1ed8c6fb7c73abec6aaa73d3fef498d231bc") (:authors ("Steve Purcell" . "steve@sanityinc.com") ("Fanael Linithien" . "fanael4@gmail.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "lisp") (:url . "https://github.com/purcell/flycheck-package"))]) (flycheck-pact . [(20180920 2052) ((emacs (24 3)) (flycheck (0 25)) (pact-mode (0 0 4))) "Flycheck support for pact-mode" single ((:commit . "0e10045064ef89ec8b6f5a473073d47b976a2ca3") (:authors ("Stuart Popejoy")) (:maintainer "Stuart Popejoy" . "stuart@kadena.io") (:keywords "pact" "lisp" "languages" "blockchain" "smartcontracts" "tools" "linting") (:url . "http://github.com/kadena-io/flycheck-pact"))]) @@ -1678,12 +1682,12 @@ (flycheck-pyre . [(20190215 1222) ((emacs (24)) (flycheck (29)) (cl-lib (0 6))) "Support Pyre in flycheck" tar ((:commit . "0560122caae207d99d8af1ac2b4e5d6f6a1ce444") (:authors ("Vyacheslav Linnik" . "vyacheslav.linnik@gmail.com")) (:maintainer "Vyacheslav Linnik" . "vyacheslav.linnik@gmail.com") (:url . "https://github.com/linnik/flycheck-pyre"))]) (flycheck-raku . [(20220420 732) ((emacs (26 3)) (flycheck (0 22))) "Raku support in Flycheck" single ((:commit . "4da1970a75396aff1957b07f7579c1de6b817e6b") (:authors ("Hinrik Örn Sigurðsson" . "hinrik.sig@gmail.com") ("Johnathon Weare" . "jrweare@gmail.com") ("Siavash Askari Nasr" . "siavash.askari.nasr@gmail.com")) (:maintainer "Hinrik Örn Sigurðsson" . "hinrik.sig@gmail.com") (:keywords "tools" "convenience") (:url . "https://github.com/Raku/flycheck-raku"))]) (flycheck-relint . [(20200721 2217) ((emacs (26 1)) (flycheck (0 22)) (relint (1 15))) "A Flycheck checker for elisp regular expressions" single ((:commit . "c66d0c8d2e3a8abb6a3dfda597801e460b2eeb6f") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "lisp") (:url . "https://github.com/purcell/flycheck-relint"))]) - (flycheck-rtags . [(20191222 920) ((emacs (24)) (flycheck (0 23)) (rtags (2 10))) "RTags Flycheck integration" single ((:commit . "c628efc9b485470a48aec2692d79f7c140bc5b92") (:authors ("Christian Schwarzgruber" . "c.schwarzgruber.cs@gmail.com")) (:maintainer "Christian Schwarzgruber" . "c.schwarzgruber.cs@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) + (flycheck-rtags . [(20191222 920) ((emacs (24)) (flycheck (0 23)) (rtags (2 10))) "RTags Flycheck integration" single ((:commit . "b9c680e7ca003c103687e790f740d86daa6b4b17") (:authors ("Christian Schwarzgruber" . "c.schwarzgruber.cs@gmail.com")) (:maintainer "Christian Schwarzgruber" . "c.schwarzgruber.cs@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) (flycheck-rust . [(20190319 1546) ((emacs (24 1)) (flycheck (28)) (dash (2 13 0)) (seq (2 3)) (let-alist (1 0 4))) "Flycheck: Rust additions and Cargo support" single ((:commit . "a139cd53c5062697e9ed94ad80b803c37d999600") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Sebastian Wiesner" . "swiesner@lunaryorn.com") (:keywords "tools" "convenience") (:url . "https://github.com/flycheck/flycheck-rust"))]) (flycheck-stan . [(20211129 2051) ((emacs (25 1)) (flycheck (0 16 0)) (stan-mode (10 3 0))) "Add Stan support for Flycheck" tar ((:commit . "150bbbe5fd3ad2b5a3dbfba9d291e66eeea1a581") (:authors ("Jeffrey Arnold" . "jeffrey.arnold@gmail.com") ("Kazuki Yoshida" . "kazukiyoshida@mail.harvard.edu")) (:maintainer "Kazuki Yoshida" . "kazukiyoshida@mail.harvard.edu") (:keywords "c" "languages") (:url . "https://github.com/stan-dev/stan-mode/tree/master/flycheck-stan"))]) (flycheck-status-emoji . [(20180330 2325) ((cl-lib (0 1)) (emacs (24)) (flycheck (0 20)) (let-alist (1 0))) "Show flycheck status using cute, compact emoji" single ((:commit . "4bd113ab42dec9544b66e0a27ed9008ce8148433") (:authors ("Ben Liblit" . "liblit@acm.org")) (:maintainer "Ben Liblit" . "liblit@acm.org") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/liblit/flycheck-status-emoji"))]) (flycheck-swift . [(20170129 549) ((emacs (24 4)) (flycheck (0 25))) "Flycheck extension for Apple's Swift." single ((:commit . "4c5ad401252400a78da395fd56a71e67ff8c2761") (:keywords "languages" "swift"))]) - (flycheck-swift3 . [(20210910 1244) ((emacs (24 4)) (flycheck (26))) "Flycheck: Swift support for Apple swift-mode" single ((:commit . "54193175c87a4c0bbf7ed16a3e76d6daff35c76f") (:authors ("Goichi Hirakawa" . "gooichi@gyazsquare.com")) (:maintainer "Goichi Hirakawa" . "gooichi@gyazsquare.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/GyazSquare/flycheck-swift3"))]) + (flycheck-swift3 . [(20210910 1244) ((emacs (24 4)) (flycheck (26))) "Flycheck: Swift support for Apple swift-mode" single ((:commit . "d52d7edd51e0b06e174b6744e5aa12ab15602cfa") (:authors ("Goichi Hirakawa" . "gooichi@gyazsquare.com")) (:maintainer "Goichi Hirakawa" . "gooichi@gyazsquare.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/GyazSquare/flycheck-swift3"))]) (flycheck-swiftlint . [(20180830 340) ((emacs (25 1)) (flycheck (0 25))) "Flycheck extension for Swiftlint." single ((:commit . "8861ddbd9c1c2a951630d9ea29162ad0916580cb") (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "languages" "swiftlint" "swift" "emacs") (:url . "https://github.com/jojojames/flycheck-swiftlint"))]) (flycheck-swiftx . [(20200814 845) ((emacs (26 1)) (flycheck (26)) (xcode-project (1 0))) "Flycheck: Swift backend" single ((:commit . "84f42393dea362d3bdfc9253a205a17ec7a12a76") (:authors ("John Buckley" . "john@olivetoast.com")) (:maintainer "John Buckley" . "john@olivetoast.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/nhojb/flycheck-swiftx"))]) (flycheck-tcl . [(20180327 1259) ((emacs (24 4)) (flycheck (0 22))) "A flycheck checker for Tcl using tclchecker" single ((:commit . "7ca23f4673e178b9f5dcc8a82b86cf05b15d7236") (:authors ("Niels Widger" . "niels.widger@gmail.com")) (:maintainer "Niels Widger" . "niels.widger@gmail.com") (:url . "https://github.com/nwidger/flycheck-tcl"))]) @@ -1699,7 +1703,7 @@ (flymake-coffee . [(20170723 146) ((flymake-easy (0 1))) "A flymake handler for coffee script" single ((:commit . "dee295acf30820ed15fe0de17137d50bc27fc80c") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-coffee"))]) (flymake-collection . [(20220612 1340) ((emacs (28 1)) (let-alist (1 0)) (flymake (1 2 1))) "Collection of checkers for flymake, bringing flymake to the level of flycheck" tar ((:commit . "cd2574574bda1f3d94007cdc3b7e0f8007d69716") (:authors ("Mohsin Kaleem" . "mohkale@kisara.moe")) (:maintainer "Mohsin Kaleem" . "mohkale@kisara.moe") (:keywords "language" "tools") (:url . "https://github.com/mohkale/flymake-collection"))]) (flymake-css . [(20170723 146) ((flymake-easy (0 1))) "Flymake support for css using csslint" single ((:commit . "de090163ba289910ceeb61b13368ce42d0f2dfd8") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-css"))]) - (flymake-cursor . [(20220506 1458) ((flymake (0 3))) "Show flymake messages in the minibuffer after delay" single ((:commit . "d3c632f26a2d13fb288252c288faaf8107b3d86a") (:authors ("Unknown Original Author") ("Dino Chiesa" . "dpchiesa@hotmail.com") ("Sam Graham ")) (:maintainer "Sam Graham ") (:keywords "languages" "mode" "flymake") (:url . "https://github.com/flymake/emacs-flymake-cursor"))]) + (flymake-cursor . [(20220506 1458) ((flymake (0 3))) "Show flymake messages in the minibuffer after delay" single ((:commit . "238d872c40b2a5ea26710845369cbb15dd37f351") (:authors ("Unknown Original Author") ("Dino Chiesa" . "dpchiesa@hotmail.com") ("Sam Graham ")) (:maintainer "Sam Graham ") (:keywords "languages" "mode" "flymake") (:url . "https://github.com/flymake/emacs-flymake-cursor"))]) (flymake-diagnostic-at-point . [(20180815 1004) ((emacs (26 1)) (popup (0 5 3))) "Display flymake diagnostics at point" single ((:commit . "379616b1c6f5ebeaf08fbe54ae765008a78b3be7") (:authors ("Ricardo Martins" . "ricardo@scarybox.net")) (:maintainer "Ricardo Martins" . "ricardo@scarybox.net") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/meqif/flymake-diagnostic-at-point"))]) (flymake-easy . [(20140818 755) nil "Helpers for easily building flymake checkers" single ((:commit . "de41ea49503f71f997e5c359a2ad08df696c0147") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "convenience" "internal") (:url . "https://github.com/purcell/flymake-easy"))]) (flymake-elixir . [(20130810 1417) nil "A flymake handler for elixir-mode .ex files." single ((:commit . "3810566cffe35d04cc3f01e27fe397d68d52f802") (:authors ("Sylvain Benner" . "syl20bnr@gmail.com")) (:maintainer "Sylvain Benner" . "syl20bnr@gmail.com"))]) @@ -1707,10 +1711,10 @@ (flymake-flycheck . [(20220313 924) ((flycheck (31)) (emacs (26 1))) "Use flycheck checkers as flymake backends" single ((:commit . "9040be3763b8f9952dccd9a04be25ac20a0f8745") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/purcell/flymake-flycheck"))]) (flymake-gjshint . [(20130327 1232) nil "A flymake handler for javascript using both jshint and gjslint" single ((:commit . "dc957c14cb060819585de8aedb330e24efa4b784") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainer "Yasuyuki Oka" . "yasuyk@gmail.com") (:keywords "flymake" "javascript" "jshint" "gjslint"))]) (flymake-go . [(20150714 733) nil "A flymake handler for go-mode files" single ((:commit . "ae83761aa908c1a50ff34af04f00dcc46bca2ce9") (:authors ("Michael Fellinger" . "michael@iron.io") ("Robert Zaremba" . "robert.marek.zaremba@wp.eu")) (:maintainer "Michael Fellinger" . "michael@iron.io") (:keywords "go" "flymake") (:url . "https://github.com/robert-zaremba/flymake-go"))]) - (flymake-go-staticcheck . [(20220803 1929) ((emacs (26 1))) "Go staticcheck linter for flymake" single ((:commit . "d29b158acc2c131cdbb5c62f54cb0fc024339575") (:authors ("Sergey Kostyaev" . "feo.me@ya.ru")) (:maintainer "Sergey Kostyaev" . "feo.me@ya.ru") (:keywords "languages" "tools") (:url . "https://github.com/s-kostyaev/flymake-go-staticcheck"))]) + (flymake-go-staticcheck . [(20220804 1907) ((emacs (26 1))) "Go staticcheck linter for flymake" single ((:commit . "9098f7e07ea6513667dc6af6d9ad2fa854464d20") (:authors ("Sergey Kostyaev" . "feo.me@ya.ru")) (:maintainer "Sergey Kostyaev" . "feo.me@ya.ru") (:keywords "languages" "tools") (:url . "https://github.com/s-kostyaev/flymake-go-staticcheck"))]) (flymake-golangci . [(20191028 1927) ((flymake-easy (0 1)) (emacs (24))) "A flymake handler for go-mode files using Golang CI lint" single ((:commit . "dfc31a1a6ae3f087b49fe6f5f21b3866780aa91c") (:authors ("Jorge Javier Araya Navarro" . "jorgejavieran@yahoo.com.mx")) (:maintainer "Jorge Javier Araya Navarro" . "jorgejavieran@yahoo.com.mx") (:url . "https://gitlab.com/shackra/flymake-golangci"))]) (flymake-gradle . [(20190315 233) ((emacs (26 1))) "Flymake extension for Gradle." single ((:commit . "dbedd29b78d4828ef57d4de20867be5df3eaab99") (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "languages" "gradle") (:url . "https://github.com/jojojames/flymake-gradle"))]) - (flymake-grammarly . [(20220704 626) ((emacs (26 1)) (grammarly (0 3 0)) (s (1 12 0))) "Flymake support for Grammarly" single ((:commit . "7ad726f27031fbe816885632155851cc2cfcd522") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-grammarly/flymake-grammarly"))]) + (flymake-grammarly . [(20220704 626) ((emacs (26 1)) (grammarly (0 3 0)) (s (1 12 0))) "Flymake support for Grammarly" single ((:commit . "2e3c0851b322c21e4147c92f05d2488687eccd8e") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-grammarly/flymake-grammarly"))]) (flymake-hadolint . [(20220328 823) ((emacs (26 1))) "Flymake backend for hadolint, a Dockerfile linter" single ((:commit . "82a6df7f6cc95e1ab95c5d28f2edcd8c1d4c7382") (:authors ("Taiki Sugawara" . "buzz.taiki@gmail.com")) (:maintainer "Taiki Sugawara" . "buzz.taiki@gmail.com") (:keywords "convenience" "processes" "docker" "flymake") (:url . "https://github.com/buzztaiki/flymake-hadolint"))]) (flymake-haml . [(20170723 146) ((flymake-easy (0 1))) "A flymake handler for haml files" single ((:commit . "22a81e8484734552d461e7ae7305664dc244447e") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-haml"))]) (flymake-haskell-multi . [(20170723 146) ((flymake-easy (0 1))) "Syntax-check haskell-mode using both ghc and hlint" tar ((:commit . "b564a94312259885b1380272eb867bf52a164020") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-haskell-multi"))]) @@ -1721,15 +1725,15 @@ (flymake-json . [(20180511 911) ((flymake-easy (0 1))) "A flymake handler for json using jsonlint" single ((:commit . "03b4e5e7ad11938781257a783e717ab95fe65952") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-json"))]) (flymake-kondor . [(20211026 501) ((emacs (26 1))) "Linter with clj-kondo" single ((:commit . "784e57f36812a37e323409b90b935ef3c6920a22") (:authors ("https://turbocafe.keybase.pub")) (:maintainer "https://turbocafe.keybase.pub") (:url . "https://github.com/turbo-cafe/flymake-kondor"))]) (flymake-ktlint . [(20180831 346) ((emacs (26 1))) "Flymake extension for Ktlint." single ((:commit . "56aab6f2d22061999050783dbc3166cdb456d0bb") (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "languages" "ktlint") (:url . "https://github.com/jojojames/flymake-ktlint"))]) - (flymake-languagetool . [(20220704 637) ((emacs (27 1)) (s (1 9 0))) "Flymake support for LanguageTool" single ((:commit . "857f4f94a7615d7de5305a40045b2b85ae1ff760") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-languagetool/flymake-languagetool"))]) + (flymake-languagetool . [(20220814 550) ((emacs (27 1)) (s (1 9 0))) "Flymake support for LanguageTool" single ((:commit . "0860650caa5538ccb679b5149dffb9b4992cfa75") (:keywords "convenience" "grammar" "check") (:url . "https://github.com/emacs-languagetool/flymake-languagetool"))]) (flymake-less . [(20151111 738) ((less-css-mode (0 15)) (flymake-easy (0 1))) "Flymake handler for LESS stylesheets (lesscss.org)" single ((:commit . "32d3c28a9a5c52b82d1741ff9d715013b6498421") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "languages"))]) (flymake-lua . [(20170129 154) nil "Flymake for Lua" single ((:commit . "84589f20066921a5b79cf3a1f914a223a2552d2a") (:authors (nil . "Sébastien Roccaserra (format \"<%s%s@%s.%s>\" \"s\" \"roccaserra\" \"yahoo\" \"com\")")) (:maintainer nil . "Sébastien Roccaserra (format \"<%s%s@%s.%s>\" \"s\" \"roccaserra\" \"yahoo\" \"com\")") (:keywords "lua"))]) (flymake-markdownlint . [(20220320 1208) ((emacs (27 1))) "Markdown linter with markdownlint" single ((:commit . "59e3520668d9394c573e07b7980a2d48d9f6086c") (:authors ("Martin Kjær Jørgensen" . "mkj@gotu.dk")) (:maintainer "Martin Kjær Jørgensen" . "mkj@gotu.dk") (:url . "https://github.com/shaohme/flymake-markdownlint"))]) (flymake-nasm . [(20210310 1540) ((flymake-quickdef (1 0 0)) (emacs (26 1))) "A flymake handler for asm-mode files using nasm" single ((:commit . "27e58d7f3a48ca6fc12238fe6c888a3fdffc3f75") (:authors ("Jürgen Hötzel" . "juergen@hoetzel.info")) (:maintainer "Jürgen Hötzel") (:keywords "tools" "languages") (:url . "http://github.com/juergenhoetzel/flymake-nasm"))]) - (flymake-perlcritic . [(20120328 814) ((flymake (0 3))) "Flymake handler for Perl to invoke Perl::Critic" tar ((:commit . "394b961b27b1fddc3c7107046a53d47f58757300") (:authors ("Sam Graham ")) (:maintainer "Sam Graham ") (:url . "https://github.com/illusori/emacs-flymake-perlcritic"))]) + (flymake-perlcritic . [(20120328 814) ((flymake (0 3))) "Flymake handler for Perl to invoke Perl::Critic" tar ((:commit . "a0bc983c6187c03b72a93e78371c391a28321b81") (:authors ("Sam Graham ")) (:maintainer "Sam Graham ") (:url . "https://github.com/illusori/emacs-flymake-perlcritic"))]) (flymake-pest . [(20200317 1503) ((emacs (26 3)) (pest-mode (0 1))) "A flymake handler for Pest files" single ((:commit . "af677327f185113442e95b00986097b30cab650c") (:authors ("ksqsf" . "i@ksqsf.moe") ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "ksqsf" . "i@ksqsf.moe") (:keywords "languages" "flymake") (:url . "https://github.com/ksqsf/pest-mode"))]) (flymake-php . [(20170723 146) ((flymake-easy (0 1))) "A flymake handler for php-mode files" single ((:commit . "c045d01e002ba5e09b05f40e25bf5068d02126bc") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-php"))]) - (flymake-phpcs . [(20140713 631) ((flymake-easy (0 9))) "making flymake work with PHP CodeSniffer" single ((:commit . "433fd05b608f06a40bb209fbea76117d896449ed") (:authors ("Akiha Senda")) (:maintainer "Akiha Senda") (:keywords "flymake" "phpcs" "php") (:url . "https://github.com/senda-akiha/flymake-phpcs/"))]) + (flymake-phpcs . [(20140713 631) ((flymake-easy (0 9))) "making flymake work with PHP CodeSniffer" single ((:commit . "dca5d00fa1fb3f25d2108e9cbbd0c74b71d8dc64") (:authors ("Akiha Senda")) (:maintainer "Akiha Senda") (:keywords "flymake" "phpcs" "php") (:url . "https://github.com/senda-akiha/flymake-phpcs/"))]) (flymake-phpstan . [(20210714 1805) ((emacs (26 1)) (phpstan (0 5 0))) "Flymake backend for PHP using PHPStan" single ((:commit . "e229e990e36a2bfb88503bfe2bb6f2836eaa2874") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "tools" "php") (:url . "https://github.com/emacs-php/phpstan.el"))]) (flymake-puppet . [(20170801 554) ((flymake-easy (0 9))) "Flymake handler using puppet-lint" single ((:commit . "8a772395f4ccc59d883712ab53a92a17c1d9a429") (:authors ("Ben Prew")) (:maintainer "Ben Prew") (:url . "https://github.com/benprew/flymake-puppet"))]) (flymake-python-pyflakes . [(20170723 146) ((flymake-easy (0 8))) "A flymake handler for python-mode files using pyflakes (or flake8)" single ((:commit . "1d65c26bf65a5dcbd29fcd967e2feb90e1e7a33d") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-python-pyflakes"))]) @@ -1774,9 +1778,9 @@ (foreign-regexp . [(20200325 50) nil "search and replace by foreign regexp." tar ((:commit . "e2dd47f2160cadc194eb156e7c76c3c869e6706e") (:authors ("K-talo Miyazaki ")) (:maintainer "K-talo Miyazaki ") (: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" single ((:commit . "22b3bb13134b617870ed1e888af739f4818be929") (:authors ("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." single ((:commit . "58096ce1a25615d2bae806c3775bae3e2775019d") (:authors ("olkinn")) (:maintainer "olkinn"))]) - (forge . [(20220622 1929) ((emacs (25 1)) (compat (28 1 1 0)) (closql (1 2 0)) (dash (2 19 1)) (emacsql-sqlite (3 0 0)) (ghub (20220621)) (let-alist (1 0 6)) (magit (20220621)) (markdown-mode (2 4)) (transient (0 3 6)) (yaml (0 3 5))) "Access Git forges from Magit." tar ((:commit . "ed8abdafd8b15852538bbe064fef037345772627") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/forge"))]) + (forge . [(20220821 2143) ((emacs (25 1)) (compat (28 1 1 0)) (closql (1 2 0)) (dash (2 19 1)) (emacsql-sqlite (3 0 0)) (ghub (20220621)) (let-alist (1 0 6)) (magit (20220621)) (markdown-mode (2 4)) (transient (0 3 6)) (yaml (0 3 5))) "Access Git forges from Magit." tar ((:commit . "6a820a424465900b0cc7bc54ccdad6491764e581") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/forge"))]) (form-feed . [(20210508 1627) ((emacs (24 1))) "Display ^L glyphs as horizontal lines" single ((:commit . "ac1f0ef30a11979f5dfe12d8c05a666739e486ff") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "faces") (:url . "https://depp.brause.cc/form-feed"))]) - (format-all . [(20220718 1014) ((emacs (24 4)) (inheritenv (0 1)) (language-id (0 19))) "Auto-format C, C++, JS, Python, Ruby and 50 other languages" single ((:commit . "9e6170437f0c2aa9bc27804c5b06b9cb44ccd02a") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/lassik/emacs-format-all-the-code"))]) + (format-all . [(20220815 632) ((emacs (24 4)) (inheritenv (0 1)) (language-id (0 19))) "Auto-format C, C++, JS, Python, Ruby and 50 other languages" single ((:commit . "f5e97b4feaba46be2cde825f22094e4b6e595850") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/lassik/emacs-format-all-the-code"))]) (format-sql . [(20150422 1333) nil "Use format-sql to make your SQL readable in directly Emacs." single ((:commit . "97f475c245cd6c81a72a265678e2087cee66ac7b") (:authors ("Friedrich Paetzke" . "paetzke@fastmail.fm")) (:maintainer "Friedrich Paetzke" . "paetzke@fastmail.fm") (:url . "https://github.com/paetzke/format-sql.el"))]) (format-table . [(20181223 1616) ((emacs (25)) (dash (2 14 1))) "Parse and reformat tabular data." single ((:commit . "dfcae3a867e574577fc09a43b045889ff155b58f") (:authors ("Jason Duncan" . "jasond496@msn.com")) (:maintainer "Jason Duncan" . "jasond496@msn.com") (:keywords "data") (:url . "https://github.com/functionreturnfunction/format-table"))]) (forth-mode . [(20220629 519) nil "Programming language mode for Forth" tar ((:commit . "162b79f005a64b1f91e60b8f4c022d1d90cd3d95") (:authors ("Lars Brinkhoff" . "lars@nocrew.org")) (:maintainer "Lars Brinkhoff" . "lars@nocrew.org") (:keywords "languages" "forth") (:url . "http://github.com/larsbrinkhoff/forth-mode"))]) @@ -1808,17 +1812,17 @@ (fsbot-data-browser . [(20160921 1533) nil "browse the fsbot database using tabulated-list-mode" single ((:commit . "6bca4f7de63e31839d2542f6c678b79931dec344") (:authors ("Benaiah Mischenko")) (:maintainer "Benaiah Mischenko") (:keywords "fsbot" "irc" "tabulated-list-mode") (:url . "http://github.com/benaiah/fsbot-data-browser"))]) (fsharp-mode . [(20220630 1931) ((emacs (25))) "Support for the F# programming language" tar ((:commit . "42e8db5ff67c37228f92ca8750d203aa5bd9e1e5") (:authors ("1993-1997 Xavier Leroy, Jacques Garrigue and Ian T Zimmerman") ("2010-2011 Laurent Le Brun" . "laurent@le-brun.eu") ("2012-2014 Robin Neatherway" . "robin.neatherway@gmail.com") ("2017-2022 Jürgen Hötzel")) (:maintainer "Jürgen Hötzel") (:keywords "languages"))]) (fstar-mode . [(20220725 2139) ((emacs (24 3)) (dash (2 11)) (company (0 8 12)) (quick-peek (1 0)) (yasnippet (0 11 0)) (flycheck (30 0)) (company-quickhelp (2 2 0))) "Support for F* programming" tar ((:commit . "60489e75c6f26417068bf861b6db2935e72c38fe") (:authors ("Clément Pit-Claudel" . "clement.pitclaudel@live.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "convenience" "languages") (:url . "https://github.com/FStarLang/fstar-mode.el"))]) - (fuel . [(20211221 2127) ((cl-lib (0 2)) (emacs (24 2))) "Major mode for the Factor programming language." tar ((:commit . "a827b197c5896fab1bf8101e68e1e741cb7a410d"))]) + (fuel . [(20211221 2127) ((cl-lib (0 2)) (emacs (24 2))) "Major mode for the Factor programming language." tar ((:commit . "cda1e5f9ad1d6b55077b7a88f18e89e882e8ffef"))]) (fuff . [(20170202 1503) ((seq (2 3))) "Find files with findutils, recursively" single ((:commit . "278e849913df87bd8756c59382282d87474802c3") (:authors ("Joel Moberg")) (:maintainer "Joel Moberg") (:keywords "files" "project" "convenience") (:url . "https://github.com/joelmo/fuff"))]) (full-ack . [(20140223 1732) nil "a front-end for ack" single ((:commit . "761d846e105b150f8e6d13d7a8983f0248313a45") (:authors ("Nikolaj Schumacher ")) (:maintainer "Nikolaj Schumacher ") (:keywords "tools" "matching") (:url . "http://nschum.de/src/emacs/full-ack/"))]) (fullframe . [(20210226 1057) ((cl-lib (0 5))) "Generalized automatic execution in a single frame" single ((:commit . "886b831c001b44ec95aec4ff36e8bc1b3003c786") (:authors ("Tom Regner" . "tom@goochesa.de")) (:maintainer "Tom Regner" . "tom@goochesa.de") (:keywords "fullscreen"))]) (function-args . [(20211231 1150) ((ivy (0 9 1))) "C++ completion for GNU Emacs" tar ((:commit . "503e78fad9e7741ef4b8f5c24ff70c8909240db2") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:url . "https://github.com/abo-abo/function-args"))]) (fuo . [(20190812 927) ((emacs (24 4))) "feeluown client." single ((:commit . "0e4122f94a336a50c02bc96652d25ac3d74bedeb") (:authors ("cosven" . "yinshaowen241@gmail.com")) (:maintainer "cosven" . "yinshaowen241@gmail.com") (:keywords "feeluown" "multimedia" "unix") (:url . "http://github.com/cosven/emacs-fuo"))]) (furl . [(20150509 316) nil "Friendly URL retrieval" single ((:commit . "014438271e0ef27333dfcd599cb247f12a20d870") (:authors ("Natalie Weizenbaum" . "nweiz@google.com")) (:maintainer "Natalie Weizenbaum" . "nweiz@google.com"))]) - (fussy . [(20220723 1500) ((emacs (27 2)) (flx (0 5))) "Fuzzy completion style using `flx'" single ((:commit . "d789dd9eb292ef2ff8a0959dc015f2edc67af6f7") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "matching") (:url . "https://github.com/jojojames/fussy"))]) - (futhark-mode . [(20220425 1144) ((emacs (24 3)) (cl-lib (0 5))) "major mode for editing Futhark source files" tar ((:commit . "7fd0a3c6c96ed8afd0249ab0734d9b63d4fd1cb1") (:keywords "languages") (:url . "https://github.com/diku-dk/futhark-mode"))]) + (fussy . [(20220817 1234) ((emacs (27 2)) (flx (0 5))) "Fuzzy completion style using `flx'" single ((:commit . "fa62f9624a6847dac594ab9f9a3154105ba2e3ef") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "matching") (:url . "https://github.com/jojojames/fussy"))]) + (futhark-mode . [(20220824 828) ((emacs (24 3)) (cl-lib (0 5))) "major mode for editing Futhark source files" tar ((:commit . "adf92a6c38b059f8ead65a08ccebdc5855cf9d1b") (:keywords "languages") (:url . "https://github.com/diku-dk/futhark-mode"))]) (fuz . [(20200104 524) ((emacs (25 1))) "Fast and precise fuzzy scoring/matching utils" tar ((:commit . "fee874aa35d2ee6b12b836290b5c8eaa44175a28") (:authors ("Zhu Zihao" . "all_but_last@163.com")) (:maintainer "Zhu Zihao" . "all_but_last@163.com") (:keywords "lisp") (:url . "https://github.com/cireu/fuz.el"))]) - (fuzzy . [(20211231 1837) ((emacs (24 3))) "Fuzzy Matching" single ((:commit . "74867a4c991b98cf636ff1a2b81c2a6b41ebbcb9") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Tomohiro Matsuyama" . "m2ym.pub@gmail.com") (:keywords "convenience") (:url . "https://github.com/auto-complete/fuzzy-el"))]) + (fuzzy . [(20211231 1837) ((emacs (24 3))) "Fuzzy Matching" single ((:commit . "c848480e81028a29c634c43b8162230cc3601b32") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Tomohiro Matsuyama" . "m2ym.pub@gmail.com") (:keywords "convenience") (:url . "https://github.com/auto-complete/fuzzy-el"))]) (fuzzy-finder . [(20210906 217) ((emacs (24 4))) "Fuzzy Finder App Integration" single ((:commit . "915a281fc8e50df84dcc205f9357e8314d60fa54") (:authors ("10sr" . "8.slashes@gmail.com")) (:maintainer "10sr" . "8.slashes@gmail.com") (:keywords "matching") (:url . "https://github.com/10sr/fuzzy-finder-el"))]) (fvwm-mode . [(20160411 1138) nil "A major mode for editing Fvwm configuration files" single ((:commit . "6832a1c1f68bf6249c3fd6672ea8e27dc7a5c79e") (:authors ("Bert Geens" . "bert@lair.be")) (:maintainer "Bert Geens" . "bert@lair.be") (:keywords "files") (:url . "https://github.com/theBlackDragon/fvwm-mode"))]) (fwb-cmds . [(20220422 1610) ((emacs (25 1)) (compat (28 1 1 0))) "misc frame, window and buffer commands" single ((:commit . "e6eeac7552b61d4f7abd51aff6ce72394133663e") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "https://github.com/tarsius/fwb-cmds"))]) @@ -1829,7 +1833,7 @@ (gams-ac . [(20180423 926) ((emacs (24)) (auto-complete (1 0)) (gams-mode (4 0))) "auto-complete source file for GAMS mode" single ((:commit . "66d04ff36033f54205c19bc1d893e926d4dbf02e") (:authors ("Shiro Takeda")) (:maintainer "Shiro Takeda") (:keywords "languages" "tools" "gams-mode" "auto-complete") (:url . "https://github.com/ShiroTakeda/gams-ac"))]) (gams-mode . [(20220512 222) ((emacs (24 3))) "Major mode for General Algebraic Modeling System (GAMS)" tar ((:commit . "d7f5bb688e569c7c517e4c3af32a5319c492362b") (:authors ("Shiro Takeda")) (:maintainer "Shiro Takeda") (:keywords "languages" "tools" "gams") (:url . "http://shirotakeda.org/en/gams/gams-mode/"))]) (gandalf-theme . [(20130809 947) nil "Gandalf color theme" single ((:commit . "4e472fc851431458537d458d09c1f5895e338536") (:authors ("Peter Vasil" . "mail@petervasil.net")) (:maintainer "Peter Vasil" . "mail@petervasil.net") (:keywords "color" "theme"))]) - (gap-mode . [(20220503 1555) nil "Major mode for editing files in the GAP programing language." tar ((:commit . "99237f714c28981142674e8cfeb155863c834858") (:authors ("Michael Smith" . "smith@pell.anu.edu.au") ("Gary Zablackis") ("Goetz Pfeiffer") ("Ivan Andrus" . "darthandrus@gmail.com")) (:maintainer "Ivan Andrus" . "darthandrus@gmail.com") (:keywords "gap") (:url . "https://gitlab.com/gvol/gap-mode"))]) + (gap-mode . [(20220815 2355) nil "Major mode for editing files in the GAP programing language." tar ((:commit . "afa70e8e396ce15f348612f09146650795fe58f8") (:authors ("Michael Smith" . "smith@pell.anu.edu.au") ("Gary Zablackis") ("Goetz Pfeiffer") ("Ivan Andrus" . "darthandrus@gmail.com")) (:maintainer "Ivan Andrus" . "darthandrus@gmail.com") (:keywords "gap") (:url . "https://gitlab.com/gvol/gap-mode"))]) (gather . [(20141230 1338) nil "Gather string in buffer." single ((:commit . "50809fbc22d70a1c724c2dd99ac5a1f818ffeb6b") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "matching" "convenience" "tools") (:url . "https://github.com/mhayashi1120/Emacs-gather/raw/master/gather.el"))]) (gcmh . [(20201116 2251) ((emacs (24))) "the Garbage Collector Magic Hack" single ((:commit . "0089f9c3a6d4e9a310d0791cf6fa8f35642ecfd9") (:authors ("Andrea Corallo" . "akrl@sdf.org")) (:maintainer nil . "akrl@sdf.org") (:keywords "internal") (:url . "https://gitlab.com/koral/gcmh"))]) (gcode-mode . [(20210522 1025) ((emacs (24 4))) "Simple G-Code major mode" tar ((:commit . "1f83845af4102efc5e5856b55bd5ad165b2f0cdd") (:authors ("Yuri D'Elia" . "wavexx@thregr.org")) (:maintainer "Yuri D'Elia" . "wavexx@thregr.org") (:keywords "gcode" "languages" "highlight" "syntax") (:url . "https://gitlab.com/wavexx/gcode-mode.el"))]) @@ -1837,7 +1841,7 @@ (geben . [(20210830 422) ((emacs (24 3)) (cl-lib (0 5))) "DBGp protocol frontend, a script debugger" tar ((:commit . "d3706387ed25b3037338572f3968b4cc2d8825a0") (:authors ("Matthew Carter" . "m@ahungry.com")) (:maintainer "Matthew Carter" . "m@ahungry.com") (:keywords "c" "comm" "tools") (:url . "https://github.com/ahungry/geben"))]) (geben-helm-projectile . [(20160611 59) ((emacs (24)) (geben (0 26)) (helm-projectile (0 13 0))) "Integrate helm-projectile with geben" single ((:commit . "31ce0faca5dcc71924884f03fd5a7a25d00ccd9b") (:authors ("Matthew Carter" . "m@ahungry.com")) (:maintainer "Matthew Carter" . "m@ahungry.com") (:keywords "ahungry" "emacs" "geben" "helm" "projectile" "debug") (:url . "https://github.com/ahungry/geben-helm-projectile"))]) (geeknote . [(20220213 612) ((emacs (24))) "Use Evernote in Emacs through geeknote" single ((:commit . "ce2738aebeeda35f9d31027e9b7bad0813b975c3") (:authors ("Evan Dale Aromin")) (:maintainer "Evan Dale Aromin") (:keywords "evernote" "geeknote" "note" "emacs-evernote" "evernote-mode") (:url . "http://github.com/avendael/emacs-geeknote"))]) - (geiser . [(20220802 1327) ((emacs (25 1)) (transient (0 3)) (project (0 8 1))) "GNU Emacs and Scheme talk to each other" tar ((:commit . "bd3d4ab6d7dffd9f8565af32471dc58aaf5a2214") (:authors ("Jose Antonio Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose Antonio Ortega Ruiz" . "jao@gnu.org") (:keywords "languages" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/"))]) + (geiser . [(20220825 46) ((emacs (25 1)) (transient (0 3)) (project (0 8 1))) "GNU Emacs and Scheme talk to each other" tar ((:commit . "621642856832dff4f80ad60153b240991299d419") (:authors ("Jose Antonio Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose Antonio Ortega Ruiz" . "jao@gnu.org") (:keywords "languages" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/"))]) (geiser-chez . [(20211216 2332) ((emacs (26 1)) (geiser (0 19))) "Chez Scheme's implementation of the geiser protocols" tar ((:commit . "48427d4aecc6fed751d266673f1ce2ad57ddbcfc") (:authors ("Peter" . "craven@gmx.net")) (:maintainer "Jose A Ortega Ruiz" . "jao@gnu.org") (:keywords "languages" "chez" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/chez"))]) (geiser-chibi . [(20211204 1938) ((emacs (24 4)) (geiser (0 18))) "Chibi Scheme's implementation of the geiser protocols" tar ((:commit . "5a6a5a580ea45cd4974df21629a8d50cbe3d6e99") (:authors ("Peter" . "craven@gmx.net")) (:maintainer "Jose A Ortega Ruiz" . "jao@gnu.org") (:keywords "languages" "chibi" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/chibi"))]) (geiser-chicken . [(20220717 1130) ((emacs (24 4)) (geiser (0 19))) "Chicken's implementation of the geiser protocols" tar ((:commit . "a480598b5908c95bc8d3178a48f13e9072a9235b") (:authors ("Daniel Leslie")) (:maintainer "Daniel Leslie") (:keywords "languages" "chicken" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/chicken"))]) @@ -1856,7 +1860,7 @@ (geolocation . [(20200317 1559) ((request-deferred (0 3 2)) (deferred (0 5 1)) (emacs (25 1))) "Get your location on Earth" single ((:commit . "bc7848832eb0352e3a71f4b9d89d917fe12d18be") (:authors ("Neil Okamoto" . "neil.okamoto+melpa@gmail.com")) (:maintainer "Neil Okamoto" . "neil.okamoto+melpa@gmail.com") (:keywords "hardware") (:url . "https://github.com/gonewest818/geolocation.el"))]) (german-holidays . [(20181213 644) nil "German holidays for Emacs calendar" single ((:commit . "a8462dffccaf2b665f2032e646b5370e993a386a") (:authors ("Sebastian Christ" . "rudolfo.christ@gmail.com")) (:maintainer "Sebastian Christ" . "rudolfo.christ@gmail.com") (:url . "https://github.com/rudolfochrist/german-holidays"))]) (germanium . [(20220716 1500) ((emacs (26 1))) "Generate image from source code using germanium" single ((:commit . "7292aa6870cf8b0acb34a8750da32b44d83cd65c") (:authors ("Masaya Watanabe")) (:maintainer "Masaya Watanabe") (:keywords "convenience") (:url . "https://github.com/matsuyoshi30/germanium-el"))]) - (gerrit . [(20220801 2144) ((emacs (25 1)) (magit (2 13 1)) (s (1 12 0)) (dash (0 2 15))) "Gerrit client" tar ((:commit . "554aecaea4709a16f1ecee73907fd0e6762ad2b9") (:authors ("Thomas Hisch" . "t.hisch@gmail.com")) (:maintainer "Thomas Hisch" . "t.hisch@gmail.com") (:keywords "extensions") (:url . "https://github.com/thisch/gerrit.el"))]) + (gerrit . [(20220812 2150) ((emacs (25 1)) (magit (2 13 1)) (s (1 12 0)) (dash (0 2 15))) "Gerrit client" tar ((:commit . "4de561d1295d4c86ca9b159ab0c746bedc2d0380") (:authors ("Thomas Hisch" . "t.hisch@gmail.com")) (:maintainer "Thomas Hisch" . "t.hisch@gmail.com") (:keywords "extensions") (:url . "https://github.com/thisch/gerrit.el"))]) (gerrit-download . [(20150714 1408) ((emacs (24 0)) (magit (2 1 0))) "Show gerrit reviews in a diff buffer." single ((:commit . "d568acc7c5935188c9bc19ba72719a6092d9f6fd") (:authors ("Chmouel Boudjnah" . "chmouel@chmouel.com")) (:maintainer "Chmouel Boudjnah" . "chmouel@chmouel.com") (:keywords "tools" "gerrit" "git") (:url . "https://github.com/chmouel/gerrit-download.el"))]) (gf . [(20181028 1542) ((s (1 0)) (ht (2 0))) "Major mode for editing GF code" single ((:commit . "30b3127f229e0db522c7752f6957ca01b2ea2821") (:authors ("Johan Bockgård" . "bojohan+mail@dd.chalmers.se")) (:maintainer "bruno cuconato" . "bcclaro+emacs@gmail.com") (:keywords "languages") (:url . "https://github.com/GrammaticalFramework/gf-emacs-mode"))]) (ggo-mode . [(20210310 1345) nil "Gengetopt major mode" single ((:commit . "6a7617b5af3d13029e4d680a375e8107c40d0fac") (:authors ("Matthew K. Junker" . "junker@alum.mit.edu")) (:maintainer "Matthew K. Junker" . "junker@alum.mit.edu") (:keywords "extensions" "convenience" "local"))]) @@ -1869,22 +1873,22 @@ (gherkin-mode . [(20171224 1353) nil "An emacs major mode for editing gherkin files." single ((:commit . "0313492e7da152f0aa73ddf96c0287ded8f51253") (:authors ("Craig Andera")) (:maintainer "Craig Andera") (:keywords "languages"))]) (ghost-blog . [(20171023 742) ((markdown-mode (1 0))) "A package to manage Ghost blog" single ((:commit . "71b358643cc9a2db1bf752281ff94aba9b59e4cc") (:authors ("Javier Aguirre" . "hello@javaguirre.net")) (:maintainer "Javier Aguirre" . "hello@javaguirre.net") (:keywords "ghost" "blog") (:url . "https://github.com/javaguirre/ghost-blog"))]) (ghq . [(20210504 902) nil "Ghq interface for emacs" single ((:commit . "582bd6daa505d04c7cc06d6c82ed8aee0624bfbe") (:authors ("Roman Coedo" . "romancoedo@gmail.com")) (:maintainer "Roman Coedo" . "romancoedo@gmail.com") (:keywords "ghq"))]) - (ghub . [(20220624 803) ((emacs (25 1)) (compat (28 1 1 0)) (let-alist (1 0 6)) (treepy (0 1 1))) "Client libraries for Git forge APIs." tar ((:commit . "94f5aa2ddecc6426ebc69a0c67934ccba6ae9797") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/magit/ghub"))]) + (ghub . [(20220810 1157) ((emacs (25 1)) (compat (28 1 1 0)) (let-alist (1 0 6)) (treepy (0 1 1))) "Client libraries for Git forge APIs." tar ((:commit . "4e5bb75305a763ef9ccfc1971b3cba4520f290d7") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/magit/ghub"))]) (ghub+ . [(20191229 1748) ((emacs (25)) (ghub (2 0)) (apiwrap (0 5))) "a thick GitHub API client built on ghub" single ((:commit . "b1adef2402d7599911d4dd447a987a0cea04e6fe") (:authors ("Sean Allred" . "code@seanallred.com")) (:maintainer "Sean Allred" . "code@seanallred.com") (:keywords "extensions" "multimedia" "tools") (:url . "https://github.com/vermiculus/ghub-plus"))]) (gif-screencast . [(20220714 1300) ((emacs (25 1))) "One-frame-per-action GIF recording" single ((:commit . "adec408e6adab2e8e057fe0ad828749f473bfb83") (:authors ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainer "Pierre Neidhardt" . "mail@ambrevar.xyz") (:keywords "multimedia" "screencast") (:url . "https://gitlab.com/ambrevar/emacs-gif-screencast"))]) (gift-mode . [(20210528 1459) nil "major mode for editing GIFT format quizzes" single ((:commit . "c93354e8fe1173b22f398f17b127875807f15b87") (:authors ("Christophe Rhodes" . "christophe@rhodes.io")) (:maintainer "Christophe Rhodes" . "christophe@rhodes.io") (:url . "https://github.com/csrhodes/gift-mode"))]) (gildas-mode . [(20181022 649) ((polymode (0 1 5)) (emacs (25))) "Major mode for Gildas" single ((:commit . "d0c9e997e2aa0bcd9b8b7db082d69100448cb1b2") (:authors ("Sébastien Maret" . "sebastien.maret@icloud.com")) (:maintainer "Sébastien Maret" . "sebastien.maret@icloud.com") (:keywords "languages" "gildas") (:url . "https://github.com/smaret/gildas-mode"))]) (gist . [(20171128 406) ((emacs (24 1)) (gh (0 10 0))) "Emacs integration for gist.github.com" single ((:commit . "314fe6ab80fae35b95f0734eceb82f72813b6f41") (:authors ("Yann Hodique" . "yann.hodique@gmail.com")) (:maintainer "Yann Hodique" . "yann.hodique@gmail.com") (:keywords "tools") (:url . "https://github.com/defunkt/gist.el"))]) (git . [(20140128 1041) ((s (1 7 0)) (dash (2 2 0)) (f (0 10 0))) "An Elisp API for programmatically using Git" single ((:commit . "a3396a7027a7d986598c6a2d6d5599bac918f3da") (:authors ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Johan Andersson" . "johan.rejeep@gmail.com") (:keywords "git") (:url . "http://github.com/rejeep/git.el"))]) - (git-annex . [(20190625 2118) nil "Mode for easy editing of git-annex'd files" single ((:commit . "1324d3f23c534fe79391a2c256bb8803054e383b") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainer "John Wiegley" . "jwiegley@gmail.com") (:keywords "files" "data" "git" "annex") (:url . "https://github.com/jwiegley/git-annex-el"))]) + (git-annex . [(20220807 1542) nil "Mode for easy editing of git-annex'd files" single ((:commit . "92f2d97c89980d2cea85850353836c68903514a1") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainer "John Wiegley" . "jwiegley@gmail.com") (:keywords "files" "data" "git" "annex") (:url . "https://github.com/jwiegley/git-annex-el"))]) (git-assembler-mode . [(20210207 1545) ((emacs (24 4))) "git-assembler major mode" single ((:commit . "1243bdc1a9cdc79802ece05c90731ee14e4f92c9") (:authors ("Yuri D'Elia" . "wavexx@thregr.org")) (:maintainer "Yuri D'Elia" . "wavexx@thregr.org") (:keywords "git" "git-assembler" "languages" "highlight" "syntax") (:url . "https://gitlab.com/wavexx/git-assembler-mode.el"))]) (git-attr . [(20180925 2003) ((emacs (24 3))) "Git attributes of buffer file" tar ((:commit . "50df0630eba2a931146f676d349b29bde6b6b37b") (:authors ("Arne Jørgensen" . "arne@arnested.dk")) (:maintainer "Arne Jørgensen" . "arne@arnested.dk") (:keywords "vc") (:url . "https://github.com/arnested/emacs-git-attr"))]) (git-auto-commit-mode . [(20200828 653) nil "Emacs Minor mode to automatically commit and push" single ((:commit . "a6b6e0fa183be381463e2b44ef128db1b6c4234b") (:authors ("Tom Willemse" . "tom@ryuslash.org")) (:maintainer "Tom Willemse" . "tom@ryuslash.org") (:keywords "vc") (:url . "https://github.com/ryuslash/git-auto-commit-mode"))]) (git-backup . [(20191209 2144) ((emacs (24 3)) (s (1 8 0))) "Backup each file change using git" single ((:commit . "67e38c659c918e98642171ba3f385a15182347f4") (:authors ("Anthony HAMON" . "hamon.anth@gmail.com")) (:maintainer "Anthony HAMON" . "hamon.anth@gmail.com") (:keywords "backup" "files" "tools" "git") (:url . "http://github.com/antham/git-backup"))]) - (git-backup-ivy . [(20220412 1914) ((ivy (0 12 0)) (git-backup (0 0 1)) (emacs (25 1))) "An ivy interface to git-backup" single ((:commit . "c53e1bc800963c0d826226c37c22e36f2353c70d") (:authors ("Sebastian Wålinder" . "s.walinder@gmail.com")) (:maintainer "Sebastian Wålinder" . "s.walinder@gmail.com") (:keywords "backup" "convenience" "files" "tools" "vc") (:url . "https://github.com/walseb/git-backup-ivy"))]) + (git-backup-ivy . [(20220816 30) ((ivy (0 12 0)) (git-backup (0 0 1)) (emacs (25 1))) "An ivy interface to git-backup" single ((:commit . "ad6434bcd540de8e3cd9953035680f0a745040d5") (:authors ("Sebastian Wålinder" . "s.walinder@gmail.com")) (:maintainer "Sebastian Wålinder" . "s.walinder@gmail.com") (:keywords "backup" "convenience" "files" "tools" "vc") (:url . "https://github.com/walseb/git-backup-ivy"))]) (git-blamed . [(20161028 1926) nil "Minor mode for incremental blame for Git" single ((:commit . "cef196abf398e2dd11f775d1e6cd8690567408aa") (:keywords "git" "version control" "release management"))]) (git-command . [(20191028 333) ((term-run (0 1 4)) (with-editor (2 3 1))) "A Git Command-Line interface" single ((:commit . "a773d40da39dfb1c6ecf2b0758aa370ddea8f06d") (:authors ("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 . [(20220803 2341) ((emacs (25 1)) (compat (28 1 1 2)) (transient (20210920)) (with-editor (20211001))) "Edit Git commit messages." tar ((:commit . "ac7fae6a9893b55ad01942d9ea5a571d44426665") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li") ("Sebastian Wiesner" . "lunaryorn@gmail.com") ("Florian Ragwitz" . "rafl@debian.org") ("Marius Vollmer" . "marius.vollmer@gmail.com")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) + (git-commit . [(20220803 2341) ((emacs (25 1)) (compat (28 1 1 2)) (transient (20210920)) (with-editor (20211001))) "Edit Git commit messages." tar ((:commit . "712be4632b0ddc7899ca90db8f9be20d90b4326f") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li") ("Sebastian Wiesner" . "lunaryorn@gmail.com") ("Florian Ragwitz" . "rafl@debian.org") ("Marius Vollmer" . "marius.vollmer@gmail.com")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) (git-commit-insert-issue . [(20210107 2018) ((emacs (25)) (projectile (0)) (s (0)) (ghub (0)) (bitbucket (0))) "Get issues list when typing \"Fixes #\"" single ((:commit . "6cfb8b4b5b23ae881cf3d005da4d7f60d91cd2cd") (:authors ("Vindarel")) (:maintainer "Vindarel") (:keywords "tools" "vc" "github" "gitlab" "bitbucket" "commit" "issues") (:url . "https://gitlab.com/emacs-stuff/git-commit-insert-issue/"))]) (git-dwim . [(20170126 1214) nil "Context-aware git commands such as branch handling" single ((:commit . "485c732130686c2f28a026e385366006435394b9") (:authors ("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"))]) (git-grep . [(20200920 1751) ((projectile (0 10 0))) "Search tools using git grep" single ((:commit . "12ff6045e9b6aa42f98abd4ddc44d670268a0849") (:authors ("Sam Kleinman")) (:maintainer "tychoish" . "garen@tychoish.com") (:keywords "matching" "files" "grep" "search" "using" "git-grep") (:url . "https://github.com/tychoish/git-grep.el"))]) @@ -1992,7 +1996,7 @@ (gomacro-mode . [(20200326 1103) ((emacs (24 4)) (go-mode (1 5 0))) "Gomacro mode and Go REPL integration" single ((:commit . "3112e56d2d5e645a3e0fd877f3e810dbccbf989f") (:authors ("Petter Storvik")) (:maintainer "Petter Storvik") (:keywords "gomacro" "repl" "languages" "tools" "processes") (:url . "https://github.com/storvik/gomacro-mode"))]) (good-scroll . [(20211101 942) ((emacs (27 1))) "Good pixel line scrolling" tar ((:commit . "a7ffd5c0e5935cebd545a0570f64949077f71ee3") (:authors ("Benjamin Levy" . "blevy@protonmail.com")) (:maintainer "Benjamin Levy" . "blevy@protonmail.com") (:url . "https://github.com/io12/good-scroll.el"))]) (google . [(20140416 1748) nil "Emacs interface to the Google API" single ((:commit . "3b3189a8b201c8d36fed6e61496274e530dd40bd") (:authors ("Edward O'Connor" . "ted@oconnor.cx")) (:maintainer "Edward O'Connor" . "ted@oconnor.cx") (:keywords "comm" "processes" "tools"))]) - (google-c-style . [(20220210 1659) nil "Google's C/C++ style for c-mode" single ((:commit . "e8808406eab41dab5dbfbaec0aee620d6c618365") (:keywords "c" "tools"))]) + (google-c-style . [(20220210 1659) nil "Google's C/C++ style for c-mode" single ((:commit . "099770e27c0727d3e9489f4c4c5226f5e41affab") (:keywords "c" "tools"))]) (google-contacts . [(20201012 1056) ((oauth2 (0 10)) (cl-lib (0 5))) "Support for Google Contacts in Emacs" tar ((:commit . "8923c238fe0906184d2254b33ba72792ed12cd47") (:authors ("Julien Danjou" . "julien@danjou.info")) (:maintainer "Julien Danjou" . "julien@danjou.info") (:keywords "comm") (:url . "https://github.com/jd/google-contacts.el"))]) (google-maps . [(20181121 1532) ((emacs (24 3))) "Access Google Maps from Emacs" tar ((:commit . "2eb16ff609f5a9f8d02c15238a111fbb7db6c146") (:authors ("Julien Danjou" . "julien@danjou.info")) (:maintainer "Julien Danjou" . "julien@danjou.info") (:keywords "comm") (:url . "https://julien.danjou.info/projects/emacs-packages#google-maps"))]) (google-this . [(20170810 1215) ((emacs (24 1))) "A set of functions and bindings to google under point." single ((:commit . "8a2e3ca5da6a8c89bfe99a21486c6c7db125dc84") (:authors ("Artur Malabarba" . "bruce.connor.am@gmail.com")) (:maintainer "Artur Malabarba" . "bruce.connor.am@gmail.com") (:keywords "convenience" "hypermedia") (:url . "http://github.com/Malabarba/emacs-google-this"))]) @@ -2002,12 +2006,12 @@ (gorepl-mode . [(20170905 945) ((emacs (24)) (s (1 11 0)) (f (0 19 0)) (hydra (0 13 0))) "Go REPL Interactive Development in top of Gore" single ((:commit . "6a73bf352e8d893f89cad36c958c4db2b5e35e07") (:authors ("Manuel Alonso" . "manuteali@gmail.com")) (:maintainer "Manuel Alonso" . "manuteali@gmail.com") (:keywords "languages" "go" "golang" "gorepl") (:url . "http://www.github.com/manute/gorepl-mode"))]) (gotest . [(20220728 750) ((emacs (24 3)) (s (1 11 0)) (f (0 19 0)) (go-mode (1 5 0))) "Launch GO unit tests" single ((:commit . "2ec82dcc70d5f6aa22f66b44f8b537be33bd7903") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "languages" "go" "tests") (:url . "https://github.com/nlamirault/gotest.el"))]) (gotham-theme . [(20220107 1730) ((emacs (24 1))) "A very dark Emacs color theme" single ((:commit . "4b8214df0851bb69b44c3e864568b7e0030a95d2") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:url . "https://depp.brause.cc/gotham-theme"))]) - (goto-char-preview . [(20220704 630) ((emacs (24 3))) "Preview character when executing `goto-char` command" single ((:commit . "104a04c3228b3cde2432ae802d739859f96c8509") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "character" "navigation") (:url . "https://github.com/emacs-vs/goto-char-preview"))]) + (goto-char-preview . [(20220704 630) ((emacs (24 3))) "Preview character when executing `goto-char` command" single ((:commit . "b4c6f5ef2f0b2449d29e45e6fb95fe3b0f3c8bb6") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "character" "navigation") (:url . "https://github.com/emacs-vs/goto-char-preview"))]) (goto-chg . [(20220107 1733) ((emacs (24 1))) "Go to last change" single ((:commit . "278cd3e6d5107693aa2bb33189ca503f22f227d0") (:authors ("David Andersson ")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "convenience" "matching") (:url . "https://github.com/emacs-evil/goto-chg"))]) (goto-last-change . [(20150109 1823) nil "Move point through buffer-undo-list positions" single ((:commit . "58b0928bc255b47aad318cd183a5dce8f62199cc") (:authors ("Kevin Rodgers" . "ihs_4664@yahoo.com")) (:maintainer "Kevin Rodgers" . "ihs_4664@yahoo.com") (:keywords "convenience") (:url . "https://github.com/camdez/goto-last-change.el"))]) - (goto-last-point . [(20190525 1855) ((emacs (24 3))) "Record and jump to the last point in the buffer." single ((:commit . "6218441a0e644c23e247594aece004e80775d307") (:authors ("Manuel Uberti" . "manuel.uberti@inventati.org")) (:maintainer "Manuel Uberti" . "manuel.uberti@inventati.org") (:keywords "convenience") (:url . "https://github.com/manuel-uberti/goto-last-point"))]) - (goto-line-preview . [(20220704 630) ((emacs (25))) "Preview line when executing `goto-line` command" single ((:commit . "b58af255e822b6bb226085592ca471dee6027b95") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "line" "navigation") (:url . "https://github.com/emacs-vs/goto-line-preview"))]) - (govc . [(20220509 1455) ((emacs (24 3)) (dash (1 5 0)) (s (1 9 0)) (magit-popup (2 0 50)) (json-mode (1 6 0))) "Interface to govc for managing VMware ESXi and vCenter" single ((:commit . "f036314826293cb5df72f124a20b175d17e6daa4") (:authors ("The govc developers")) (:maintainer "The govc developers") (:keywords "convenience") (:url . "https://github.com/vmware/govmomi/tree/master/govc/emacs"))]) + (goto-last-point . [(20220816 153) ((emacs (24 3))) "Record and jump to the last point in the buffer" single ((:commit . "0a006c70719f479bc4e3ae75dd2fc84cbb15f049") (:authors ("Manuel Uberti" . "manuel.uberti@inventati.org")) (:maintainer "Manuel Uberti" . "manuel.uberti@inventati.org") (:keywords "convenience") (:url . "https://github.com/manuel-uberti/goto-last-point"))]) + (goto-line-preview . [(20220704 630) ((emacs (25))) "Preview line when executing `goto-line` command" single ((:commit . "162f6f3e410365399efed4122321eec23d482e22") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "line" "navigation") (:url . "https://github.com/emacs-vs/goto-line-preview"))]) + (govc . [(20220509 1455) ((emacs (24 3)) (dash (1 5 0)) (s (1 9 0)) (magit-popup (2 0 50)) (json-mode (1 6 0))) "Interface to govc for managing VMware ESXi and vCenter" single ((:commit . "cf96cab7e22d0f7e3dcfaaf6d079b9491d8af7a9") (:authors ("The govc developers")) (:maintainer "The govc developers") (:keywords "convenience") (:url . "https://github.com/vmware/govmomi/tree/master/govc/emacs"))]) (govet . [(20170808 1724) nil "linter/problem finder for the Go source code" single ((:commit . "1c05817cf8b96589076c7ac4e52ee58a860a0cbf") (:url . "https://godoc.org/golang.org/x/tools/cmd/vet"))]) (gpastel . [(20181229 1404) ((emacs (25 1))) "Integrates GPaste with the kill-ring" single ((:commit . "d5fc55bc825203f998537c5834718e665bb87c29") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:keywords "tools") (:url . "https://gitlab.petton.fr/DamienCassou/desktop-environment"))]) (grab-mac-link . [(20210511 1303) ((emacs (24))) "Grab link from Mac Apps and insert it into Emacs" single ((:commit . "2c722016ca01bd4265d57c4a7d55712c94cf4ea5") (:authors ("Xu Chunyang")) (:maintainer "Xu Chunyang") (:keywords "mac" "hyperlink") (:url . "https://github.com/xuchunyang/grab-mac-link.el"))]) @@ -2016,7 +2020,7 @@ (grails . [(20220407 1847) ((emacs (24))) "Minor mode for Grails projects" single ((:commit . "350869ecc4f429fc4e26f826d6050d068e724c5d") (:url . "https://github.com/lifeisfoo/emacs-grails"))]) (grails-mode . [(20220407 1954) nil "minor-mode that adds some Grails project management to a grails project" single ((:commit . "c612ac1e9f742856914ad6e8eb9e9dc169f489ab") (:authors ("Jim Morris" . "morris@wolfman.com")) (:maintainer "Russel Winder" . "russel@winder.org.uk") (:keywords "languages") (:url . "http://blog.wolfman.com"))]) (grails-projectile-mode . [(20160327 1324) ((projectile (0 10 0)) (emacs (24)) (cl-lib (0 5))) "Grails mode with Projectile for projects management." tar ((:commit . "8efca50ce92b556fe9d467b157d7aec635bcc017") (:authors ("Yves Zoundi" . "rimerosolutions@gmail.com")) (:maintainer "Yves Zoundi") (:keywords "grails" "projectile") (:url . "https://github.com/yveszoundi/grails-projectile-mode"))]) - (grammarly . [(20220704 624) ((emacs (26 1)) (s (1 12 0)) (request (0 3 0)) (websocket (1 6))) "Grammarly API interface" single ((:commit . "1242c4f26c70697ed2447b7fca41645e8f9daf96") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "api" "interface" "english") (:url . "https://github.com/emacs-grammarly/grammarly"))]) + (grammarly . [(20220704 624) ((emacs (26 1)) (s (1 12 0)) (request (0 3 0)) (websocket (1 6))) "Grammarly API interface" single ((:commit . "fa7463ff3692581112c6aadade570744028ecc91") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "grammar" "api" "interface" "english") (:url . "https://github.com/emacs-grammarly/grammarly"))]) (grandshell-theme . [(20180606 517) nil "Dark color theme for Emacs > 24 with intensive colors." tar ((:commit . "0ed8e4273607dd4fcaa742b4097259233b09eda6") (:authors ("steckerhalter")) (:maintainer "steckerhalter") (:keywords "color" "theme" "grand" "shell" "faces") (:url . "https://framagit.org/steckerhalter/grandshell-theme"))]) (graphene . [(20180529 1112) ((dash (2 10 0)) (exec-path-from-shell (1 9)) (ppd-sr-speedbar (0 0 6)) (sr-speedbar (20140505)) (ido-completing-read+ (4 3)) (smex (3 0)) (web-mode (11 2)) (smartparens (1 8 0)) (graphene-meta-theme (0 0 2)) (flycheck (0 23)) (company (0 8 12))) "Friendly Emacs defaults" tar ((:commit . "cc8477fcfb7771ea4e5bbaf3c01f9e679234c1c1") (:authors ("Robert Dallas Gray" . "mail@robertdallasgray.com")) (:maintainer "Robert Dallas Gray" . "mail@robertdallasgray.com") (:keywords "defaults") (:url . "https://github.com/rdallasgray/graphene"))]) (graphene-meta-theme . [(20161204 1607) nil "Integrated theming for common packages" single ((:commit . "62cc73fee31f1bd9474027b83a249feee050271e") (:authors ("Robert Dallas Gray" . "mail@robertdallasgray.com")) (:maintainer "Robert Dallas Gray" . "mail@robertdallasgray.com") (:keywords "defaults") (:url . "https://github.com/rdallasgray/graphene"))]) @@ -2033,7 +2037,7 @@ (green-screen-theme . [(20180816 1502) nil "A nice color theme for those who miss green CRTs" single ((:commit . "774e8f6c033786406267f71ec07319d906a30b75") (:authors ("Ricardo Banffy" . "rbanffy@gmail.com")) (:maintainer "Ricardo Banffy" . "rbanffy@gmail.com") (:keywords "faces" "theme") (:url . "https://github.com/rbanffy/green-screen-emacs"))]) (gregorio-mode . [(20170705 1451) nil "Gregorio Mode for .gabc files" single ((:commit . "736fd3d05fb67f707cca1a7ce24e3ee7ca5e9567") (:authors ("Fr. John Jenkins" . "jenkins@sspx.ng")) (:maintainer "Fr. John Jenkins" . "jenkins@sspx.ng") (:keywords "gregorio" "chant") (:url . "https://jsrjenkins.github.io/gregorio-mode/"))]) (grep-a-lot . [(20210618 1420) nil "manages multiple search results buffers for grep.el" single ((:commit . "223819dbea049bdeb5f97f9849fce139a5f16a75") (:authors ("Avi Rozen" . "avi.rozen@gmail.com")) (:maintainer "Avi Rozen" . "avi.rozen@gmail.com") (:keywords "tools" "convenience" "search") (:url . "https://github.com/ZungBang/emacs-grep-a-lot"))]) - (grey-paper-theme . [(20220711 2241) ((emacs (24 1))) "A greyscale theme with look-n-feel of an eink display" single ((:commit . "10e243316000e85a10b49729745ccd4262d21cda") (:authors ("Kang-min Liu" . "gugod@gugod.org")) (:maintainer "Kang-min Liu" . "gugod@gugod.org") (:keywords "faces") (:url . "https://github.com/gugod/grey-paper-theme"))]) + (grey-paper-theme . [(20220811 2301) ((emacs (24 1))) "A greyscale theme with look-n-feel of an eink display" single ((:commit . "760e8d26f5b2aeaa56b91bf435e42b1e5d6f69d7") (:authors ("Kang-min Liu" . "gugod@gugod.org")) (:maintainer "Kang-min Liu" . "gugod@gugod.org") (:keywords "faces") (:url . "https://github.com/gugod/grey-paper-theme"))]) (greymatters-theme . [(20150621 1123) ((emacs (24))) "Emacs 24 theme with a light background." single ((:commit . "a7220a8c6cf18ccae2b76946b6f01188a7c9d5d1") (:authors ("Martin Haesler")) (:maintainer "Martin Haesler"))]) (grip-mode . [(20220430 1545) ((emacs (24 4))) "Instant GitHub-flavored Markdown/Org preview using grip." single ((:commit . "6d6ddbe0af39c82a633add8499488ad8dc9e1daa") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "convenience" "markdown" "preview") (:url . "https://github.com/seagle0128/grip-mode"))]) (grizzl . [(20160818 737) ((cl-lib (0 5)) (emacs (24 3))) "Fast fuzzy search index for Emacs." single ((:commit . "1e917253ce2b846f0272b8356fad3dbff9cd513a") (:authors ("Chris Corbyn" . "chris@w3style.co.uk")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.com") (:keywords "convenience" "usability") (:url . "https://github.com/grizzl/grizzl"))]) @@ -2042,7 +2046,7 @@ (gruber-darker-theme . [(20220107 1815) nil "Gruber Darker color theme for Emacs 24." single ((:commit . "72278089c440d45c00fb8afcd53af82fd30f451b") (:authors ("Alexey Kutepov" . "reximkut@gmail.com")) (:maintainer "Alexey Kutepov" . "reximkut@gmail.com") (:url . "http://github.com/rexim/gruber-darker-theme"))]) (grugru . [(20211119 815) ((emacs (24 4))) "Rotate text at point" tar ((:commit . "ac92a8d54efe000557564a9b01a426f34cc01dfa") (:authors ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainer "ROCKTAKEY" . "rocktakey@gmail.com") (:keywords "convenience" "abbrev" "tools") (:url . "https://github.com/ROCKTAKEY/grugru"))]) (grunt . [(20160316 1528) ((dash (2 9 0)) (ansi-color (3 4 2)) (emacs (24 3))) "Some glue to stick Emacs and Gruntfiles together" single ((:commit . "4c269e2738658643ec2ed9ef61a2a3d71b08d304") (:authors ("Daniel Gempesaw" . "dgempesaw@sharecare.com")) (:maintainer "Daniel Gempesaw" . "dgempesaw@sharecare.com") (:keywords "convenience" "grunt") (:url . "https://github.com/gempesaw/grunt.el"))]) - (gruvbox-theme . [(20220804 103) ((autothemer (0 2))) "A retro-groove colour theme for Emacs" tar ((:commit . "6c54b1f453dca09e5800da5fbce7153c26dc6b82") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "http://github.com/greduan/emacs-theme-gruvbox"))]) + (gruvbox-theme . [(20220822 1711) ((autothemer (0 2))) "A retro-groove colour theme for Emacs" tar ((:commit . "74e8dc88b7fa15bba964f4f1ce1fccd76f523bfd") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "http://github.com/greduan/emacs-theme-gruvbox"))]) (gs-mode . [(20151202 1006) nil "Major mode for editing GrADS script files" single ((:commit . "1a13051db21b999c7682a015b33a03096ff9d891") (:authors ("Joe Wielgosz" . "joew@cola.iges.org")) (:maintainer "Joe Wielgosz" . "joew@cola.iges.org") (:keywords "grads" "script" "major-mode"))]) (gscholar-bibtex . [(20190130 555) nil "Retrieve BibTeX from Google Scholar and other online sources(ACM, IEEE, DBLP)" single ((:commit . "3b651e3de116860eb1f1aef9b547a561784871fe") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) (gsettings . [(20210407 2045) ((emacs (24 3)) (dash (2 16 0)) (gvariant (1 0 0)) (s (1 12 0))) "GSettings (Gnome) helpers" single ((:commit . "9f9fb1fe946bbba46307c26355f355225ea7262a") (:authors ("wouter bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "wouter bolsterlee" . "wouter@bolsterl.ee") (:keywords "languages") (:url . "https://github.com/wbolster/emacs-gsettings"))]) @@ -2054,14 +2058,14 @@ (guide-key-tip . [(20161011 823) ((guide-key (1 2 3)) (pos-tip (0 4 5))) "Show guide-key.el hints using pos-tip.el" single ((:commit . "02c5d4b0b65f3e91be5a47f0ff1ae5e86e00c64e") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "help" "convenience" "tooltip") (:url . "https://github.com/aki2o/guide-key-tip"))]) (guix . [(20210608 1653) ((emacs (24 3)) (dash (2 11 0)) (geiser (0 8)) (bui (1 2 0)) (magit-popup (2 1 0)) (edit-indirect (0 1 4))) "Interface for GNU Guix" tar ((:commit . "c9aef52121b458297e70bb50f49f7276b4a8d759") (:authors ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "tools") (:url . "https://emacs-guix.gitlab.io/website/"))]) (gulp-task-runner . [(20170718 2041) nil "Gulp task runner" single ((:commit . "877990e956b1d71e2d9c7c3e5a129ad199b9debb") (:authors ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr") (:keywords "convenience" "javascript"))]) - (gumshoe . [(20211229 152) ((emacs (25 1))) "Scoped spatial and temporal POINT movement tracking" tar ((:commit . "2366f1c65cdcf09c6b98ca466110842cd88c9db3") (:authors ("overdr0ne")) (:maintainer "overdr0ne") (:keywords "tools") (:url . "https://github.com/Overdr0ne/gumshoe"))]) + (gumshoe . [(20220816 2254) ((emacs (25 1))) "Scoped spatial and temporal POINT movement tracking" tar ((:commit . "5e30a68f1cd80c8bdf67c06afa1fe7642decbef7") (:authors ("overdr0ne")) (:maintainer "overdr0ne") (:keywords "tools") (:url . "https://github.com/Overdr0ne/gumshoe"))]) (guru-mode . [(20211025 1157) nil "Become an Emacs guru" single ((:commit . "a3370e547eab260d24774cd50ccbe865373c8631") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "convenience") (:url . "https://github.com/bbatsov/guru-mode"))]) (gvariant . [(20210507 1310) ((emacs (24)) (parsec (0 1 4))) "GVariant (GLib) helpers" single ((:commit . "f2e87076845800cbaaeed67f175ad4e4a9c01e37") (:authors ("wouter bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "wouter bolsterlee" . "wouter@bolsterl.ee") (:keywords "languages") (:url . "https://github.com/wbolster/emacs-gvariant"))]) (gvpr-mode . [(20201007 2054) nil "A major mode offering basic syntax coloring for gvpr scripts." single ((:commit . "ef6ec2d4a4c9de68078c94a0e43b05bf77ec4674") (:authors ("Rod Waldhoff" . "r.waldhoff@gmail.com")) (:maintainer "Rod Waldhoff" . "r.waldhoff@gmail.com") (:keywords "graphviz" "gv" "dot" "gvpr" "graph") (:url . "https://raw.github.com/rodw/gvpr-lib/master/extra/gvpr-mode.el"))]) (gxref . [(20170411 1753) ((emacs (25))) "xref backend using GNU Global." single ((:commit . "380b02c3c3c2586c828456716eef6a6392bb043b") (:authors ("Dedi Hirschfeld")) (:maintainer "Dedi Hirschfeld") (:keywords "xref" "global" "tools") (:url . "https://github.com/dedi/gxref"))]) (habamax-theme . [(20181001 850) ((emacs (24))) "Boring white background color that gets the job done." single ((:commit . "6e86a1b23b6e2aaf40d4374b5673da00a28be447") (:authors ("Maxim Kim" . "habamax@gmail.com")) (:maintainer "Maxim Kim" . "habamax@gmail.com") (:url . "https://github.com/habamax/habamax-theme"))]) (habitica . [(20220215 1758) ((org (8 3 5)) (emacs (24 3))) "Interface for habitica.com" single ((:commit . "9e1fde7f359f7f6a6976b857fbbdbc8dd4fd3327") (:authors ("Adrien Brochard")) (:maintainer "Adrien Brochard") (:keywords "habitica" "todo") (:url . "https://github.com/abrochard/emacs-habitica"))]) - (hack-mode . [(20211224 19) ((emacs (25 1)) (s (1 11 0))) "Major mode for the Hack programming language" single ((:commit . "a522f61c088ee2a13ab17f289a3131329e59badf") (:authors ("John Allen , Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "John Allen , Wilfred Hughes" . "me@wilfred.me.uk") (:url . "https://github.com/hhvm/hack-mode"))]) + (hack-mode . [(20220825 127) ((emacs (25 1)) (s (1 11 0))) "Major mode for the Hack programming language" single ((:commit . "26f06ffe82574f98e7da381e48202eceb8ef0793") (:authors ("John Allen , Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "John Allen , Wilfred Hughes" . "me@wilfred.me.uk") (:url . "https://github.com/hhvm/hack-mode"))]) (hacker-typer . [(20170206 1520) ((emacs (24))) "Pretend to write code like a pro" tar ((:commit . "d5a23714a4ccc5071580622f278597d5973f40bd") (:authors ("Diego A. Mundo" . "diegoamundo@gmail.com")) (:maintainer "Diego A. Mundo" . "diegoamundo@gmail.com") (:keywords "hacker" "typer" "multimedia" "games") (:url . "http://github.com/therockmandolinist/emacs-hacker-typer"))]) (hackernews . [(20210226 1226) nil "Hacker News Client for Emacs" single ((:commit . "ccfa75c0b3d67201cdf0f2324f311544ade498db") (:authors ("Lincoln de Sousa" . "lincoln@comum.org")) (:maintainer "Basil L. Contovounesios" . "contovob@tcd.ie") (:keywords "comm" "hypermedia" "news") (:url . "https://github.com/clarete/hackernews.el"))]) (hal-mode . [(20160704 1746) nil "Major mode for editing HAL files" single ((:commit . "cd2f66f219ee520198d4586fb6b169cef7ad3f21") (:authors ("Alexander Rössler")) (:maintainer "Alexander Rössler") (:keywords "language") (:url . "https://github.com/strahlex/hal-mode/"))]) @@ -2070,6 +2074,7 @@ (hamburger-menu . [(20220509 1341) ((emacs (28 1))) "Mode line hamburger menu" single ((:commit . "06bc9d6872007a31226d7410d497a0acd98b272b") (:authors ("Iain Nicol")) (:maintainer "Iain Nicol") (:keywords "hamburger" "menu") (:url . "https://gitlab.com/iain/hamburger-menu-mode"))]) (haml-mode . [(20190219 2102) ((emacs (24)) (cl-lib (0 5))) "Major mode for editing Haml files" single ((:commit . "bf5b6c11b1206759d2b28af48765e04882dd1fc4") (:authors ("Natalie Weizenbaum")) (:maintainer "Natalie Weizenbaum") (:keywords "markup" "languages" "html") (:url . "https://github.com/nex3/haml-mode"))]) (hamlet-mode . [(20131208 724) ((cl-lib (0 3)) (dash (2 3 0)) (s (1 7 0))) "Hamlet editing mode" single ((:commit . "7362b955e556a3d007fa06945a27e5b99349527d") (:authors (nil . "Kata ")) (:maintainer "myuhe") (:keywords "convenience") (:url . "https://github.com/myuhe/helm-R.el"))]) (helm-ack . [(20141030 1226) ((helm (1 0)) (cl-lib (0 5))) "Ack command with helm interface" single ((:commit . "889bc225318d14c6e3be80e73b1d9d6fb30e48c3") (:authors ("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" single ((:commit . "8ac044705d8620ee354a9cfa8cc1b865e83c0d55") (:authors ("Takahiro Noda" . "takahiro.noda+github@gmail.com")) (:maintainer "Takahiro Noda" . "takahiro.noda+github@gmail.com") (:keywords "comm"))]) @@ -2130,7 +2135,7 @@ (helm-codesearch . [(20190412 1153) ((emacs (25 1)) (s (1 11 0)) (dash (2 12 0)) (helm (1 7 7)) (cl-lib (0 5))) "helm interface for codesearch" single ((:commit . "72f1d1de746115ab7e861178b49fa3c0b6b58d90") (:authors ("Youngjoo Lee" . "youngker@gmail.com")) (:maintainer "Youngjoo Lee" . "youngker@gmail.com") (:keywords "tools"))]) (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" single ((:commit . "9ee7e018c5db23ae9c8d1c8fa969876f15b7280d") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "commandlinefu.com") (:url . "https://github.com/xuchunyang/helm-commandlinefu"))]) (helm-company . [(20190812 1429) ((helm (1 5 9)) (company (0 6 13))) "Helm interface for company-mode" single ((:commit . "6eb5c2d730a60e394e005b47c1db018697094dde") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainer "Daniel Ralston" . "Sodel-the-Vociferous@users.noreply.github.com") (:url . "https://github.com/Sodel-the-Vociferous/helm-company"))]) - (helm-core . [(20220803 1447) ((emacs (25 1)) (async (1 9 4))) "Development files for Helm" tar ((:commit . "4ede199d5d1b7050486a0fdeecbbbf49fef31118") (:authors ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:url . "https://emacs-helm.github.io/helm/"))]) + (helm-core . [(20220824 1925) ((emacs (25 1)) (async (1 9 4))) "Development files for Helm" tar ((:commit . "4e99cc8ef66aac2d824c456f58abe833be26c99d") (:authors ("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." single ((:commit . "af1d9e7f4460a88d7400b5a74d5da68084089ac1") (:authors ("alpha22jp" . "alpha22jp@gmail.com")) (:maintainer "alpha22jp" . "alpha22jp@gmail.com") (:keywords "cscope" "helm") (:url . "https://github.com/alpha22jp/helm-cscope.el"))]) (helm-css-scss . [(20191230 1549) ((emacs (24 3)) (helm (1 0))) "CSS/SCSS/LESS Selectors with helm interface" single ((:commit . "48b996f73af1fef8d6e88a1c545d98f8c50b0cf3") (:authors ("Shingo Fukuyama - http://fukuyama.co")) (:maintainer "Shingo Fukuyama - http://fukuyama.co") (:keywords "convenience" "scss" "css" "less" "selector" "helm") (:url . "https://github.com/ShingoFukuyama/helm-css-scss"))]) (helm-ctest . [(20220721 400) ((s (1 9 0)) (dash (2 11 0)) (helm-core (3 6 0))) "Run ctest from within emacs" single ((:commit . "48edc9fa862219da34feb423c06c33d8f6d43722") (:authors ("Dan LaManna" . "me@danlamanna.com")) (:maintainer "Dan LaManna" . "me@danlamanna.com") (:keywords "helm" "ctest"))]) @@ -2151,7 +2156,7 @@ (helm-eww . [(20190315 907) ((emacs (24 4)) (helm (2 8 6)) (seq (1 8))) "Helm UI wrapper for EWW." single ((:commit . "76ba59fda8dd6f32a1bc7c6df0b43c6f76169911") (:authors ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainer "Pierre Neidhardt" . "mail@ambrevar.xyz") (:keywords "helm" "packages") (:url . "https://github.com/emacs-helm/helm-eww"))]) (helm-ext . [(20200722 107) ((emacs (24 4)) (helm (2 5 3))) "A few extensions to Helm" tar ((:commit . "c30f7772ec577a5ce1de3215f0507826e0725a69") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) (helm-exwm . [(20210215 858) ((emacs (25 2)) (helm (2 8 5)) (exwm (0 15))) "Helm for EXWM buffers" single ((:commit . "5b35a42ff10fbcbf673268987df700ea6b6288e8") (:authors ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainer "Pierre Neidhardt" . "mail@ambrevar.xyz") (:keywords "helm" "exwm") (:url . "https://github.com/emacs-helm/helm-exwm"))]) - (helm-file-preview . [(20220720 531) ((emacs (25 1)) (helm (2 0))) "Preview the current helm file selection" single ((:commit . "c7584ce5e75c7f1b7b31c820fcd30bde4614c663") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "file" "helm" "preview" "select" "selection") (:url . "https://github.com/jcs-elpa/helm-file-preview"))]) + (helm-file-preview . [(20220720 531) ((emacs (25 1)) (helm (2 0))) "Preview the current helm file selection" single ((:commit . "20656a0c222708101f7738490ace67d822f482f0") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "file" "helm" "preview" "select" "selection") (:url . "https://github.com/jcs-elpa/helm-file-preview"))]) (helm-filesets . [(20140929 1835) ((helm (1 6 3)) (filesets+ (0))) "A helm source for emacs filesets" single ((:commit . "b352910af4c3099267a8aa0169c7f743b35bb1fa") (:authors ("Graham Clark" . "grclark@gmail.com")) (:maintainer "Graham Clark" . "grclark@gmail.com") (:keywords "filesets") (:url . "https://github.com/gcla/helm-filesets"))]) (helm-firefox . [(20220420 1346) ((helm (1 5)) (cl-lib (0 5)) (emacs (24 1))) "Firefox bookmarks" single ((:commit . "571cf8dfcbe43d91f9890eebefc88d7572c62e75") (:url . "https://github.com/emacs-helm/helm-firefox"))]) (helm-fish-completion . [(20200908 1504) ((emacs (25)) (helm (3)) (fish-completion (1 2))) "Helm interface for fish completion" single ((:commit . "2a2001b3a876da3c468ffec8935572509c485aac") (:authors ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainer "Pierre Neidhardt" . "mail@ambrevar.xyz") (:url . "https://github.com/emacs-helm/helm-fish-completion"))]) @@ -2162,7 +2167,7 @@ (helm-frame . [(20220803 1528) ((emacs (24 4))) "open helm buffers in a dedicated frame" single ((:commit . "1b5e895e9199deeea049010e5fe4de7a338f41f3") (:authors ("chee" . "yay@chee.party")) (:maintainer "chee" . "yay@chee.party") (:keywords "lisp" "helm" "popup" "frame"))]) (helm-fuz . [(20200812 1222) ((emacs (25 1)) (fuz (1 4 0)) (helm (3 6))) "Integrate Helm and Fuz" single ((:commit . "fee874aa35d2ee6b12b836290b5c8eaa44175a28") (:authors ("Zhu Zihao" . "all_but_last@163.com")) (:maintainer "Zhu Zihao" . "all_but_last@163.com") (:keywords "convenience") (:url . "https://github.com/cireu/fuz.el"))]) (helm-fuzzier . [(20160605 2145) ((emacs (24 3)) (helm (1 7 0))) "Better fuzzy matching for Helm" single ((:commit . "8798dcf3583b863df5b9dea7fe3b0179ba1c35bc") (:authors ("Ephram Perdition")) (:maintainer "Ephram Perdition") (:keywords "convenience" "helm" "fuzzy") (:url . "http://github.com/EphramPerdition/helm-fuzzier"))]) - (helm-fuzzy . [(20220704 651) ((emacs (24 4)) (helm (1 7 9)) (flx (0 5))) "Fuzzy matching for helm source" single ((:commit . "6098cee7fb274a814e539db85b7a4924d56493e8") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "matching" "fuzzy" "helm" "source") (:url . "https://github.com/jcs-elpa/helm-fuzzy"))]) + (helm-fuzzy . [(20220704 651) ((emacs (24 4)) (helm (1 7 9)) (flx (0 5))) "Fuzzy matching for helm source" single ((:commit . "a716d266f0042aa2164d51f82aa76932119748fe") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "matching" "fuzzy" "helm" "source") (:url . "https://github.com/jcs-elpa/helm-fuzzy"))]) (helm-fuzzy-find . [(20171106 400) ((emacs (24 1)) (helm (1 7 0))) "Find file using Fuzzy Search" single ((:commit . "de2abbf7ca13609587325bacd4a1ed4376b5c927") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "helm" "fuzzy" "find" "file") (:url . "https://github.com/xuchunyang/helm-fuzzy-find"))]) (helm-ghq . [(20210724 744) ((emacs (24)) (helm (3 8 0))) "Ghq with helm interface" single ((:commit . "7b47ac91e42762f2ecbbceeaadc05b86c9fe5f14") (:authors ("Takashi Masuda" . "masutaka.net@gmail.com")) (:maintainer "Takashi Masuda" . "masutaka.net@gmail.com") (:url . "https://github.com/masutaka/emacs-helm-ghq"))]) (helm-ghs . [(20170715 541) ((emacs (24)) (helm (2 2 0))) "ghs with helm interface" single ((:commit . "17a70bf16255d90d67c8350e88200ec8bfd47563") (:authors ("iory" . "ab.ioryz@gmail.com")) (:maintainer "iory" . "ab.ioryz@gmail.com") (:url . "https://github.com/iory/emacs-helm-ghs"))]) @@ -2198,7 +2203,7 @@ (helm-lib-babel . [(20180510 1324) ((cl-lib (0 5)) (helm (1 9 2)) (emacs (24 4))) "helm insertion of babel function references" single ((:commit . "41bc0cdea8a604c6c8dc83ed5066644d33688fad") (:authors ("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" single ((:commit . "f5ad178818d223f32a0bf60d370b50c01df5f3da") (:authors ("@torgeir")) (:maintainer "@torgeir") (:keywords "files" "helm" "rg" "ag" "pt" "vc" "git" "lines" "complete" "tools" "languages") (:url . "https://github.com/torgeir/helm-lines.el/"))]) (helm-lobsters . [(20150213 1546) ((helm (1 0)) (cl-lib (0 5))) "helm front-end for lobste.rs" single ((:commit . "53c5b42baf72776dcba891fc3d7cd7d47721e9b0") (:authors ("Julien BLANCHARD" . "julien@sideburns.eu")) (:maintainer "Julien BLANCHARD" . "julien@sideburns.eu") (:url . "https://github.com/julienXX/helm-lobste.rs"))]) - (helm-ls-git . [(20220727 505) ((helm (1 7 8))) "list git files." single ((:commit . "9d91c25c7d1776d137fd126f241d0272b6e5f90e"))]) + (helm-ls-git . [(20220818 553) ((helm (1 7 8))) "list git files." single ((:commit . "fc44fc1015bbc75d16e7d7aa5d971ff1ad85e9e1"))]) (helm-ls-hg . [(20150909 543) ((helm (1 7 8))) "List hg files in hg project." single ((:commit . "61b91a22fcfb62d0fc56e361ec01ce96973c7165"))]) (helm-ls-svn . [(20190316 2203) ((emacs (24 1)) (helm (1 7 0)) (cl-lib (0 5))) "helm extension to list svn files" single ((:commit . "a6043e1187282f649e2cb9f0e722a42daf41294b") (:authors ("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" single ((:commit . "c2c6974dadfac459b1a69a1217441283874cea92") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "languages" "debug") (:url . "https://github.com/yyoncho/helm-lsp"))]) @@ -2207,14 +2212,14 @@ (helm-migemo . [(20151010 356) ((emacs (24 4)) (helm-core (1 7 8)) (migemo (1 9)) (cl-lib (0 5))) "Migemo plug-in for helm" single ((:commit . "66c6a19d07c6a385daefd2090d0709d26b608b4e") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "Yuhei Maeda ") (:keywords "matching" "convenience" "tools" "i18n") (:url . "https://github.com/emacs-jp/helm-migemo"))]) (helm-mode-manager . [(20210108 2330) ((helm (1 5 3))) "Select and toggle major and minor modes with helm" single ((:commit . "7df8ed3ddd46a0402838b748d317c01454346164") (:authors ("istib")) (:maintainer "istib") (:url . "https://github.com/istib/helm-mode-manager"))]) (helm-mt . [(20160918 452) ((emacs (24)) (helm (0 0)) (multi-term (0 0)) (cl-lib (0 5))) "helm multi-term management" single ((:commit . "d2bff4100118483bc398c56d0ff095294209265b") (:authors ("Didier Deshommes" . "dfdeshom@gmail.com")) (:maintainer "Didier Deshommes" . "dfdeshom@gmail.com") (:keywords "helm" "multi-term") (:url . "https://github.com/dfdeshom/helm-mt"))]) - (helm-mu . [(20210816 913) ((helm (1 5 5))) "Helm sources for searching emails and contacts" single ((:commit . "b85019d01815a4b58d6016c3a30fefa60d8363f2") (:authors ("Titus von der Malsburg" . "malsburg@posteo.de")) (:maintainer "Titus von der Malsburg" . "malsburg@posteo.de") (:url . "https://github.com/emacs-helm/helm-mu"))]) + (helm-mu . [(20220825 1025) ((helm (1 5 5))) "Helm sources for searching emails and contacts." single ((:commit . "85714ac9a9db6619035c40f5b5a2cae948561b01") (:authors ("Titus von der Malsburg" . "malsburg@posteo.de")) (:maintainer "Titus von der Malsburg" . "malsburg@posteo.de") (:url . "https://github.com/emacs-helm/helm-mu"))]) (helm-navi . [(20201220 1823) ((emacs (24 4)) (helm (1 9 4)) (helm-org (1 0)) (navi-mode (2 0)) (s (1 10 0))) "Helm for navi-mode" single ((:commit . "c5666cc171288d1fa892900ee66fba2a1c892c81") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "navigation" "outlines") (:url . "http://github.com/emacs-helm/helm-navi"))]) (helm-nixos-options . [(20151013 2309) ((nixos-options (0 0 1)) (helm (1 5 6))) "Helm Interface for nixos-options" single ((:commit . "053a2d5110ce05b7f99bcc2ac4804b70cbe87916") (:authors ("Diego Berrocal" . "cestdiego@gmail.com") ("Travis B. Hartwell" . "nafai@travishartwell.net")) (:maintainer "Diego Berrocal" . "cestdiego@gmail.com") (:keywords "unix") (:url . "http://www.github.com/travisbhartwell/nix-emacs/"))]) (helm-notmuch . [(20190320 1048) ((helm (1 9 3)) (notmuch (0 21))) "Search emails with Notmuch and Helm" single ((:commit . "97a01497e079a7b6505987e9feba6b603bbec288") (:authors ("Chunyang Xu" . "mail@xuchunyang.me")) (:maintainer "Chunyang Xu" . "mail@xuchunyang.me") (:keywords "mail") (:url . "https://github.com/emacs-helm/helm-notmuch"))]) (helm-open-github . [(20170220 159) ((emacs (24 4)) (helm-core (1 7 7)) (gh (0 8 2))) "Utilities of Opening Github Page" single ((:commit . "2f03d97552a1233db7694116d5f80ecde7612756") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-helm-open-github"))]) (helm-org . [(20210324 1927) ((helm (3 3)) (emacs (24 4))) "Helm for org headlines and keywords completion" single ((:commit . "d67186d3a64e610c03a5f3d583488f018fb032e4") (:authors ("Thierry Volpiatto" . "thierry.volpiatto@gmail.com")) (:maintainer "Thierry Volpiatto" . "thierry.volpiatto@gmail.com") (:url . "https://github.com/emacs-helm/helm-org"))]) (helm-org-multi-wiki . [(20210228 1853) ((emacs (26 1)) (org (9 3)) (org-multi-wiki (0 4)) (org-ql (0 5)) (dash (2 18)) (helm-org-ql (0 5)) (helm (3 5))) "Helm interface to org-multi-wiki" single ((:commit . "bf8039aadddaf02569fab473f766071ef7e63563") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "org" "outlines") (:url . "https://github.com/akirak/org-multi-wiki"))]) - (helm-org-ql . [(20220318 1529) ((emacs (26 1)) (dash (2 18 1)) (s (1 12 0)) (helm-org (1 0)) (org-ql (0 6 -1))) "Helm support for org-ql" single ((:commit . "06f1e1be6ff5ef7e2c8c05dc1954bcedcbb6eb0b") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:url . "https://github.com/alphapapa/org-ql"))]) + (helm-org-ql . [(20220318 1529) ((emacs (26 1)) (dash (2 18 1)) (s (1 12 0)) (helm-org (1 0)) (org-ql (0 6 -1))) "Helm support for org-ql" single ((:commit . "d7ada532c7d06e91d6e07800ca22d5fbdb970e3e") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:url . "https://github.com/alphapapa/org-ql"))]) (helm-org-recent-headings . [(20211011 1519) ((emacs (26 1)) (org (9 0 5)) (dash (2 18 0)) (helm (1 9 4)) (org-recent-headings (0 2 -1)) (s (1 12 0))) "Helm source for org-recent-headings" single ((:commit . "97418d581ea030f0718794e50b005e9bae44582e") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org") (:url . "http://github.com/alphapapa/org-recent-headings"))]) (helm-org-rifle . [(20200512 1943) ((emacs (24 4)) (dash (2 12)) (f (0 18 1)) (helm (1 9 4)) (s (1 10 0))) "Rifle through your Org files" single ((:commit . "5e13a0e59606b40088927870dab116a8eab8e66c") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines") (:url . "http://github.com/alphapapa/helm-org-rifle"))]) (helm-orgcard . [(20220721 756) ((helm-core (3 6 0))) "browse the orgcard by helm" single ((:commit . "d58d35627bb1714bb2cb095f696706b6881233ed") (:authors ("Yuhei Maeda ")) (:maintainer "Yuhei Maeda") (:keywords "convenience" "helm" "org") (:url . "https://github.com/emacs-jp/helm-orgcard"))]) @@ -2227,7 +2232,7 @@ (helm-posframe . [(20211103 236) ((emacs (26 0)) (posframe (1 0 0)) (helm (0 1))) "Using posframe to show helm window" single ((:commit . "87461b52b6f3f378c63642a33f584d4a4ba28351") (:authors ("Feng Shu")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "abbrev" "convenience" "matching" "helm") (:url . "https://github.com/tumashu/helm-posframe"))]) (helm-proc . [(20161006 305) ((helm (1 6 0))) "Helm interface for managing system processes" tar ((:commit . "576d31c2d74ba3897d56e2acd2b0993f52c2547c") (:authors ("Markus Hauck" . "markus1189@gmail.com")) (:maintainer "Markus Hauck" . "markus1189@gmail.com") (:keywords "helm"))]) (helm-project-persist . [(20151210 1543) ((helm (1 5 2)) (project-persist (0 1 4))) "Helm integration for project-persist package" single ((:commit . "357950fbac18090985a750e40d5d8b10ee9dcd53") (:authors ("Sliim" . "sliim@mailoo.org")) (:maintainer "Sliim" . "sliim@mailoo.org") (:keywords "project-persist" "project" "helm"))]) - (helm-projectile . [(20201217 908) ((helm (1 9 9)) (projectile (2 2 0)) (cl-lib (0 3))) "Helm integration for Projectile" single ((:commit . "58123f14c392021714fc5d23b9f95c7f95ce07f1") (:authors ("Bozhidar Batsov")) (:maintainer "Bozhidar Batsov") (:keywords "project" "convenience") (:url . "https://github.com/bbatsov/helm-projectile"))]) + (helm-projectile . [(20220820 826) ((helm (1 9 9)) (projectile (2 2 0)) (cl-lib (0 3))) "Helm integration for Projectile" single ((:commit . "5813f7286533990783546c9c39c184faa034d1f1") (:authors ("Bozhidar Batsov")) (:maintainer "Bozhidar Batsov") (:keywords "project" "convenience") (:url . "https://github.com/bbatsov/helm-projectile"))]) (helm-prosjekt . [(20140129 717) ((prosjekt (0 3)) (helm (1 5 9))) "Helm integration for prosjekt." single ((:commit . "a864a8be5842223043702395f311e3350c28e9db") (:authors ("Sohail Somani" . "sohail@taggedtype.net")) (:maintainer "Sohail Somani" . "sohail@taggedtype.net") (:url . "https://github.com/abingham/prosjekt"))]) (helm-pt . [(20160214 2342) ((helm (1 5 6))) "Helm interface to the platinum searcher" tar ((:commit . "8acc52911dad1ed0c3975f134a468762afe0b76b") (:authors ("Rich Alesi")) (:maintainer "Rich Alesi") (:url . "https://github.com/ralesi/helm-pt"))]) (helm-purpose . [(20170114 1636) ((emacs (24)) (helm (1 9 2)) (window-purpose (1 4))) "Helm Interface for Purpose" single ((:commit . "9ff4c21c1e9ebc7afb851b738f815df7343bb287") (:authors ("Bar Magal (2016)")) (:maintainer "Bar Magal (2016)") (:url . "https://github.com/bmag/helm-purpose"))]) @@ -2242,12 +2247,12 @@ (helm-rhythmbox . [(20160524 1158) ((helm (1 5 0)) (cl-lib (0 5))) "control Rhythmbox's play queue via Helm" single ((:commit . "c92e1ded34ddd4e62e7e9a558259c232e05193fa") (:authors ("Thomas Winant" . "dewinant@gmail.com")) (:maintainer "Thomas Winant" . "dewinant@gmail.com") (:url . "https://github.com/mrBliss/helm-rhythmbox"))]) (helm-robe . [(20151209 355) ((helm (1 7 7))) "completing read function for robe" single ((:commit . "6e69543b4ee76c5f8f3f2510c76e6d9aed17a370") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-helm-robe"))]) (helm-ros . [(20160812 1752) ((helm (1 9 9)) (xterm-color (1 0)) (cl-lib (0 5))) "Interfaces ROS with helm" single ((:commit . "92b0b215f6a017f0f57f1af15466cc0b2a5a0135") (:authors ("David Landry" . "davidlandry93@gmail.com")) (:maintainer "David Landry" . "davidlandry93@gmail.com") (:keywords "helm" "ros") (:url . "https://www.github.com/davidlandry93/helm-ros"))]) - (helm-rtags . [(20191222 920) ((helm (2 0)) (rtags (2 10))) "A front-end for rtags" single ((:commit . "c628efc9b485470a48aec2692d79f7c140bc5b92") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) + (helm-rtags . [(20191222 920) ((helm (2 0)) (rtags (2 10))) "A front-end for rtags" single ((:commit . "b9c680e7ca003c103687e790f740d86daa6b4b17") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) (helm-rubygems-local . [(20130712 111) ((helm (1 5 3))) "Installed local rubygems find-file for helm" single ((:commit . "289cb33d41c703af9791d6da46b55f070013c2e3") (:authors ("hadashiA" . "dev@hadashikick.jp")) (:maintainer "hadashiA" . "dev@hadashikick.jp") (:url . "https://github.com/f-kubotar/helm-rubygems-local"))]) (helm-rubygems-org . [(20140826 1156) ((emacs (24)) (helm (1 6 3)) (cl-lib (0 5))) "Use helm to search rubygems.org" single ((:commit . "6aaed984f698cbdf9f9aceb0221404563e28764d") (:authors ("Chad Albers" . "calbers@neomantic.com")) (:maintainer "Chad Albers" . "calbers@neomantic.com") (:keywords "ruby" "rubygems" "gemfile" "helm") (:url . "https://github.com/neomantic/helm-rubygems-org"))]) (helm-safari . [(20160404 324) ((helm (1 9 1)) (emacs (24))) "Browse your Safari bookmarks and history" single ((:commit . "664c7f4488829228eed7e90cd53002e14bec555b") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "tools") (:url . "https://github.com/xuchunyang/helm-safari"))]) (helm-sage . [(20160514 745) ((cl-lib (0 5)) (helm (1 5 6)) (sage-shell-mode (0 1 0))) "A helm extension for sage-shell-mode." single ((:commit . "f14e9281d8f2162df7d8f9c2ad9ad1248a24803b") (:authors ("Sho Takemori" . "stakemorii@gmail.com")) (:maintainer "Sho Takemori" . "stakemorii@gmail.com") (:keywords "sage" "math" "helm") (:url . "https://github.com/stakemori/helm-sage"))]) - (helm-searcher . [(20220704 634) ((emacs (25 1)) (helm (2 0)) (searcher (0 1 8)) (s (1 12 0)) (f (0 20 0))) "Helm interface to use searcher" single ((:commit . "521326e61b878bce0db7e1a862ffc890bd59122f") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "replace" "grep" "ag" "rg") (:url . "https://github.com/emacs-helm/helm-searcher"))]) + (helm-searcher . [(20220704 634) ((emacs (25 1)) (helm (2 0)) (searcher (0 1 8)) (s (1 12 0)) (f (0 20 0))) "Helm interface to use searcher" single ((:commit . "4219ee9e6d173caef7f8a4a586966b054f55d8e2") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "replace" "grep" "ag" "rg") (:url . "https://github.com/emacs-helm/helm-searcher"))]) (helm-selected . [(20171223 210) ((emacs (24 4)) (helm (2 8 6)) (selected (1 1))) "helm extension for selected.el" single ((:commit . "a9c769998bc56373d19f0ec9cbbbb4bd89a43c2d") (:authors ("Takaaki ISHIKAWA ")) (:maintainer "Takaaki ISHIKAWA ") (:keywords "extensions" "convenience") (:url . "https://github.com/takaxp/helm-selected"))]) (helm-selector . [(20210125 857) ((emacs (26 1)) (helm (3))) "Helm buffer selector" tar ((:commit . "4da4711c4cfd14527abe20d66787beeb49171b26") (:authors ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainer "Pierre Neidhardt" . "mail@ambrevar.xyz") (:url . "https://github.com/emacs-helm/helm-selector"))]) (helm-sheet . [(20130630 1239) ((helm (1 0))) "helm sources for sheet" single ((:commit . "d360b68d0ddb09aa1854e7b2f3cb39caeee26463") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainer "Yasuyuki Oka" . "yasuyk@gmail.com") (:keywords "helm" "sheet") (:url . "https://github.com/yasuyk/helm-sheet"))]) @@ -2332,6 +2337,7 @@ (hl-block-mode . [(20220731 2352) ((emacs (26 1))) "Highlighting nested blocks" single ((:commit . "a9e8e8cfd83972a448bdbb0a9967989d0aa1d05a") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-hl-block-mode"))]) (hl-fill-column . [(20200607 757) ((names (0 5)) (emacs (24))) "Highlight fill column." single ((:commit . "5782a91ba0182c4e562fa0db6379ff9dd472856b") (:keywords "fill column" "faces") (:url . "https://github.com/laishulu/hl-fill-column"))]) (hl-indent . [(20170429 2104) ((emacs (24)) (cl-lib (0 5))) "Highlight irregular indentation." single ((:commit . "bdb2e0177a7c8b29af26998e688b856adc6ded93") (:authors ("Kirill Ignatiev ")) (:maintainer "Kirill Ignatiev ") (:keywords "convenience" "faces") (:url . "https://github.com/ikirill/hl-indent"))]) + (hl-indent-scope . [(20220816 426) ((emacs (26 1))) "Highlight indentation by scope" tar ((:commit . "243b6e4202502439f39ab2a485e4ecf8ea50edbe") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-hl-indent-scope"))]) (hl-prog-extra . [(20220731 2353) ((emacs (26 2))) "Customizable highlighting for source-code" tar ((:commit . "6d419cc36936f3bb3c9c63a6f77cc88a80b0db9c") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "convenience") (:url . "https://codeberg.org/ideasman42/emacs-hl-prog-extra"))]) (hl-sentence . [(20171018 1519) nil "highlight a sentence based on customizable face" single ((:commit . "86ae38d3103bd20da5485cbdd59dfbd396c45ee4") (:authors ("Donald Ephraim Curtis" . "dcurtis@milkbox.net")) (:maintainer "Donald Ephraim Curtis" . "dcurtis@milkbox.net") (:keywords "highlighting") (:url . "http://github.com/milkypostman/hl-sentence"))]) (hl-todo . [(20220422 1611) ((emacs (25 1)) (compat (28 1 1 0))) "Highlight TODO and similar keywords" single ((:commit . "2337eac8cab0d4b73a96fb3936d2ac87600e3c91") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "https://github.com/tarsius/hl-todo"))]) @@ -2354,7 +2360,7 @@ (hover . [(20220129 1935) ((emacs (25 2)) (dash (2 14 1))) "Package to use hover with flutter" single ((:commit . "4ca0638a14a8b304ac2b46e7b342b8d8732ad199") (:authors ("Eric Dallo")) (:maintainer "Eric Dallo") (:keywords "hover" "flutter" "mobile" "tools") (:url . "https://github.com/ericdallo/hover.el"))]) (howdoi . [(20150204 43) nil "Instant coding answers via Emacs." tar ((:commit . "5fbf7069ee160c597a328e5ce5fb32920e1ca88f") (:authors ("Andrey Tykhonov ")) (:maintainer "Andrey Tykhonov" . "atykhonov@gmail.com") (:keywords "howdoi" "convenience") (:url . "https://github.com/atykhonov/emacs-howdoi/"))]) (howdoyou . [(20220715 1720) ((emacs (25 1)) (promise (1 1)) (request (0 3 3)) (org (9 2))) "A stackoverflow and its sisters' sites reader" single ((:commit . "f6c659a45f59a08546578c169524a12f0945c29b") (:authors ("Thanh Vuong" . "thanhvg@gmail.com")) (:maintainer "Thanh Vuong" . "thanhvg@gmail.com") (:url . "https://github.com/thanhvg/howdoyou/"))]) - (howm . [(20211230 1221) ((cl-lib (0 5))) "Wiki-like note-taking tool" tar ((:commit . "c381e50f0c771c38306bda37bd972a37a36a5db5") (:authors ("HIRAOKA Kazuyuki" . "khi@users.osdn.me")) (:maintainer "HIRAOKA Kazuyuki" . "khi@users.osdn.me") (:url . "https://howm.osdn.jp"))]) + (howm . [(20220815 1125) ((cl-lib (0 5))) "Wiki-like note-taking tool" tar ((:commit . "9982e74e212014bc8ed67bbe780eb6ca55dc5298") (:authors ("HIRAOKA Kazuyuki" . "khi@users.osdn.me")) (:maintainer "HIRAOKA Kazuyuki" . "khi@users.osdn.me") (:url . "https://howm.osdn.jp"))]) (hsluv . [(20181127 1206) ((seq (2 20))) "hsluv color space conversions" single ((:commit . "c3bc5228e30d66e7dee9ff1a0694c2b976862fc0") (:authors ("Geert Vermeiren")) (:maintainer "Geert Vermeiren") (:keywords "color" "hsluv") (:url . "https://github.com/hsluv/hsluv-emacs"))]) (ht . [(20210119 741) ((dash (2 12 0))) "The missing hash table library for Emacs" single ((:commit . "c4c1be487d6ecb353d07881526db05d7fc90ea87") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "hash table" "hash map" "hash"))]) (html-check-frag . [(20201106 1748) ((emacs (24 3))) "Check html-fragments" single ((:commit . "b9d1f2003a126c2e8b6d469964ec2278ad55c9df") (:authors ("Tobias.Zawada" . "i@tn-home.de")) (:maintainer "Tobias.Zawada" . "i@tn-home.de") (:keywords "html"))]) @@ -2363,7 +2369,7 @@ (html-to-markdown . [(20151105 840) ((cl-lib (0 5))) "HTML to Markdown converter written in Emacs-lisp." single ((:commit . "60c5498c801be186478cf7c05be05b4430c4a144") (:authors ("Artur Malabarba" . "bruce.connor.am@gmail.com")) (:maintainer "Artur Malabarba" . "bruce.connor.am@gmail.com") (:keywords "tools" "wp" "languages") (:url . "http://github.com/Bruce-Connor/html-to-markdown"))]) (html2org . [(20170418 501) ((emacs (24 4))) "Convert html to org format text" single ((:commit . "6904aed40259ad8afccff079ebd8a07bff319ebc") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "html" "org") (:url . "http://github.com/lujun9972/html2org.el"))]) (htmlize . [(20210825 2150) nil "Convert buffer text and decorations to HTML." single ((:commit . "dd27bc3f26efd728f2b1f01f9e4ac4f61f2ffbf9") (:authors ("Hrvoje Niksic" . "hniksic@gmail.com")) (:maintainer "Hrvoje Niksic" . "hniksic@gmail.com") (:keywords "hypermedia" "extensions") (:url . "https://github.com/hniksic/emacs-htmlize"))]) - (htmltagwrap . [(20220704 652) ((emacs (24 4))) "Wraps a chunk of HTML code in tags" single ((:commit . "aeabbb7606e033bccbb70d2a1c463ba8a733a4e9") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:url . "https://github.com/jcs-elpa/htmltagwrap"))]) + (htmltagwrap . [(20220704 652) ((emacs (24 4))) "Wraps a chunk of HTML code in tags" single ((:commit . "31ebfdaa333c6ef7d5d249de9d859030bfb3cb82") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:url . "https://github.com/jcs-elpa/htmltagwrap"))]) (http . [(20201010 920) ((emacs (24 4)) (request (0 2 0)) (edit-indirect (0 1 4))) "Yet another HTTP client" single ((:commit . "5fdceed1fbf36e274e578e349a53ce922c574774") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "convenience") (:url . "https://github.com/emacs-pe/http.el"))]) (http-post-simple . [(20170715 940) nil "HTTP POST requests using the url library" single ((:commit . "f53697fca278c741051aeb668b00466b5e0fd3fe") (:authors ("Tom Schutzer-Weissmann")) (:maintainer "Tom Schutzer-Weissmann") (:keywords "comm" "data" "processes" "hypermedia"))]) (http-twiddle . [(20160801 1911) nil "send & twiddle & resend HTTP requests" single ((:commit . "4d0c73b7dcbde8b483d4f3a75c49c74d2fe3ca45") (:authors ("Luke Gorrie" . "luke@synap.se")) (:maintainer "Hasan Veldstra" . "h@vidiowiki.com") (:keywords "http" "rest" "soap") (:url . "https://github.com/hassy/http-twiddle/blob/master/http-twiddle.el"))]) @@ -2376,7 +2382,7 @@ (hungry-delete . [(20210409 1643) nil "hungry delete minor mode" single ((:commit . "d919e555e5c13a2edf4570f3ceec84f0ade71657") (:authors ("Nathaniel Flath" . "flat0103@gmail.com")) (:maintainer "Nathaniel Flath" . "flat0103@gmail.com") (:url . "http://github.com/nflath/hungry-delete"))]) (hy-mode . [(20211016 2011) ((dash (2 18 0)) (s (1 11 0)) (emacs (24))) "Major mode for Hylang" tar ((:commit . "df814865a1faa8414dacdbb35b2a9029995312ec") (:keywords "languages" "lisp" "python") (:url . "http://github.com/hylang/hy-mode"))]) (hyai . [(20170301 1447) ((cl-lib (0 5)) (emacs (24))) "Haskell Yet Another Indentation" single ((:commit . "9efad2ac6a57059b3be624588f649e276a96fdd4") (:authors ("Iku Iwasa" . "iku.iwasa@gmail.com")) (:maintainer "Iku Iwasa" . "iku.iwasa@gmail.com") (:url . "https://github.com/iquiw/hyai"))]) - (hybrid-reverse-theme . [(20210806 1955) ((emacs (24 1))) "Emacs theme with material color scheme" single ((:commit . "cb784a69e60938efe14b48130558f1bb1af92d3c") (:authors ("Riyyi")) (:maintainer "Riyyi") (:keywords "faces" "theme") (:url . "https://github.com/riyyi/emacs-hybrid-reverse"))]) + (hybrid-reverse-theme . [(20220807 2029) ((emacs (24 1))) "Emacs theme with material color scheme" single ((:commit . "4b5da51c78b319e16ada6b431bddbacb61d5f2c5") (:authors ("Riyyi")) (:maintainer "Riyyi") (:keywords "faces" "theme") (:url . "https://github.com/riyyi/emacs-hybrid-reverse"))]) (hydandata-light-theme . [(20190809 1925) nil "A light color theme that is easy on your eyes" single ((:commit . "180c3797fa7ef3e4bb679baaf5b492c33bbb9b8b") (:authors ("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 . [(20220102 803) ((cl-lib (0 5)) (lv (0))) "Make bindings that stick around." tar ((:commit . "9e9e00cb240ea1903ffd36a54956b3902c379d29") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "bindings") (:url . "https://github.com/abo-abo/hydra"))]) @@ -2385,6 +2391,7 @@ (hyperspace . [(20210603 1825) ((emacs (25)) (s (1 12 0))) "Get there from here" single ((:commit . "c4c363c140250ba6b775516082063878975a6154") (:authors ("Ian Eure" . "ian@retrospec.tv")) (:maintainer "Ian Eure" . "ian@retrospec.tv") (:keywords "tools" "convenience") (:url . "https://github.com/ieure/hyperspace-el"))]) (i-ching . [(20220619 817) ((emacs (25 1)) (request (0 3))) "The Book of Changes" tar ((:commit . "54f19e2dcb1d16735b94fc7e06a2aa8b1b6f165a") (:authors ("nik gaffney" . "nik@fo.am")) (:maintainer "nik gaffney" . "nik@fo.am") (:keywords "games" "divination" "stochastism" "cleromancy" "change") (:url . "https://github.com/zzkt/i-ching"))]) (i2b2-mode . [(20140710 104) nil "Highlights corresponding PHI data in the text portion of an i2b2 XML Document." single ((:commit . "db10efcfc8bed369a516bbf7526ede41f98cb95a") (:authors ("Dan LaManna" . "dan.lamanna@gmail.com")) (:maintainer "Dan LaManna" . "dan.lamanna@gmail.com") (:keywords "xml" "phi" "i2b2" "deidi2b2"))]) + (i3bar . [(20220808 1551) ((emacs (28 1))) "Display status from an i3status command in the tab bar" single ((:commit . "7c182fef33578ae32f945758123601396de227d0") (:authors ("Steven Allen" . "steven@stebalien.com")) (:maintainer "Steven Allen" . "steven@stebalien.com") (:keywords "unix") (:url . "https://github.com/Stebalien/i3bar.el"))]) (i3wm . [(20170822 1438) nil "i3wm integration library" single ((:commit . "71391dc61063fee77ad174f3b2ca25c60b41009e") (:authors ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Samuel W. Flint" . "swflint@flintfam.org") (:keywords "convenience" "extensions") (:url . "https://git.flintfam.org/swf-projects/emacs-i3"))]) (i3wm-config-mode . [(20220617 1339) ((emacs (24 1))) "Better syntax highlighting for i3wm's config file" single ((:commit . "3574d88241118ed6cc5a3022b6dde58d6f5af9dd") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:keywords "faces" "languages" "i3wm" "font-lock") (:url . "https://github.com/Alexander-Miller/i3wm-Config-Mode"))]) (ialign . [(20220629 1241) ((emacs (24 4))) "visual align-regexp" single ((:commit . "bc4d30d79f2f4b413288195ef19894ac0fd258b7") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "tools" "editing" "align" "interactive") (:url . "https://github.com/mkcms/interactive-align"))]) @@ -2447,12 +2454,12 @@ (immortal-scratch . [(20160517 2118) nil "respawn the scratch buffer when it's killed" single ((:commit . "faeab0ad6c33c74c0cbd1dfcebffaa0690de40c6") (:authors ("Jonathan Kotta" . "jpkotta@gmail.com")) (:maintainer "Jonathan Kotta" . "jpkotta@gmail.com"))]) (immutant-server . [(20140311 2208) nil "Run your Immutant server in Emacs" single ((:commit . "2a21e65588acb6a976f2998e30b21fdabdba4dbb") (:authors ("David Leatherman" . "leathekd@gmail.com")) (:maintainer "David Leatherman" . "leathekd@gmail.com") (:url . "http://www.github.com/leathekd/immutant-server.el"))]) (impatient-mode . [(20200723 2117) ((emacs (24 3)) (simple-httpd (1 5 0)) (htmlize (1 40))) "Serve buffers live over HTTP" tar ((:commit . "479a2412596ff1dbdddeb7bdbba45482ce5b230c") (:authors ("Brian Taylor" . "el.wubo@gmail.com")) (:maintainer "Brian Taylor" . "el.wubo@gmail.com") (:url . "https://github.com/netguy204/imp.el"))]) - (impatient-showdown . [(20220730 1259) ((emacs (24 3)) (impatient-mode (1 1))) "Preview markdown buffer live over HTTP using showdown" tar ((:commit . "501d8eb255022d832bb30178db154d0004eac18c") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "live" "preview" "markdown" "http" "server" "impatient") (:url . "https://github.com/jcs-elpa/impatient-showdown"))]) + (impatient-showdown . [(20220730 1259) ((emacs (24 3)) (impatient-mode (1 1))) "Preview markdown buffer live over HTTP using showdown" tar ((:commit . "3d07d3377d8e5cec93f113d57e656f76d2f52afe") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "live" "preview" "markdown" "http" "server" "impatient") (:url . "https://github.com/jcs-elpa/impatient-showdown"))]) (import-js . [(20220215 1948) ((grizzl (0 1 0)) (emacs (24))) "Import Javascript dependencies" single ((:commit . "d2bbb53f96395415f9f01de4fa88d82c1f59ba63") (:authors ("Kevin Kehl" . "kevin.kehl@gmail.com")) (:maintainer "Kevin Kehl" . "kevin.kehl@gmail.com") (:keywords "javascript") (:url . "http://github.com/Galooshi/emacs-import-js/"))]) (import-popwin . [(20170218 1407) ((emacs (24 3)) (popwin (0 6))) "popwin buffer near by import statements with popwin" single ((:commit . "bb05a9e226f8c63fe7b18a3e92010357049ab5ba") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-import-popwin"))]) (importmagic . [(20180520 303) ((f (0 11 0)) (epc (0 1 0)) (emacs (24 3))) "Fix Python imports using importmagic." tar ((:commit . "570fb4f519d5e84dd681f932447cb995e8460840") (:authors ("Nicolás Salas V." . "nikosalas@gmail.com")) (:maintainer "Nicolás Salas V." . "nikosalas@gmail.com") (:keywords "languages" "convenience") (:url . "https://github.com/anachronic/importmagic.el"))]) - (impostman . [(20220102 1856) ((emacs (27 1))) "Import Postman collections" single ((:commit . "5b122f3d5a3421aa2d89bdc9dc4aafaf19cf85d4") (:authors ("Sébastien Helleu" . "flashcode@flashtux.org")) (:maintainer "Sébastien Helleu" . "flashcode@flashtux.org") (:keywords "tools") (:url . "https://github.com/flashcode/impostman"))]) - (indent-control . [(20220704 652) ((emacs (26 1))) "Management for indentation level" single ((:commit . "9d4474980f6955187a0800fd1f5669e3ba291915") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "control" "indent" "tab" "generic" "level") (:url . "https://github.com/jcs-elpa/indent-control"))]) + (impostman . [(20220818 1845) ((emacs (27 1))) "Import Postman collections" single ((:commit . "16ee00e35e9ee82bb06c6feafb148d98165f822f") (:authors ("Sébastien Helleu" . "flashcode@flashtux.org")) (:maintainer "Sébastien Helleu" . "flashcode@flashtux.org") (:keywords "tools") (:url . "https://github.com/flashcode/impostman"))]) + (indent-control . [(20220704 652) ((emacs (26 1))) "Management for indentation level" single ((:commit . "3f992c8d4bfcdd92a936621058f677ee0a0ef5a9") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "control" "indent" "tab" "generic" "level") (:url . "https://github.com/jcs-elpa/indent-control"))]) (indent-guide . [(20210115 400) nil "show vertical lines to guide indentation" single ((:commit . "d388c3387781a370ca13233ff445d03f3c5cf12f") (:authors ("zk_phi")) (:maintainer "zk_phi") (:url . "http://hins11.yu-yake.com/"))]) (indent-info . [(20210111 745) ((emacs (24 3))) "Show indentation information in status bar" single ((:commit . "05a787afeb9946714d8b0c724868195a678db49e") (:authors ("Terje Larsen" . "terlar@gmail.com")) (:maintainer "Terje Larsen" . "terlar@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/terlar/indent-info.el"))]) (indent-lint . [(20200812 949) ((emacs (25 1)) (async-await (1 0)) (async (1 9 4))) "Async indentation checker" single ((:commit . "c55f4ded11e8e50a96f43675a071354a8fb501c3") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "tools") (:url . "https://github.com/conao3/indent-lint.el"))]) @@ -2461,11 +2468,11 @@ (indicators . [(20161211 1126) ((dash (2 13 0)) (cl-lib (0 5 0))) "Display the buffer relative location of line in the fringe." single ((:commit . "f62a1201f21453e3aca93f48483e65ae8251432e") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "fringe" "frames") (:url . "https://github.com/Fuco1/indicators.el"))]) (indium . [(20210309 1210) ((emacs (25)) (seq (2 16)) (js2-mode (20140114)) (js2-refactor (0 9 0)) (company (0 9 0)) (json-process-client (0 2 0))) "JavaScript Awesome Development Environment" tar ((:commit . "8499e156bf7286846c3a2bf8c9e0c4d4f24b224c") (:authors ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr") (:keywords "tools" "javascript") (:url . "https://github.com/NicolasPetton/indium"))]) (indy . [(20190807 625) nil "A minor mode and EDSL to manage your mode's indentation rules." single ((:commit . "abc5bee424780ad2de5520f8fefbf8e120c0d9ed") (:authors ("Kevin W. van Rooijen" . "kevin.van.rooijen@attichacker.com")) (:maintainer "Kevin W. van Rooijen" . "kevin.van.rooijen@attichacker.com") (:keywords "convenience" "matching" "tools"))]) - (inf-clojure . [(20220723 1536) ((emacs (25 1)) (clojure-mode (5 11))) "Run an external Clojure process in an Emacs buffer" single ((:commit . "f436760489cd95400b5a5449158161031beac7ba") (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "processes" "comint" "clojure") (:url . "http://github.com/clojure-emacs/inf-clojure"))]) + (inf-clojure . [(20220807 2113) ((emacs (25 1)) (clojure-mode (5 11))) "Run an external Clojure process in an Emacs buffer" single ((:commit . "59a9f0695f3d97a593f8d5ea04b51ea5dcb2718a") (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "processes" "comint" "clojure") (:url . "http://github.com/clojure-emacs/inf-clojure"))]) (inf-crystal . [(20180119 211) ((emacs (24 3)) (crystal-mode (0 1 0))) "Run a Inferior-Crystal process in a buffer" single ((:commit . "02007b2a2a3bea44902d7c83c4acba1e39d278e3") (:authors ("Brantou" . "brantou89@gmail.com")) (:maintainer "Brantou" . "brantou89@gmail.com") (:keywords "languages" "crystal") (:url . "https://github.com/brantou/inf-crystal.el"))]) (inf-elixir . [(20220721 1939) ((emacs (25 1))) "Run an interactive Elixir shell" single ((:commit . "5b45f5bd346446d87c629794b3c3e586c3eefd9c") (:authors ("Jonathan Arnett" . "jonathan.arnett@protonmail.com")) (:maintainer "Jonathan Arnett" . "jonathan.arnett@protonmail.com") (:keywords "languages" "processes" "tools") (:url . "https://github.com/J3RN/inf-elixir"))]) (inf-mongo . [(20180408 1338) nil "Run a MongoDB shell process in a buffer" single ((:commit . "2e498d1c88bd1904eeec18ed06b1a0cf8bdc2a92") (:authors ("Tobias Svensson")) (:maintainer "Tobias Svensson") (:keywords "databases" "mongodb") (:url . "http://github.com/endofunky/inf-mongo"))]) - (inf-ruby . [(20220804 18) ((emacs (24 3))) "Run a Ruby process in a buffer" single ((:commit . "7dfc779dc6038125c516c7c7746994a54b96e409") (:authors ("Yukihiro Matsumoto") ("Nobuyoshi Nakada") ("Cornelius Mika" . "cornelius.mika@gmail.com") ("Dmitry Gutov" . "dgutov@yandex.ru") ("Kyle Hargraves" . "pd@krh.me")) (:maintainer "Dmitry Gutov" . "dgutov@yandex.ru") (:keywords "languages" "ruby") (:url . "http://github.com/nonsequitur/inf-ruby"))]) + (inf-ruby . [(20220811 949) ((emacs (24 3))) "Run a Ruby process in a buffer" single ((:commit . "eb7bf95d5b03bdb9b28647c89ea0a73e35fe0ad1") (:authors ("Yukihiro Matsumoto") ("Nobuyoshi Nakada") ("Cornelius Mika" . "cornelius.mika@gmail.com") ("Dmitry Gutov" . "dgutov@yandex.ru") ("Kyle Hargraves" . "pd@krh.me")) (:maintainer "Dmitry Gutov" . "dgutov@yandex.ru") (:keywords "languages" "ruby") (:url . "http://github.com/nonsequitur/inf-ruby"))]) (inflections . [(20210110 2237) ((cl-lib (0 5)) (emacs (24))) "convert english words between singular and plural" single ((:commit . "55caa66a7cc6e0b1a76143fd40eff38416928941") (:authors ("Dmitry Galinsky, Howard Yeh")) (:maintainer "Dmitry Galinsky, Howard Yeh") (:keywords "languages" "tools" "wp") (:url . "https://github.com/eschulte/jump.el"))]) (info-beamer . [(20210427 1033) ((emacs (24 4))) "Utilities for working with info-beamer" single ((:commit . "6b4cc29f1aec72d8e23b2c25a99cdd84e6cdc92b") (:authors ("Daniel Kraus" . "daniel@kraus.my")) (:maintainer "Daniel Kraus" . "daniel@kraus.my") (:keywords "tools" "processes" "comm") (:url . "https://github.com/dakra/info-beamer.el"))]) (info-buffer . [(20170112 1422) nil "Display info topics in separate buffers" single ((:commit . "d35dad6e766c6e2ddb8dc6acb4ce5b6e10fbcaa7") (:authors ("Lluís Vilanova" . "vilanova@ac.upc.edu")) (:maintainer "Lluís Vilanova" . "vilanova@ac.upc.edu") (:keywords "docs" "info") (:url . "http://www.github.com/llvilanova/info-buffer"))]) @@ -2480,7 +2487,7 @@ (init-open-recentf . [(20220220 2004) ((emacs (24 4))) "Invoke a command immediately after startup" single ((:commit . "51463effe54ca9390ec339b9678968f35a40dbfd") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "files" "recentf" "after-init-hook") (:url . "https://github.com/zonuexe/init-open-recentf.el"))]) (initsplit . [(20160919 1818) nil "code to split customizations into different files" single ((:commit . "c941d436eb2b10b01c76a582c5a2b23fb30751aa") (:authors ("John Wiegley , Dave Abrahams" . "dave@boostpro.com")) (:maintainer "John Wiegley , Dave Abrahams" . "dave@boostpro.com") (:keywords "lisp") (:url . "http://www.gci-net.com/users/j/johnw/emacs.html"))]) (ink-mode . [(20201105 2242) ((emacs (26 1))) "Major mode for writing interactive fiction in Ink" tar ((:commit . "63c7ef39acf434a1682951bcf352e8fe1e1ac6d9") (:authors ("Erik Sjöstrand") ("Damien Picard")) (:maintainer "Damien Picard") (:keywords "languages" "wp" "hypermedia") (:url . "https://github.com/Kungsgeten/ink-mode"))]) - (inkpot-theme . [(20220731 435) ((emacs (24 1))) "A port of vim's inkpot theme" single ((:commit . "a8a09d1c4f9044f89b480fcfec9cc9cd868190e5") (:authors ("Sarah Iovan" . "sarah@hwaetageek.com") ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Sarah Iovan" . "sarah@hwaetageek.com") (:url . "https://codeberg.org/ideasman42/emacs-inkpot-theme"))]) + (inkpot-theme . [(20220819 28) ((emacs (24 1))) "A port of vim's inkpot theme" single ((:commit . "4a488ddf2cd47949ee627af03743f66ee91177cd") (:authors ("Sarah Iovan" . "sarah@hwaetageek.com") ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Sarah Iovan" . "sarah@hwaetageek.com") (:url . "https://codeberg.org/ideasman42/emacs-inkpot-theme"))]) (inline-crypt . [(20170824 900) nil "Simple inline encryption via openssl" tar ((:commit . "281385b383f850fd2e895926b1cef804dd052633") (:authors ("Daniel Ralston" . "Wubbulous@gmail.com")) (:maintainer "Daniel Ralston" . "Wubbulous@gmail.com") (:keywords "crypt") (:url . "https://github.com/Sodel-the-Vociferous/inline-crypt-el"))]) (inline-docs . [(20220210 1402) ((emacs (24 3))) "Show inline contextual docs." single ((:commit . "cda596d9ff4c2aa5035692a97c430f6589eafbb1") (:authors ("stardiviner" . "numbchild@gmail.com")) (:maintainer "stardiviner" . "numbchild@gmail.com") (:keywords "inline" "docs" "overlay") (:url . "https://repo.or.cz/inline-docs.git"))]) (inlineR . [(20191017 1920) nil "insert Tag for inline image of R graphics" single ((:commit . "bf6450a3540aa3538546d312324c41befd0a4e54") (:authors ("myuhe ")) (:maintainer "myuhe") (:keywords "convenience" "iimage.el" "cacoo.el") (:url . "https://github.com/myuhe/inlineR.el"))]) @@ -2511,7 +2518,7 @@ (irony-eldoc . [(20200622 2214) ((emacs (24)) (cl-lib (0 5)) (irony (0 1))) "irony-mode support for eldoc-mode" single ((:commit . "73e79a89fad982a2ba072f2fcc1b4e41f0aa2978") (:authors ("Kirill Ignatiev ")) (:maintainer "Kirill Ignatiev ") (:keywords "c" "c++" "objc" "convenience" "tools") (:url . "https://github.com/ikirill/irony-eldoc"))]) (iscroll . [(20220612 310) ((emacs (26 0))) "Smooth scrolling over images" single ((:commit . "76aa4e7e72f907e95715351819d9efb6336b8238") (:authors ("Yuan Fu" . "casouri@gmail.com")) (:maintainer "Yuan Fu" . "casouri@gmail.com") (:keywords "convenience" "image") (:url . "https://github.com/casouri/iscroll"))]) (isearch-dabbrev . [(20141224 622) ((cl-lib (0 5))) "Use dabbrev in isearch" single ((:commit . "1efe7abba4923015cbc2462395deaec5446a9cc8") (:authors ("Dewdrops" . "v_v_4474@126.com")) (:maintainer "Dewdrops" . "v_v_4474@126.com") (:keywords "dabbrev" "isearch") (:url . "https://github.com/Dewdrops/isearch-dabbrev"))]) - (isearch-project . [(20220704 652) ((emacs (26 1)) (f (0 20 0))) "Incremental search through the whole project" single ((:commit . "08b1102c1f55011952aff818261a9478175a5b92") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "search") (:url . "https://github.com/jcs-elpa/isearch-project"))]) + (isearch-project . [(20220704 652) ((emacs (26 1)) (f (0 20 0))) "Incremental search through the whole project" single ((:commit . "a8c9a9f71c34ba35f9754621ce4f70d19381e807") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "search") (:url . "https://github.com/jcs-elpa/isearch-project"))]) (isearch-symbol-at-point . [(20130728 2221) nil "Use isearch to search for the symbol at point" single ((:commit . "51a1029bec1ec414885f9edb7e5947603dffdab2") (:authors ("atom smith")) (:maintainer "atom smith") (:keywords "isearch") (:url . "https://github.com/re5et/isearch-symbol-at-point"))]) (isend-mode . [(20210106 1506) nil "Interactively send parts of an Emacs buffer to an interpreter" single ((:commit . "ea855f63be7febc15bd08aec6229fab9407734fb") (:authors ("François Févotte" . "fevotte@gmail.com")) (:maintainer "François Févotte" . "fevotte@gmail.com") (:url . "https://github.com/ffevotte/isend-mode.el"))]) (isgd . [(20150414 936) nil "Shorten URLs using the isgd.com shortener service" single ((:commit . "764306dadd5a9213799081a48aba22f7c75cca9a") (:authors ("Chmouel Boudjnah" . "chmouel@chmouel.com")) (:maintainer "Chmouel Boudjnah" . "chmouel@chmouel.com") (:url . "https://github.com/chmouel/isgd.el"))]) @@ -2534,7 +2541,7 @@ (ivy-emoji . [(20200316 2351) ((emacs (26 1)) (ivy (0 13 0))) "Insert emojis with ivy" single ((:commit . "a1b7d32048278afd9b06536a8af96f533639d146") (:authors ("Gabriele Bozzola" . "sbozzolator@gmail.com")) (:maintainer "Gabriele Bozzola" . "sbozzolator@gmail.com") (:keywords "emoji" "ivy" "convenience") (:url . "https://github.com/sbozzolo/ivy-emoji.git"))]) (ivy-erlang-complete . [(20211019 447) ((async (1 9)) (counsel (0 13 4)) (ivy (0 13 4)) (erlang (19 2)) (emacs (25 1))) "Erlang context sensitive completion at point using ivy. It also support xref and eldoc." tar ((:commit . "6913f6ef7c942a5a2c42bc17635d09c91353e7ca") (:authors ("Sergey Kostyaev" . "feo.me@ya.ru")) (:maintainer "Sergey Kostyaev" . "feo.me@ya.ru") (:keywords "languages" "tools"))]) (ivy-explorer . [(20190909 1921) ((emacs (25)) (ivy (0 10 0))) "Dynamic file browsing grid using ivy" single ((:commit . "a413966cfbcecacc082d99297fa1abde0c10d3f3") (:authors ("Clemens Radermacher" . "clemera@posteo.net")) (:maintainer "Clemens Radermacher" . "clemera@posteo.net") (:keywords "convenience" "files" "matching") (:url . "https://github.com/clemera/ivy-explorer"))]) - (ivy-file-preview . [(20220704 653) ((emacs (25 1)) (ivy (0 8 0)) (s (1 12 0)) (f (0 20 0))) "Preview the current ivy file selection" single ((:commit . "3b78a194298290babd1a35ed689f333a271471b6") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "file" "ivy" "swiper" "preview" "select" "selection") (:url . "https://github.com/jcs-elpa/ivy-file-preview"))]) + (ivy-file-preview . [(20220704 653) ((emacs (25 1)) (ivy (0 8 0)) (s (1 12 0)) (f (0 20 0))) "Preview the current ivy file selection" single ((:commit . "30af5edfe6ce27738233050774356f9ab15d05d0") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "file" "ivy" "swiper" "preview" "select" "selection") (:url . "https://github.com/jcs-elpa/ivy-file-preview"))]) (ivy-fuz . [(20191222 946) ((emacs (25 1)) (fuz (1 3 0)) (ivy (0 13 0))) "Integration between fuz and ivy." single ((:commit . "f171ac73422a4bae1503d63d804e691482ed35b2") (:authors ("Zhu Zihao" . "all_but_last@163.com")) (:maintainer "Philippe Vaucher" . "philippe.vaucher@gmail.com") (:keywords "convenience") (:url . "https://github.com/Silex/ivy-fuz.el"))]) (ivy-gitlab . [(20181228 826) ((s (1 9 0)) (dash (2 9 0)) (ivy (0 8 0)) (gitlab (0 8))) "Ivy interface to Gitlab" single ((:commit . "8c2324c02119500f094c2f92dfaba4c9977ce1ba") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "gitlab" "ivy") (:url . "https://github.com/nlamirault/emacs-gitlab"))]) (ivy-historian . [(20210714 56) ((emacs (24 4)) (historian (20170111)) (ivy (0 8 0)) (flx (0 6 1))) "Persistently store selected minibuffer candidates" single ((:commit . "852cb4e72c0f78c8dbb2c972bdcb4e7b0108ff4c") (:authors ("PythonNut" . "pythonnut@pythonnut.com")) (:maintainer "PythonNut" . "pythonnut@pythonnut.com") (:keywords "convenience" "ivy") (:url . "https://github.com/PythonNut/historian.el"))]) @@ -2549,19 +2556,18 @@ (ivy-prescient . [(20220601 1652) ((emacs (25 1)) (prescient (5 2 1)) (ivy (0 11 0))) "prescient.el + Ivy" single ((:commit . "07d61b7779c4cca3009390383e7f98a55de7e17e") (:authors ("Radian LLC" . "contact+prescient@radian.codes")) (:maintainer "Radian LLC" . "contact+prescient@radian.codes") (:keywords "extensions") (:url . "https://github.com/raxod502/prescient.el"))]) (ivy-purpose . [(20160724 1003) ((emacs (24)) (ivy (0 8)) (window-purpose (1 5))) "Ivy Interface for Purpose" single ((:commit . "0495f2f3aed64d7e0028125e76a9a68f8fc4107e") (:authors ("Bar Magal (2016)")) (:maintainer "Bar Magal (2016)") (:url . "https://github.com/bmag/ivy-purpose"))]) (ivy-rich . [(20210409 931) ((emacs (25 1)) (ivy (0 13 0))) "More friendly display transformer for ivy" single ((:commit . "600b8183ed0be8668dcc548cc2c8cb94b001363b") (:authors ("Yevgnen Koh" . "wherejoystarts@gmail.com")) (:maintainer "Yevgnen Koh" . "wherejoystarts@gmail.com") (:keywords "convenience" "ivy") (:url . "https://github.com/Yevgnen/ivy-rich"))]) - (ivy-rtags . [(20191222 920) ((ivy (0 7 0)) (rtags (2 10))) "RTags completion back-end for ivy" single ((:commit . "c628efc9b485470a48aec2692d79f7c140bc5b92") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) - (ivy-searcher . [(20220704 653) ((emacs (25 1)) (ivy (0 8 0)) (searcher (0 1 8)) (s (1 12 0)) (f (0 20 0))) "Ivy interface to use searcher" single ((:commit . "fcaa8087a0cb9e61bedc2102f47c8b9754968288") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "ivy" "interface" "searcher" "search" "replace" "grep" "ag" "rg") (:url . "https://github.com/jcs-elpa/ivy-searcher"))]) + (ivy-rtags . [(20191222 920) ((ivy (0 7 0)) (rtags (2 10))) "RTags completion back-end for ivy" single ((:commit . "b9c680e7ca003c103687e790f740d86daa6b4b17") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) + (ivy-searcher . [(20220704 653) ((emacs (25 1)) (ivy (0 8 0)) (searcher (0 1 8)) (s (1 12 0)) (f (0 20 0))) "Ivy interface to use searcher" single ((:commit . "0779b4122553375c169ba7a53001c55c9f6e42f9") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "ivy" "interface" "searcher" "search" "replace" "grep" "ag" "rg") (:url . "https://github.com/jcs-elpa/ivy-searcher"))]) (ivy-spotify . [(20210329 312) ((emacs (26 1)) (espotify (0 1)) (ivy (0 13 1))) "Search spotify with ivy" single ((:commit . "ea6d6021e5acc550560325db2f09198839ee702f") (:authors ("Jose A Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose A Ortega Ruiz") (:keywords "multimedia") (:url . "https://codeberg.org/jao/espotify"))]) (ivy-todo . [(20200323 2005) ((ivy (0 8 0)) (emacs (25))) "Manage org-mode TODOs with ivy" single ((:commit . "d74501cd334b7d709659946c5e02b21cfd5507de") (:authors ("Erik Sjöstrand" . "sjostrand.erik@gmail.com")) (:maintainer "Erik Sjöstrand" . "sjostrand.erik@gmail.com") (:keywords "convenience") (:url . "https://github.com/Kungsgeten/ivy-todo"))]) (ivy-xcdoc . [(20160917 1055) ((ivy (0 8 0)) (emacs (24 4))) "Search Xcode documents with ivy interface." single ((:commit . "5ea22af36c4c2737fb0bec53432c233482d8b314") (:authors ("C.T.Chen" . "chenct@7adybird.com")) (:maintainer "C.T.Chen" . "chenct@7adybird.com") (:keywords "ivy" "xcode" "xcdoc") (:url . "https://github.com/hex2010/emacs-ivy-xcdoc"))]) (ivy-xref . [(20211008 1103) ((emacs (25 1)) (ivy (0 10 0))) "Ivy interface for xref results" single ((:commit . "a82e8e117d2dd62c28b6a3e3d6e4cfb11c0bda38") (:authors ("Alex Murray" . "murray.alex@gmail.com")) (:maintainer "Alex Murray" . "murray.alex@gmail.com") (:url . "https://github.com/alexmurray/ivy-xref"))]) (ivy-yasnippet . [(20200704 700) ((emacs (24 1)) (cl-lib (0 6)) (ivy (0 10 0)) (yasnippet (0 12 2)) (dash (2 14 1))) "Preview yasnippets with ivy" single ((:commit . "83402d91b4eba5307f71884a72df8e11cc6a994e") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "convenience") (:url . "https://github.com/mkcms/ivy-yasnippet"))]) (ivy-ycmd . [(20180909 1225) ((ycmd (1 3)) (emacs (24)) (ivy (0 10 0)) (dash (2 14 1))) "Ivy interface to ycmd" single ((:commit . "25bfee8f676e4ecbb645e4f30b47083410a00c58") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:keywords "tools") (:url . "https://github.com/abingham/emacs-ivy-ycmd"))]) - (ivy-youtube . [(20181126 1039) ((request (0 2 0)) (ivy (0 8 0)) (cl-lib (0 5))) "Query YouTube and play videos in your browser" single ((:commit . "273788e0d22a06cca1050eb1205d3fbc2245d001") (:authors ("Brunno dos Santos")) (:maintainer "Brunno dos Santos") (:keywords "youtube" "multimedia" "mpv" "vlc") (:url . "https://github.com/squiter/ivy-youtube"))]) + (ivy-youtube . [(20220818 2146) ((request (0 2 0)) (ivy (0 8 0)) (cl-lib (0 5))) "Query YouTube and play videos in your browser" single ((:commit . "3ef64d912a6b8e239a07d4fb67d6cbc7b3af2223") (:authors ("Brunno dos Santos")) (:maintainer "Brunno dos Santos") (:keywords "youtube" "multimedia" "mpv" "vlc") (:url . "https://github.com/squiter/ivy-youtube"))]) (ix . [(20131027 1629) ((grapnel (0 5 3))) "Emacs client for http://ix.io pastebin" single ((:commit . "aea4c54a5cc5a6f26637353c16a3a0e70fc76963") (:authors ("Abhishek L" . "abhishekl.2006@gmail.com")) (:maintainer "Abhishek L" . "abhishekl.2006@gmail.com") (:url . "http://www.github.com/theanalyst/ix.el"))]) (j-mode . [(20171224 1856) nil "Major mode for editing J programs" tar ((:commit . "e8725ac8af95498faabb2ca3ab3bd809a8f148e6") (:keywords "j" "languages") (:url . "http://github.com/zellio/j-mode"))]) (jabber . [(20220713 1538) ((fsm (0 2)) (srv (0 2))) "A Jabber client for Emacs." tar ((:commit . "af0315e174fa6446d5c4dd3e6465d48912950e58") (:authors ("Magnus Henoch" . "mange@freemail.hu")) (:maintainer "wgreenhouse" . "wgreenhouse@tilde.club") (:keywords "comm") (:url . "https://codeberg.org/emacs-jabber/emacs-jabber"))]) - (jabber-otr . [(20150918 1144) ((emacs (24)) (jabber (0 8 92))) "Off-The-Record messaging for jabber.el" tar ((:commit . "2692b1530234e0ba9a0d6c1eaa1cbe8679f193c0") (:authors ("Magnus Henoch" . "magnus.henoch@gmail.com")) (:maintainer "Magnus Henoch" . "magnus.henoch@gmail.com") (:keywords "comm") (:url . "https://github.com/legoscia/emacs-jabber-otr/"))]) (jack-connect . [(20220201 1417) nil "Manage jack connections within Emacs" single ((:commit . "1acaebfe8f37f0194e95c3e812c9515a6f688eee") (:authors ("Stefano Barbi" . "stefanobarbi@gmail.com")) (:maintainer "Stefano Barbi" . "stefanobarbi@gmail.com"))]) (jade-mode . [(20210908 2121) nil "Major mode for editing .jade files" single ((:commit . "1ad7c51f3c6a6ae64550d9510c5e4e8470014375") (:authors ("Brian M. Carlson and other contributors")) (:maintainer "Brian M. Carlson and other contributors") (:keywords "languages") (:url . "https://github.com/brianc/jade-mode"))]) (jammer . [(20210508 1633) ((emacs (24 1))) "Punish yourself for using Emacs inefficiently" single ((:commit . "a780e4c2adb2e85a4daadcefd1a2b189d761872f") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "games") (:url . "https://depp.brause.cc/jammer"))]) @@ -2589,7 +2595,7 @@ (jenkins . [(20200524 2016) ((dash (2 12)) (emacs (24 3)) (json (1 4))) "Minimalistic Jenkins client for Emacs" single ((:commit . "bd06cdc57c0cb9217d773eeba06ecc998f10033b") (:authors ("Rustem Muslimov" . "r.muslimov@gmail.com")) (:maintainer "Rustem Muslimov" . "r.muslimov@gmail.com") (:keywords "jenkins" "convenience"))]) (jenkins-watch . [(20121004 2326) nil "Watch continuous integration build status" single ((:commit . "37b84dfbd98240a57ff798e1ff8bc7dba2913577") (:authors ("Andrew Taylor" . "ataylor@redtoad.ca")) (:maintainer "Andrew Taylor" . "ataylor@redtoad.ca") (:url . "https://github.com/ataylor284/jenkins-watch"))]) (jenkinsfile-mode . [(20220428 1113) ((emacs (24)) (groovy-mode (2 0))) "Major mode for editing Jenkins declarative pipeline syntax" single ((:commit . "fa5545be1329df3067dcfd81749bbd99df070d6b") (:url . "https://github.com/john2x/jenkinsfile-mode"))]) - (jest . [(20220703 1950) ((emacs (24 4)) (dash (2 18 0)) (magit-popup (2 12 0)) (projectile (0 14 0)) (s (1 12 0)) (js2-mode (20180301)) (cl-lib (0 6 1))) "helpers to run jest" tar ((:commit . "398fcd7920dc3e9865574f5228baeaba03d1d297") (:authors ("Edmund Miller" . "edmund.a.miller@gmail.com")) (:maintainer "Edmund Miller" . "edmund.a.miller@gmail.com") (:keywords "jest" "javascript" "testing") (:url . "https://github.com/emiller88/emacs-jest/"))]) + (jest . [(20220807 2243) ((emacs (24 4)) (dash (2 18 0)) (magit-popup (2 12 0)) (projectile (0 14 0)) (s (1 12 0)) (js2-mode (20180301)) (cl-lib (0 6 1))) "helpers to run jest" tar ((:commit . "c8145635c54bd7df9711000e889753d267afcdc4") (:authors ("Edmund Miller" . "edmund.a.miller@gmail.com")) (:maintainer "Edmund Miller" . "edmund.a.miller@gmail.com") (:keywords "jest" "javascript" "testing") (:url . "https://github.com/emiller88/emacs-jest/"))]) (jest-test-mode . [(20220722 1947) ((emacs (25 1))) "Minor mode for running Node.js tests using jest" single ((:commit . "3126c5c5c5632da639ea34867a7342d4410d78aa") (:authors ("Raymond Huang" . "rymndhng@gmail.com")) (:maintainer "Raymond Huang" . "rymndhng@gmail.com") (:url . "https://github.com/rymndhng/jest-test-mode.el"))]) (jetbrains . [(20180301 502) ((emacs (24 3)) (cl-lib (0 5)) (f (0 17))) "JetBrains IDE bridge" single ((:commit . "56f71a17d455581c10d48f6dbb31d9e2126227bf") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "tools" "php") (:url . "https://github.com/emacs-php/jetbrains.el"))]) (jetbrains-darcula-theme . [(20210602 1430) nil "A complete port of the default JetBrains Darcula theme" single ((:commit . "296e3ca6e0341d9882f4771131a386fe2991e913") (:authors ("Ian Y.E. Pan")) (:maintainer "Ian Y.E. Pan") (:url . "https://github.com/ianpan870102/jetbrains-darcula-emacs-theme"))]) @@ -2599,7 +2605,7 @@ (jiralib2 . [(20200520 2031) ((emacs (25)) (request (0 3)) (dash (2 14 1))) "JIRA REST API bindings to Elisp" single ((:commit . "c21c4e759eff549dbda11099f2f680b78d7f5a01") (:authors ("Henrik Nyman" . "h@nyymanni.com")) (:maintainer "Henrik Nyman" . "h@nyymanni.com") (:keywords "comm" "jira" "rest" "api") (:url . "https://github.com/nyyManni/jiralib2"))]) (jist . [(20161229 1721) ((emacs (24 4)) (dash (2 12 0)) (seq (1 11)) (let-alist (1 0 4)) (magit (2 1 0)) (request (0 2 0))) "Gist integration" single ((:commit . "da0692452e312a99bb27d8708504b521798aca48") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "convenience") (:url . "https://github.com/emacs-pe/jist.el"))]) (jknav . [(20121006 2025) nil "Automatically enable j/k keys for line-based navigation" single ((:commit . "861245715c728503dad6573278fdd75c271dbf8b") (:authors ("Aaron Culich" . "aculich@gmail.com")) (:maintainer "Aaron Culich" . "aculich@gmail.com") (:keywords "keyboard" "navigation"))]) - (jmt-mode . [(20220524 2327) ((emacs (24 4))) "Java Mode Tamed" single ((:commit . "5e8eb048dd398597378eaf20842e410398bd5b55") (:authors ("Michael Allan" . "mike@reluk.ca")) (:maintainer "Michael Allan" . "mike@reluk.ca") (:keywords "c" "languages") (:url . "http://reluk.ca/project/Java/Emacs/"))]) + (jmt-mode . [(20220812 145) ((emacs (24 4))) "Java Mode Tamed" single ((:commit . "75e484849a43e4ee844c1a63258e9ff1979cd06f") (:authors ("Michael Allan" . "mike@reluk.ca")) (:maintainer "Michael Allan" . "mike@reluk.ca") (:keywords "c" "languages") (:url . "http://reluk.ca/project/Java/Emacs/"))]) (jonprl-mode . [(20160819 59) ((emacs (24 3)) (cl-lib (0 5)) (yasnippet (0 8 0))) "A major mode for editing JonPRL files" tar ((:commit . "6059bb64891fae45827174e044d6a87ac07172d8") (:authors ("David Raymond Christiansen" . "david@davidchristiansen.dk")) (:maintainer "David Raymond Christiansen" . "david@davidchristiansen.dk") (:keywords "languages"))]) (journalctl-mode . [(20201217 1625) ((emacs (24 1))) "Sample major mode for viewing output journalctl" single ((:commit . "c5bca1a5f42d2fe2a00fdf52fe480137ace971d3") (:authors ("Sebastian Meisel" . "sebastian.meisel@gmail.com")) (:maintainer "Sebastian Meisel" . "sebastian.meisel@gmail.com") (:keywords "unix") (:url . "https://github.com/SebastianMeisel/journalctl-mode"))]) (jpop . [(20170410 1250) ((emacs (24)) (dash (2 11 0)) (cl-lib (0 5))) "Lightweight project caching and navigation framework" tar ((:commit . "7628b03260be96576b34459d45959ee77d8b2110") (:authors ("Dom Charlesworth" . "dgc336@gmail.com")) (:maintainer "Dom Charlesworth" . "dgc336@gmail.com") (:keywords "project" "convenience") (:url . "https://github.com/domtronn/jpop.el"))]) @@ -2624,7 +2630,7 @@ (jsfmt . [(20180920 1008) nil "Interface to jsfmt command for javascript files" single ((:commit . "ca141a135c7700eaedef92561d334e1fb7dc28a1") (:authors ("Brett Langdon" . "brett@blangdon.com")) (:maintainer "Brett Langdon" . "brett@blangdon.com") (:url . "https://github.com/brettlangdon/jsfmt.el"))]) (json-mode . [(20211011 630) ((json-snatcher (1 0 0)) (emacs (24 4))) "Major mode for editing JSON files." single ((:commit . "eedb4560034f795a7950fa07016bd4347c368873") (:authors ("Josh Johnston")) (:maintainer "Josh Johnston") (:url . "https://github.com/joshwnj/json-mode"))]) (json-navigator . [(20191213 755) ((emacs (25 1)) (hierarchy (0 6 0))) "View and navigate JSON structures" single ((:commit . "afd902e0b5cde37fad4786515a695d17f1625286") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:url . "https://github.com/DamienCassou/json-navigator"))]) - (json-par . [(20220723 829) ((emacs (24 4)) (json-mode (1 7 0))) "Minor mode for structural editing of JSON" tar ((:commit . "2c13ad1bc3c2a62141d3312501d2c2012555972b") (:authors ("taku0 (http://github.com/taku0)")) (:maintainer "taku0 (http://github.com/taku0)") (:keywords "abbrev" "convenience" "files") (:url . "https://github.com/taku0/json-par"))]) + (json-par . [(20220815 1101) ((emacs (24 4)) (json-mode (1 7 0))) "Minor mode for structural editing of JSON" tar ((:commit . "4f1fffd2595be3e3af0b5fb1e0c499ecee698273") (:authors ("taku0 (http://github.com/taku0)")) (:maintainer "taku0 (http://github.com/taku0)") (:keywords "abbrev" "convenience" "files") (:url . "https://github.com/taku0/json-par"))]) (json-process-client . [(20210525 733) ((emacs (25 1))) "Interact with a TCP process using JSON" single ((:commit . "373b2cc7e3d26dc00594e0b2c1bb66815aad2826") (:authors ("Nicolas Petton" . "nicolas@petton.fr") ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr") (:url . "https://gitlab.petton.fr/nico/json-process-client"))]) (json-reformat . [(20160212 853) nil "Reformatting tool for JSON" single ((:commit . "8eb6668ed447988aea06467ba8f42e1f2178246f") (:authors ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainer "Wataru MIYAGUNI" . "gonngo@gmail.com") (:keywords "json") (:url . "https://github.com/gongo/json-reformat"))]) (json-rpc . [(20200417 1629) ((emacs (24 1)) (cl-lib (0 5))) "JSON-RPC library" single ((:commit . "81a5a520072e20d18aeab2aac4d66c046b031e56") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/elisp-json-rpc"))]) @@ -2640,8 +2646,8 @@ (julia-mode . [(20220418 809) ((emacs (24 3))) "Major mode for editing Julia source code" tar ((:commit . "adf4029be778c5983c436873b8a78bc72a6b09f8") (:keywords "languages") (:url . "https://github.com/JuliaEditorSupport/julia-emacs"))]) (julia-repl . [(20220428 541) ((emacs (25 1)) (s (1 12))) "A minor mode for a Julia REPL" single ((:commit . "2342003662071cf7b256f0a7cd8f545bcffaf22a") (:authors ("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")) (:maintainer "Dennis Ogbe" . "dogbe@purdue.edu"))]) - (julia-snail . [(20220722 547) ((emacs (26 2)) (dash (2 16 0)) (julia-mode (0 3)) (s (1 12 0)) (spinner (1 7 3)) (vterm (0 0 1)) (popup (0 5 9))) "Julia Snail" tar ((:commit . "d275f8a070431d2ce669a2844a28e5a65ee0c430") (:url . "https://github.com/gcv/julia-snail"))]) - (julia-vterm . [(20220720 1410) ((emacs (25 1)) (vterm (0 0 1))) "A mode for Julia REPL using vterm" single ((:commit . "698ca35da04d99f25c8f075e8342015434f6a662") (:authors ("Shigeaki Nishina")) (:maintainer "Shigeaki Nishina") (:keywords "languages" "julia") (:url . "https://github.com/shg/julia-vterm.el"))]) + (julia-snail . [(20220812 2248) ((emacs (26 2)) (dash (2 16 0)) (julia-mode (0 3)) (s (1 12 0)) (spinner (1 7 3)) (vterm (0 0 1)) (popup (0 5 9))) "Julia Snail" tar ((:commit . "d411c022b9259d4e31055fc33323f878d07c6b7e") (:url . "https://github.com/gcv/julia-snail"))]) + (julia-vterm . [(20220825 834) ((emacs (25 1)) (vterm (0 0 1))) "A mode for Julia REPL using vterm" single ((:commit . "738b9eb50b9febbca2bdcd5e880450eec707c1ee") (:authors ("Shigeaki Nishina")) (:maintainer "Shigeaki Nishina") (: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"))]) (jump . [(20161127 128) ((findr (0 7)) (inflections (2 4)) (cl-lib (0 5))) "build functions which contextually jump between files" single ((:commit . "55caa66a7cc6e0b1a76143fd40eff38416928941") (:authors ("Eric Schulte")) (:maintainer "Eric Schulte") (:keywords "project" "convenience" "navigation") (:url . "http://github.com/eschulte/jump.el"))]) (jump-char . [(20180601 1348) nil "navigation by char" single ((:commit . "1e31a3c687f2b3c71bbfab881c6d75915534bb9e") (:authors ("Le Wang")) (:maintainer "Le Wang") (:url . "https://github.com/lewang/jump-char"))]) @@ -2662,7 +2668,7 @@ (kaleidoscope-evil-state-flash . [(20170728 1020) ((evil (1 2 12)) (kaleidoscope (0 1 0)) (s (1 11 0))) "Flash keyboard LEDs when changing Evil state" single ((:commit . "af4034dcace867c4ede0bce744d5cb888c318f23") (:authors ("Gergely Nagy")) (:maintainer "Gergely Nagy") (:url . "https://github.com/algernon/kaleidoscope.el"))]) (kana . [(20210531 1427) ((emacs (24 4)) (dash (2 17 0))) "Learn Japanese hiragana and katakana" single ((:commit . "d3d550aad67ef8625b3860598bf3622f5b2a7d32") (:authors ("Damon Chan" . "elecming@gmail.com")) (:maintainer "Damon Chan" . "elecming@gmail.com") (:keywords "tools") (:url . "https://github.com/chenyanming/kana"))]) (kanban . [(20170418 810) nil "Parse org-todo headlines to use org-tables as Kanban tables" single ((:commit . "fcf0173ce0144e59de97ba8a7808192620e5f8f4") (:authors ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainer "Arne Babenhauserheide" . "arne_bab@web.de") (:keywords "outlines" "convenience"))]) - (kanji-mode . [(20160826 1139) nil "View stroke order for kanji characters at cursor" tar ((:commit . "eda4f8666486689d36317db7dbda54fb73d3e3d2") (:authors ("Wojciech Gac" . "wojciech.s.gac@gmail.com")) (:maintainer "Wojciech Gac" . "wojciech.s.gac@gmail.com") (:url . "http://github.com/wsgac/kanji-mode "))]) + (kanji-mode . [(20160826 1139) nil "View stroke order for kanji characters at cursor" tar ((:commit . "9785fdca081a44ebb9869347b26130c4c3c4474e") (:authors ("Wojciech Gac" . "wojciech.s.gac@gmail.com")) (:maintainer "Wojciech Gac" . "wojciech.s.gac@gmail.com") (:url . "http://github.com/wsgac/kanji-mode "))]) (kaocha-runner . [(20190904 1950) ((emacs (26)) (s (1 4 0)) (cider (0 21 0)) (parseedn (0 1 0))) "A package for running Kaocha tests via CIDER." single ((:commit . "755b0dfb3bd676c769c4b4aeb81c2cd5828bd207") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:url . "https://github.com/magnars/kaocha-runner.el"))]) (kaolin-themes . [(20220519 720) ((emacs (25 1)) (autothemer (0 2 2)) (cl-lib (0 6))) "A set of eye pleasing themes" tar ((:commit . "5fd75d41647d9535eb730b99b5adddc9edf55cd2") (:authors ("Ogden Webb" . "ogdenwebb@gmail.com")) (:maintainer "Ogden Webb" . "ogdenwebb@gmail.com") (:keywords "dark" "light" "teal" "blue" "violet" "purple" "brown" "theme" "faces") (:url . "https://github.com/ogdenwebb/emacs-kaolin-themes"))]) (kaomoji . [(20220721 441) ((emacs (24 3)) (helm-core (3 6 0))) "Input kaomoji superb easily" tar ((:commit . "fba0018a13eba70c2bffc6153dcfee99937fa3d6") (:authors ("Ono Hiroko" . "azazabc123@gmail.com")) (:maintainer "Ono Hiroko" . "azazabc123@gmail.com") (:keywords "tools" "fun") (:url . "https://github.com/kuanyui/kaomoji.el"))]) @@ -2691,7 +2697,7 @@ (keyset . [(20150220 530) ((dash (2 8 0)) (cl-lib (0 5))) "A small library for structuring key bindings." single ((:commit . "45ce83c4b56f064874256db37e697a63b2c69e65") (:authors ("Hiroki YAMAKAWA" . "s06139@gmail.com")) (:maintainer "Hiroki YAMAKAWA" . "s06139@gmail.com") (:url . "https://github.com/HKey/keyset"))]) (keystore-mode . [(20190409 1946) ((emacs (24 3)) (origami (1 0)) (s (1 12 0)) (seq (2 20))) "A major mode for viewing and managing (java) keystores" tar ((:commit . "43bd5926348298d077c7221f37902c990df3f951") (:authors ("Peterpaul Taekele Klein Haneveld" . "pp.kleinhaneveld@gmail.com")) (:maintainer "Peterpaul Taekele Klein Haneveld" . "pp.kleinhaneveld@gmail.com") (:keywords "tools") (:url . "https://github.com/peterpaul/keystore-mode"))]) (keyswap . [(20160813 957) ((emacs (24 3))) "swap bindings between key pairs" single ((:commit . "cd682a7c4a8d64d6bae6a005db5045232e5e7b95") (:authors ("Matthew Malcomson" . "hardenedapple@gmail.com")) (:maintainer "Matthew Malcomson" . "hardenedapple@gmail.com") (:keywords "convenience") (:url . "http://github.com/hardenedapple/keyswap.el"))]) - (keytar . [(20220704 626) ((emacs (24 4))) "Emacs Lisp interface for node-keytar" single ((:commit . "4b874662b2eb19d31426faa061d1307359f10013") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "keytar" "password" "credential" "secret" "security") (:url . "https://github.com/emacs-grammarly/keytar"))]) + (keytar . [(20220704 626) ((emacs (24 4))) "Emacs Lisp interface for node-keytar" single ((:commit . "ecbc2677de1cb68bb8b8e405304d7dfe6bce5668") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "keytar" "password" "credential" "secret" "security") (:url . "https://github.com/emacs-grammarly/keytar"))]) (keyword-search . [(20180424 1102) nil "browser keyword search from Emacs" tar ((:commit . "f8475ecaddb8804a9be6bee47678207c86ac8dee") (:maintainer "Jens Petersen") (:keywords "web" "search" "keyword") (:url . "https://github.com/juhp/keyword-search"))]) (kfg . [(20140909 538) ((f (0 17 1))) "an emacs configuration system" single ((:commit . "d2c9dd26618fb2f7bf1e7b6eae193b1cceba3c97") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:url . "https://github.com/abingham/kfg"))]) (khalel . [(20211114 1233) ((emacs (27 1))) "Import, edit and create calendar events through khal" single ((:commit . "a0503498ae43a50157549c661381d94578ad2bd7") (:authors ("Hanno Perrey ")) (:maintainer "Hanno Perrey" . "hanno@hoowl.se") (:keywords "event" "calendar" "ics" "khal") (:url . "https://gitlab.com/hperrey/khalel"))]) @@ -2702,7 +2708,7 @@ (killer . [(20190128 10) nil "kill and delete text" single ((:commit . "ace0547944933440384ceeb5876b1f68c082d540") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "http://github.com/tarsius/killer"))]) (kite . [(20130201 1938) ((json (1 2)) (websocket (0 93 1))) "WebKit inspector front-end" tar ((:commit . "7ed74d1147a6ddd152d3da65dc30df3517d53144") (:authors ("Julian Scheid" . "julians37@gmail.com")) (:maintainer "Julian Scheid" . "julians37@gmail.com") (:keywords "tools"))]) (kite-mini . [(20160508 1106) ((dash (2 11 0)) (websocket (1 5))) "Remotely evaluate JavaScript in the WebKit debugger" tar ((:commit . "a68619dbc109c7989f3448426d8c1ee9e797c11f") (:authors ("Tung Dao" . "me@tungdao.com")) (:maintainer "Tung Dao" . "me@tungdao.com") (:keywords "webkit") (:url . "https://github.com/tungd/kite-mini.el"))]) - (kivy-mode . [(20210318 2106) nil "Emacs major mode for editing Kivy files" single ((:commit . "714f4935085d4ac0c220fdf7183fb35175f8bcad") (:authors ("Dean Serenevy" . "dean@serenevy.net")) (:maintainer "Dean Serenevy" . "dean@serenevy.net"))]) + (kivy-mode . [(20210318 2106) nil "Emacs major mode for editing Kivy files" single ((:commit . "9922e1261971d729f4dfa08464c08b84221c9adb") (:authors ("Dean Serenevy" . "dean@serenevy.net")) (:maintainer "Dean Serenevy" . "dean@serenevy.net"))]) (kiwix . [(20220316 847) ((emacs (25 1)) (request (0 3 0))) "Searching offline Wikipedia through Kiwix." tar ((:commit . "444f686a7f75db788d54f544b923a3532732eb8b") (:authors ("stardiviner" . "numbchild@gmail.com")) (:maintainer "stardiviner" . "numbchild@gmail.com") (:keywords "kiwix" "wikipedia") (:url . "https://repo.or.cz/kiwix.el.git"))]) (kixtart-mode . [(20150611 1604) ((emacs (24))) "major mode for Kixtart scripting files" single ((:commit . "1c2356797e7b766bbaaa2b341176a8b10499cd79") (:authors ("Ryrun ")) (:maintainer "Ryrun ") (:keywords "languages") (:url . "https://github.com/ryrun/kixtart-mode"))]) (klere-theme . [(20210320 1912) ((emacs (24))) "A dark theme with lambent color highlights and incremental grays" single ((:commit . "f9eacacc00455e6c42961ec41f24f864c2a05ace") (:authors ("Wamm K. D." . "jaft.r@outlook.com")) (:maintainer "Wamm K. D." . "jaft.r@outlook.com") (:url . "https://codeberg.org/WammKD/emacs-klere-theme"))]) @@ -2725,7 +2731,7 @@ (kubernetes-evil . [(20220625 534) ((kubernetes (0 18 0)) (evil (1 2 12))) "Kubernetes keybindings for evil-mode." single ((:commit . "8163fd38015cbde0485f6eaab41450132bf6e19d") (:authors ("Chris Barrett" . "chris+emacs@walrus.cool")) (:maintainer "Chris Barrett" . "chris+emacs@walrus.cool"))]) (kubernetes-helm . [(20210902 2232) ((yaml-mode (0 0 13)) (emacs (25 3))) "extension for helm, the package manager for kubernetes" single ((:commit . "95cf92600436f67bd7bfe650763e68635f5ecc8e") (:authors ("Adrien Brochard")) (:maintainer "Adrien Brochard") (:keywords "kubernetes" "helm" "k8s" "tools" "processes") (:url . "https://github.com/abrochard/kubernetes-helm"))]) (kubernetes-tramp . [(20181228 922) ((emacs (24)) (cl-lib (0 5))) "TRAMP integration for kubernetes containers" single ((:commit . "8713571b66940f8f3f496b55baa23cdf1df7a869") (:authors ("Giovanni Ruggiero" . "giovanni.ruggiero+github@gmail.com")) (:maintainer "Giovanni Ruggiero" . "giovanni.ruggiero+github@gmail.com") (:keywords "kubernetes" "convenience") (:url . "https://github.com/gruggiero/kubernetes-tramp"))]) - (kurecolor . [(20220719 143) ((emacs (24 1)) (s (1 0))) "color editing goodies" single ((:commit . "7fcb1472d2e3e9dfd02663bb5f3809c271402368") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com"))]) + (kurecolor . [(20220818 1336) ((emacs (24 1)) (s (1 12))) "color editing goodies" tar ((:commit . "e4b02142bb955e7f3f621b405520a42f9dd35aec") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "https://github.com/emacsfodder/kurecolor.el"))]) (kuronami-theme . [(20220602 339) ((emacs (24 1))) "A deep blue theme with cool autumnal colors" single ((:commit . "a51d5ff3883bd94d0a181bb5d60f747190eda4f6") (:authors ("Eric Chung <>")) (:maintainer "Eric Chung <>") (:url . "https://github.com/inj0h/kuronami"))]) (kv . [(20140108 1534) nil "key/value data structure functions" single ((:commit . "721148475bce38a70e0b678ba8aa923652e8900e") (:authors ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp"))]) (kwin . [(20220120 2125) nil "communicatewith the KWin window manager" single ((:commit . "20fac6508e5535a26df783ba05f04d1800b7382c") (:authors ("Simon Hafner")) (:maintainer "Simon Hafner") (:url . "http://github.com/reactormonk/kwin-minor-mode"))]) @@ -2733,7 +2739,7 @@ (laas . [(20220509 1234) ((emacs (26 3)) (auctex (11 88)) (aas (1 1)) (yasnippet (0 14))) "A bundle of as-you-type LaTeX snippets" tar ((:commit . "44533de4968fee924d9cc81ce9a23c9d82847db3") (:maintainer "Yoav Marco" . "yoavm448@gmail.com") (:keywords "tools" "tex") (:url . "https://github.com/tecosaur/LaTeX-auto-activating-snippets"))]) (lab-themes . [(20200815 2104) ((emacs (24))) "A custom theme carefully constructed in the LAB space" tar ((:commit . "9d7deb9635959d3a50ccb1082eb1207275f4b3e8") (:authors ("MetroWind" . "chris.corsair@gmail.com")) (:maintainer "MetroWind" . "chris.corsair@gmail.com") (:keywords "lisp") (:url . "https://github.com/MetroWind/lab-theme"))]) (labburn-theme . [(20200822 2153) nil "A lab color space zenburn theme." single ((:commit . "4ef2892f56c973907361bc91495d14204744f678") (:authors ("Johannes Goslar")) (:maintainer "Johannes Goslar") (:keywords "theme" "zenburn") (:url . "https://github.com/ksjogo/labburn-theme"))]) - (lacquer . [(20220722 140) ((emacs (25 2))) "Switch theme/font by selecting from a cache" tar ((:commit . "71bf9e8464ae240db17204dd2b968260a9c4c305") (:authors ("zakudriver" . "zy.hua1122@outlook.com")) (:maintainer "zakudriver" . "zy.hua1122@outlook.com") (:keywords "tools") (:url . "https://github.com/zakudriver/lacquer"))]) + (lacquer . [(20220811 649) ((emacs (25 2))) "Switch theme/font by selecting from a cache" tar ((:commit . "70650105be1b4c1ade34c0a1c3263d80b2388593") (:authors ("zakudriver" . "zy.hua1122@gmail.com")) (:maintainer "zakudriver" . "zy.hua1122@gmail.com") (:keywords "tools") (:url . "https://github.com/zakudriver/lacquer"))]) (laguna-theme . [(20220804 227) nil "An updated blue-green Laguna Theme." single ((:commit . "680ab8c936cb1c249b5a6a07976bcc83ef217e25") (:authors ("Henry Newcomer" . "a.cliche.email@gmail.com")) (:maintainer "Henry Newcomer" . "a.cliche.email@gmail.com") (:url . "https://github.com/HenryNewcomer/laguna-theme"))]) (lakota-input . [(20200823 2146) nil "Input modes for Lakota language orthographies" single ((:commit . "b74b9de284a0404a120bb15340def4dd2f9a4779") (:authors ("Grant Shangreaux" . "shshoshin@protonmail.com")) (:maintainer "Grant Shangreaux" . "shshoshin@protonmail.com") (:url . "https://git.sr.ht/~shoshin/lakota-input.git"))]) (lambdapi-mode . [(20220106 1308) ((emacs (26 1)) (eglot (1 5)) (math-symbol-lists (1 2 1)) (highlight (20190710 1527))) "A major mode for editing Lambdapi source code" tar ((:commit . "16644a4d9d0bcdd43c009c597dddb89f6a64a482") (:maintainer "Deducteam" . "dedukti-dev@inria.fr") (:keywords "languages") (:url . "https://github.com/Deducteam/lambdapi"))]) @@ -2743,12 +2749,12 @@ (langtool . [(20200529 230) ((cl-lib (0 3))) "Grammar check utility using LanguageTool" single ((:commit . "8276eccc5587bc12fd205ee58a7a982f0a136e41") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "docs") (:url . "https://github.com/mhayashi1120/Emacs-langtool"))]) (langtool-ignore-fonts . [(20210526 2340) ((emacs (25 1)) (langtool (2 2 1))) "Force langtool to ignore certain fonts" single ((:commit . "c3291c85b733b9047653cbb1f525655394610bdb") (:authors ("Christopher Lloyd" . "cjl8zf@virginia.edu")) (:maintainer "Christopher Lloyd" . "cjl8zf@virginia.edu") (:url . "https://github.com/cjl8zf/langtool-ignore-fonts"))]) (language-detection . [(20161123 1813) ((emacs (24)) (cl-lib (0 5))) "Automatic language detection from code snippets" single ((:commit . "54a6ecf55304fba7d215ef38a4ec96daff2f35a4") (:authors ("Andreas Jansson" . "andreas@jansson.me.uk")) (:maintainer "Andreas Jansson" . "andreas@jansson.me.uk") (:url . "https://github.com/andreasjansson/language-detection.el"))]) - (language-id . [(20220702 529) ((emacs (24 3))) "Library to work with programming language identifiers" single ((:commit . "1d05bb74075879351a33fedc363a5f375a18b8e5") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/lassik/emacs-language-id"))]) + (language-id . [(20220810 1351) ((emacs (24 3))) "Library to work with programming language identifiers" single ((:commit . "ba65c244ac931dbf9e8569f080dedffed88fb029") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/lassik/emacs-language-id"))]) (languagetool . [(20220514 309) ((emacs (27 0)) (request (0 3 2))) "LanguageTool integration for grammar and spell check" tar ((:commit . "503d18bd3c074fe8f495cfa6a34ccca1ef6961ce") (:authors ("Joar Buitrago" . "jebuitragoc@unal.edu.co")) (:maintainer "Joar Buitrago" . "jebuitragoc@unal.edu.co") (:keywords "grammar" "text" "docs" "tools" "convenience" "checker") (:url . "https://github.com/PillFall/Emacs-LanguageTool.el"))]) (lastfm . [(20211018 838) ((emacs (26 1)) (request (0 3 0)) (anaphora (1 0 4)) (memoize (1 1)) (elquery (0 1 0)) (s (1 12 0))) "Last.fm API for Emacs Lisp" single ((:commit . "b4b19f0aadc5087febeeb3f59944a89c4cdcf325") (:authors ("Mihai Olteanu" . "mihai_olteanu@fastmail.fm")) (:maintainer "Mihai Olteanu" . "mihai_olteanu@fastmail.fm") (:keywords "multimedia" "api") (:url . "https://github.com/mihaiolteanu/lastfm.el/"))]) (lastpass . [(20201229 2109) ((emacs (24 4)) (seq (1 9)) (cl-lib (0 5))) "LastPass command wrapper" single ((:commit . "2366de7824b6c5f8e9ec6811d219dc06794e8630") (:authors ("Petter Storvik")) (:maintainer "Petter Storvik") (:keywords "extensions" "processes" "lpass" "lastpass") (:url . "https://github.com/storvik/emacs-lastpass"))]) (latex-change-env . [(20220710 933) ((emacs (27 1)) (auctex (13 1))) "Change in and out of LaTeX environments" single ((:commit . "4e6f75f678b207b3bb5031c8b2e31f8d577df445") (:authors ("Tony Zorman" . "soliditsallgood@mailbox.org")) (:maintainer "Tony Zorman" . "soliditsallgood@mailbox.org") (:keywords "convenience" "tex") (:url . "https://gitlab.com/slotThe/change-env"))]) - (latex-extra . [(20170817 147) ((auctex (11 86 1)) (cl-lib (0 5))) "Adds several useful functionalities to LaTeX-mode." single ((:commit . "82d99b8b0c2db20e5270749582e03bcc2443ffb5") (:authors ("Artur Malabarba" . "artur@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "artur@endlessparentheses.com") (:keywords "tex") (:url . "http://github.com/Malabarba/latex-extra"))]) + (latex-extra . [(20220817 5) ((auctex (11 86 1)) (cl-lib (0 5))) "Adds several useful functionalities to LaTeX-mode." single ((:commit . "a81e7588448f85c5fcc3f3fc71cf957d0928a656") (:authors ("Artur Malabarba" . "artur@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "artur@endlessparentheses.com") (:keywords "tex") (:url . "http://github.com/Malabarba/latex-extra"))]) (latex-math-preview . [(20211228 641) nil "preview LaTeX mathematical expressions." single ((:commit . "1c082179493eed3ce8bc255f87791eb4acb1fbdb") (:authors ("Takayuki YAMAGUCHI" . "d@ytak.info")) (:maintainer "Takayuki YAMAGUCHI" . "d@ytak.info") (:keywords "latex" "tex") (:url . "https://gitlab.com/latex-math-preview/latex-math-preview"))]) (latex-pretty-symbols . [(20151112 1044) nil "Display many latex symbols as their unicode counterparts" single ((:commit . "83d5888147bb734a94dfd4847a11e975a7d86ba8") (:authors ("Erik Parmann" . "eparmann@gmail.com") ("Pål Drange")) (:maintainer "Erik Parmann" . "eparmann@gmail.com") (:keywords "convenience" "display") (:url . "https://bitbucket.org/mortiferus/latex-pretty-symbols.el"))]) (latex-preview-pane . [(20181008 1822) nil "Makes LaTeX editing less painful by providing a updatable preview pane" tar ((:commit . "5297668a89996b50b2b62f99cba01cc544dbed2e") (:authors ("John L. Singleton" . "jsinglet@gmail.com")) (:maintainer "John L. Singleton" . "jsinglet@gmail.com") (:keywords "latex" "preview") (:url . "http://www.emacswiki.org/emacs/LaTeXPreviewPane"))]) @@ -2788,21 +2794,22 @@ (lexbind-mode . [(20141027 1429) nil "Puts the value of lexical-binding in the mode line" single ((:commit . "fa0a6848c1cfd3fbf45db43dc2deef16377d887d") (:authors ("Andrew Kirkpatrick" . "ubermonk@gmail.com")) (:maintainer "Andrew Kirkpatrick" . "ubermonk@gmail.com") (:keywords "convenience" "lisp") (:url . "https://github.com/spacebat/lexbind-mode"))]) (lexic . [(20220501 1432) ((emacs (26 3))) "A major mode to find out more about words" single ((:commit . "f9b3de4d9c2dd1ce5022383e1a504b87bf7d1b09") (:authors ("pluskid" . "pluskid@gmail.com") ("gucong" . "gucong43216@gmail.com") ("TEC" . "tec@tecosaur.com")) (:maintainer "TEC" . "tec@tecosaur.com") (:url . "https://github.com/tecosaur/lexic"))]) (lf . [(20210808 1921) ((s (1 12 0)) (dash (2 16 0)) (emacs (27 1))) "A Language Features library for Emacs Lisp" single ((:commit . "35db92ca765a0544721fdeea036d77b7d192d083") (:authors ("Musa Al-hassy" . "alhassy@gmail.com")) (:maintainer "Musa Al-hassy" . "alhassy@gmail.com") (:keywords "convenience" "programming") (:url . "https://alhassy.github.io/lf.el/"))]) - (lfe-mode . [(20220709 1956) nil "Lisp Flavoured Erlang mode" tar ((:commit . "d10af0a774d6d9c41ea78fe0185fdd0065a05d66"))]) + (lfe-mode . [(20220822 911) nil "Lisp Flavoured Erlang mode" tar ((:commit . "f7cfdd30620448df1b058467ac160b34d4b8105b"))]) (libbcel . [(20191203 654) ((emacs (26 1)) (request (0 3 1))) "Library to connect to basecamp 3 API" tar ((:commit . "df466d31544c53d8550f9c08e58b70adc559c48c") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:url . "https://gitlab.petton.fr/bcel/libbcel"))]) (libelcouch . [(20200923 1836) ((emacs (26 1)) (request (0 3 0))) "Communication with CouchDB" single ((:commit . "5ae35266c9a2eb33f0c708bc8c0687339cee9133") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:keywords "tools") (:url . "https://gitlab.petton.fr/elcouch/libelcouch/"))]) (liberime . [(20211203 244) ((emacs (25 1))) "Rime elisp binding" tar ((:commit . "79b709debe036f98d74ac129934e59c4d08c1dd5") (:authors ("A.I.")) (:maintainer "A.I.") (:keywords "convenience" "chinese" "input-method" "rime") (:url . "https://github.com/merrickluo/liberime"))]) (libgit . [(20220620 1118) ((emacs (25 1))) "Thin bindings to libgit2." tar ((:commit . "3f3d600f1708afbac449d02046e05782ac974866") (:authors ("Eivind Fonn" . "evfonn@gmail.com")) (:maintainer "Eivind Fonn" . "evfonn@gmail.com") (:keywords "git" "vc") (:url . "https://github.com/magit/libegit2"))]) (liblouis . [(20220426 657) ((emacs (26 1))) "Mode for editing liblouis braille translation tables" single ((:commit . "a341a0c434cdbe7f46956c8db13203c3fc941a34") (:authors ("Christian Egli" . "christian.egli@sbs.ch")) (:maintainer "Christian Egli" . "christian.egli@sbs.ch") (:keywords "languages") (:url . "https://github.com/liblouis/liblouis-mode"))]) - (libmpdee . [(20160117 2301) nil "Client end library for mpd, a music playing daemon" single ((:commit . "a6ca3b7d6687f3ba60996b9b5044ad1d3b228290") (:authors ("Ramkumar R. Aiyengar" . "andyetitmoves@gmail.com")) (:maintainer "Ramkumar R. Aiyengar" . "andyetitmoves@gmail.com") (:keywords "music" "mpd"))]) + (libmpdee . [(20220825 957) nil "Client end library for mpd, a music playing daemon" single ((:commit . "9a84e074385cd085622f94e720a968a0e05ceae5") (:authors ("Ramkumar R. Aiyengar" . "andyetitmoves@gmail.com")) (:maintainer "Ramkumar R. Aiyengar" . "andyetitmoves@gmail.com") (:keywords "music" "mpd"))]) (libmpdel . [(20220706 1952) ((emacs (25 1))) "Communication with an MPD server" tar ((:commit . "c27c08949a742a888eb9921a8528882b2aec6137") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:keywords "multimedia") (:url . "https://github.com/mpdel/libmpdel"))]) (librera-sync . [(20210827 2300) ((emacs (26 1)) (f (0 17))) "Sync document's position with Librera Reader for Android" tar ((:commit . "b6b97adc08c26b1595249e1c129793100f4ca26e") (:authors ("Dmitriy Pshonko" . "jumper047@gmail.com")) (:maintainer "Dmitriy Pshonko" . "jumper047@gmail.com") (:keywords "multimedia" "sync") (:url . "https://github.com/jumper047/librera-sync"))]) (lice . [(20220312 2215) nil "License And Header Template" tar ((:commit . "0b69ba54057146f1473e85c0760029e584e3eb13") (:authors ("Taiki Sugawara" . "buzz.taiki@gmail.com")) (:maintainer "Taiki Sugawara" . "buzz.taiki@gmail.com") (:keywords "template" "license" "tools") (:url . "https://github.com/buzztaiki/lice-el"))]) (license-snippets . [(20201117 1619) ((emacs (26)) (yasnippet (0 8 0))) "LICENSE templates for yasnippet" tar ((:commit . "a729748b7d7f38a916fe61f23db6e7446c0a5e8f") (:authors ("Seong Yong-ju" . "sei40kr@gmail.com")) (:maintainer "Seong Yong-ju" . "sei40kr@gmail.com") (:keywords "tools") (:url . "https://github.com/sei40kr/license-snippets"))]) - (license-templates . [(20220704 654) ((emacs (24 3)) (request (0 3 0))) "Create LICENSE using GitHub API" single ((:commit . "8182fa959477388b61c3a5d226e88e63ca2e87d3") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "license" "api" "template") (:url . "https://github.com/jcs-elpa/license-templates"))]) + (license-templates . [(20220704 654) ((emacs (24 3)) (request (0 3 0))) "Create LICENSE using GitHub API" single ((:commit . "a926f07abc7b4daa22944f0e9ce393fe45094a5f") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "license" "api" "template") (:url . "https://github.com/jcs-elpa/license-templates"))]) + (ligature . [(20220808 1225) ((emacs (28))) "Display typographical ligatures in major modes" single ((:commit . "5eb950ada991705fdda4456970b0321241ee2bfa") (:authors ("Mickey Petersen" . "mickey@masteringemacs.org")) (:maintainer "Mickey Petersen" . "mickey@masteringemacs.org") (:keywords "tools" "faces") (:url . "https://www.github.com/mickeynp/ligature.el"))]) (light-soap-theme . [(20150607 1445) ((emacs (24))) "Emacs 24 theme with a light background." single ((:commit . "76a787bd40c6b567ae68ced7f5d9f9f10725e00d"))]) - (ligo-mode . [(20220209 755) ((emacs (27 1))) "A major mode for editing LIGO source code" single ((:commit . "81d2f8d91ff9cbedd0503de1078b0d729ac86692") (:authors ("LigoLang SASU")) (:maintainer "LigoLang SASU") (:keywords "languages") (:url . "https://gitlab.com/ligolang/ligo/-/tree/dev/tools/emacs"))]) - (line-reminder . [(20220721 451) ((emacs (25 1)) (indicators (0 0 4)) (fringe-helper (1 0 1)) (ov (1 0 6)) (ht (2 0))) "Line annotation for changed and saved lines" single ((:commit . "1b2dfa899409f4af2896fce6b9acbe98072abd59") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "annotation") (:url . "https://github.com/emacs-vs/line-reminder"))]) + (ligo-mode . [(20220209 755) ((emacs (27 1))) "A major mode for editing LIGO source code" single ((:commit . "9642210b1ad3eecf9b273a3a70657841b35681b8") (:authors ("LigoLang SASU")) (:maintainer "LigoLang SASU") (:keywords "languages") (:url . "https://gitlab.com/ligolang/ligo/-/tree/dev/tools/emacs"))]) + (line-reminder . [(20220721 451) ((emacs (25 1)) (indicators (0 0 4)) (fringe-helper (1 0 1)) (ov (1 0 6)) (ht (2 0))) "Line annotation for changed and saved lines" single ((:commit . "3928cf1ba8492b8c0d79340ad7ba5bb5badb5374") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "annotation") (:url . "https://github.com/emacs-vs/line-reminder"))]) (line-up-words . [(20180219 1024) nil "Align words in an intelligent way" single ((:commit . "254ee815eb3fe77edea7c9da6f6f3839163735f3") (:url . "https://github.com/janestreet/line-up-words"))]) (lines-at-once . [(20180422 247) ((emacs (25))) "Insert and edit multiple lines at once" single ((:commit . "31bce4b79fe16251b7cf118f0d343b0b46f72360") (:authors ("Jiahao Li" . "jiahaowork@gmail.com")) (:maintainer "Jiahao Li" . "jiahaowork@gmail.com") (:keywords "abbrev" "tools") (:url . "https://github.com/jiahaowork/lines-at-once.el"))]) (lingr . [(20100807 1731) nil "Lingr Client for GNU Emacs" single ((:commit . "4215a8704492d3c860097cbe2649936c22c196df") (:authors ("lugecy" . "lugecy@gmail.com")) (:maintainer "lugecy" . "lugecy@gmail.com") (:keywords "chat" "client" "internet") (:url . "http://github.com/lugecy/lingr-el"))]) @@ -2815,13 +2822,13 @@ (linum-off . [(20160217 2137) nil "Provides an interface for turning line-numbering off" single ((:commit . "116e66ac259b183e0763b85616888316ab196822") (:authors ("Matthew L. Fidler, Florian Adamsky (see wiki)")) (:maintainer "Matthew L. Fidler") (:keywords "line" "numbering") (:url . "http://www.emacswiki.org/emacs/auto-indent-mode.el "))]) (linum-relative . [(20180124 1047) nil "display relative line number in emacs." single ((:commit . "c74a6981b688a5e1e6b8e0809363963ff558ce4d") (:authors ("coldnew" . "coldnew.tw@gmail.com")) (:maintainer "coldnew" . "coldnew.tw@gmail.com") (:keywords "converience") (:url . "http://github.com/coldnew/linum-relative"))]) (liquid-types . [(20151202 735) ((flycheck (0 13)) (dash (1 2)) (emacs (24 1)) (popup (0 5 2)) (pos-tip (0 5 0)) (flycheck-liquidhs (0 0 1)) (button-lock (1 0 2))) "show inferred liquid-types" single ((:commit . "cc4bacbbf204ef9cf0756f78dfebee2c6ae14d7b") (:authors ("Ranjit Jhala" . "jhala@cs.ucsd.edu")) (:maintainer "Ranjit Jhala" . "jhala@cs.ucsd.edu"))]) - (liquidmetal . [(20220704 654) ((emacs (24 4))) "A mimetic poly-alloy of the Quicksilver scoring algorithm" single ((:commit . "2d8df67f62b3aba0ea3c1dcdd41d83e39e3d8f8f") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "matching" "fuzzy") (:url . "https://github.com/jcs-elpa/liquidmetal"))]) + (liquidmetal . [(20220704 654) ((emacs (24 4))) "A mimetic poly-alloy of the Quicksilver scoring algorithm" single ((:commit . "e2d62849a2295dbc9d457cfcfadca3769ee62199") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "matching" "fuzzy") (:url . "https://github.com/jcs-elpa/liquidmetal"))]) (liso-theme . [(20160410 2029) nil "Eclectic Dark Theme for GNU Emacs" single ((:commit . "844688245eb860d23043455e165ee24503454c81") (:authors ("Vlad Piersec" . "vlad.piersec@gmail.com")) (:maintainer "Vlad Piersec" . "vlad.piersec@gmail.com") (:keywords "theme" "themes") (:url . "https://github.com/caisah/liso-theme"))]) (lisp-butt-mode . [(20210215 2206) ((emacs (25))) "Slim Lisp Butts" single ((:commit . "7330f08dd85ee715096f3596df516877894c6c2f") (:authors ("Marco Wahl" . "marcowahlsoft@gmail.com")) (:maintainer "Marco Wahl" . "marcowahlsoft@gmail.com") (:keywords "lisp") (:url . "https://gitlab.com/marcowahl/lisp-butt-mode"))]) (lisp-extra-font-lock . [(20181008 1921) nil "Highlight bound variables and quoted exprs." single ((:commit . "4605eccbe1a7fcbd3cacf5b71249435413b4db4f") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "languages" "faces") (:url . "https://github.com/Lindydancer/lisp-extra-font-lock"))]) (lisp-local . [(20210605 1347) ((emacs (24 3))) "Allow different Lisp indentation in each buffer" single ((:commit . "22e221c9330d2b5dc07e8b2caa34c83ac7c20b0d") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "lisp") (:url . "https://github.com/lispunion/emacs-lisp-local"))]) (lispxmp . [(20170926 23) nil "Automagic emacs lisp code annotation" single ((:commit . "7ad077b4ee91ce8a42f84eeddb9fc7ea4eac7814") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "lisp" "convenience") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/lispxmp.el"))]) - (lispy . [(20220526 1832) ((emacs (24 3)) (ace-window (0 9 0)) (iedit (0 9 9)) (swiper (0 13 4)) (hydra (0 14 0)) (zoutline (0 2 0))) "vi-like Paredit" tar ((:commit . "dbab5899f26fa2ee27f5c2e8b32c20f2f69242fb") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "lisp") (:url . "https://github.com/abo-abo/lispy"))]) + (lispy . [(20220817 1839) ((emacs (24 3)) (ace-window (0 9 0)) (iedit (0 9 9)) (swiper (0 13 4)) (hydra (0 14 0)) (zoutline (0 2 0))) "vi-like Paredit" tar ((:commit . "887a5a0a296fb361bdc6bb56974a5ac901c5ad21") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "lisp") (:url . "https://github.com/abo-abo/lispy"))]) (lispyville . [(20220715 29) ((lispy (0)) (evil (1 2 12)) (cl-lib (0 5)) (emacs (24 4))) "A minor mode for integrating evil with lispy." single ((:commit . "14ee8711d58b649aeac03581d22b10ab077f06bd") (:authors ("Fox Kiester" . "noct@posteo.net")) (:maintainer "Fox Kiester" . "noct@posteo.net") (:keywords "vim" "evil" "lispy" "lisp" "parentheses") (:url . "https://github.com/noctuid/lispyville"))]) (list-environment . [(20210930 1439) nil "A tabulated process environment editor" single ((:commit . "0a72a5a9c1abc090b25202a0387e3f766994b053") (:authors ("Charles L.G. Comstock" . "dgtized@gmail.com")) (:maintainer "Charles L.G. Comstock" . "dgtized@gmail.com") (:keywords "processes" "unix"))]) (list-packages-ext . [(20151115 1716) ((s (1 6 0)) (ht (1 5 0)) (persistent-soft (0 8 6))) "Extras for list-packages" single ((:commit . "b4dd644e4369c9aa66f5bb8895ea49ebbfd0a27a") (:authors ("Alessandro Piras" . "laynor@gmail.com")) (:maintainer "Alessandro Piras" . "laynor@gmail.com") (:keywords "convenience" "tools"))]) @@ -2837,10 +2844,10 @@ (literate-coffee-mode . [(20170211 1515) ((coffee-mode (0 5 0))) "major-mode for Literate CoffeeScript" single ((:commit . "55ce0305495f4a38c8063c4bd63deb1e1252373d") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-literate-coffee-mode"))]) (literate-elisp . [(20220626 932) ((emacs (26 1))) "Load Emacs Lisp code blocks from Org files" single ((:commit . "62c9ed0478d7e8aa832f848074e15c5be2a0cae7") (:authors ("Jingtao Xu" . "jingtaozf@gmail.com")) (:maintainer "Jingtao Xu" . "jingtaozf@gmail.com") (:keywords "lisp" "docs" "extensions" "tools") (:url . "https://github.com/jingtaozf/literate-elisp"))]) (literate-starter-kit . [(20150730 1854) ((emacs (24 3))) "A literate starter kit to configure Emacs using Org-mode files." tar ((:commit . "6dce1d01781966c14558aa553cfc85008c06e115"))]) - (litex-mode . [(20220415 1704) ((cl-lib (0 5)) (emacs (24 1))) "Minor mode for converting lisp to LaTeX" tar ((:commit . "5d5750af2990c050c8d36baa4b8e7a45850d5a6a") (:authors ("Gaurav Atreya" . "allmanpride@gmail.com")) (:maintainer "Gaurav Atreya" . "allmanpride@gmail.com") (:keywords "calculator" "lisp" "latex") (:url . "https://github.com/Atreyagaurav/litex-mode"))]) + (litex-mode . [(20220823 2231) ((emacs (24 4))) "Minor mode for converting lisp to LaTeX" tar ((:commit . "c4a1071fcf6d2bec9eb367ede8c33b190e9ff802") (:authors ("Gaurav Atreya" . "allmanpride@gmail.com")) (:maintainer "Gaurav Atreya" . "allmanpride@gmail.com") (:keywords "calculator" "lisp" "latex") (:url . "https://github.com/Atreyagaurav/litex-mode"))]) (live-code-talks . [(20180907 1647) ((emacs (24)) (cl-lib (0 5)) (narrowed-page-navigation (0 1))) "Support for slides with live code in them" single ((:commit . "97f16a9ee4e6ff3e0f9291eaead772c66e3e12ae") (:authors ("David Raymond Christiansen" . "david@davidchristiansen.dk")) (:maintainer "David Raymond Christiansen" . "david@davidchristiansen.dk") (:keywords "docs" "multimedia"))]) (live-preview . [(20201010 1948) ((emacs (24 4))) "Live preview by any shell command while editing" single ((:commit . "603a4a1759fbec92e7a1cabc249517c78e59ce7e") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/lassik/emacs-live-preview"))]) - (live-py-mode . [(20220518 204) ((emacs (24 3))) "Live Coding in Python" tar ((:commit . "a97e83f5dc9101359beadc0bc409a9bda79e91eb") (:authors ("Don Kirkby http://donkirkby.github.io")) (:maintainer "Don Kirkby http://donkirkby.github.io") (:keywords "live" "coding") (:url . "http://donkirkby.github.io/live-py-plugin/"))]) + (live-py-mode . [(20220518 204) ((emacs (24 3))) "Live Coding in Python" tar ((:commit . "351820438f6f416ac9bc991a4d120cd6a4720158") (:authors ("Don Kirkby http://donkirkby.github.io")) (:maintainer "Don Kirkby http://donkirkby.github.io") (:keywords "live" "coding") (:url . "http://donkirkby.github.io/live-py-plugin/"))]) (lively . [(20171005 754) nil "interactively updating text" single ((:commit . "348675828c6a81bfa1ac311ca465aad813542c1b") (:authors ("Luke Gorrie" . "luke@bup.co.nz")) (:maintainer "Steve Purcell" . "steve@sanityinc.com"))]) (livereload . [(20170629 650) ((emacs (25)) (websocket (1 8))) "Livereload server" tar ((:commit . "1e501d7e46dbd476c2c7cc9d20b5ac9d41fb1955") (:authors ("João Távora" . "joaotavora@gmail.com")) (:maintainer "João Távora" . "joaotavora@gmail.com") (:keywords "convenience"))]) (livescript-mode . [(20140613 421) nil "Major mode for editing LiveScript files" single ((:commit . "90a918d9686e256e6d4d439cc20f24dad8d3b804") (:authors ("Hisamatsu Yasuyuki" . "yas@null.net")) (:maintainer "Hisamatsu Yasuyuki" . "yas@null.net") (:keywords "languages" "livescript") (:url . "https://github.com/yhisamatsu/livescript-mode"))]) @@ -2860,7 +2867,7 @@ (log4j-mode . [(20160108 1918) nil "major mode for viewing log files" single ((:commit . "26171b1e723502055e085393b0ecdcb6db406010") (:authors ("Johan Dykstrom" . "jody4711-sf@yahoo.se")) (:maintainer "Johan Dykstrom" . "jody4711-sf@yahoo.se") (:keywords "tools") (:url . "http://log4j-mode.sourceforge.net"))]) (logalimacs . [(20131021 1829) ((popwin (0 6 2)) (popup (0 5 0)) (stem (20130120))) "Front-end to logaling-command for Ruby gems" single ((:commit . "8286e39502250fc6c3c6656a7f46a8eee8e9a713") (:authors ("Yuta Yamada ")) (:maintainer "Yuta Yamada ") (:keywords "translation" "logaling-command") (:url . "https://github.com/logaling/logalimacs"))]) (logito . [(20201226 534) ((emacs (25 1))) "logging library for Emacs" single ((:commit . "d5934ce10ba3a70d3fcfb94d742ce3b9136ce124") (:authors ("Yann Hodique" . "yann.hodique@gmail.com")) (:maintainer "Yann Hodique" . "yann.hodique@gmail.com") (:keywords "lisp" "extensions"))]) - (logms . [(20220704 654) ((emacs (27 1)) (f (0 20 0)) (s (1 9 0)) (ht (2 3))) "Log message with clickable links to context" single ((:commit . "c5c2e8c3850417d8c1b0733b77a364002805863c") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "maint" "debug" "log") (:url . "https://github.com/jcs-elpa/logms"))]) + (logms . [(20220704 654) ((emacs (27 1)) (f (0 20 0)) (s (1 9 0)) (ht (2 3))) "Log message with clickable links to context" single ((:commit . "7c110d94a7d17c55fc1fac489f307f2e6055d82e") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "maint" "debug" "log") (:url . "https://github.com/jcs-elpa/logms"))]) (lognav-mode . [(20220410 1344) ((emacs (24 3))) "Navigate Log Error Messages" single ((:commit . "100541ec31468b771073a7d2ad4512c1dcb1eb07") (:authors ("Shawn Ellis" . "shawn.ellis17@gmail.com")) (:maintainer "Shawn Ellis" . "shawn.ellis17@gmail.com") (:keywords "log" "error" "lognav-mode" "convenience") (:url . "https://hg.osdn.net/view/lognav-mode/lognav-mode"))]) (logpad . [(20201113 917) nil "Simulate Windows Notepad for logging." single ((:commit . "166543873e665936b468d9f120155cce515da3f8") (:authors ("Sven Knurr" . "git@tuxproject.de")) (:maintainer "Sven Knurr" . "git@tuxproject.de") (:keywords "files" "outlines" "notepad") (:url . "https://github.com/dertuxmalwieder/logpad.el"))]) (logstash-conf . [(20210123 1949) nil "basic mode for editing logstash configuration" single ((:commit . "ebc4731c45709ad1e0526f4f4164020ae83cbeff") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk"))]) @@ -2872,27 +2879,27 @@ (look-mode . [(20220626 641) nil "quick file viewer for image and text file browsing" single ((:commit . "726c5b9098926278603a83e978b488371c0e9143") (:authors ("Peter H. Mao " . "petermao@jpl.nasa.gov")) (:maintainer "Peter H. Mao " . "petermao@jpl.nasa.gov"))]) (loop . [(20160813 1407) nil "friendly imperative loop structures" single ((:commit . "9db6372791bbd0cf3fa907ed0ae3e6b7bcf6cc57") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "loop" "while" "for each" "break" "continue"))]) (loophole . [(20220611 1100) ((emacs (27 1))) "Manage temporary key bindings" single ((:commit . "f7e9cca980b7467a03d9a161a5e9e5a828bfd6c9") (:authors ("0x60DF" . "0x60df@gmail.com")) (:maintainer "0x60DF" . "0x60df@gmail.com") (:keywords "convenience") (:url . "https://github.com/0x60df/loophole"))]) - (loopy . [(20220731 202) ((emacs (27 1)) (map (3 0)) (seq (2 22))) "A looping macro" tar ((:commit . "10a9be5f0b143dee454bb64d2b5146e4509553d6") (:authors ("Earl Hyatt")) (:maintainer "Earl Hyatt") (:keywords "extensions") (:url . "https://github.com/okamsn/loopy"))]) - (loopy-dash . [(20220330 127) ((emacs (25 1)) (loopy (0 10 1)) (dash (2 19))) "Dash destructuring for `loopy'" single ((:commit . "10a9be5f0b143dee454bb64d2b5146e4509553d6") (:authors ("Earl Hyatt")) (:maintainer "Earl Hyatt") (:keywords "extensions") (:url . "https://github.com/okamsn/loopy"))]) + (loopy . [(20220821 1746) ((emacs (27 1)) (map (3 0)) (seq (2 22))) "A looping macro" tar ((:commit . "e3835d4013da423cbdabbeac889a9a2a70ef17c7") (:authors ("Earl Hyatt")) (:maintainer "Earl Hyatt") (:keywords "extensions") (:url . "https://github.com/okamsn/loopy"))]) + (loopy-dash . [(20220330 127) ((emacs (25 1)) (loopy (0 10 1)) (dash (2 19))) "Dash destructuring for `loopy'" single ((:commit . "e3835d4013da423cbdabbeac889a9a2a70ef17c7") (:authors ("Earl Hyatt")) (:maintainer "Earl Hyatt") (:keywords "extensions") (:url . "https://github.com/okamsn/loopy"))]) (lorem-ipsum . [(20190819 2042) nil "Insert dummy pseudo Latin text." single ((:commit . "da75c155da327c7a7aedb80f5cfe409984787049") (:authors ("Jean-Philippe Theberge" . "jphil21@sourceforge.net")) (:maintainer "Joe Schafer" . "joe@jschaf.com") (:keywords "tools" "language" "convenience"))]) (lox-mode . [(20200619 1700) ((emacs (24 3))) "Major mode for the Lox programming language" single ((:commit . "b6935b3f5b131d2c1c7685cf6464274f7cd64943") (:authors ("Timmy Jose" . "zoltan.jose@gmail.com")) (:maintainer "Timmy Jose" . "zoltan.jose@gmail.com") (:keywords "languages" "lox") (:url . "https://github.com/timmyjose-projects/lox-mode"))]) - (lpy . [(20201027 1425) ((emacs (25 1)) (lispy (0 27 0))) "A lispy interface to Python" tar ((:commit . "076ce9acb68f6ac1b39127b634a91ffd865d13d8") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "python" "lisp") (:url . "https://github.com/abo-abo/lpy"))]) - (lsp-cfn . [(20220707 824) ((emacs (27 0)) (lsp-mode (8 0 0)) (yaml-mode (0 0 15))) "LSP integration for cfn-lsp-extra" tar ((:commit . "8228864e0b16f7107300fb6b8ba595bb84f016fd") (:authors ("Laurence Warne")) (:maintainer "Laurence Warne") (:url . "https://github.com/LaurenceWarne/lsp-cfn.el"))]) + (lpy . [(20220818 1613) ((emacs (25 1)) (lispy (0 27 0))) "A lispy interface to Python" tar ((:commit . "ce78a4613458790cc785c1687af7eed8f0d8d66c") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "python" "lisp") (:url . "https://github.com/abo-abo/lpy"))]) + (lsp-cfn . [(20220822 1545) ((emacs (27 0)) (lsp-mode (8 0 0)) (yaml-mode (0 0 15))) "LSP integration for cfn-lsp-extra" tar ((:commit . "55203d41d767c091511599fca236c1f9c39cd58a") (:authors ("Laurence Warne")) (:maintainer "Laurence Warne") (:url . "https://github.com/LaurenceWarne/lsp-cfn.el"))]) (lsp-dart . [(20220717 1834) ((emacs (26 3)) (lsp-treemacs (0 3)) (lsp-mode (7 0 1)) (dap-mode (0 6)) (f (0 20 0)) (dash (2 14 1)) (dart-mode (1 0 5))) "Dart support lsp-mode" tar ((:commit . "a312fe8cd941b641216d4654da2e8be82b16d611") (:keywords "languages" "extensions") (:url . "https://emacs-lsp.github.io/lsp-dart"))]) (lsp-docker . [(20220513 1434) ((emacs (26 1)) (dash (2 14 1)) (lsp-mode (6 2 1)) (f (0 20 0)) (yaml (0 2 0)) (ht (2 0))) "LSP Docker integration" single ((:commit . "a0d7cbf80652429c0be4dc7d39e1887ba4691ec7") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "languages" "langserver") (:url . "https://github.com/emacs-lsp/lsp-docker"))]) (lsp-focus . [(20200906 1917) ((emacs (26 1)) (focus (0 1 1)) (lsp-mode (6 1))) "focus.el support for lsp-mode" single ((:commit . "d01f0af156e4e78dcb9fa8e080a652cf8f221d30") (:authors ("Vibhav Pant")) (:maintainer "Vibhav Pant") (:keywords "languages" "lsp-mode") (:url . "https://github.com/emacs-lsp/lsp-focus"))]) - (lsp-grammarly . [(20220704 626) ((emacs (27 1)) (lsp-mode (6 1)) (grammarly (0 3 0)) (request (0 3 0)) (s (1 12 0)) (ht (2 3))) "LSP Clients for Grammarly" single ((:commit . "ece2c7d8c119213d4622deca756f4affedbabc42") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp" "grammarly" "checker") (:url . "https://github.com/emacs-grammarly/lsp-grammarly"))]) - (lsp-haskell . [(20220307 2312) ((emacs (24 3)) (lsp-mode (3 0))) "Haskell support for lsp-mode" single ((:commit . "daa51072e1718ca075987901fccbbc2357bca1fc") (:keywords "haskell") (:url . "https://github.com/emacs-lsp/lsp-haskell"))]) + (lsp-grammarly . [(20220704 626) ((emacs (27 1)) (lsp-mode (6 1)) (grammarly (0 3 0)) (request (0 3 0)) (s (1 12 0)) (ht (2 3))) "LSP Clients for Grammarly" single ((:commit . "1fa44a3f9ccffb09021bf05a5e7d905061fe5a95") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp" "grammarly" "checker") (:url . "https://github.com/emacs-grammarly/lsp-grammarly"))]) + (lsp-haskell . [(20220809 2129) ((emacs (24 3)) (lsp-mode (3 0))) "Haskell support for lsp-mode" single ((:commit . "485c1148ce4d27030bb95b21c7289809294e7d31") (:keywords "haskell") (:url . "https://github.com/emacs-lsp/lsp-haskell"))]) (lsp-intellij . [(20180831 2051) ((emacs (25 1)) (lsp-mode (4 1))) "intellij lsp client" single ((:commit . "cf30f0ac63bd0140e758840b8ab070e8313697b2") (:authors ("Ruin0x11" . "ipickering2@gmail.com")) (:maintainer "Ruin0x11" . "ipickering2@gmail.com") (:keywords "languages" "processes" "tools") (:url . "https://github.com/Ruin0x11/lsp-intellij"))]) (lsp-ivy . [(20210904 2043) ((emacs (25 1)) (dash (2 14 1)) (lsp-mode (6 2 1)) (ivy (0 13 0))) "LSP ivy integration" single ((:commit . "3e87441a625d65ced5a208a0b0442d573596ffa3") (:keywords "languages" "debug") (:url . "https://github.com/emacs-lsp/lsp-ivy"))]) - (lsp-java . [(20220623 455) ((emacs (25 1)) (lsp-mode (6 0)) (markdown-mode (2 3)) (dash (2 18 0)) (f (0 20 0)) (ht (2 0)) (request (0 3 0)) (treemacs (2 5)) (dap-mode (0 5))) "Java support for lsp-mode" tar ((:commit . "a1aff851bcf4f397f2a968557d213db1fede0c8a") (:keywords "languague" "tools") (:url . "https://github.com/emacs-lsp/lsp-java"))]) + (lsp-java . [(20220825 450) ((emacs (25 1)) (lsp-mode (6 0)) (markdown-mode (2 3)) (dash (2 18 0)) (f (0 20 0)) (ht (2 0)) (request (0 3 0)) (treemacs (2 5)) (dap-mode (0 5))) "Java support for lsp-mode" tar ((:commit . "13b92b097f91f9296ca65a1dc3eda992315c67ea") (:keywords "languague" "tools") (:url . "https://github.com/emacs-lsp/lsp-java"))]) (lsp-javacomp . [(20190124 1755) ((emacs (25 1)) (lsp-mode (3 0)) (s (1 2 0))) "Provide Java IDE features powered by JavaComp." single ((:commit . "82aa4ad6ca03a74565c35e855b318b1887bcd89b") (:keywords "java" "tools" "lsp") (:url . "https://github.com/tigersoldier/lsp-javacomp"))]) (lsp-jedi . [(20220430 18) ((emacs (25 1)) (lsp-mode (6 0))) "Lsp client plugin for Python Jedi Language Server" single ((:commit . "5e3eb3e160c2d38b8bd2b5cd3b86fa4f823f9330") (:authors ("Fred Campos" . "fred.tecnologia@gmail.com")) (:maintainer "Fred Campos") (:keywords "language-server" "tools" "python" "jedi" "ide") (:url . "http://github.com/fredcamps/lsp-jedi"))]) (lsp-julia . [(20211229 1534) ((emacs (25 1)) (lsp-mode (6 3)) (julia-mode (0 3))) "Julia support for lsp-mode" tar ((:commit . "d6688bb131ff4a5a0201f6d3826ef0b018265389") (:authors ("Martin Wolke" . "vibhavp@gmail.com") ("Adam Beckmeyer" . "adam_git@thebeckmeyers.xyz") ("Guido Kraemer" . "gdkrmr@users.noreply.github.com")) (:maintainer "Guido Kraemer" . "gdkrmr@users.noreply.github.com") (:keywords "languages" "tools") (:url . "https://github.com/gdkrmr/lsp-julia"))]) (lsp-latex . [(20210815 1426) ((emacs (25 1)) (lsp-mode (6 0))) "LSP-mode client for LaTeX, on texlab" single ((:commit . "3f6b2ac9585682828eef81f895757f74cfba7309") (:authors ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainer "ROCKTAKEY" . "rocktakey@gmail.com") (:keywords "languages" "tex") (:url . "https://github.com/ROCKTAKEY/lsp-latex"))]) - (lsp-ltex . [(20220802 1532) ((emacs (27 1)) (lsp-mode (6 1))) "LSP Clients for LTEX" single ((:commit . "5c26bae674990537b512da00a8b00e69d8e38e01") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp" "languagetool" "checker") (:url . "https://github.com/emacs-languagetool/lsp-ltex"))]) + (lsp-ltex . [(20220806 1456) ((emacs (27 1)) (lsp-mode (6 1))) "LSP Clients for LTEX" single ((:commit . "b673fce2c46d00da216a32a6c04558c3cbb68939") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp" "languagetool" "checker") (:url . "https://github.com/emacs-languagetool/lsp-ltex"))]) (lsp-metals . [(20220715 2128) ((emacs (26 1)) (scala-mode (1 1)) (lsp-mode (7 0)) (lsp-treemacs (0 2)) (dap-mode (0 3)) (dash (2 18 0)) (f (0 20 0)) (ht (2 0)) (treemacs (2 5))) "Scala Client settings" tar ((:commit . "097d6021a4ff0eae704cc3074e064c9509c5cafc") (:authors ("Ross A. Baker" . "ross@rossabaker.com") ("Evgeny Kurnevsky" . "kurnevsky@gmail.com")) (:maintainer "Ross A. Baker" . "ross@rossabaker.com") (:keywords "languages" "extensions") (:url . "https://github.com/emacs-lsp/lsp-metals"))]) - (lsp-mode . [(20220804 1407) ((emacs (26 1)) (dash (2 18 0)) (f (0 20 0)) (ht (2 3)) (spinner (1 7 3)) (markdown-mode (2 3)) (lv (0))) "LSP mode" tar ((:commit . "0f56580e98eb8d4fed32bebd62281249604735b3") (:authors ("Vibhav Pant, Fangrui Song, Ivan Yonchovski")) (:maintainer "Vibhav Pant, Fangrui Song, Ivan Yonchovski") (:keywords "languages") (:url . "https://github.com/emacs-lsp/lsp-mode"))]) + (lsp-mode . [(20220824 721) ((emacs (26 1)) (dash (2 18 0)) (f (0 20 0)) (ht (2 3)) (spinner (1 7 3)) (markdown-mode (2 3)) (lv (0))) "LSP mode" tar ((:commit . "9c47b6d44f6fd78410f13e496d83682dbd52da0e") (:authors ("Vibhav Pant, Fangrui Song, Ivan Yonchovski")) (:maintainer "Vibhav Pant, Fangrui Song, Ivan Yonchovski") (:keywords "languages") (:url . "https://github.com/emacs-lsp/lsp-mode"))]) (lsp-mssql . [(20191204 1150) ((emacs (25 1)) (lsp-mode (6 2)) (dash (2 14 1)) (f (0 20 0)) (ht (2 0)) (lsp-treemacs (0 1))) "MSSQL LSP bindings" tar ((:commit . "8d5d4d4a7f72b4cae89a48ea8618f3ef28bcb121") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "data" "languages") (:url . "https://github.com/emacs-lsp/lsp-mssql"))]) (lsp-origami . [(20211016 1045) ((origami (1 0)) (lsp-mode (6 1))) "origami.el support for lsp-mode" single ((:commit . "7df9c91a309aa4229bec41f109920b37c4197618") (:authors ("Vibhav Pant")) (:maintainer "Vibhav Pant") (:keywords "languages" "lsp-mode") (:url . "https://github.com/emacs-lsp/lsp-origami"))]) (lsp-p4 . [(20190127 1049) ((lsp-mode (3 0))) "P4 support for lsp-mode" tar ((:commit . "669460d93b87fb876df11b2b68229677e7ad1a26") (:authors ("Dmitri Makarov")) (:maintainer "Dmitri Makarov") (:keywords "lsp" "p4") (:url . "https://github.com/dmakarov/p4ls"))]) @@ -2901,12 +2908,12 @@ (lsp-pyright . [(20220614 1545) ((emacs (26 1)) (lsp-mode (7 0)) (dash (2 18 0)) (ht (2 0))) "Python LSP client using Pyright" single ((:commit . "c745228f39fdb35372b29b909f25fa6c98dc7c88") (:authors ("Arif Rezai, Vincent Zhang, Andrew Christianson")) (:maintainer "Arif Rezai, Vincent Zhang, Andrew Christianson") (:keywords "languages" "tools" "lsp") (:url . "https://github.com/emacs-lsp/lsp-pyright"))]) (lsp-python-ms . [(20211204 1209) ((emacs (25 1)) (lsp-mode (6 1))) "The lsp-mode client for Microsoft python-language-server" single ((:commit . "f8e7c4bcaefbc3fd96e1ca53d17589be0403b828") (:authors ("Charl Botha")) (:maintainer "Andrew Christianson, Vincent Zhang") (:keywords "languages" "tools") (:url . "https://github.com/emacs-lsp/lsp-python-ms"))]) (lsp-rescript . [(20220314 1957) ((lsp-mode (7 0 1)) (emacs (25 1)) (rescript-mode (0 1))) "LSP client configuration for lsp-mode and rescript-vscode" single ((:commit . "7baf9adf10234cf964feefae99050268e9bc5681") (:authors ("John Lee")) (:maintainer "John Lee") (:keywords "languages") (:url . "https://github.com/jjlee/lsp-rescript"))]) - (lsp-scheme . [(20220804 1433) ((emacs (25 1)) (f (0 20 0)) (lsp-mode (8 0 0))) "Scheme support for lsp-mode" tar ((:commit . "55783a92c65bfabcb8252f4db0c1099e2b31d8d9") (:authors ("Ricardo G. Herdt" . "r.herdt@posteo.de")) (:maintainer "Ricardo G. Herdt" . "r.herdt@posteo.de") (:keywords "languages" "lisp" "tools") (:url . "https://codeberg.org/rgherdt/emacs-lsp-scheme"))]) + (lsp-scheme . [(20220809 2014) ((emacs (26 1)) (f (0 20 0)) (lsp-mode (8 0 0))) "Scheme support for lsp-mode" tar ((:commit . "02e56f4c4981bc5497cdd516969206418858a357") (:authors ("Ricardo G. Herdt" . "r.herdt@posteo.de")) (:maintainer "Ricardo G. Herdt" . "r.herdt@posteo.de") (:keywords "languages" "lisp" "tools") (:url . "https://codeberg.org/rgherdt/emacs-lsp-scheme"))]) (lsp-sonarlint . [(20210820 2044) ((emacs (25)) (dash (2 12 0)) (lsp-mode (6 3)) (ht (2 3))) "Emacs Sonarlint lsp client" tar ((:commit . "a429be2aea7797369a3c751ef54e3554733117be") (:authors ("Fermin MF" . "fmfs@posteo.net")) (:maintainer "Fermin MF" . "fmfs@posteo.net") (:keywords "languages" "tools" "php" "javascript" "xml" "ruby" "html" "scala" "java" "python") (:url . "https://github.com/emacs-lsp/lsp-sonarlint"))]) (lsp-sourcekit . [(20210905 2017) ((emacs (25 1)) (lsp-mode (5))) "sourcekit-lsp client for lsp-mode" single ((:commit . "f877659babd3b5f8ec09a8ad7d08193d95b6822e") (:authors ("Daniel Martín")) (:maintainer "Daniel Martín") (:keywords "languages" "lsp" "swift" "objective-c" "c++") (:url . "https://github.com/emacs-lsp/lsp-sourcekit"))]) - (lsp-tailwindcss . [(20220601 1509) ((lsp-mode (7 1)) (f (0 20 0)) (emacs (26 1))) "A lsp-mode client for tailwindcss" single ((:commit . "09377d5bbb742f11c458481905ddbb022dd2b0da") (:authors ("A.I." . "merrick@luois.me")) (:maintainer "A.I." . "merrick@luois.me") (:keywords "language" "tools") (:url . "https://github.com/merrickluo/lsp-tailwindcss"))]) + (lsp-tailwindcss . [(20220816 1602) ((lsp-mode (7 1)) (f (0 20 0)) (emacs (26 1))) "A lsp-mode client for tailwindcss" single ((:commit . "6cff7196d0742a359a58c21a719a2cd436006e12") (:authors ("A.I." . "merrick@luois.me")) (:maintainer "A.I." . "merrick@luois.me") (:keywords "language" "tools") (:url . "https://github.com/merrickluo/lsp-tailwindcss"))]) (lsp-treemacs . [(20220502 459) ((emacs (26 1)) (dash (2 18 0)) (f (0 20 0)) (ht (2 0)) (treemacs (2 5)) (lsp-mode (6 0))) "LSP treemacs" tar ((:commit . "9859326df6b8e8c954a3c227e53b6878e54aaae8") (:authors ("Ivan Yonchovski")) (:maintainer "Ivan Yonchovski") (:keywords "languages") (:url . "https://github.com/emacs-lsp/lsp-treemacs"))]) - (lsp-ui . [(20220723 1213) ((emacs (26 1)) (dash (2 18 0)) (lsp-mode (6 0)) (markdown-mode (2 3))) "UI modules for lsp-mode" tar ((:commit . "8d4fa5a14f5b5c6f57bc69f454eba6861ed2ba9f") (:authors ("Sebastien Chapuis , Fangrui Song" . "i@maskray.me")) (:maintainer "Sebastien Chapuis , Fangrui Song" . "i@maskray.me") (:keywords "languages" "tools") (:url . "https://github.com/emacs-lsp/lsp-ui"))]) + (lsp-ui . [(20220823 1626) ((emacs (26 1)) (dash (2 18 0)) (lsp-mode (6 0)) (markdown-mode (2 3))) "UI modules for lsp-mode" tar ((:commit . "d8cce7dc154aa3216c080dd5c6fb827bdba9a312") (:authors ("Sebastien Chapuis , Fangrui Song" . "i@maskray.me")) (:maintainer "Sebastien Chapuis , Fangrui Song" . "i@maskray.me") (:keywords "languages" "tools") (:url . "https://github.com/emacs-lsp/lsp-ui"))]) (lua-mode . [(20220801 503) ((emacs (24 3))) "a major-mode for editing Lua scripts" single ((:commit . "d17a00ca50aee197cd017d573b83367eb241cc44") (:authors ("2011-2013 immerrr" . "immerrr+lua@gmail.com") ("2010-2011 Reuben Thomas" . "rrt@sc3d.org") ("2006 Juergen Hoetzel" . "juergen@hoetzel.info") ("2004 various (support for Lua 5 and byte compilation)") ("2001 Christian Vogler" . "cvogler@gradient.cis.upenn.edu") ("1997 Bret Mogilefsky" . "mogul-lua@gelatinous.com") ("tcl-mode by Gregor Schmid" . "schmid@fb3-s7.math.tu-berlin.de") ("with tons of assistance from") ("Paul Du Bois" . "pld-lua@gelatinous.com") ("Aaron Smith" . "aaron-lua@gelatinous.com")) (:maintainer "2011-2013 immerrr" . "immerrr+lua@gmail.com") (:keywords "languages" "processes" "tools") (:url . "https://immerrr.github.io/lua-mode"))]) (luarocks . [(20170430 2305) ((emacs (24)) (cl-lib (0 5))) "luarocks tools" single ((:commit . "cee27ba0716edf338077387969883226dd2b7484") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "convenience") (:url . "https://github.com/emacs-pe/luarocks.el"))]) (lush-theme . [(20180816 2200) ((emacs (24))) "A dark theme with lush colors" single ((:commit . "645e1959143532df8f7ef90e1184e9556df18af7") (:authors ("Andre Richter" . "andre.o.richter@gmail.com")) (:maintainer "Andre Richter" . "andre.o.richter@gmail.com") (:keywords "theme" "dark" "strong colors") (:url . "https://github.com/andre-richter/emacs-lush-theme"))]) @@ -2921,7 +2928,7 @@ (m-buffer . [(20220719 1850) ((seq (2 14))) "List-Oriented, Functional Buffer Manipulation" tar ((:commit . "ecdc61282155739acd6b03233d5733e1ad57da1b") (:authors ("Phillip Lord" . "phillip.lord@russet.org.uk")) (:maintainer "Phillip Lord" . "phillip.lord@russet.rg.uk"))]) (mac-pseudo-daemon . [(20200215 513) ((cl-lib (0 1))) "Daemon mode that plays nice with Mac OS." single ((:commit . "94240ebb716f11af8427b6295c3f44c0c43419d3") (:authors ("Ryan C. Thompson")) (:maintainer "Ryan C. Thompson") (:keywords "convenience" "osx" "mac") (:url . "https://github.com/DarwinAwardWinner/osx-pseudo-daemon"))]) (maces-game . [(20170903 1551) ((dash (2 12 0)) (cl-lib (0 5)) (emacs (24))) "another anagram game." tar ((:commit . "c0fb795f5642467ea528d2f04d904547e8a77ecd") (:authors ("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 . [(20220730 1035) ((emacs (25 1)) (transient (0 1 0))) "A porcelain for MacPorts" tar ((:commit . "f449d3f29077f8a5088f9cbc27f5d70fe87b18fc") (:authors ("Aaron Madlon-Kay")) (:maintainer "Aaron Madlon-Kay") (:keywords "convenience") (:url . "https://github.com/amake/macports.el"))]) + (macports . [(20220820 1110) ((emacs (25 1)) (transient (0 1 0))) "A porcelain for MacPorts" tar ((:commit . "771f1a71ac9e93b4328089ec133ef2dd0f287b0c") (:authors ("Aaron Madlon-Kay")) (:maintainer "Aaron Madlon-Kay") (:keywords "convenience") (:url . "https://github.com/amake/macports.el"))]) (macro-math . [(20130328 1604) nil "in-buffer mathematical operations" single ((:commit . "216e59371e9ee39c34117ba79b9acd78bb415750") (:authors ("Nikolaj Schumacher ")) (:maintainer "Nikolaj Schumacher ") (:keywords "convenience") (:url . "http://nschum.de/src/emacs/macro-math/"))]) (macrostep . [(20161120 2106) ((cl-lib (0 5))) "interactive macro expander" tar ((:commit . "424e3734a1ee526a1bd7b5c3cd1d3ef19d184267") (:authors ("joddie" . "j.j.oddie@gmail.com")) (:maintainer "joddie" . "j.j.oddie@gmail.com") (:keywords "lisp" "languages" "macro" "debugging") (:url . "https://github.com/joddie/macrostep"))]) (macrostep-geiser . [(20210717 801) ((emacs (24 4)) (macrostep (0 9)) (geiser (0 12))) "Macrostep for `geiser'" single ((:commit . "f6a2d5bb96ade4f23df557649af87ebd0cc45125") (:authors ("Nikita Bloshchanevich")) (:maintainer "Nikita Bloshchanevich") (:keywords "languages" "scheme") (:url . "https://github.com/nbfalcon/macrostep-geiser"))]) @@ -2930,10 +2937,10 @@ (magic-filetype . [(20180219 1552) ((emacs (24)) (s (1 9 0))) "Enhance filetype major mode" single ((:commit . "019494add5ff02dd36cb3f500142fc51125522cc") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "emulations" "vim" "ft" "file" "magic-mode") (:url . "https://github.com/zonuexe/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" single ((:commit . "903ec91872760e47c0e5715795f8465173615098") (:authors ("zk_phi")) (:maintainer "zk_phi") (:url . "http://zk-phi.github.io/"))]) (magik-mode . [(20220702 1158) nil "mode for editing Magik + some utils." tar ((:commit . "a7c5553d2780640630d31b469d522ecffdf052f5") (:keywords "languages") (:url . "http://github.com/roadrunner1776/magik"))]) - (magit . [(20220803 2341) ((emacs (25 1)) (compat (28 1 1 2)) (dash (20210826)) (git-commit (20220222)) (magit-section (20220325)) (transient (20220325)) (with-editor (20220318))) "A Git porcelain inside Emacs." tar ((:commit . "ac7fae6a9893b55ad01942d9ea5a571d44426665") (:authors ("Marius Vollmer" . "marius.vollmer@gmail.com") ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) + (magit . [(20220821 1819) ((emacs (25 1)) (compat (28 1 1 2)) (dash (20210826)) (git-commit (20220222)) (magit-section (20220325)) (transient (20220325)) (with-editor (20220318))) "A Git porcelain inside Emacs." tar ((:commit . "712be4632b0ddc7899ca90db8f9be20d90b4326f") (:authors ("Marius Vollmer" . "marius.vollmer@gmail.com") ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) (magit-annex . [(20220302 1725) ((cl-lib (0 3)) (magit (3 0 0))) "Control git-annex from Magit" single ((:commit . "efe484644666c6b7c544b0fb7b87e30703fa9425") (:authors ("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-circleci . [(20191209 2113) ((dash (2 16 0)) (transient (0 1 0)) (magit (2 90 0)) (emacs (25 3))) "CircleCI integration for Magit" single ((:commit . "2d4bdacf498ed3ff7d2c3574d346b2d24cbb12da") (:authors ("Adrien Brochard")) (:maintainer "Adrien Brochard") (:keywords "circleci" "continuous" "integration" "magit" "vc" "tools") (:url . "https://github.com/abrochard/magit-circleci"))]) - (magit-commit-mark . [(20220722 36) ((emacs (28 1)) (magit (3 3 0))) "Support marking commits as read" single ((:commit . "0e67320abc7f2198d5a5688d339f0c4ae7b63d32") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-magit-commit-mark"))]) + (magit-commit-mark . [(20220809 625) ((emacs (28 1)) (magit (3 3 0))) "Support marking commits as read" single ((:commit . "9367f7e4038792073f090b2c881cdbde1ab47f40") (:authors ("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" single ((:commit . "5fc7dbddcfacfe46d3fd876172ad02a9ab6ac616") (:authors ("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" single ((:commit . "28acf74f59e385865746cccf4b1e4c4025ae9433") (:authors ("Alex Ragone" . "ragonedk@gmail.com")) (:maintainer "Alex Ragone" . "ragonedk@gmail.com") (:keywords "convenience" "matching") (:url . "https://github.com/ragone/magit-diff-flycheck"))]) (magit-filenotify . [(20151116 2340) ((magit (1 3 0)) (emacs (24 4))) "Refresh status buffer when git tree changes" single ((:commit . "c0865b3c41af20b6cd89de23d3b0beb54c8401a4") (:authors ("Rüdiger Sonderfeld" . "ruediger@c-plusplus.de")) (:maintainer "Rüdiger Sonderfeld" . "ruediger@c-plusplus.de") (:keywords "tools"))]) @@ -2943,16 +2950,18 @@ (magit-gitflow . [(20170929 824) ((magit (2 1 0)) (magit-popup (2 2 0))) "gitflow extension for magit" single ((:commit . "cc41b561ec6eea947fe9a176349fb4f771ed865b") (:authors ("Jan Tatarik" . "Jan.Tatarik@gmail.com")) (:maintainer "Jan Tatarik" . "Jan.Tatarik@gmail.com") (:keywords "vc" "tools") (:url . "https://github.com/jtatarik/magit-gitflow"))]) (magit-imerge . [(20220306 2311) ((emacs (25 1)) (magit (3 0 0))) "Magit extension for git-imerge" single ((:commit . "37bca48218dc32cad964e01e0f9936a90f634fba") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-imerge"))]) (magit-lfs . [(20220314 1957) ((emacs (24 4)) (magit (2 10 3)) (dash (2 13 0))) "Magit plugin for Git LFS" single ((:commit . "7bf66a164504bcc9564507312a6e95c839cdac30") (:authors ("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-libgit . [(20220615 1159) ((emacs (25 1)) (compat (28 1 1 2)) (libgit (0)) (magit (20211004))) "(POC) Teach Magit to use Libgit2." tar ((:commit . "ac7fae6a9893b55ad01942d9ea5a571d44426665") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) + (magit-libgit . [(20220615 1159) ((emacs (25 1)) (compat (28 1 1 2)) (libgit (0)) (magit (20211004))) "(POC) Teach Magit to use Libgit2." tar ((:commit . "712be4632b0ddc7899ca90db8f9be20d90b4326f") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) (magit-org-todos . [(20180709 1950) ((magit (2 0 0)) (emacs (24))) "Add local todo items to the magit status buffer" single ((:commit . "9ffa3efb098434d837cab4bacd1601fdfc6fe999") (:authors ("Daniel Ma")) (:maintainer "Daniel Ma") (: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" single ((:commit . "0fd0f882eb14510714393c15c2ccb8d2c259f01e") (:authors ("Damian T. Dobroczyński" . "qoocku@gmail.com")) (:maintainer "Aleksey Fedotov" . "lexa@cfotr.com") (:keywords "vc" "tools") (:url . "https://github.com/qoocku/magit-p4"))]) (magit-patch-changelog . [(20220313 1229) ((emacs (25 1)) (magit (3 3 0))) "Generate a patch according to emacs-mirror/CONTRIBUTE" single ((:commit . "96936d2bd92c8bbf87f65bc293f3246014bc2764") (:keywords "git" "tools" "vc") (:url . "https://github.com/dickmao/magit-patch-changelog"))]) (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")) (: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" single ((:commit . "029203b3e48537205052a058e964f058cd802c3c") (:authors ("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" single ((:commit . "aceedff88921f1dfef8a6b2fb18fe316fb7223a8") (:authors ("Jules Tamagnan" . "jtamagnan@gmail.com")) (:maintainer "Jules Tamagnan" . "jtamagnan@gmail.com") (:keywords "magit" "vc") (:url . "http://github.com/jtamagnan/magit-reviewboard"))]) - (magit-section . [(20220803 2341) ((emacs (25 1)) (compat (28 1 1 2)) (dash (20210826))) "Sections for read-only buffers." tar ((:commit . "ac7fae6a9893b55ad01942d9ea5a571d44426665") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/magit/magit"))]) - (magit-svn . [(20210426 2114) ((emacs (25 1)) (magit (2 90 1)) (transient (0 3 2))) "Git-Svn extension for Magit" single ((:commit . "350493217afdb7637564e089f475909adecd9208") (:authors ("Phil Jackson" . "phil@shellarchive.co.uk")) (:maintainer "Phil Jackson" . "phil@shellarchive.co.uk") (:keywords "vc" "tools"))]) + (magit-section . [(20220810 1158) ((emacs (25 1)) (compat (28 1 1 2)) (dash (20210826))) "Sections for read-only buffers." tar ((:commit . "712be4632b0ddc7899ca90db8f9be20d90b4326f") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "tools") (:url . "https://github.com/magit/magit"))]) + (magit-stgit . [(20220822 2023) ((emacs (24 4)) (magit (2 12 0)) (magit-popup (2 12 0))) "StGit extension for Magit" single ((:commit . "cd1e04e63002ea47f7b858dbe475e90150ae6c00") (:authors ("Lluís Vilanova" . "vilanova@ac.upc.edu")) (:maintainer "UNMAINTAINED") (:keywords "vc" "tools"))]) + (magit-svn . [(20220314 1451) ((emacs (25 1)) (magit (2 90 1)) (transient (0 3 2))) "Git-Svn extension for Magit" single ((:commit . "b8277081db90977247ae3900ea6afeb0ca644d36") (:authors ("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" single ((:commit . "ff416b87a77a2f7cea234d9063ff629fc0e4c6c8") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-tbdiff"))]) - (magit-todos . [(20220326 519) ((emacs (25 2)) (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" single ((:commit . "67fd80c2f10aec4d5b2a24b5d3d53c08cc1f05dc") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "magit" "vc") (:url . "http://github.com/alphapapa/magit-todos"))]) + (magit-todos . [(20220822 2224) ((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" single ((:commit . "c5030cc27c7c1a48db52b0134bf2648a59a43176") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "magit" "vc") (:url . "http://github.com/alphapapa/magit-todos"))]) (magit-topgit . [(20160313 1954) ((emacs (24 4)) (magit (2 1 0))) "TopGit extension for Magit" single ((:commit . "11489ea798bc88d0ea5244bbf725285eedfefbef") (:authors ("Yann Hodique" . "yann.hodique@gmail.com")) (:maintainer "Robin Green" . "greenrd@greenrd.org") (:keywords "vc" "tools"))]) (magit-vcsh . [(20190817 2014) ((magit (2 90 1)) (vcsh (0 4)) (emacs (24 4))) "Magit vcsh integration" single ((:commit . "fcff128cdbe3ef547dc64f2496cb6405b8ee21ca") (:authors ("Štěpán Němec" . "stepnem@gmail.com")) (:maintainer "Štěpán Němec" . "stepnem@gmail.com") (:keywords "vc" "files" "magit") (:url . "https://gitlab.com/stepnem/magit-vcsh-el"))]) (magithub . [(20220315 117) ((emacs (25)) (magit (2 12)) (s (1 12 0)) (ghub+ (0 3)) (git-commit (2 12)) (markdown-mode (2 3))) "Magit interfaces for GitHub" tar ((:commit . "dd62c7057155c0a334e6d9087779a2923d2300b8") (:authors ("Sean Allred" . "code@seanallred.com")) (:maintainer "Sean Allred" . "code@seanallred.com") (:keywords "git" "tools" "vc") (:url . "https://github.com/vermiculus/magithub"))]) @@ -2972,7 +2981,7 @@ (malyon . [(20161208 2125) ((cl-lib (0 5))) "mode to execute Z-code files version 3, 5, 8" single ((:commit . "0d9882650720b4a791556f5e2d917388965d6fc0") (:authors ("Peter Ilberg , Christopher Madsen , Erik Selberg" . "erik@selberg.org")) (:maintainer "Christopher Madsen , Erik Selberg" . "erik@selberg.org") (:keywords "games" "emulations") (:url . "https://github.com/speedenator/malyon"))]) (man-commands . [(20151221 2221) ((cl-lib (0 5))) "Add interactive commands for every manpages installed in your computer." single ((:commit . "f4ba0c3790855d7544dff92d470d212f24de1d9d") (:authors ("Nathaniel Flath" . "nflath@gmail.com")) (:maintainer "Nathaniel Flath" . "nflath@gmail.com") (:url . "http://github.com/nflath/man-commands"))]) (manage-minor-mode . [(20210108 1832) ((emacs (24 3))) "Manage your minor-modes easily" single ((:commit . "7d886dddf81568c9387410701f60302cd33b4f63") (:authors ("Shingo Fukuyama - http://fukuyama.co")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "tools" "minor-mode" "manage" "emacs") (:url . "https://github.com/ShingoFukuyama/manage-minor-mode"))]) - (manage-minor-mode-table . [(20220616 1932) ((emacs (25 1)) (manage-minor-mode (1 1))) "Manage minor-modes in table" single ((:commit . "e47d0e1856f0a9eb9935abdaf6e14e67ef2ab4cc") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "tools" "minor-mode" "manage") (:url . "https://github.com/jcs-elpa/manage-minor-mode-table"))]) + (manage-minor-mode-table . [(20220616 1932) ((emacs (25 1)) (manage-minor-mode (1 1))) "Manage minor-modes in table" single ((:commit . "dd1c23ecaeaabba8d72f2a0822eefd6a2df9fcb7") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "tools" "minor-mode" "manage") (:url . "https://github.com/jcs-elpa/manage-minor-mode-table"))]) (mandm-theme . [(20220426 1131) nil "An M&M color theme." single ((:commit . "4991bbc4b17308f5dc53742dc528cbfdc467ee01") (:authors ("Christian Hopps" . "chopps@gmail.com")) (:maintainer "Christian Hopps" . "chopps@gmail.com") (:url . "https://github.com/choppsv1/emacs-mandm-theme.git"))]) (mandoku . [(20180403 1106) ((org (8)) (github-clone (20150705 1705))) "A tool to access repositories of premodern Chinese texts" tar ((:commit . "d65dbaa329ecf931f4142be72862972ea6a24e63") (:authors ("Christian Wittern" . "cwittern@gmail.com")) (:maintainer "Christian Wittern" . "cwittern@gmail.com") (:keywords "convenience") (:url . "http://www.mandoku.org"))]) (mandoku-tls . [(20171118 240) ((emacs (24 4)) (mandoku (20170301)) (github-clone (0 2)) (hydra (0 13 6)) (helm (1 7 0)) (org (9 0)) (helm-charinfo (20170601))) "A tool to access the TLS database" single ((:commit . "ffeebf5bd451ac1806ddfe1744fbbd036a56f902") (:authors ("Christian Wittern" . "cwittern@gmail.com")) (:maintainer "Christian Wittern" . "cwittern@gmail.com") (:keywords "convenience") (:url . "https://github.com/mandoku/mandoku-tls"))]) @@ -2994,20 +3003,20 @@ (markup . [(20170420 1129) ((cl-lib (0 5))) "Simple markup generation helpers." single ((:commit . "876da2d3f23473475bb0fd0a1480ae11d2671291") (:authors ("Arthur Leonard Andersen" . "leoc.git@gmail.com")) (:maintainer "Arthur Leonard Andersen" . "leoc.git@gmail.com") (:keywords "convenience" "markup" "html") (:url . "http://github.com/leoc/markup.el"))]) (markup-faces . [(20141110 817) nil "collection of faces for markup language modes" single ((:commit . "98a807ed82473eb41c6a201ed7ef816d6bcd67b0") (:authors ("Florian Kaufmann" . "sensorflo@gmail.com")) (:maintainer "Florian Kaufmann" . "sensorflo@gmail.com") (:keywords "wp" "faces") (:url . "https://github.com/sensorflo/markup-faces"))]) (marmalade-client . [(20141231 2007) ((web (0 5 2)) (kv (0 0 19)) (gh (0 8 0))) "client for marmalade API from emacs" tar ((:commit . "f315dea57e4fbebd9ee0668c0bafd4c45c7b754a") (:authors ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp") (:url . "https://github.com/nicferrier/emacs-marmalade-upload"))]) - (marquee-header . [(20220704 753) ((emacs (25 1))) "Code interface for displaying marquee in header" single ((:commit . "14f490b8683bdce53a6272fcc28a9b4137f86a24") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "wp" "animation" "marquee") (:url . "https://github.com/jcs-elpa/marquee-header"))]) + (marquee-header . [(20220704 753) ((emacs (25 1))) "Code interface for displaying marquee in header" single ((:commit . "8a54972a893d28ba79dcd832d55c446a8db43d62") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "wp" "animation" "marquee") (:url . "https://github.com/jcs-elpa/marquee-header"))]) (marshal . [(20201223 1853) ((emacs (25 1)) (ht (2 0))) "eieio extension for automatic (un)marshalling" single ((:commit . "bc00044d9073482f589aad959e34d563598f682a") (:authors ("Yann Hodique" . "yann.hodique@gmail.com")) (:maintainer "Yann Hodique" . "yann.hodique@gmail.com") (:keywords "extensions") (:url . "https://github.com/sigma/marshal.el"))]) (maruo-macro-mode . [(20160616 1349) ((emacs (24 3))) "Major mode for editing Hidemaru/Maruo macro script" single ((:commit . "8fc9a38ad051eafa8eb94038711acc52c5d1d8d5") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "programming" "editor" "macro"))]) (masm-mode . [(20200308 1450) ((emacs (25 1))) "MASM x86 and x64 assembly major mode" single ((:commit . "626b9255c2bb967a53d1d50be0b98a1bcae3250c") (:authors ("YiGeeker" . "zyfchinese@yeah.net")) (:maintainer "YiGeeker" . "zyfchinese@yeah.net") (:keywords "languages") (:url . "https://github.com/YiGeeker/masm-mode"))]) - (mastodon . [(20220803 1937) ((emacs (27 1)) (request (0 3 2)) (seq (1 0))) "Client for Mastodon" tar ((:commit . "a74340a72789ed12011d3f8618449bbe515a7e77") (:authors ("Johnson Denen" . "johnson.denen@gmail.com")) (:maintainer "Marty Hiatt" . "martianhiatus@riseup.net") (:url . "https://codeberg.org/martianh/mastodon.el"))]) + (mastodon . [(20220820 1427) ((emacs (27 1)) (request (0 3 2)) (seq (1 0))) "Client for Mastodon" tar ((:commit . "003fada37fee8e2e0b627f10356fcfd64c86a616") (:authors ("Johnson Denen" . "johnson.denen@gmail.com")) (:maintainer "Marty Hiatt" . "martianhiatus@riseup.net") (:url . "https://codeberg.org/martianh/mastodon.el"))]) (material-theme . [(20210904 1226) ((emacs (24 1))) "A Theme based on the colors of the Google Material Design" tar ((:commit . "6823009bc92f82aa3a90e27e1009f7da8e87b648") (:authors ("Christoph Paulik" . "cpaulik@gmail.com")) (:maintainer "Christoph Paulik" . "cpaulik@gmail.com") (:keywords "themes") (:url . "http://github.com/cpaulik/emacs-material-theme"))]) - (math-preview . [(20220604 1107) ((emacs (26 1)) (json (1 4)) (dash (2 18 0)) (s (1 12 0))) "Preview TeX math equations inline" single ((:commit . "a7254ba89a524f30c1acfdbde07a179271f02658") (:authors ("Matsievskiy S.V.")) (:maintainer "Matsievskiy S.V.") (:keywords "convenience") (:url . "https://gitlab.com/matsievskiysv/math-preview"))]) + (math-preview . [(20220812 430) ((emacs (26 1)) (json (1 4)) (dash (2 18 0)) (s (1 12 0))) "Preview TeX math equations inline" single ((:commit . "226f2be25d74f6f8224eb7aa96dfb1985aeac722") (:authors ("Matsievskiy S.V.")) (:maintainer "Matsievskiy S.V.") (:keywords "convenience") (:url . "https://gitlab.com/matsievskiysv/math-preview"))]) (math-symbol-lists . [(20200131 2338) nil "Lists of Unicode math symbols and latex commands" tar ((:commit . "590d9f09f8ad9aab747b97f077396a2035dcf50f") (:authors ("Vitalie Spinu" . "spinuvit@gmail.com")) (:maintainer "Vitalie Spinu" . "spinuvit@gmail.com") (:keywords "unicode" "symbols" "mathematics") (:url . "https://github.com/vspinu/math-symbol-lists"))]) (math-symbols . [(20201005 2313) nil "Math Symbol Input methods and conversion tools" tar ((:commit . "091b81cb40ceaff97614999ffe85b572ace182f0") (:authors ("KAWABATA, Taichi ")) (:maintainer "KAWABATA, Taichi ") (:keywords "i18n" "languages" "tex") (:url . "https://github.com/kawabata/math-symbols"))]) (matlab-mode . [(20220412 913) nil "Major mode for MATLAB(R) dot-m files" tar ((:commit . "5069e3ca0034e0da64eb9b3cd426f52992938d06"))]) (maude-mode . [(20220419 1454) ((emacs (25))) "Emacs mode for the programming language Maude" single ((:commit . "68de3c11ae16c409afa74516aaf465996d1a9e59") (:authors ("Ellef Gjelstad ")) (:maintainer "Rudi Schlatte" . "rudi@constantly.at") (:keywords "languages" "maude") (:url . "https://github.com/rudi/abs-mode"))]) (maven-test-mode . [(20141220 557) ((s (1 9)) (emacs (24))) "Utilities for navigating test files and running maven test tasks." single ((:commit . "a19151861df2ad8ae4880a2e7c86ddf848cb569a") (:authors ("Renan Ranelli")) (:maintainer "Renan Ranelli") (:keywords "java" "maven" "test") (:url . "http://github.com/rranelli/maven-test-mode"))]) (maxframe . [(20170120 1705) nil "maximize the emacs frame based on display size" single ((:commit . "f7048ce95443f2c06cb6b140814451e3a037103a") (:authors ("Ryan McGeary")) (:maintainer "Ryan McGeary") (:keywords "display" "frame" "window" "maximize"))]) - (maxima . [(20220531 1751) ((emacs (25 1)) (s (1 11 0)) (test-simple (1 3 0))) "Major mode for Maxima" tar ((:commit . "1334f44725bd80a265de858d652f3fde4ae401fa") (:authors ("William F. Schelter") ("Jay Belanger") ("Fermin Munoz")) (:maintainer "Fermin Munoz" . "fmfs@posteo.net") (:keywords "maxima" "tools" "math") (:url . "https://gitlab.com/sasanidas/maxima"))]) + (maxima . [(20220531 1751) ((emacs (25 1)) (s (1 11 0)) (test-simple (1 3 0))) "Major mode for Maxima" tar ((:commit . "1913ee496bb09430e85f76dfadf8ba4d4f95420f") (:authors ("William F. Schelter") ("Jay Belanger") ("Fermin Munoz")) (:maintainer "Fermin Munoz" . "fmfs@posteo.net") (:keywords "maxima" "tools" "math") (:url . "https://gitlab.com/sasanidas/maxima"))]) (mb-url . [(20211205 1100) ((emacs (25))) "Multiple Backends for Emacs URL package" tar ((:commit . "59f58a7e236329e14229b0a9f59766f829336b93") (:authors ("ZHANG Weiyi" . "dochang@gmail.com")) (:maintainer "ZHANG Weiyi" . "dochang@gmail.com") (:keywords "comm" "data" "processes" "hypermedia") (:url . "https://github.com/dochang/mb-url"))]) (mbe . [(20151126 1134) ((emacs (24)) (cl-lib (0 5))) "Macros by Example" single ((:commit . "bb10aa8f26bb7e9b1d5746934c94edb00402940c") (:authors ("Ian Price" . "ianprice90@googlemail.com")) (:maintainer "Ian Price" . "ianprice90@googlemail.com") (:keywords "tools" "macros") (:url . "https://github.com/ijp/mbe.el"))]) (mbo70s-theme . [(20170808 1315) ((emacs (24 0))) "70s style palette, with similarities to mbo theme" single ((:commit . "bed3db8965708ed4e9482b224a9b084765c052f2") (:authors ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) @@ -3025,11 +3034,11 @@ (memolist . [(20150804 1721) ((markdown-mode (22 0)) (ag (0 45))) "memolist.el is Emacs port of memolist.vim." single ((:commit . "c437a32d3955f859d9bbcbadf0911bbe27d877ff") (:authors ("mikanfactory ")) (:maintainer "mikanfactory") (:keywords "markdown" "memo") (:url . "http://github.com/mikanfactory/emacs-memolist"))]) (mentor . [(20220729 1756) ((emacs (25 1)) (xml-rpc (1 6 15)) (seq (1 11)) (async (1 9 3))) "Frontend for the rTorrent bittorrent client" tar ((:commit . "a820c8492392d2e3480845af4f6573c942996de8") (:authors ("Stefan Kangas" . "stefankangas@gmail.com")) (:maintainer "Stefan Kangas" . "stefankangas@gmail.com") (:keywords "comm" "processes" "bittorrent") (:url . "https://github.com/skangas/mentor"))]) (meow . [(20220727 2104) ((emacs (27 1))) "Yet Another modal editing" tar ((:commit . "7471762ec043fa85a91398b2b5b05859da544200") (:authors ("Shi Tianshu")) (:maintainer "Shi Tianshu") (:keywords "convenience" "modal-editing") (:url . "https://www.github.com/DogLooksGood/meow"))]) - (merlin . [(20220630 1249) ((emacs (25 1))) "Mode for Merlin, an assistant for OCaml" tar ((:commit . "be753d9412387aedcf32aba88a1be9bcd33d97ba") (:authors ("Frédéric Bour ")) (:maintainer "Frédéric Bour ") (:keywords "ocaml" "languages") (:url . "https://github.com/ocaml/merlin"))]) - (merlin-ac . [(20210615 1208) ((emacs (25 1)) (merlin (3)) (auto-complete (1 5))) "Merlin and auto-complete integration." single ((:commit . "be753d9412387aedcf32aba88a1be9bcd33d97ba") (:authors ("Simon Castellan ") ("Frédéric Bour ") ("Thomas Refis ")) (:maintainer "Simon Castellan ") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) - (merlin-company . [(20210615 1208) ((emacs (25 1)) (merlin (3)) (company (0 9))) "Merlin and company mode integration." single ((:commit . "be753d9412387aedcf32aba88a1be9bcd33d97ba") (:authors ("Simon Castellan ") ("Frédéric Bour ") ("Thomas Refis ")) (:maintainer "Simon Castellan ") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) + (merlin . [(20220630 1249) ((emacs (25 1))) "Mode for Merlin, an assistant for OCaml" tar ((:commit . "60518062c77e1d27a7d135a24a7c31bbf27290ba") (:authors ("Frédéric Bour ")) (:maintainer "Frédéric Bour ") (:keywords "ocaml" "languages") (:url . "https://github.com/ocaml/merlin"))]) + (merlin-ac . [(20210615 1208) ((emacs (25 1)) (merlin (3)) (auto-complete (1 5))) "Merlin and auto-complete integration." single ((:commit . "60518062c77e1d27a7d135a24a7c31bbf27290ba") (:authors ("Simon Castellan ") ("Frédéric Bour ") ("Thomas Refis ")) (:maintainer "Simon Castellan ") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) + (merlin-company . [(20210615 1208) ((emacs (25 1)) (merlin (3)) (company (0 9))) "Merlin and company mode integration." single ((:commit . "60518062c77e1d27a7d135a24a7c31bbf27290ba") (:authors ("Simon Castellan ") ("Frédéric Bour ") ("Thomas Refis ")) (:maintainer "Simon Castellan ") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) (merlin-eldoc . [(20190830 517) ((emacs (24 4)) (merlin (3 0))) "eldoc for OCaml and Reason" single ((:commit . "db7fab1eddfe34781b7e79694f8923b285698032") (:authors ("Louis Roché" . "louis@louisroche.net")) (:maintainer "Louis Roché" . "louis@louisroche.net") (:keywords "merlin" "ocaml" "languages" "eldoc") (:url . "https://github.com/khady/merlin-eldoc"))]) - (merlin-iedit . [(20220330 1736) ((emacs (25 1)) (merlin (3)) (iedit (0 9))) "Merlin and iedit integration." single ((:commit . "be753d9412387aedcf32aba88a1be9bcd33d97ba") (:authors ("Simon Castellan ") ("Frédéric Bour ") ("Thomas Refis ")) (:maintainer "Simon Castellan ") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) + (merlin-iedit . [(20220330 1736) ((emacs (25 1)) (merlin (3)) (iedit (0 9))) "Merlin and iedit integration." single ((:commit . "60518062c77e1d27a7d135a24a7c31bbf27290ba") (:authors ("Simon Castellan ") ("Frédéric Bour ") ("Thomas Refis ")) (:maintainer "Simon Castellan ") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) (mermaid-mode . [(20220716 1705) ((f (0 20 0)) (emacs (25 3))) "major mode for working with mermaid graphs" single ((:commit . "a98a9e733b1da1e6a19e68c1db4367bf46283479") (:authors ("Adrien Brochard")) (:maintainer "Adrien Brochard") (:keywords "mermaid" "graphs" "tools" "processes") (:url . "https://github.com/abrochard/mermaid-mode"))]) (meson-mode . [(20210820 905) ((emacs (26 1))) "Major mode for the Meson build system files" tar ((:commit . "1a2e2abb098c9288c2cdb3affbad76edd98abf59") (:authors ("Michal Sojka" . "sojkam1@fel.cvut.cz")) (:maintainer "Michal Sojka" . "sojkam1@fel.cvut.cz") (:keywords "languages" "tools") (:url . "https://github.com/wentasah/meson-mode"))]) (message-attachment-reminder . [(20200428 124) ((emacs (24 1))) "Remind if missing attachment" single ((:commit . "15498a6f424a4ddea7a3bdcc9d160e6a4dfb27c1") (:authors ("Alex Murray" . "murray.alex@gmail.com")) (:maintainer "Alex Murray" . "murray.alex@gmail.com") (:url . "https://github.com/alexmurray/message-attachment-reminder"))]) @@ -3041,13 +3050,13 @@ (metalheart-theme . [(20160710 641) ((emacs (24))) "Low-contrast theme with a dark blue-green background." single ((:commit . "ec98ea2c11dc1213dae8cbe1fe0cee73ca138bb2") (:authors ("Martin Haesler")) (:maintainer "Martin Haesler"))]) (metamorph . [(20220328 129) ((emacs (26 1))) "Transform your buffers with lisp" single ((:commit . "3633e32a9601c491df32d6c2212dbe63dc6484f4") (:authors ("Adam Niederer" . "adam.niederer@gmail.com")) (:maintainer "Adam Niederer" . "adam.niederer@gmail.com") (:keywords "metaprogramming" "wp") (:url . "http://github.com/AdamNiederer/metamorph"))]) (metascript-mode . [(20150709 57) ((emacs (24 3))) "Major mode for the Metascript programming language" single ((:commit . "edb361c7b0e5de231e5334a17b90652fb1df78f9") (:keywords "languages" "metascript" "mjs") (:url . "http://github.com/metascript/metascript-mode"))]) - (metaweblog . [(20210422 326) ((emacs (26 3))) "An XML-RPC MetaWeblog and WordPress API client." single ((:commit . "68695ed0e012379556d57f9564ac5ad8cd68fbb8") (:authors ("Puneeth Chaganti" . "punchagan+org2blog@gmail.com")) (:maintainer "Grant Rettke" . "grant@wisdomandwonder.com") (:keywords "comm") (:url . "https://github.com/org2blog/org2blog"))]) + (metaweblog . [(20220824 148) ((emacs (27 1))) "An XML-RPC MetaWeblog and WordPress API client." single ((:commit . "b641fbcf33ac2b8a0de7b80536b42ce035428625") (:authors ("Puneeth Chaganti" . "punchagan+org2blog@gmail.com")) (:maintainer "Grant Rettke" . "grant@wisdomandwonder.com") (:keywords "comm") (:url . "https://github.com/org2blog/org2blog"))]) (metrics-tracker . [(20220713 1131) ((emacs (24 4)) (seq (2 3))) "Generate reports of personal metrics from diary entries" single ((:commit . "b524398c768227bc14fd12c833dcd4e22d66efd3") (:authors ("Ian Martins" . "ianxm@jhu.edu")) (:maintainer "Ian Martins" . "ianxm@jhu.edu") (:keywords "calendar") (:url . "https://github.com/ianxm/emacs-tracker"))]) (metronome . [(20220210 147) ((emacs (25 1))) "A simple metronome" tar ((:commit . "1e1bd5234f3ecfb608041d423be7412c461ad3c2") (:authors ("Jonathan Gregory ")) (:maintainer "Jonathan Gregory ") (:url . "https://gitlab.com/jagrg/metronome"))]) (mew . [(20210625 240) nil "Messaging in the Emacs World" tar ((:commit . "fc4bca6d95d8b8d5e169ecf1433d968c2eec299d") (:authors ("Kazu Yamamoto" . "Kazu@Mew.org")) (:maintainer "Kazu Yamamoto" . "Kazu@Mew.org"))]) (mexican-holidays . [(20210604 1421) nil "Mexico holidays for Emacs calendar." single ((:commit . "8e28907ea69f2c0ed9aad9f3b99664ca147379d0") (:authors ("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 . [(20220723 1800) ((emacs (25 1)) (cider (0 23)) (projectile (2 1)) (s (1 12)) (dash (2 17)) (parseedn (0 1 0)) (geiser (0 12))) "Meyvn client" single ((:commit . "20878c2c059d1302b169f6c0252641a176a3f327") (:authors ("Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com")) (:maintainer "Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com") (:url . "https://github.com/danielsz/meyvn-el"))]) - (mgmtconfig-mode . [(20210131 2152) ((emacs (24 3))) "mgmt configuration management language" single ((:commit . "00f6045b1292d23a0579208521a470d685bdc59f") (:authors ("Peter Oliver" . "mgmtconfig@mavit.org.uk")) (:maintainer "Mgmt contributors ") (:keywords "languages") (:url . "https://github.com/purpleidea/mgmt/misc/emacs"))]) + (meyvn . [(20220821 2242) ((emacs (25 1)) (cider (0 23)) (projectile (2 1)) (s (1 12)) (dash (2 17)) (parseedn (0 1 0)) (geiser (0 12))) "Meyvn client" single ((:commit . "43b2061f907dda533abd3136dccdb1ce467ec6c0") (:authors ("Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com")) (:maintainer "Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com") (:url . "https://github.com/danielsz/meyvn-el"))]) + (mgmtconfig-mode . [(20220806 306) ((emacs (24 3))) "mgmt configuration management language" single ((:commit . "d547c39a164a7de0c7d2dee800f565313a6c53a6") (:authors ("Peter Oliver" . "mgmtconfig@mavit.org.uk")) (:maintainer "Mgmt contributors ") (:keywords "languages") (:url . "https://github.com/purpleidea/mgmt/misc/emacs"))]) (mhc . [(20201227 406) ((calfw (20150703))) "Message Harmonized Calendaring system." tar ((:commit . "67f9596dcd43b7ece3ab6e7a6ce8dc18a4851fe8") (:authors ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainer "Yoshinari Nomura" . "nom@quickhack.net") (:keywords "calendar") (:url . "http://www.quickhack.net/mhc"))]) (mic-paren . [(20170731 1907) nil "advanced highlighting of matching parentheses" single ((:commit . "d0410c7d805c9aaf51a1bcefaaef092bed5824c4") (:authors ("Mikael Sjödin" . "mic@docs.uu.se") ("Klaus Berndl " . "berndl@sdm.de") ("Jonathan Kotta" . "jpkotta@gmail.com")) (:maintainer "ttn") (:keywords "languages" "faces" "parenthesis" "matching"))]) (micgoline . [(20160415 326) ((emacs (24 3)) (powerline (2 3))) "powerline mode, color schemes from microsoft and google's logo." single ((:commit . "837504263bb1711203b0f7efecd6b7b5f272fae0") (:authors ("yzprofile" . "yzprofiles@gmail.com")) (:maintainer "yzprofile" . "yzprofiles@gmail.com") (:keywords "mode-line" "powerline" "theme") (:url . "https://github.com/yzprofile/micgoline"))]) @@ -3055,9 +3064,9 @@ (migemo . [(20200913 12) ((cl-lib (0 5))) "Japanese incremental search through dynamic pattern expansion" single ((:commit . "f756cba3d5268968da361463c2e29b3a659a3de7") (:authors ("Satoru Takabayashi" . "satoru-t@is.aist-nara.ac.jp")) (:maintainer "Satoru Takabayashi" . "satoru-t@is.aist-nara.ac.jp") (:url . "https://github.com/emacs-jp/migemo"))]) (milkode . [(20140927 529) nil "Command line search and direct jump with Milkode" single ((:commit . "ba97e2aeefa1d9d0b3835bf08edd0de248b0c513") (:authors ("ongaeshi")) (:maintainer "ongaeshi") (:keywords "milkode" "search" "grep" "jump" "keyword"))]) (mimetypes . [(20201115 1605) ((emacs (25 1))) "Guess a file's mimetype by extension" single ((:commit . "1663054ce266ed25e47ec707c19f619d33225903") (:authors ("Craig Niles ")) (:maintainer "Craig Niles ") (:url . "https://github.com/cniles/emacs-mimetypes"))]) - (mindre-theme . [(20220725 1404) ((emacs (26 1))) "Minimal, light theme" single ((:commit . "b8a2942524c75aa94505ee05c82ecfb803f04f7f") (:authors ("Erik Bäckman" . "contact@ebackman.net")) (:maintainer "Erik Bäckman" . "contact@ebackman.net") (:keywords "faces") (:url . "https://github.com/erikbackman/mindre-theme"))]) + (mindre-theme . [(20220819 1) ((emacs (26 1))) "Minimal, light theme" single ((:commit . "9fd58e5d60d786451bf2d2836947d0c7ab25c896") (:authors ("Erik Bäckman" . "contact@ebackman.net")) (:maintainer "Erik Bäckman" . "contact@ebackman.net") (:keywords "faces") (:url . "https://github.com/erikbackman/mindre-theme"))]) (minesweeper . [(20200416 2342) nil "play minesweeper in Emacs" single ((:commit . "d4248e3c9b3e9e7277cb9e6d081330611898f334") (:authors ("Zachary Kanfer" . "zkanfer@gmail.com")) (:maintainer "Zachary Kanfer" . "zkanfer@gmail.com") (:keywords "game" "fun" "minesweeper" "inane" "diversion") (:url . "https://hg.sr.ht/~zck/minesweeper"))]) - (mingus . [(20190106 1443) ((libmpdee (2 1))) "MPD Interface" tar ((:commit . "4223be618f57f10f18114a74393a71955b568884") (:authors ("Niels Giesen ")) (:maintainer "Niels Giesen ") (:keywords "multimedia" "elisp" "music" "mpd") (:url . "https://github.com/pft/mingus"))]) + (mingus . [(20220825 1103) ((libmpdee (2 2))) "MPD Interface" tar ((:commit . "aca914bfd81e18a37b2cc91f8ad219719a46c108") (:authors ("Niels Giesen ")) (:maintainer "Niels Giesen ") (:keywords "multimedia" "elisp" "music" "mpd") (:url . "https://github.com/pft/mingus"))]) (mini-frame . [(20220627 2041) ((emacs (26 1))) "Show minibuffer in child frame on read-from-minibuffer" single ((:commit . "60838f3cab438dcbda8eaa15ab3e5d1af88910e9") (:authors ("Andrii Kolomoiets" . "andreyk.mad@gmail.com")) (:maintainer "Andrii Kolomoiets" . "andreyk.mad@gmail.com") (:keywords "frames") (:url . "https://github.com/muffinmad/emacs-mini-frame"))]) (mini-header-line . [(20170621 1221) ((emacs (24 4))) "a minimal header-line" single ((:commit . "73b6724e0a26c4528d93768191c8aa59e6bce2e5") (:authors ("Johannes Goslar")) (:maintainer "Johannes Goslar") (:keywords "header-line" "mode-line") (:url . "https://github.com/ksjogo/mini-header-line"))]) (mini-modeline . [(20211130 604) ((emacs (25 1)) (dash (2 12 0))) "Display modeline in minibuffer" single ((:commit . "434b98b22c69c8b3b08e9c267c935591c49a8301") (:authors ("Kien Nguyen" . "kien.n.quang@gmail.com")) (:maintainer "Kien Nguyen" . "kien.n.quang@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/kiennq/emacs-mini-modeline"))]) @@ -3098,7 +3107,7 @@ (modern-sh . [(20211101 1001) ((emacs (25 1)) (hydra (0 15 0)) (eval-in-repl (0 9 7))) "Minor mode for editing shell script" single ((: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." single ((:commit . "c0e49523aa26b2263a8693691ac775988015f592") (:authors ("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" single ((:commit . "2bd77193fa3a7ec0541db284b4034821a8f59fea") (:authors ("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 . [(20220804 500) ((emacs (27 1))) "Elegant, highly legible and customizable themes" tar ((:commit . "a9399d08ce2199b31f7e64a3f4740156e8f501eb") (:authors ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainer "Modus-Themes Development" . "~protesilaos/modus-themes@lists.sr.ht") (:keywords "faces" "theme" "accessibility") (:url . "https://git.sr.ht/~protesilaos/modus-themes"))]) + (modus-themes . [(20220823 1919) ((emacs (27 1))) "Elegant, highly legible and customizable themes" tar ((:commit . "777089c0ffaabadc10cefead3737fabe24b9004c") (:authors ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainer "Modus-Themes Development" . "~protesilaos/modus-themes@lists.sr.ht") (:keywords "faces" "theme" "accessibility") (:url . "https://git.sr.ht/~protesilaos/modus-themes"))]) (moe-theme . [(20220707 1110) nil "A colorful eye-candy theme. Moe, moe, kyun!" tar ((:commit . "786c5f9aefa38b36f6bee0aa56aaa8da00fa0924") (:authors ("kuanyui" . "azazabc123@gmail.com")) (:maintainer "kuanyui" . "azazabc123@gmail.com") (:keywords "themes") (:url . "https://github.com/kuanyui/moe-theme.el"))]) (molar-mass . [(20210519 1342) ((emacs (24 3))) "Calculates molar mass of a molecule" single ((:commit . "838db1486a2dc5a3774eb195d62fbcdef71a63f7") (:authors ("Sergi Ruiz Trepat")) (:maintainer "Sergi Ruiz Trepat") (:keywords "convenience" "chemistry") (:url . "https://github.com/sergiruiztrepat/molar-mass.el"))]) (molecule . [(20180527 743) ((emacs (25 1))) "Simple wrapper for molecule" single ((:commit . "2ef72b81d9aa24ea782b71a061a3abdad6cae162") (:authors (": drymer ")) (:maintainer ": drymer ") (:keywords ":" "languages" "terminals") (:url . "https://git.daemons.it/drymer/molecule.el"))]) @@ -3129,7 +3138,7 @@ (move-dup . [(20210127 1938) ((emacs (25 1))) "Eclipse-like moving and duplicating lines or rectangles" single ((:commit . "5906503e0b9b832b1d5062c9cd27cf72a2ce4817") (:authors ("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) (:maintainer "Jimmy Yuen Ho Wong" . "wyuenho@gmail.com") (:keywords "convenience" "text" "edit") (:url . "https://github.com/wyuenho/move-dup"))]) (move-text . [(20170909 330) nil "Move current line or region with M-up or M-down." single ((:commit . "bfc255110ad05732a43cf25d6a0e3b4a6710b58c") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:keywords "edit") (:url . "https://github.com/emacsfodder/move-text"))]) (mowedline . [(20161122 235) nil "elisp utilities for using mowedline" single ((:commit . "6121b7d4aacd18f7b24da226e61dbae054e50a7c") (:authors ("John Foerch" . "jjfoerch@earthlink.net")) (:maintainer "John Foerch" . "jjfoerch@earthlink.net"))]) - (mozc . [(20210306 1053) nil "minor mode to input Japanese with Mozc" single ((:commit . "d1f5657f9b43fdf06852424cfb4f1a6fe2bc5171") (:keywords "mule" "multilingual" "input method"))]) + (mozc . [(20210306 1053) nil "minor mode to input Japanese with Mozc" single ((:commit . "72f91f31936afa9b8f0bece819bb767915a7f14d") (:keywords "mule" "multilingual" "input method"))]) (mozc-cand-posframe . [(20200208 750) ((emacs (26 1)) (posframe (0 5 0)) (mozc (20180101 800)) (s (1 12))) "Posframe frontend for mozc.el" single ((:commit . "1d07d5055381008ccbb29b97315d140e09a7ee95") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "i18n" "tooltip") (:url . "https://github.com/akirak/mozc-posframe"))]) (mozc-im . [(20160412 22) ((mozc (0))) "Mozc with input-method-function interface." single ((:commit . "df614a1076c28a11551fb3e822868bae47e855a5") (:authors ("Daisuke Kobayashi" . "d5884jp@gmail.com")) (:maintainer "Daisuke Kobayashi" . "d5884jp@gmail.com") (:keywords "i18n" "extentions"))]) (mozc-popup . [(20150224 34) ((popup (0 5 2)) (mozc (0))) "Mozc with popup" single ((:commit . "f0684b875a7427ec08f8df13939a486e5d5cf420") (:authors ("Daisuke Kobayashi" . "d5884jp@gmail.com")) (:maintainer "Daisuke Kobayashi" . "d5884jp@gmail.com") (:keywords "i18n" "extentions"))]) @@ -3167,7 +3176,7 @@ (multi-web-mode . [(20130824 354) nil "multiple major mode support for web editing" tar ((:commit . "ad1c8d1c870334052d244c7ae3636cb7b9357b7c") (:authors ("Fabián E. Gallina" . "fabian@anue.biz")) (:maintainer "Fabián E. Gallina" . "fabian@anue.biz") (:keywords "convenience" "languages" "wp") (:url . "https://github.com/fgallina/multi-web-mode"))]) (multicolumn . [(20150202 2251) nil "Creating and managing multiple side-by-side windows." single ((:commit . "c7a3afecd470859b2e60aa7c554d6e4d436df7fa") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:url . "https://github.com/Lindydancer/multicolumn"))]) (multifiles . [(20130615 2133) nil "View and edit parts of multiple files in one buffer" single ((:commit . "dddfe64b8e1c1cd1f9ccc1f03405477fc0d53897") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "multiple" "files"))]) - (multiple-cursors . [(20220613 2122) ((cl-lib (0 5))) "Multiple cursors for Emacs." tar ((:commit . "225fc0e889e094bfd2913cfd448084cb49211ac6") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "editing" "cursors") (:url . "https://github.com/magnars/multiple-cursors.el"))]) + (multiple-cursors . [(20220821 932) ((cl-lib (0 5))) "Multiple cursors for Emacs." tar ((:commit . "1e4842d1297241a5277bfd5c7bfab9e8711da60a") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "editing" "cursors") (:url . "https://github.com/magnars/multiple-cursors.el"))]) (multistate . [(20210124 2014) ((emacs (25 1)) (ht (2 3))) "Multistate mode" single ((:commit . "a7ab9dc7aac0b6d6d2f872de4e0d1b8550834a9b") (:authors ("Matsievskiy S.V.")) (:maintainer "Matsievskiy S.V.") (:keywords "convenience") (:url . "https://gitlab.com/matsievskiysv/multistate"))]) (multitran . [(20211027 1833) ((emacs (24)) (cl-lib (0 5))) "Interface to multitran" single ((:commit . "910f4c929e1d9c1844ddc467f72eef2e03aa3f97") (:authors ("Zajcev Evgeny" . "zevlg@yandex.ru")) (:maintainer "Zajcev Evgeny" . "zevlg@yandex.ru") (:keywords "dictionary" "hypermedia"))]) (mustache . [(20210224 710) ((ht (0 9)) (s (1 3 0)) (dash (1 2 0))) "Mustache templating library in emacs lisp" single ((:commit . "6fcb31f5075edc5fc70c63426b2aef91352ca80f") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "convenience" "mustache" "template") (:url . "https://github.com/Wilfred/mustache.el"))]) @@ -3177,7 +3186,7 @@ (mutant . [(20160124 1353) ((emacs (24 4)) (dash (2 1 0))) "An interface for the Mutant testing tool" single ((:commit . "de9cdefe48c880128a8f62c6699d7416e9c8ced1") (:authors ("Pedro Lambert")) (:maintainer "Pedro Lambert") (:keywords "mutant" "testing") (:url . "http://github.com/p-lambert/mutant.el"))]) (mutt-mode . [(20191102 2330) ((emacs (24))) "major mode for editing mutt configuration" single ((:commit . "1d495de49e6f536459b00d5396a2f5ce5ad4757b") (:authors ("Felix Weilbach" . "felix.weilbach@t-online.de")) (:maintainer "Felix Weilbach" . "felix.weilbach@t-online.de") (:keywords "languages") (:url . "https://gitlab.com/flexw/mutt-mode"))]) (mvn . [(20181002 1617) nil "helpers for compiling with maven" single ((:commit . "223723d9ceeb2878b884e83abb8ca74ad2e42081") (:authors ("Andrew Gwozdziewycz" . "git@apgwoz.com")) (:maintainer "Andrew Gwozdziewycz" . "git@apgwoz.com") (:keywords "compilation" "maven" "java") (:url . "https://github.com/apgwoz/mvn-el"))]) - (mw-thesaurus . [(20220629 2024) ((emacs (25)) (request (0 3 0)) (dash (2 16 0))) "Merriam-Webster Thesaurus" single ((:commit . "39d27eb4511588160b72078b67e11ce645dc76ee") (:authors ("Ag Ibragimov")) (:maintainer "Ag Ibragimov") (:keywords "wp" "matching") (:url . "https://github.com/agzam/mw-thesaurus.el"))]) + (mw-thesaurus . [(20220822 2112) ((emacs (25)) (request (0 3 0)) (dash (2 16 0))) "Merriam-Webster Thesaurus" single ((:commit . "bae48760b09f750359ef702875fdde783be2ce4d") (:authors ("Ag Ibragimov")) (:maintainer "Ag Ibragimov") (:keywords "wp" "matching") (:url . "https://github.com/agzam/mw-thesaurus.el"))]) (mwim . [(20181110 1900) nil "Switch between the beginning/end of line or code" single ((:commit . "b4f3edb4c0fb8f8b71cecbf8095c2c25a8ffbf85") (:authors ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "convenience") (:url . "https://github.com/alezost/mwim.el"))]) (mxf-view . [(20180501 740) ((emacs (25))) "Simple MXF viewer" single ((:commit . "6ca3cc93d995fac5fc4d72275e1e984e9857ffcb") (:authors ("Tomotaka SUWA" . "tomotaka.suwa@gmail.com")) (:maintainer "Tomotaka SUWA" . "tomotaka.suwa@gmail.com") (:keywords "data" "multimedia") (:url . "https://github.com/t-suwa/mxf-view"))]) (my-repo-pins . [(20220726 813) ((emacs (26 1))) "Keep your git repositories organized" single ((:commit . "f460f17c524db2c815966a0b1ffe86ac450d4908") (:authors ("Félix Baylac Jacqué ")) (:maintainer "Félix Baylac Jacqué ") (:url . "https://alternativebit.fr/projects/my-repo-pins/"))]) @@ -3189,7 +3198,7 @@ (mysql-to-org . [(20210622 447) ((emacs (24 3)) (s (1 11 0))) "Minor mode to output the results of mysql queries to org tables" single ((:commit . "c5eefc71200f2e1d0d67a13ed897b3cdfa835117") (:authors ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainer "Tijs Mallaerts" . "tijs.mallaerts@gmail.com"))]) (myterminal-controls . [(20210904 516) ((emacs (24)) (cl-lib (0 5))) "Quick toggle controls at a key-stroke" single ((:commit . "c635868e13ee898ec77925d98b36421640e22aa4") (:authors ("Mohammed Ismail Ansari" . "team.terminal@gmail.com")) (:maintainer "Mohammed Ismail Ansari" . "team.terminal@gmail.com") (:keywords "convenience" "shortcuts") (:url . "http://ismail.teamfluxion.com"))]) (n4js . [(20150714 231) ((emacs (24)) (cypher-mode (0))) "Neo4j Shell" single ((:commit . "3991ed8975151d5e8d568e952362df810f7ffab7") (:authors ("TruongTx" . "me@truongtx.me")) (:maintainer "TruongTx" . "me@truongtx.me") (:keywords "neo4j" "shell" "comint") (:url . "https://github.com/tmtxt/n4js.el"))]) - (naga-theme . [(20220724 2023) ((emacs (24 1))) "Dark color theme with green foreground color" single ((:commit . "c5ef0baf1d2e95bc613e99700c38a1227cabe187") (:authors ("Johannes Maier" . "johannes.maier@mailbox.org")) (:maintainer "Johannes Maier" . "johannes.maier@mailbox.org") (:keywords "faces" "themes") (:url . "https://github.com/kenranunderscore/emacs-naga-theme"))]) + (naga-theme . [(20220824 2032) ((emacs (24 1))) "Dark color theme with green foreground color" single ((:commit . "5f22695256f909667ee416d5f26abc1bc809daf9") (:authors ("Johannes Maier" . "johannes.maier@mailbox.org")) (:maintainer "Johannes Maier" . "johannes.maier@mailbox.org") (:keywords "faces" "themes") (:url . "https://github.com/kenranunderscore/emacs-naga-theme"))]) (name-this-color . [(20151014 2030) ((emacs (24)) (cl-lib (0 5)) (dash (2 11 0))) "Match RGB codes to names easily and precisely" single ((:commit . "e37cd1291d5d68d4c8d6386eab9cb9d94fd3bcfa") (:keywords "lisp" "color" "hex" "rgb" "shade" "name") (:url . "https://github.com/knl/name-this-color.el"))]) (named-timer . [(20181120 2224) ((emacs (24 4))) "Simplified timer management for Emacs Lisp" single ((:commit . "d8baeada19b56176c66aed5fa220751e3de11cb8") (:authors ("Ryan C. Thompson")) (:maintainer "Ryan C. Thompson") (:keywords "tools") (:url . "https://github.com/DarwinAwardWinner/emacs-named-timer"))]) (nameframe . [(20171107 56) nil "Manage frames by name." single ((:commit . "aafb8c5c5fbe0510e2f5d5b6b6b5dd0b73abe5d8") (:authors ("John Del Rosario" . "john2x@gmail.com")) (:maintainer "John Del Rosario" . "john2x@gmail.com") (:url . "https://github.com/john2x/nameframe"))]) @@ -3218,7 +3227,7 @@ (ncl-mode . [(20180129 703) ((emacs (24))) "Major Mode for editing NCL scripts and other goodies" tar ((:commit . "602292712a9e6b7e7c25155978999e77d06b7338") (:authors ("Yagnesh Raghava Yakkala" . "hi@yagnesh.org")) (:maintainer "Yagnesh Raghava Yakkala" . "hi@yagnesh.org") (:keywords "ncl" "major mode" "ncl-mode" "atmospheric science.") (:url . "https://github.com/yyr/ncl-mode"))]) (nclip . [(20130617 2015) nil "Network (HTTP) Clipboard" tar ((:commit . "af88e38b1f04be02bf2e57affc662dbd0f828e67") (:authors ("Marian Schubert" . "marian.schubert@gmail.com")) (:maintainer "Marian Schubert" . "marian.schubert@gmail.com") (:keywords "nclip" "clipboard" "network") (:url . "http://www.github.com/maio/nclip.el"))]) (neato-graph-bar . [(20181130 1649) ((emacs (24 3))) "Neat-o graph bars CPU/memory etc." single ((:commit . "a7ae35afd67911e8924f36e646bce0d3e3c1bbe6") (:authors ("Robert Cochran" . "robert-git@cochranmail.com")) (:maintainer "Robert Cochran" . "robert-git@cochranmail.com") (:url . "https://gitlab.com/RobertCochran/neato-graph-bar"))]) - (neil . [(20220514 2039) ((emacs (27 1))) "companion for Babashka Neil" single ((:commit . "001b80eaa59705fe081e982ab7774681507fd0d7") (:authors ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainer "Ag Ibragimov" . "agzam.ibragimov@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/babashka/neil"))]) + (neil . [(20220514 2039) ((emacs (27 1))) "companion for Babashka Neil" single ((:commit . "2e08696672f64fcacfa148c45f255c8c1bd441f8") (:authors ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainer "Ag Ibragimov" . "agzam.ibragimov@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/babashka/neil"))]) (nemerle . [(20161029 2023) nil "major mode for editing nemerle programs" single ((:commit . "db4bc9078f1b6238da32df1519c1957e74b6834a") (:authors ("Jacek Sliwerski (rzyjontko)" . "rzyj@o2.pl")) (:maintainer "Jacek Sliwerski (rzyjontko)" . "rzyj@o2.pl") (:keywords "nemerle" "mode" "languages"))]) (neon-mode . [(20180406 1156) nil "Simple major mode for editing neon files" single ((:commit . "99d15e46beaf1e7d71e39a00cce810df1f33229d") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "conf"))]) (neotree . [(20200324 1946) ((cl-lib (0 5))) "A tree plugin like NerdTree for Vim" tar ((:commit . "98fe21334affaffe2334bf7c987edaf1980d2d0b") (:authors ("jaypei" . "jaypei97159@gmail.com")) (:maintainer "jaypei" . "jaypei97159@gmail.com") (:url . "https://github.com/jaypei/emacs-neotree"))]) @@ -3242,14 +3251,14 @@ (nikola . [(20170703 2021) ((async (1 5)) (emacs (24 3))) "Simple wrapper for nikola" single ((:commit . "964715ac30943c9d6976999cad208dc60d09def0") (:authors (": drymer ")) (:maintainer ": drymer ") (:keywords ":" "nikola") (:url . ": https://git.daemons.it/drymer/nikola.el"))]) (nim-mode . [(20211102 917) ((emacs (24 4)) (epc (0 1 1)) (let-alist (1 0 1)) (commenter (0 5 1)) (flycheck-nimsuggest (0 8 1))) "A major mode for the Nim programming language" tar ((:commit . "744e076f0bea1c5ddc49f92397d9aa98ffa7eff8") (:authors ("Simon Hafner")) (:maintainer "Simon Hafner" . "hafnersimon@gmail.com") (:keywords "nim" "languages"))]) (nimbus-theme . [(20220610 207) ((emacs (24 1))) "An awesome dark theme" single ((:commit . "5beeda54ece58c8eb91ffcf2b365c832c9b454aa") (:authors ("Marcin Swieczkowski" . "marcin.swieczkowski@gmail.com") ("See README.md for full list of contributors.")) (:maintainer "Marcin Swieczkowski" . "marcin.swieczkowski@gmail.com") (:keywords "faces") (:url . "https://github.com/m-cat/nimbus-theme"))]) - (ninja-mode . [(20181024 1439) ((emacs (24))) "Major mode for editing .ninja files" single ((:commit . "d4017a2b1ea642f12dabe05ec99b2a16c93e99aa"))]) + (ninja-mode . [(20181024 1439) ((emacs (24))) "Major mode for editing .ninja files" single ((:commit . "b5f521a16173f51995523da436d1fe5bbd4dbd46"))]) (nix-buffer . [(20180212 1518) ((f (0 17 3)) (emacs (24 4))) "Set up buffer environments with nix" single ((:commit . "db57cda36e7477bdc7ef5a136357b971b1d4d099") (:authors ("Shea Levy")) (:maintainer "Shea Levy") (:url . "https://github.com/shlevy/nix-buffer/tree/master/"))]) (nix-env-install . [(20200812 1305) ((emacs (25 1))) "Install packages using nix-env" single ((:commit . "79c34bc117ba1cebeb67fab32c364951d2ec37a0") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "processes" "tools") (:url . "https://github.com/akirak/nix-env-install"))]) (nix-haskell-mode . [(20190615 135) ((emacs (25)) (haskell-mode (16 0)) (nix-mode (1 3 0))) "haskell-mode integrations for Nix" single ((:commit . "68efbcbf949a706ecca6409506968ed2ef928a20") (:authors ("Matthew Bauer" . "mjbauer95@gmail.com")) (:maintainer "Matthew Bauer" . "mjbauer95@gmail.com") (:keywords "nix" "haskell" "languages" "processes") (:url . "https://github.com/matthewbauer/nix-haskell"))]) (nix-mode . [(20220719 505) ((emacs (25 1)) (magit-section (0)) (transient (0 3))) "Major mode for editing .nix files" tar ((:commit . "b3f71c75f7d43a32e7cbc632e9be80f2a03788d4") (:maintainer "Matthew Bauer" . "mjbauer95@gmail.com") (:keywords "nix" "languages" "tools" "unix") (:url . "https://github.com/NixOS/nix-mode"))]) (nix-modeline . [(20210405 742) ((emacs (25 1))) "Info about in-progress Nix evaluations on your modeline" single ((:commit . "ecda866b960321bb82deac26af45918e172ef0ba") (:authors ("Jordan Mulcahey" . "snhjordy@gmail.com")) (:maintainer "Jordan Mulcahey" . "snhjordy@gmail.com") (:keywords "processes" "unix" "tools") (:url . "https://github.com/ocelot-project/nix-modeline"))]) (nix-sandbox . [(20210325 1622) ((dash (2 12 1)) (s (1 10 0))) "Utility functions to work with nix-shell sandboxes" single ((:commit . "053a2d5110ce05b7f99bcc2ac4804b70cbe87916") (:authors ("Sven Keidel" . "svenkeidel@gmail.com")) (:maintainer "Sven Keidel" . "svenkeidel@gmail.com") (:url . "https://github.com/travisbhartwell/nix-emacs"))]) - (nix-update . [(20190124 1935) ((emacs (25))) "Update \"fetch\" blocks in .nix expressions" single ((:commit . "fc6c39c2da3fcfa62f4796816c084a6389c8b6e7") (:authors ("John Wiegley" . "johnw@newartisans.com")) (:maintainer "John Wiegley" . "johnw@newartisans.com") (:keywords "nix") (:url . "https://github.com/jwiegley/nix-update-el"))]) + (nix-update . [(20220816 2212) ((emacs (25))) "Update \"fetch\" blocks in .nix expressions" single ((:commit . "aab70a38165575a9cb41726f1cc67df60fbf2832") (:authors ("John Wiegley" . "johnw@newartisans.com")) (:maintainer "John Wiegley" . "johnw@newartisans.com") (:keywords "nix") (:url . "https://github.com/jwiegley/nix-update-el"))]) (nixos-options . [(20160209 1841) ((emacs (24))) "Interface for browsing and completing NixOS options." single ((:commit . "053a2d5110ce05b7f99bcc2ac4804b70cbe87916") (:authors ("Diego Berrocal" . "cestdiego@gmail.com") ("Travis B. Hartwell" . "nafai@travishartwell.net")) (:maintainer "Diego Berrocal" . "cestdiego@gmail.com") (:keywords "unix") (:url . "http://www.github.com/travisbhartwell/nix-emacs/"))]) (nixpkgs-fmt . [(20200327 2302) ((emacs (24)) (reformatter (0 3))) "Reformat Nix using nixpkgs-fmt" single ((:commit . "487b8e26c1ea816894c590790978762daf2ee339") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "languages") (:url . "https://github.com/purcell/emacs-nixpkgs-fmt"))]) (nlinum-hl . [(20211112 1241) ((emacs (24 4)) (nlinum (1 7)) (cl-lib (0 5))) "heal nlinum's line numbers" single ((:commit . "22f8d75ecdaab67e0d6d0d2da4766358456ca4f5") (:authors ("Henrik Lissner ")) (:maintainer "Henrik Lissner" . "git@henrik.io") (:keywords "nlinum" "highlight" "current" "line" "faces") (:url . "https://github.com/hlissner/emacs-nlinum-hl"))]) @@ -3262,7 +3271,7 @@ (no-emoji . [(20180515 1837) ((emacs (24))) "Show :emoji-name: instead of emoji characters" single ((:commit . "ebceeab50dbfe4d60235180a57633745dbc18c77") (:authors ("Peter" . "craven@gmx.net")) (:maintainer "Peter" . "craven@gmx.net") (:keywords "extensions") (:url . "https://github.com/ecraven/no-emoji"))]) (no-littering . [(20220625 1106) ((emacs (25 1)) (compat (28 1 1 0))) "Help keeping ~/.emacs.d clean" single ((:commit . "d9db6d88ccae0727fa96125fae8601cac80efde0") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "https://github.com/emacscollective/no-littering"))]) (no-spam . [(20190724 1854) ((emacs (25 1))) "Add repeat delays to commands" single ((:commit . "860860e4a0d59bd15c8e092dc42f5f7f769a428e") (:authors ("Daniel Phan" . "daniel.phan36@gmail.com")) (:maintainer "Daniel Phan" . "daniel.phan36@gmail.com") (:keywords "keyboard" "tools") (:url . "https://github.com/mamapanda/no-spam"))]) - (noaa . [(20220803 1427) ((emacs (27 1)) (kv (0 0 19)) (request (0 2 0)) (s (1 12 0))) "Get NOAA weather data" single ((:commit . "beacb05f7298228dce4d494fb41c73d26991e15c") (:authors ("David Thompson")) (:maintainer "David Thompson") (:keywords "calendar") (:url . "https://github.com/thomp/noaa"))]) + (noaa . [(20220812 1535) ((emacs (27 1)) (kv (0 0 19)) (request (0 2 0)) (s (1 12 0))) "Get NOAA weather data" single ((:commit . "c691e770da0f1ed5b83c656087dfbc2ff231bef7") (:authors ("David Thompson")) (:maintainer "David Thompson") (:keywords "calendar") (:url . "https://github.com/thomp/noaa"))]) (noccur . [(20191015 719) nil "Run multi-occur on project/dired files" single ((:commit . "fa91647a305e89561d3dbe53da002fff49abe0bb") (:authors ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainer "Nicolas Petton" . "petton.nicolas@gmail.com") (:keywords "convenience"))]) (nocomments-mode . [(20170213 2037) nil "Minor mode that makes comments invisible." single ((:commit . "5a41a20cc44dfe4a9ea584354ed6dbc15dd92f46") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:url . "https://github.com/Lindydancer/nocomments-mode"))]) (noctilux-theme . [(20161113 1442) ((emacs (24))) "Dark theme inspired by LightTable" single ((:commit . "a3265a1be7f4d73f44acce6d968ca6f7add1f2ca") (:authors ("Simon Manning" . "simon@ecksdee.org")) (:maintainer "Simon Manning" . "simon@ecksdee.org") (:url . "https://github.com/sjrmanning/noctilux-theme"))]) @@ -3273,17 +3282,17 @@ (nofrils-acme-theme . [(20180620 1248) ((emacs (24))) "Port of \"No Frils Acme\" Vim theme." tar ((:commit . "98ad7bfaff1d85b33dc162645670285b067c6f92") (:authors ("Eric Sessoms" . "esessoms@protonmail.com")) (:maintainer "Eric Sessoms" . "esessoms@protonmail.com") (:url . "https://gitlab.com/esessoms/nofrils-theme"))]) (nord-theme . [(20200620 1122) ((emacs (24))) "An arctic, north-bluish clean and elegant theme" single ((:commit . "4f5b64605709d5803285953026137e905756c35f") (:authors ("Arctic Ice Studio" . "development@arcticicestudio.com")) (:maintainer "Arctic Ice Studio" . "development@arcticicestudio.com") (:url . "https://github.com/arcticicestudio/nord-emacs"))]) (nordless-theme . [(20201222 1627) ((colorless-themes (0 2))) "A mostly colorless version of nord-theme" single ((:commit . "c1ed1e12541cf05cc6c558d23c089c07e10b54d7") (:authors ("Thomas Letan" . "lthms@soap.coffee")) (:maintainer "Thomas Letan" . "lthms@soap.coffee") (:keywords "faces" "theme") (:url . "https://git.sr.ht/~lthms/colorless-themes.el"))]) - (norns . [(20220707 1006) ((emacs (27 1)) (dash (2 17 0)) (s (1 12 0)) (f (0 20 0)) (request (0 3 2)) (websocket (1 13))) "Interactive development environment for monome norns" single ((:commit . "fea64be77d413ef3d8442a9699b3bfc53859ae80") (:keywords "processes" "terminals") (:url . "https://github.com/p3r7/norns.el"))]) + (norns . [(20220821 1614) ((emacs (27 1)) (dash (2 17 0)) (s (1 12 0)) (f (0 20 0)) (request (0 3 2)) (websocket (1 13))) "Interactive development environment for monome norns" single ((:commit . "022b433334cd1db4f83e77f055cd89e9f857076d") (:keywords "processes" "terminals") (:url . "https://github.com/p3r7/norns.el"))]) (northcode-theme . [(20180423 1649) ((emacs (24))) "A dark theme focused on blue and orange colors." single ((:commit . "4d3750461ba25ec45321318b5f1af4e8fdf16147") (:authors ("Andreas Larsen" . "andreas@northcode.no")) (:maintainer "Andreas Larsen" . "andreas@northcode.no") (:url . "https://github.com/Northcode/northcode-theme.el"))]) (nothing-theme . [(20200504 402) ((emacs (24 1))) "Monochrome theme" single ((:commit . "d2514bb9707f66dda0d60f40f465e79914c50946") (:authors ("Jared Gorski," . "jaredgorski6@gmail.com")) (:maintainer "Jared Gorski," . "jaredgorski6@gmail.com") (:url . "https://github.com/jaredgorski/nothing.el"))]) (notink-theme . [(20220114 1955) ((emacs (26 1))) "A custom theme inspired by e-ink displays" single ((:commit . "6115857fe75c1adbbce4165a2b77a11a271aaf31") (:authors ("MetroWind" . "chris.corsair@gmail.com")) (:maintainer "MetroWind" . "chris.corsair@gmail.com") (:keywords "faces") (:url . "https://github.com/MetroWind/notink-theme"))]) - (notmuch . [(20220730 1153) nil "run notmuch within emacs" tar ((:commit . "54190d091cbceb345c489bd3f20fdca7e4b9a111") (:url . "https://notmuchmail.org"))]) + (notmuch . [(20220811 1025) nil "run notmuch within emacs" tar ((:commit . "76c3147613d0cb624573a5ba1ac7d0a5f81113bc") (:url . "https://notmuchmail.org"))]) (notmuch-addr . [(20220422 1618) ((emacs (27 1)) (compat (28 1 1 0)) (notmuch (0 32))) "An alternative to notmuch-address.el" single ((:commit . "dd852b09415e755cef6a345a2ee454a6cf1e1d06") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "mail") (:url . "https://git.sr.ht/~tarsius/notmuch-addr"))]) (notmuch-bookmarks . [(20200322 1925) ((seq (2 20)) (emacs (26 1)) (notmuch (0 29 3))) "Add bookmark handling for notmuch buffers" single ((:commit . "ec8edfdbd1ac475530591d73a570ded5c18ed86a") (:authors ("Jörg Volbers" . "joerg@joergvolbers.de")) (:maintainer "Jörg Volbers" . "joerg@joergvolbers.de") (:keywords "mail") (:url . "https://github.com/publicimageltd/notmuch-bookmarks"))]) (notmuch-labeler . [(20131230 1719) ((notmuch (0))) "Improve notmuch way of displaying labels" tar ((:commit . "d65d1129555d368243df4770ecc1e7ccb88efc58") (:authors ("Damien Cassou" . "damien.cassou@gmail.com")) (:maintainer "Damien Cassou" . "damien.cassou@gmail.com") (:keywords "emacs" "package" "elisp" "notmuch" "emails") (:url . "https://github.com/DamienCassou/notmuch-labeler"))]) (notmuch-maildir . [(20220422 1621) ((emacs (26 1)) (compat (28 1 1 0)) (notmuch (0 30))) "Visualize maildirs as a tree" single ((:commit . "b39cdeaec1afda6015cd0d5f4d851b3d59d0fd2b") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "mail") (:url . "https://git.sr.ht/~tarsius/notmuch-maildir"))]) (notmuch-transient . [(20220503 1117) ((emacs (27 1)) (compat (28 1 1 0)) (notmuch (0 31 4))) "Command dispatchers for Notmuch" single ((:commit . "4f64de401b8d955dce528f76575142edd9815dc7") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "mail") (:url . "https://git.sr.ht/~tarsius/notmuch-transient"))]) - (nov . [(20220428 1417) ((esxml (0 3 6)) (emacs (25 1))) "Featureful EPUB reader mode" single ((:commit . "8f5b42e9d9f304b422c1a7918b43ee323a7d3532") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "hypermedia" "multimedia" "epub") (:url . "https://depp.brause.cc/nov.el"))]) + (nov . [(20220805 2031) ((esxml (0 3 6)) (emacs (25 1))) "Featureful EPUB reader mode" single ((:commit . "cb5f45cbcfbcf263cdeb2d263eb15edefc8b07cb") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "hypermedia" "multimedia" "epub") (:url . "https://depp.brause.cc/nov.el"))]) (nova-theme . [(20210512 1802) ((emacs (24 3))) "A dark, pastel color theme" single ((:commit . "1498f756a4c1c9ea9740cd3208f74d071283b930") (:authors ("Muir Manders" . "muir+emacs@mnd.rs")) (:maintainer "Muir Manders" . "muir+emacs@mnd.rs") (:keywords "theme" "dark" "nova" "pastel" "faces") (:url . "https://github.com/muirmanders/emacs-nova-theme"))]) (noxml-fold . [(20170823 1357) nil "Fold away XML things." single ((:commit . "46c7f6a008672213238a9f8d7a416ce80916aa62") (:authors ("Patrick McAllister" . "pma@rdorte.org")) (:maintainer "Patrick McAllister" . "pma@rdorte.org") (:keywords "xml" "folding") (:url . "https://github.com/paddymcall/noxml-fold"))]) (npm . [(20220428 839) ((emacs (25 1)) (transient (0 1 0)) (jest (20200625))) "Run your npm workflows" tar ((:commit . "6eb0a58274870dd75bf848cf5a916a9f2c6ddae5") (:authors ("Shane Kennedy")) (:maintainer "Shane Kennedy") (:keywords "tools") (:url . "https://github.com/shaneikennedy/npm.el"))]) @@ -3354,9 +3363,9 @@ (ob-powershell . [(20220314 1359) ((emacs (26 1))) "Run Powershell from org mode source blocks" single ((:commit . "f351429590ed68b26a9c8f9847066ca4205e524b") (:authors ("Rob Kiggen" . "robby.kiggen@essential-it.be")) (:maintainer "Mois Moshev" . "mois.moshev@bottleshipvfx.com") (:keywords "powershell" "shell" "execute" "outlines" "processes") (:url . "https://github.com/rkiggen/ob-powershell"))]) (ob-prolog . [(20190410 2130) nil "org-babel functions for prolog evaluation." single ((:commit . "331899cfe345c934026c70b78352d320f7d8e239") (:authors ("Bjarte Johansen")) (:maintainer "Bjarte Johansen") (:keywords "literate programming" "reproducible research") (:url . "https://github.com/ljos/ob-prolog"))]) (ob-redis . [(20220221 1249) ((org (8))) "Execute Redis queries within org-mode blocks." single ((:commit . "44c83636ccbea0b3e9838b0180471905c30224c5") (:authors ("stardiviner" . "numbchild@gmail.com")) (:maintainer "stardiviner" . "numbchild@gmail.com") (:keywords "org" "babel" "redis") (:url . "https://repo.or.cz/ob-redis.git"))]) - (ob-restclient . [(20220618 2139) ((restclient (0))) "org-babel functions for restclient-mode" single ((:commit . "3ac834b02b8276aae1b760312612c3b940598f90") (:authors ("Alf Lervåg")) (:maintainer "Alf Lervåg") (:keywords "literate programming" "reproducible research") (:url . "https://github.com/alf/ob-restclient.el"))]) + (ob-restclient . [(20220819 2228) ((restclient (0))) "org-babel functions for restclient-mode" single ((:commit . "1b021ce1c67c97fa1aa4d2c0816edb7add129e48") (:authors ("Alf Lervåg")) (:maintainer "Alf Lervåg") (:keywords "literate programming" "reproducible research") (:url . "https://github.com/alf/ob-restclient.el"))]) (ob-reticulate . [(20210214 2229) ((org (9 4)) (emacs (24 4))) "Babel Functions for reticulate" single ((:commit . "8109fb02fb6339b1cf9290df29fc0c1109a33c04") (:authors ("Jack Kamm")) (:maintainer "Jack Kamm") (:keywords "literate programming" "reproducible research" "r" "python" "statistics" "languages" "outlines" "processes") (:url . "https://github.com/jackkamm/ob-reticulate"))]) - (ob-rust . [(20210204 244) nil "Org-babel functions for Rust" tar ((:commit . "30fe7e7181f44443d02e905dda77f83ec4944e76") (:authors ("Mican Zhang")) (:maintainer "Mican Zhang") (:keywords "rust" "languages" "org" "babel") (:url . "https://github.com/micanzhang/ob-rust"))]) + (ob-rust . [(20220824 1923) nil "Org-babel functions for Rust" tar ((:commit . "be059d231fafeb24a658db212a55ccdc55c0c500") (:authors ("Mican Zhang")) (:maintainer "Mican Zhang") (:keywords "rust" "languages" "org" "babel") (:url . "https://github.com/micanzhang/ob-rust"))]) (ob-sagemath . [(20191106 828) ((sage-shell-mode (0 0 8)) (s (1 8 0)) (emacs (24))) "org-babel functions for SageMath evaluation" tar ((:commit . "79645bce0c25a650bae61e550434bed836995dce") (:authors ("Sho Takemori" . "stakemorii@gmail.com")) (:maintainer "Sho Takemori" . "stakemorii@gmail.com") (:keywords "sagemath" "org-babel") (:url . "https://github.com/stakemori/ob-sagemath"))]) (ob-smiles . [(20220221 1255) ((smiles-mode (0 0 1)) (org (8))) "Org-mode Babel support for SMILES." single ((:commit . "d178f3d4a7e3c1ca9910f0a063d2a3cfd97d8609") (:authors (nil . "John Kitchin [jkitchin@andrew.cmu.edu]")) (:maintainer nil . "stardiviner [numbchild@gmail.com]") (:keywords "org" "babel" "smiles") (:url . "https://repo.or.cz/ob-smiles.git"))]) (ob-sml . [(20130829 1843) ((sml-mode (6 4))) "org-babel functions for template evaluation" single ((:commit . "958165c92b6cff6cada5c85c8ae5887806b8451b") (:authors ("David Nolen")) (:maintainer "David Nolen") (:keywords "literate programming" "reproducible research") (:url . "http://orgmode.org"))]) @@ -3365,7 +3374,7 @@ (ob-sql-mode . [(20190421 1539) ((emacs (24 4))) "SQL code blocks evaluated by sql-mode" single ((:commit . "b31a016585324ad91f1742ff6205bcb76f3ece6e") (:authors (nil . "Nik Clayton nik@google.com")) (:maintainer nil . "Nik Clayton nik@google.com") (:keywords "languages" "org" "org-babel" "sql") (:url . "http://github.com/nikclayton/ob-sql-mode"))]) (ob-svgbob . [(20190911 300) ((emacs (24))) "Babel Functions for svgbob" single ((:commit . "5747f96fb4fdb8711546b3313df9412177eb3c1a") (:authors ("Marcio Giaxa" . "i@mgxm.me")) (:maintainer "Marcio Giaxa" . "i@mgxm.me") (:keywords "tools" "files") (:url . "https://github.com/mgxm/ob-svgbob"))]) (ob-swift . [(20170921 1325) ((org (8))) "org-babel functions for swift evaluation" single ((:commit . "ed478ddbbe41ce5373efde06b4dd0c3663c9055f") (:authors ("Feng Zhou" . "zf.pascal@gmail.com")) (:maintainer "Feng Zhou" . "zf.pascal@gmail.com") (:keywords "org" "babel" "swift") (:url . "http://github.com/zweifisch/ob-swift"))]) - (ob-swiftui . [(20210618 856) ((emacs (25 1)) (swift-mode (8 2 0)) (org (9 2 0))) "Org babel functions for SwiftUI evaluation" single ((:commit . "31cfe991eb171bb0d2f53cf621be1b9d91573ac3") (:authors ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/ob-swiftui"))]) + (ob-swiftui . [(20210618 856) ((emacs (25 1)) (swift-mode (8 2 0)) (org (9 2 0))) "Org babel functions for SwiftUI evaluation" single ((:commit . "0a568c309eb2601b6e832e6c1661fdc908d761ca") (:authors ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/ob-swiftui"))]) (ob-tmux . [(20190708 1202) ((emacs (25 1)) (seq (2 3)) (s (1 9 0))) "Babel Support for Interactive Terminal" single ((:commit . "3687ed7b874bdfe14617f5d14492887cb0836a85") (:authors ("Allard Hendriksen")) (:maintainer "Allard Hendriksen") (:keywords "literate programming" "interactive shell" "tmux") (:url . "https://github.com/ahendriksen/ob-tmux"))]) (ob-translate . [(20170720 1919) ((google-translate (0 11)) (org (8))) "Translation of text blocks in org-mode." single ((:commit . "9d9054a51bafd5a29a8135964069b4fa3a80b169") (:authors ("Kris Jenkins" . "krisajenkins@gmail.com")) (:maintainer "Kris Jenkins" . "krisajenkins@gmail.com") (:keywords "org" "babel" "translate" "translation") (:url . "https://github.com/krisajenkins/ob-translate"))]) (ob-typescript . [(20190910 946) ((emacs (24)) (org (8 0))) "org-babel functions for typescript evaluation" single ((:commit . "0b2766b9d136cd6d81f4c15f1ad4b28542f484b9") (:authors ("KURASHIKI Satoru")) (:maintainer "KURASHIKI Satoru") (:keywords "literate programming" "reproducible research" "typescript") (:url . "https://github.com/lurdan/ob-typescript"))]) @@ -3375,8 +3384,9 @@ (objc-font-lock . [(20141021 1822) nil "Highlight Objective-C method calls." single ((:commit . "34b457d577f97ca94b8792d025f9a909c7610612") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "languages" "faces") (:url . "https://github.com/Lindydancer/objc-font-lock"))]) (objed . [(20200911 1435) ((emacs (25)) (cl-lib (0 5))) "Navigate and edit text objects." tar ((:commit . "70f9fb5e0aa1627b0afc7c6b3d0aea9bac70a210") (:authors ("Clemens Radermacher" . "clemera@posteo.net")) (:maintainer "Clemens Radermacher" . "clemera@posteo.net") (:keywords "convenience") (:url . "https://github.com/clemera/objed"))]) (oblivion-theme . [(20220710 1045) ((emacs (24 1))) "A port of GEdit oblivion theme" single ((:commit . "3349191b123e5bdcf9177e97d2301bb571800da3") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-oblivion-theme"))]) + (obsidian . [(20220824 1728) ((emacs (27 2)) (s (1 12 0)) (dash (2 13)) (markdown-mode (2 6)) (elgrep (1 0 0)) (yaml (0 5 1))) "Obsidian Notes interface" single ((:commit . "324d614d36c2dc9dd5326952b7c44d40453b2b9d") (:authors ("Mykhaylo Bilyanskyy")) (:maintainer "Mykhaylo Bilyanskyy") (:keywords "obsidian" "pkm" "convenience") (:url . "https://github.com./licht1stein/obsidian.el"))]) (obsidian-theme . [(20170719 948) nil "port of the eclipse obsidian theme" single ((:commit . "f45efb2ebe9942466c1db6abbe2d0e6847b785ea") (:authors ("martin haesler")) (:maintainer "martin haesler") (:url . "http://github.com/mswift42/obsidian-theme"))]) - (ocamlformat . [(20220707 751) ((emacs (24 3))) "Utility functions to format ocaml code" single ((:commit . "e54ecd396917ba5bab9be6104c42e5be4c1215d6") (:keywords "languages" "ocaml") (:url . "https://github.com/ocaml-ppx/ocamlformat"))]) + (ocamlformat . [(20220707 751) ((emacs (24 3))) "Utility functions to format ocaml code" single ((:commit . "aabb34f3eced86d1b834b6bbcbb258816f540508") (:keywords "languages" "ocaml") (:url . "https://github.com/ocaml-ppx/ocamlformat"))]) (occidental-theme . [(20130312 1958) nil "Custom theme for faces based on Adwaita" single ((:commit . "fd2db7256d4f78c43d99c3cddb1c39106d479816") (:authors ("William Stevenson" . "yhvh2000@gmail.com") ("Erik Timan" . "dev@timan.info")) (:maintainer "William Stevenson" . "yhvh2000@gmail.com") (:url . "http://github.com/olcai/occidental-theme"))]) (occur-context-resize . [(20210121 50) nil "dynamically resize context around matches in occur-mode" single ((:commit . "9d62a5b5c39ab7921dfc12dd0ab139b38dd16582") (:authors ("Charles L.G. Comstock" . "dgtized@gmail.com")) (:maintainer "Charles L.G. Comstock" . "dgtized@gmail.com") (:keywords "matching") (:url . "https://github.com/dgtized/occur-context-resize.el"))]) (occur-x . [(20130610 1343) nil "Extra functionality for occur" single ((:commit . "352f5fab207d8a1d3dd048073ff127a83e97c82b") (:authors ("Juan-Leon Lahoz" . "juanleon1@gmail.com")) (:maintainer "Juan-Leon Lahoz" . "juanleon1@gmail.com") (:keywords "occur" "search" "convenience"))]) @@ -3418,6 +3428,7 @@ (openstack-cgit-browse-file . [(20130819 927) nil "Browse the current file in OpenStack cgit" single ((:commit . "244219288b9aef41155044697bb114b7af83ab8f") (:authors ("Chmouel Boudjnah" . "chmouel@chmouel.com")) (:maintainer "Chmouel Boudjnah" . "chmouel@chmouel.com") (:keywords "convenience" "vc" "git" "cgit" "gerrit" "openstack") (:url . "https://github.com/chmouel/openstack-cgit-browse-file"))]) (openwith . [(20120531 2136) nil "Open files with external programs" single ((:commit . "1dc89670822966fab6e656f6519fdd7f01e8301a") (:authors ("Markus Triska" . "markus.triska@gmx.at")) (:maintainer "Markus Triska" . "markus.triska@gmx.at") (:keywords "files" "processes") (:url . "https://bitbucket.org/jpkotta/openwith"))]) (operate-on-number . [(20150707 623) nil "Operate on number at point with arithmetic functions" single ((:commit . "ceb3be565a29326c1098244fac0c50606723a56e") (:authors ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainer "Akinori MUSHA" . "knu@iDaemons.org") (:keywords "editing") (:url . "https://github.com/knu/operate-on-number.el"))]) + (orangey-bits-theme . [(20220822 324) ((autothemer (0 2)) (emacs (27 1))) "A Theme with smashing orangey bits" single ((:commit . "b3b8844c41792e08b65342f9479e1738d4e329c2") (:url . "http://github.com/emacsfodder/emacs-theme-orangey-bits"))]) (orca . [(20210828 1639) ((emacs (24 3)) (zoutline (0 1 0))) "Org Capture" single ((:commit . "47c03af0c1df2b679d800f3708d675a4c2a3e722") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "org" "convenience") (:url . "https://github.com/abo-abo/orca"))]) (orderless . [(20220527 2228) ((emacs (26 1))) "Completion style for matching regexps in any order" tar ((:commit . "8b9af2796fa0eb87eea4140bc08d16880a493803") (:authors ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainer "Omar Antolín Camarena" . "omar@matem.unam.mx") (:keywords "extensions") (:url . "https://github.com/oantolin/orderless"))]) (ordinal . [(20210519 1442) ((emacs (24 3))) "Convert number to ordinal number notation" single ((:commit . "a7f378306290b6807fb6b87cee3ef79b31cec711") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "lisp") (:url . "https://github.com/zonuexe/ordinal.el"))]) @@ -3425,17 +3436,17 @@ (org-agenda-property . [(20140626 2116) ((emacs (24 2))) "Display org properties in the agenda buffer." single ((:commit . "3b469f3e93de0036547f3631cd0366d53f7584c8") (:authors ("Artur Malabarba" . "bruce.connor.am@gmail.com")) (:maintainer "Artur Malabarba" . "bruce.connor.am@gmail.com") (:keywords "calendar") (:url . "http://github.com/Bruce-Connor/org-agenda-property"))]) (org-alert . [(20220721 1721) ((org (9 0)) (alert (1 2))) "Notify org deadlines via notify-send" single ((:commit . "f1801e061722843329b95409957c7dbd5cc223e9") (:authors ("Stephen Pegoraro" . "spegoraro@tutive.com")) (:maintainer "Stephen Pegoraro" . "spegoraro@tutive.com") (:keywords "org" "org-mode" "notify" "notifications" "calendar") (:url . "https://github.com/spegoraro/org-alert"))]) (org-analyzer . [(20191001 1717) nil "org-analyzer is a tool that extracts time tracking data from org files." tar ((:commit . "19da62aa4dcf1090be8f574f6f2d4c7e116163a8") (:authors ("Robert Krahn" . "robert@kra.hn")) (:maintainer "Robert Krahn" . "robert@kra.hn") (:keywords "calendar") (:url . "https://github.com/rksm/clj-org-analyzer"))]) - (org-anki . [(20220712 1916) ((emacs (27 1)) (request (0 3 2)) (dash (2 17)) (promise (1 1))) "Synchronize org-mode entries to Anki" single ((:commit . "4c3b27efe8eed9a9c8b5636fb15b86975ef7e00e") (:authors ("Markus Läll" . "markus.l2ll@gmail.com")) (:maintainer "Markus Läll" . "markus.l2ll@gmail.com") (:keywords "outlines" "flashcards" "memory") (:url . "https://github.com/eyeinsky/org-anki"))]) + (org-anki . [(20220817 655) ((emacs (27 1)) (request (0 3 2)) (dash (2 17)) (promise (1 1))) "Synchronize org-mode entries to Anki" single ((:commit . "4ed63cbebb35d7427d92652df209b0450655e5ac") (:authors ("Markus Läll" . "markus.l2ll@gmail.com")) (:maintainer "Markus Läll" . "markus.l2ll@gmail.com") (:keywords "outlines" "flashcards" "memory") (:url . "https://github.com/eyeinsky/org-anki"))]) (org-appear . [(20220617 2355) ((emacs (25 1)) (org (9 3))) "Auto-toggle Org elements" single ((:commit . "60ba267c5da336e75e603f8c7ab3f44e6f4e4dac") (:authors ("Alice Istleyeva" . "awth13@gmail.com")) (:maintainer "Alice Istleyeva" . "awth13@gmail.com") (:url . "https://github.com/awth13/org-appear"))]) - (org-arbeitszeit . [(20220328 1951) ((emacs (27 1))) "Calculate your worktime" single ((:commit . "60e6adfe457bcc4ee47e3e5805b6b40544f98ee0") (:authors ("Benjamin Kästner" . "benjamin.kaestner@gmail.com")) (:maintainer "Benjamin Kästner" . "benjamin.kaestner@gmail.com") (:keywords "tools" "org" "calendar" "convenience") (:url . "https://github.com/bkaestner/org-arbeitszeit"))]) + (org-arbeitszeit . [(20220816 1447) ((emacs (27 1))) "Calculate your worktime" single ((:commit . "b22ae3292b24772aa37dd5a54cd551f7312b6213") (:authors ("Benjamin Kästner" . "benjamin.kaestner@gmail.com")) (:maintainer "Benjamin Kästner" . "benjamin.kaestner@gmail.com") (:keywords "tools" "org" "calendar" "convenience") (:url . "https://github.com/bkaestner/org-arbeitszeit"))]) (org-attach-screenshot . [(20210221 1336) ((emacs (24 3))) "Screenshots integrated with org attachment dirs" single ((:commit . "55fa23e69c8ac4c40f8600300301a9cdc5c6732f") (:authors ("Derek Feichtinger" . "derek.feichtinger@psi.ch")) (:maintainer "Derek Feichtinger" . "derek.feichtinger@psi.ch") (:keywords "org" "multimedia") (:url . "https://github.com/dfeich/org-screenshot"))]) (org-auto-expand . [(20210923 243) ((emacs (26 1))) "Automatically expand certain headings" single ((:commit . "edc27b155befab5626dcf6ceec7938126f7e31d4") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "convenience" "outlines" "org") (:url . "https://github.com/alphapapa/org-auto-expand"))]) - (org-auto-tangle . [(20220715 329) ((emacs (24 1)) (async (1 9 3))) "Automatically and Asynchronously tangles org files on save" single ((:commit . "bce665c79fc29f1e80f1eae7db7e91c56b0788fc") (:authors ("Yilkal Argaw" . "yilkalargawworkneh@gmail.com")) (:maintainer "Yilkal Argaw" . "yilkalargawworkneh@gmail.com") (:keywords "outlines") (:url . "https://github.com/yilkalargaw/org-auto-tangle"))]) + (org-auto-tangle . [(20220812 2327) ((emacs (24 1)) (async (1 9 3))) "Automatically and Asynchronously tangles org files on save" single ((:commit . "2494a6f78c9db5311123abc7cad119851a29a55c") (:authors ("Yilkal Argaw" . "yilkalargawworkneh@gmail.com")) (:maintainer "Yilkal Argaw" . "yilkalargawworkneh@gmail.com") (:keywords "outlines") (:url . "https://github.com/yilkalargaw/org-auto-tangle"))]) (org-autolist . [(20220530 1620) nil "Improved list management in org-mode" single ((:commit . "0f5dc4e00cb050b94289504925b36c7650552a1a") (:authors ("Calvin Young")) (:maintainer "Calvin Young") (:keywords "lists" "checklists" "org-mode") (:url . "https://github.com/calvinwyoung/org-autolist"))]) (org-babel-eval-in-repl . [(20201206 1540) ((eval-in-repl (0 9 2)) (matlab-mode (3 3 6)) (ess (16 10)) (emacs (24))) "Eval org-mode babel code blocks in various REPLs." tar ((:commit . "3591f062873de2d64cc6f83b3555d030506e6ee7") (:authors ("Takeshi Teshima" . "diadochos.developer@gmail.com")) (:maintainer "Takeshi Teshima" . "diadochos.developer@gmail.com") (:keywords "literate programming" "reproducible research" "async execution") (:url . "https://github.com/diadochos/org-babel-eval-in-repl"))]) (org-beautify-theme . [(20170908 2218) nil "A sub-theme to make org-mode more beautiful." single ((:commit . "df6a1114fda313e1689363e196c8284fbe2a2738") (:authors ("Jonathan Arkell" . "jonnay@jonnay.net")) (:maintainer "Jonathan Arkell" . "jonnay@jonnay.net") (:keywords "org" "theme"))]) (org-board . [(20200619 1016) nil "bookmarking and web archival system for Org mode." single ((:commit . "1393bd46d11a81328ed4fb8471831415a3efe224") (:authors ("Charles A. Roelli " . "charles@aurox.ch")) (:maintainer "Charles A. Roelli " . "charles@aurox.ch") (:keywords "org" "bookmarks" "archives") (:url . "https://github.com/scallywag/org-board"))]) - (org-bookmark-heading . [(20200103 514) ((emacs (24 4)) (f (0 17 2))) "Emacs bookmark support for org-mode" single ((:commit . "38a2813f72ff65f3ae91e2ebb23e0bbb42a8d1df") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines") (:url . "http://github.com/alphapapa/org-bookmark-heading"))]) + (org-bookmark-heading . [(20220805 2357) ((emacs (24 4))) "Emacs bookmark support for org-mode" single ((:commit . "fac3edac3b70a00f5412e3e7e2830a5cfee84432") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines") (:url . "http://github.com/alphapapa/org-bookmark-heading"))]) (org-books . [(20210408 1913) ((enlive (0 0 1)) (s (1 11 0)) (helm (2 9 2)) (helm-org (1 0)) (dash (2 14 1)) (org (9 3)) (emacs (25))) "Reading list management with Org mode and helm" single ((:commit . "9f4ec4a981bfc5eebff993c3ad49a4bed26aebd1") (:authors ("Abhinav Tushar" . "abhinav@lepisma.xyz")) (:maintainer "Abhinav Tushar" . "abhinav@lepisma.xyz") (:keywords "outlines") (:url . "https://github.com/lepisma/org-books"))]) (org-brain . [(20210706 1519) ((emacs (25 1)) (org (9 2))) "Org-mode concept mapping" single ((:commit . "46ca9f766322cff31279ecdf02251ff24a0e9431") (:authors ("Erik Sjöstrand" . "sjostrand.erik@gmail.com")) (:maintainer "Erik Sjöstrand" . "sjostrand.erik@gmail.com") (:keywords "outlines" "hypermedia") (:url . "http://github.com/Kungsgeten/org-brain"))]) (org-bullets . [(20200317 1740) nil "Show bullets in org-mode as UTF-8 characters" single ((:commit . "767f55feb58b840a5a04eabfc3fbbf0d257c4792") (:authors ("sabof")) (:maintainer "D. Williams" . "d.williams@posteo.net") (:url . "https://github.com/integral-dw/org-bullets"))]) @@ -3473,7 +3484,7 @@ (org-fancy-priorities . [(20210830 1657) nil "Display org priorities as custom strings" single ((:commit . "7f677c6c14ecf05eab8e0efbfe7f1b00ae68eb1d") (:authors ("Harry Bournis" . "harrybournis@gmail.com")) (:maintainer "Harry Bournis" . "harrybournis@gmail.com") (:keywords "convenience" "faces" "outlines") (:url . "https://github.com/harrybournis/org-fancy-priorities"))]) (org-fragtog . [(20220714 2146) ((emacs (27 1))) "Auto-toggle Org LaTeX fragments" single ((:commit . "c675563af3f9ab5558cfd5ea460e2a07477b0cfd") (:authors ("Benjamin Levy" . "blevy@protonmail.com")) (:maintainer "Benjamin Levy" . "blevy@protonmail.com") (:url . "https://github.com/io12/org-fragtog"))]) (org-gamedb . [(20210525 2338) ((emacs (25 1))) "Track video games in org-mode with giantbomb.com's API" single ((:commit . "f283b6f6a7e8ad090405be57202caa3d3c424447") (:authors ("repelliuss ")) (:maintainer "repelliuss" . "repelliuss@gmail.com") (:keywords "outlines" "org" "games" "convenience" "api") (:url . "https://github.com/repelliuss/org-gamedb"))]) - (org-gcal . [(20220731 2104) ((request (20190901)) (request-deferred (20181129)) (alert (0)) (persist (0)) (emacs (26)) (org (9 3))) "Org sync with Google Calendar" tar ((:commit . "229463ae02f9c0d8ddb510e611b2344ec9a17111") (:authors ("myuhe ")) (:maintainer "Raimon Grau" . "raimonster@gmail.com") (:keywords "convenience") (:url . "https://github.com/kidd/org-gcal.el"))]) + (org-gcal . [(20220809 1955) ((request (20190901)) (request-deferred (20181129)) (alert (0)) (persist (0)) (emacs (26)) (org (9 3))) "Org sync with Google Calendar" tar ((:commit . "40291bec0cd0bf8a2f5db656e4d3077b256092ae") (:authors ("myuhe ")) (:maintainer "Raimon Grau" . "raimonster@gmail.com") (:keywords "convenience") (:url . "https://github.com/kidd/org-gcal.el"))]) (org-generate . [(20200815 736) ((emacs (26 1)) (org (9 3)) (mustache (0 23))) "Generate template files/folders from org document" single ((:commit . "98825efb73c4537f05f653ce40e639a220d2ee5d") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "convenience") (:url . "https://github.com/conao3/org-generate.el"))]) (org-gnome . [(20150614 1457) ((alert (1 2)) (telepathy (0 1)) (gnome-calendar (0 1))) "Orgmode integration with the GNOME desktop" single ((:commit . "122e14cf6f8104150a65246a9a7c10e1d7939862") (:authors ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainer "Nicolas Petton" . "petton.nicolas@gmail.com") (:keywords "org" "gnome"))]) (org-grep . [(20151202 1229) ((cl-lib (0 5))) "Kind of M-x rgrep adapted for Org mode." single ((:commit . "5bdd04c0f53b8a3d656f36ea17bba3df7f0cb684") (:authors ("François Pinard" . "pinard@iro.umontreal.ca")) (:maintainer "François Pinard" . "pinard@iro.umontreal.ca") (:url . "https://github.com/pinard/org-grep"))]) @@ -3485,14 +3496,14 @@ (org-inline-anim . [(20211101 413) ((emacs (25 3)) (org (9 4))) "Inline playback of animated GIF/PNG for Org" single ((:commit . "ea7feb924c991f3a2cdc4a70fb176eaceae87938") (:authors ("Shigeaki Nishina")) (:maintainer "Shigeaki Nishina") (:keywords "org" "outlines" "hypermedia" "multimedia") (:url . "https://github.com/shg/org-inline-anim.el"))]) (org-inline-pdf . [(20220429 1012) ((emacs (25 1)) (org (9 4))) "Inline PDF previewing for Org" single ((:commit . "b790818ecbb85cd6dee44754935eb12153a79679") (:authors ("Shigeaki Nishina")) (:maintainer "Shigeaki Nishina") (:keywords "org" "outlines" "hypermedia") (:url . "https://github.com/shg/org-inline-pdf.el"))]) (org-iv . [(20171001 1022) ((impatient-mode (1 0 0)) (org (8 0)) (cl-lib (0 5))) "a tool used to view html (in browser) generated by org-file once the org-file changes" tar ((:commit . "7f2bb1b32647655fd9d6684f6f09dcc66b61b0cd") (:authors ("kuangdash" . "kuangdash@163.com")) (:maintainer "kuangdash" . "kuangdash@163.com") (:url . "https://github.com/kuangdash/org-iv"))]) - (org-jira . [(20220725 1808) ((emacs (24 5)) (cl-lib (0 5)) (request (0 2 0)) (dash (2 14 1))) "Syncing between Jira and Org-mode." tar ((:commit . "93629335727d855e8795470e78282c9dc532c839") (:maintainer "Matthew Carter" . "m@ahungry.com") (:keywords "ahungry" "jira" "org" "bug" "tracker") (:url . "https://github.com/ahungry/org-jira"))]) + (org-jira . [(20220725 1808) ((emacs (24 5)) (cl-lib (0 5)) (request (0 2 0)) (dash (2 14 1))) "Syncing between Jira and Org-mode." tar ((:commit . "f6e9b905ff3c6212aca8182764e1371fb71c309f") (:maintainer "Matthew Carter" . "m@ahungry.com") (:keywords "ahungry" "jira" "org" "bug" "tracker") (:url . "https://github.com/ahungry/org-jira"))]) (org-journal . [(20220408 629) ((emacs (25 1)) (org (9 1))) "a simple org-mode based journaling mode" single ((:commit . "839a2e19865a03bec30ef32431f981f33880a754") (:authors ("Bastian Bechtold") ("Christian Schwarzgruber")) (:maintainer "Bastian Bechtold") (:url . "http://github.com/bastibe/org-journal"))]) (org-journal-list . [(20190221 2052) ((emacs (25))) "Org mode Journal List" single ((:commit . "2b26d00181bb49bff64b31ad020490acd1b6ae02") (:authors ("Huy Tran" . "huytd189@gmail.com")) (:maintainer "Huy Tran" . "huytd189@gmail.com") (:url . "https://github.com/huytd/org-journal-list"))]) - (org-journal-tags . [(20220803 1358) ((emacs (27 1)) (org-journal (2 1 2)) (magit-section (3 3 0)) (transient (0 3 7))) "Tagging and querying system of org-journal" single ((:commit . "687c95eea69b515b194b416db27def686392e06f") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/org-journal-tags"))]) + (org-journal-tags . [(20220815 1604) ((emacs (27 1)) (org-journal (2 1 2)) (magit-section (3 3 0)) (transient (0 3 7))) "Tagging and querying system for org-journal" single ((:commit . "c7db858aef69501b2878a7c2aed4ad0447842b4e") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/org-journal-tags"))]) (org-kanban . [(20220723 1216) ((s (0)) (dash (2 17 0)) (emacs (24 4)) (org (9 1))) "kanban dynamic block for org-mode." single ((:commit . "e78deb03880ae89d6bceae6563ef1383526233a1") (:authors ("Christian Köstlin" . "christian.koestlin@gmail.com")) (:maintainer "Christian Köstlin" . "christian.koestlin@gmail.com") (:keywords "org-mode" "org" "kanban" "tools") (:url . "http://github.com/gizmomogwai/org-kanban"))]) (org-kindle . [(20220210 1408) ((emacs (25)) (cl-lib (0 5)) (seq (2 20))) "Send org link file to ebook reader." single ((:commit . "fadcfd62e254d0c45e87d63128a82a08ae21869a") (:keywords "org" "link" "ebook" "kindle" "epub" "azw3" "mobi") (:url . "https://repo.or.cz/org-kindle.git"))]) (org-latex-impatient . [(20210409 2251) ((emacs (26)) (s (1 8 0)) (posframe (0 8 0)) (org (9 3)) (dash (2 17 0))) "Preview org-latex Fragments Instantly via MathJax" single ((:commit . "832bbb9bbdee8b58170c984ead487f3ad612820c") (:authors ("Sheng Yang" . "styang@fastmail.com")) (:maintainer "Sheng Yang" . "styang@fastmail.com") (:keywords "tex" "tools") (:url . "https://github.com/yangsheng6810/org-latex-instant-preview"))]) - (org-link-beautify . [(20220701 359) ((emacs (27 1)) (all-the-icons (5 0 0))) "Beautify Org Links" single ((:commit . "ee06be6d82d36330cf0f96544e7cfcfdc1b7bb02") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-link-beautify.git"))]) + (org-link-beautify . [(20220822 1337) ((emacs (28 1)) (all-the-icons (5 0 0))) "Beautify Org Links" single ((:commit . "5309895a7616e04a55a9dd397100adfe35fcec60") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-link-beautify.git"))]) (org-link-travis . [(20140405 2327) ((org (7))) "Insert/Export the link of Travis CI on org-mode" single ((:commit . "596615ad8373d9090bd4138da683524f0ad0bda5") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "org") (:url . "https://github.com/aki2o/org-link-travis"))]) (org-linkotron . [(20200112 2235) ((emacs (26 1)) (org (9 3))) "Org-mode link selector" tar ((:commit . "d0adc5247b205bc73d2f1a83d4a512d2be541eb5") (:authors ("Per Weijnitz" . "per.weijnitz@gmail.com")) (:maintainer "Per Weijnitz" . "per.weijnitz@gmail.com") (:keywords "hypermedia" "org") (:url . "https://gitlab.com/perweij/org-linkotron"))]) (org-listcruncher . [(20210706 1741) ((seq (2 3)) (emacs (26 1))) "Planning tool - Parse Org mode lists into table" single ((:commit . "d2f4b9e8f1c7c9b495f71deeaa77e4f6f47bc7bf") (:authors ("Derek Feichtinger" . "dfeich@gmail.com")) (:maintainer "Derek Feichtinger" . "dfeich@gmail.com") (:keywords "convenience") (:url . "https://github.com/dfeich/org-listcruncher"))]) @@ -3502,10 +3513,10 @@ (org-mind-map . [(20180826 2340) ((emacs (24)) (dash (1 8 0)) (org (8 2 10))) "Creates a directed graph from org-mode files" single ((:commit . "95347b2f9291f5c5eb6ebac8e726c03634c61de3") (:authors ("Ted Wiles" . "theodore.wiles@gmail.com")) (:maintainer "Ted Wiles" . "theodore.wiles@gmail.com") (:keywords "orgmode" "extensions" "graphviz" "dot") (:url . "https://github.com/theodorewiles/org-mind-map"))]) (org-ml . [(20220711 1528) ((emacs (27 1)) (org (9 3)) (dash (2 17)) (s (1 12))) "Functional Org Mode API" tar ((:commit . "385e3bee497f858705144d7ab5e6570d31d3ffe8") (:authors ("Nathan Dwarshuis" . "ndwar@yavin4.ch")) (:maintainer "Nathan Dwarshuis" . "ndwar@yavin4.ch") (:keywords "org-mode" "outlines") (:url . "https://github.com/ndwarshuis/org-ml"))]) (org-mobile-sync . [(20180606 524) ((emacs (24 3 50)) (org (8 0))) "automatically sync org-mobile on changes" single ((:commit . "06764b943a528827df1e2acc6bc7806cc2c1351f") (:authors ("steckerhalter")) (:maintainer "steckerhalter") (:keywords "org-mode" "org" "mobile" "sync" "todo") (:url . "https://framagit.org/steckerhalter/org-mobile-sync"))]) - (org-modern . [(20220730 827) ((emacs (27 1))) "Modern looks for Org" single ((:commit . "c82b50a61d04571e11c242fb91944753d8bf945c") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:url . "https://github.com/minad/org-modern"))]) + (org-modern . [(20220821 1927) ((emacs (27 1))) "Modern looks for Org" single ((:commit . "23347906b826656c5054b8e35714a1c3d74bdcc4") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:url . "https://github.com/minad/org-modern"))]) (org-movies . [(20210920 101) ((emacs (26 1)) (org (9 0)) (request (0 3 0))) "Manage watchlist with Org mode" single ((:commit . "e96fecaffa2924de64a507aa31d2934e667ee1ea") (:authors ("Anh T Nguyen")) (:maintainer "Anh T Nguyen") (:keywords "hypermedia" "outlines" "org") (:url . "https://github.com/teeann/org-movies"))]) (org-mru-clock . [(20211029 1147) ((emacs (26 1))) "Clock in/out of tasks with completion and persistent history" single ((:commit . "a74322f0cfd6e52151f9bb8d4f90833330f69120") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "convenience" "calendar") (:url . "https://github.com/unhammer/org-mru-clock"))]) - (org-msg . [(20220331 1707) ((emacs (24 4)) (htmlize (1 54))) "Org mode to send and reply to email in HTML." single ((:commit . "60e22e446325a9b3387396459d98be7c1c52579d") (:authors ("Jérémy Compostella" . "jeremy.compostella@gmail.com")) (:maintainer "Jérémy Compostella" . "jeremy.compostella@gmail.com") (:keywords "extensions" "mail") (:url . "https://github.com/jeremy-compostella/org-msg"))]) + (org-msg . [(20220809 1736) ((emacs (24 4)) (htmlize (1 54))) "Org mode to send and reply to email in HTML." single ((:commit . "e0174324ac37a63ed36869c7632dd7139f1b2419") (:authors ("Jérémy Compostella" . "jeremy.compostella@gmail.com")) (:maintainer "Jérémy Compostella" . "jeremy.compostella@gmail.com") (:keywords "extensions" "mail") (:url . "https://github.com/jeremy-compostella/org-msg"))]) (org-multi-wiki . [(20210324 1820) ((emacs (26 1)) (dash (2 12)) (s (1 12)) (org-ql (0 5)) (org (9 3))) "Multiple wikis based on Org mode" single ((:commit . "bf8039aadddaf02569fab473f766071ef7e63563") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "org" "outlines" "files") (:url . "https://github.com/akirak/org-multi-wiki"))]) (org-multiple-keymap . [(20191017 1920) ((org (8 2 4)) (emacs (24)) (cl-lib (0 5))) "Set keymap to elements, such as timestamp and priority." single ((:commit . "4eb8aa0aada012b2346cc7f0c55e07783141a2c3") (:authors ("myuhe ")) (:maintainer "myuhe") (:keywords "convenience" "org-mode") (:url . "https://github.com/myuhe/org-multiple-keymap.el"))]) (org-notebook . [(20170322 452) ((emacs (24)) (org (8)) (cl-lib (0 5))) "Ease the use of org-mode as a notebook" single ((:commit . "86042d866bf441e2c9bb51f995e5994141b78517") (:authors ("Paul Elder" . "paul.elder@amanokami.net")) (:maintainer "Paul Elder" . "paul.elder@amanokami.net") (:keywords "convenience" "tools"))]) @@ -3522,32 +3533,32 @@ (org-picklink . [(20210210 516) ((emacs (24 4))) "Pick a headline link from org-agenda" single ((:commit . "bfdc22b436482752be41c5d6f6f37dca76b1c7c3") (:authors ("Feng Shu" . "tumashu@163.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "convenience") (:url . "https://github.com/tumashu/org-picklink"))]) (org-pivotal . [(20210705 408) ((a (0 1 1)) (dash (2 18 0)) (emacs (26 1)) (request (0 3 0))) "Sync Pivotal Tracker to org buffer" tar ((:commit . "6403cefb8440567fc593a8d267536138cd6165e2") (:authors ("Huy Duong" . "qhuyduong@hotmail.com")) (:maintainer "Huy Duong" . "qhuyduong@hotmail.com") (:url . "https://github.com/org-pivotal/org-pivotal"))]) (org-pomodoro . [(20220318 1618) ((alert (0 5 10)) (cl-lib (0 5))) "Pomodoro implementation for org-mode." tar ((:commit . "3f5bcfb80d61556d35fc29e5ddb09750df962cc6") (:authors ("Arthur Leonard Andersen" . "leoc.git@gmail.com")) (:maintainer "Arthur Leonard Andersen" . "leoc.git@gmail.com") (:url . "https://github.com/lolownia/org-pomodoro"))]) - (org-present . [(20220108 1802) ((org (7))) "Minimalist presentation minor-mode for Emacs org-mode." single ((:commit . "c0f1f36b2384b58b00a2000f2e30895a6230bb6b") (:authors ("Ric Lister")) (:maintainer "Ric Lister") (:url . "https://github.com/rlister/org-present"))]) + (org-present . [(20220806 1847) ((org (7))) "Minimalist presentation minor-mode for Emacs org-mode." single ((:commit . "4ec04e1b77dea76d7c30066ccf3200d2e0b7bee9") (:authors ("Ric Lister")) (:maintainer "Ric Lister") (:url . "https://github.com/rlister/org-present"))]) (org-pretty-tags . [(20211228 1546) ((emacs (25))) "Surrogates for tags" single ((:commit . "e127a1e08df8273b909a99594ffaad84960ff212") (:authors ("Marco Wahl" . "marcowahlsoft@gmail.com")) (:maintainer "Marco Wahl" . "marcowahlsoft@gmail.com") (:keywords "reading" "outlines") (:url . "https://gitlab.com/marcowahl/org-pretty-tags"))]) - (org-preview-html . [(20220718 118) ((emacs (25 1)) (org (8 0))) "Automatically preview org-exported HTML files within Emacs" single ((:commit . "eb3454c9df8911d13ac25700084d9675d0a5f1b2") (:authors ("Jake B" . "jakebox0@protonmail.com")) (:maintainer "Jake B" . "jakebox0@protonmail.com") (:keywords "org" "convenience" "outlines") (:url . "https://github.com/jakebox/org-preview-html"))]) + (org-preview-html . [(20220809 1033) ((emacs (25 1)) (org (8 0))) "Automatically preview org-exported HTML files within Emacs" single ((:commit . "785e1f5c99c0f2d76a9a6611a06b4552a343e221") (:authors ("Jake B" . "jakebox0@protonmail.com")) (:maintainer "Jake B" . "jakebox0@protonmail.com") (:keywords "org" "convenience" "outlines") (:url . "https://github.com/jakebox/org-preview-html"))]) (org-projectile . [(20220114 730) ((projectile (0 11 0)) (dash (2 10 0)) (emacs (24)) (s (1 9 0)) (org-category-capture (0 0 0))) "Repository todo management for org-mode" single ((:commit . "642b39c698db00bc535c1c2335f425fb9f4855a9") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "org-mode" "projectile" "todo" "tools" "outlines") (:url . "https://github.com/IvanMalison/org-projectile"))]) (org-projectile-helm . [(20180601 1822) ((org-projectile (1 0 0)) (helm (2 3 1)) (emacs (25))) "helm functions for org-projectile" single ((:commit . "642b39c698db00bc535c1c2335f425fb9f4855a9") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "org" "projectile" "todo" "helm" "outlines") (:url . "https://github.com/IvanMalison/org-projectile"))]) (org-protocol-jekyll . [(20170328 1639) ((cl-lib (0 5))) "Jekyll's handler for org-protocol" single ((:commit . "dec064a42d6dfe81dfde7ba59ece5ca103ac6334") (:authors ("Vladimir S. Ivanov" . "ivvl82@gmail.com")) (:maintainer "Vladimir S. Ivanov" . "ivvl82@gmail.com"))]) - (org-ql . [(20220718 1844) ((emacs (26 1)) (dash (2 18 1)) (f (0 17 2)) (map (2 1)) (org (9 0)) (org-super-agenda (1 2)) (ov (1 0 6)) (peg (1 0)) (s (1 12 0)) (transient (0 1)) (ts (0 2 -1))) "Org Query Language, search command, and agenda-like view" tar ((:commit . "06f1e1be6ff5ef7e2c8c05dc1954bcedcbb6eb0b") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org" "agenda") (:url . "https://github.com/alphapapa/org-ql"))]) + (org-ql . [(20220819 838) ((emacs (26 1)) (dash (2 18 1)) (f (0 17 2)) (map (2 1)) (org (9 0)) (org-super-agenda (1 2)) (ov (1 0 6)) (peg (1 0)) (s (1 12 0)) (transient (0 1)) (ts (0 2 -1))) "Org Query Language, search command, and agenda-like view" tar ((:commit . "d7ada532c7d06e91d6e07800ca22d5fbdb970e3e") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org" "agenda") (:url . "https://github.com/alphapapa/org-ql"))]) (org-radiobutton . [(20210519 1225) ((dash (2 13 0)) (emacs (24))) "Radiobutton for org-mode lists." single ((:commit . "86d7581202a37d2004589b8c8e9d8638806c6bcc") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "outlines") (:url . "https://github.com/Fuco1/org-radiobutton"))]) (org-random-todo . [(20190214 2057) ((emacs (24 3)) (alert (1 3))) "show a random TODO (with alert) every so often" single ((:commit . "a019c7186ec60c8c7c3657914cdce029811cf4e0") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "org" "todo" "notification" "calendar") (:url . "https://github.com/unhammer/org-random-todo"))]) (org-randomnote . [(20200110 1407) ((f (0 19 0)) (dash (2 12 0)) (org (0))) "Find a random note in your Org-Mode files" single ((:commit . "ea8cf4385970637efffff8f79e14576ba6d7ad13") (:authors ("Michael Fogleman" . "michaelwfogleman@gmail.com")) (:maintainer "Michael Fogleman" . "michaelwfogleman@gmail.com") (:url . "http://github.com/mwfogleman/org-randomnote"))]) - (org-re-reveal . [(20220803 1058) ((emacs (24 4)) (org (8 3)) (htmlize (1 34))) "Org export to reveal.js presentations" tar ((:commit . "5235df04b79b223e338bcfcb511e5bc6445791a4") (:keywords "tools" "outlines" "hypermedia" "slideshow" "presentation" "oer") (:url . "https://gitlab.com/oer/org-re-reveal"))]) + (org-re-reveal . [(20220808 734) ((emacs (24 4)) (org (8 3)) (htmlize (1 34))) "Org export to reveal.js presentations" tar ((:commit . "6f78a0a2287e7eecd4d22aebdb597ebadcc3eab3") (:keywords "tools" "outlines" "hypermedia" "slideshow" "presentation" "oer") (:url . "https://gitlab.com/oer/org-re-reveal"))]) (org-re-reveal-citeproc . [(20211028 1328) ((emacs (25 1)) (org (9 5)) (citeproc (0 9)) (org-re-reveal (3 0 0))) "Citations and bibliography for org-re-reveal" tar ((:commit . "faa9ea387917b20bd1499ad90199ff3d417c00c2") (:authors ("Jens Lechtenbörger")) (:maintainer "Jens Lechtenbörger") (:keywords "hypermedia" "tools" "slideshow" "presentation" "bibliography") (:url . "https://gitlab.com/oer/org-re-reveal-citeproc"))]) (org-re-reveal-ref . [(20211029 551) ((emacs (25 1)) (org-ref (1 1 1)) (org-re-reveal (0 9 3))) "Citations and bibliography for org-re-reveal" tar ((:commit . "ea9661864d5fbef87b12b78f516c13a40c683f24") (:authors ("Jens Lechtenbörger")) (:maintainer "Jens Lechtenbörger") (:keywords "hypermedia" "tools" "slideshow" "presentation" "bibliography") (:url . "https://gitlab.com/oer/org-re-reveal-ref"))]) (org-recent-headings . [(20211011 1519) ((emacs (26 1)) (org (9 0 5)) (dash (2 18 0)) (frecency (0 1)) (s (1 12 0))) "Jump to recently used Org headings" single ((:commit . "97418d581ea030f0718794e50b005e9bae44582e") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org") (:url . "http://github.com/alphapapa/org-recent-headings"))]) (org-recur . [(20211007 238) ((emacs (24)) (org (9 0))) "Recurring org-mode tasks" single ((:commit . "2bdf71d79f11afa3777c6542f84cef4ad3fce916") (:authors ("Marcin Swieczkowski" . "marcin.swieczkowski@gmail.com")) (:maintainer "Marcin Swieczkowski" . "marcin.swieczkowski@gmail.com") (:url . "https://github.com/m-cat/org-recur"))]) (org-redmine . [(20160711 1114) nil "Redmine tools using Emacs OrgMode" single ((:commit . "e77d013bc3784947c46a5c53f03cd7d3c68552fc") (:authors ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainer "Wataru MIYAGUNI" . "gonngo@gmail.com") (:keywords "redmine" "org") (:url . "https://github.com/gongo/org-redmine"))]) - (org-ref . [(20220715 1202) ((org (9 4)) (dash (0)) (s (0)) (f (0)) (htmlize (0)) (hydra (0)) (avy (0)) (parsebib (0)) (bibtex-completion (0)) (citeproc (0)) (ox-pandoc (0))) "citations, cross-references and bibliographies in org-mode" tar ((:commit . "ebe3bb6f58350ac10da73e583d3ad1078f0690e2") (:authors ("John Kitchin" . "jkitchin@andrew.cmu.edu")) (:maintainer "John Kitchin" . "jkitchin@andrew.cmu.edu") (:keywords "org-mode" "cite" "ref" "label") (:url . "https://github.com/jkitchin/org-ref"))]) + (org-ref . [(20220818 1215) ((org (9 4)) (dash (0)) (s (0)) (f (0)) (htmlize (0)) (hydra (0)) (avy (0)) (parsebib (0)) (bibtex-completion (0)) (citeproc (0)) (ox-pandoc (0))) "citations, cross-references and bibliographies in org-mode" tar ((:commit . "a65e3020759ca695c843965295f43d5f3e4fe6c3") (:authors ("John Kitchin" . "jkitchin@andrew.cmu.edu")) (:maintainer "John Kitchin" . "jkitchin@andrew.cmu.edu") (:keywords "org-mode" "cite" "ref" "label") (:url . "https://github.com/jkitchin/org-ref"))]) (org-ref-prettify . [(20220507 649) ((emacs (24 3)) (org-ref (3 0)) (bibtex-completion (1 0 0))) "Prettify org-ref citation links" single ((:commit . "0ec3b6e398ee117c8b8a787a0422b95d9e95f7bb") (:authors ("Alex Kost" . "alezost@gmail.com") ("Vitus Schäfftlein" . "vitusschaefftlein@live.de")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "convenience") (:url . "https://github.com/alezost/org-ref-prettify.el"))]) (org-repo-todo . [(20171228 119) nil "Simple repository todo management with org-mode" single ((:commit . "f73ebd91399c5760ad52c6ad9033de1066042003") (:authors ("justin talbott" . "justin@waymondo.com")) (:maintainer "justin talbott" . "justin@waymondo.com") (:keywords "convenience") (:url . "https://github.com/waymondo/org-repo-todo"))]) - (org-reverse-datetree . [(20220731 1246) ((emacs (26 1)) (dash (2 12)) (org (9 3))) "Create reverse date trees in org-mode" single ((:commit . "e53d0fde875d137eeb03eddd6b50d3c827f9947f") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "outlines") (:url . "https://github.com/akirak/org-reverse-datetree"))]) + (org-reverse-datetree . [(20220824 1824) ((emacs (26 1)) (dash (2 12)) (org (9 3))) "Create reverse date trees in org-mode" single ((:commit . "5794fd8d1aa7bf89abb37f79574118951ad036a6") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "outlines") (:url . "https://github.com/akirak/org-reverse-datetree"))]) (org-review . [(20220411 1205) nil "schedule reviews for Org entries" single ((:commit . "466f7d8f183f226f1e665cf806cb094471903d9c") (:authors ("Alan Schmitt" . "alan.schmitt@polytechnique.org")) (:maintainer "Alan Schmitt" . "alan.schmitt@polytechnique.org") (:keywords "org" "review") (:url . "https://github.com/brabalan/org-review"))]) (org-rich-yank . [(20220227 2154) ((emacs (24 4))) "Paste with org-mode markup and link to source" single ((:commit . "4bcd030f0d736d77c647955739b61fae541417e9") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "convenience" "hypermedia" "org") (:url . "https://github.com/unhammer/org-rich-yank"))]) (org-roam . [(20220804 437) ((emacs (26 1)) (dash (2 13)) (org (9 4)) (emacsql (3 0 0)) (emacsql-sqlite (1 0 0)) (magit-section (3 0 0))) "A database abstraction layer for Org-mode" tar ((:commit . "7f453f3fffb924ca4ae3f8d34cabc03fbcae0127") (:authors ("Jethro Kuan" . "jethrokuan95@gmail.com")) (:maintainer "Jethro Kuan" . "jethrokuan95@gmail.com") (:keywords "org-mode" "roam" "convenience") (:url . "https://github.com/org-roam/org-roam"))]) (org-roam-bibtex . [(20220626 1121) ((emacs (27 1)) (org-roam (2 2 0)) (bibtex-completion (2 0 0))) "Org Roam meets BibTeX" tar ((:commit . "201262a839db20af2a49165a80f85f82dad159d1") (:authors ("Mykhailo Shevchuk" . "mail@mshevchuk.com") ("Leo Vivier" . "leo.vivier+dev@gmail.com")) (:maintainer "Mykhailo Shevchuk" . "mail@mshevchuk.com") (:keywords "bib" "hypermedia" "outlines" "wp") (:url . "https://github.com/org-roam/org-roam-bibtex"))]) (org-roam-timestamps . [(20220111 1755) ((emacs (26 1)) (org-roam (2 0 0))) "Keep track of modification times for org-roam" single ((:commit . "604fdad0feb61419751d3d6b828cc443a99f418f") (:authors ("Thomas F. K. Jorna ")) (:maintainer "Thomas F. K. Jorna" . "jorna@jtrialerror.com") (:keywords "calendar" "outlines" "files") (:url . "https://github.com/ThomasFKJorna/org-roam-timestamps/"))]) - (org-roam-ui . [(20220803 1024) ((emacs (27 1)) (org-roam (2 0 0)) (simple-httpd (20191103 1446)) (websocket (1 13))) "User Interface for Org-roam" tar ((:commit . "c75fc7506ee7f03840a9a93ed9336d7ed24551aa") (:authors ("Kirill Rogovoy, Thomas Jorna")) (:maintainer "Kirill Rogovoy, Thomas Jorna") (:keywords "files" "outlines") (:url . "https://github.com/org-roam/org-roam-ui"))]) + (org-roam-ui . [(20220803 1024) ((emacs (27 1)) (org-roam (2 0 0)) (simple-httpd (20191103 1446)) (websocket (1 13))) "User Interface for Org-roam" tar ((:commit . "16a8da9e5107833032893bc4c0680b368ac423ac") (:authors ("Kirill Rogovoy, Thomas Jorna")) (:maintainer "Kirill Rogovoy, Thomas Jorna") (:keywords "files" "outlines") (:url . "https://github.com/org-roam/org-roam-ui"))]) (org-ros . [(20220320 1705) ((emacs (24 1))) "Rahul's Org-Mode Screenshot" single ((:commit . "70e0f33ee027ca1dce68351ad14a9e47a452fc17") (:authors ("Rahul Martim Juliato" . "rahul.juliato@gmail.com")) (:maintainer "Rahul Martim Juliato" . "rahul.juliato@gmail.com") (:url . "https://github.com/LionyxML/ros"))]) (org-rtm . [(20160214 1236) ((rtm (0 1))) "Simple import/export from rememberthemilk to org-mode" single ((:commit . "adc42ad1fbe92ab447ccc9553780f4456f2508d2") (:authors ("Philipp Middendorf" . "pmidden@secure.mailbox.org")) (:maintainer "Philipp Middendorf" . "pmidden@secure.mailbox.org") (:keywords "outlines" "data") (:url . "https://github.com/pmiddend/org-rtm"))]) (org-runbook . [(20220512 1927) nil "Org mode for runbooks" tar ((:commit . "ec8b933c1269804546c356fe379169d1f0fce9ea") (:authors ("Tyler Dodge")) (:maintainer "Tyler Dodge") (:keywords "convenience" "processes" "terminals" "files") (:url . "https://github.com/tyler-dodge/org-runbook"))]) @@ -3562,14 +3573,14 @@ (org-starter-swiper . [(20201202 144) ((emacs (25 1)) (swiper (0 11)) (org-starter (0 2 4))) "Swiper for org-starter" single ((:commit . "cd9c5c0402de941299d1c8901f26a8f24d755022") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:url . "https://github.com/akirak/org-starter"))]) (org-static-blog . [(20220508 1410) ((emacs (24 3))) "a simple org-mode based static blog generator" single ((:commit . "a6cd8f651f971eaa68be1cbfd30cc775e3a7ee93") (:authors ("Bastian Bechtold")) (:maintainer "Bastian Bechtold") (:url . "https://github.com/bastibe/org-static-blog"))]) (org-sticky-header . [(20201223 143) ((emacs (24 4)) (org (8 3 5))) "Show off-screen Org heading at top of window" single ((:commit . "79136b8c54c48547ba8a07a72a9790cb8e23ecbd") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org") (:url . "http://github.com/alphapapa/org-sticky-header"))]) - (org-super-agenda . [(20210928 916) ((emacs (26 1)) (s (1 10 0)) (dash (2 13)) (org (9 0)) (ht (2 2)) (ts (0 2))) "Supercharge your agenda" tar ((:commit . "3108bc3f725818f0e868520d2c243abe9acbef4e") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org" "agenda") (:url . "http://github.com/alphapapa/org-super-agenda"))]) + (org-super-agenda . [(20220817 2351) ((emacs (26 1)) (s (1 10 0)) (dash (2 13)) (org (9 0)) (ht (2 2)) (ts (0 2))) "Supercharge your agenda" tar ((:commit . "fcc860190682274989fef15385c37c02895078df") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org" "agenda") (:url . "http://github.com/alphapapa/org-super-agenda"))]) (org-superstar . [(20210915 1934) ((org (9 1 9)) (emacs (26 1))) "Prettify headings and plain lists in Org mode" single ((:commit . "03be6c0a3081c46a59b108deb8479ee24a6d86c0") (:authors ("D. Williams" . "d.williams@posteo.net")) (:maintainer "D. Williams" . "d.williams@posteo.net") (:keywords "faces" "outlines") (:url . "https://github.com/integral-dw/org-superstar-mode"))]) (org-sync . [(20181204 23) ((cl-lib (0 5)) (org (8 2)) (emacs (24))) "Synchronize Org documents with External Issue Trackers" tar ((:commit . "e34a385fa9e658c8341a0a6e6bc3472d4d536bb8") (:authors ("Aurelien Aptel ")) (:maintainer "Andrei Beliankou" . "arbox@yandex.ru") (:keywords "org" "synchronization" "issue tracking" "github" "redmine") (:url . "https://github.com/arbox/org-sync"))]) (org-sync-snippets . [(20210111 1726) ((org (8 3 5)) (emacs (24 3)) (f (0 17 3))) "Export snippets to org-mode and vice versa" single ((:commit . "88f995dea188b8a645a3388c42b62a2bb88953d3") (:authors ("Adrien Brochard")) (:maintainer "Adrien Brochard") (:keywords "snippet" "org-mode" "yasnippet" "tools") (:url . "https://github.com/abrochard/org-sync-snippets"))]) (org-table-color . [(20220311 1927) ((emacs (26 1))) "Add color to your org-mode table cells" single ((:commit . "2022f301ef323953c3a0e087a1b601da85e06da1") (:authors ("Colin Woodbury" . "colin@fosskers.ca")) (:maintainer "Colin Woodbury" . "colin@fosskers.ca") (:keywords "data" "faces" "lisp") (:url . "https://github.com/fosskers/org-table-color"))]) (org-table-comment . [(20120209 1851) nil "Org table comment modes." single ((:commit . "33b9966c33ecbc3e27cca67c2f2cdea04364d74e") (:authors ("Matthew L. Fidler ")) (:maintainer "Matthew L. Fidler") (:keywords "org-mode" "orgtbl") (:url . "http://github.com/mlf176f2/org-table-comment.el"))]) (org-table-sticky-header . [(20190924 506) ((org (8 2 10)) (emacs (24 4))) "Sticky header for org-mode tables" single ((:commit . "b65442857128ab04724aaa301e60aa874a31a798") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) - (org-tag-beautify . [(20220723 758) ((emacs (26 1)) (org-pretty-tags (0 2 2)) (all-the-icons (4 0 0))) "Beautify Org Mode tags" single ((:commit . "b9c6ffcc206ffaeafbe6e3ef9561ce6a8e0f30ad") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-tag-beautify.git"))]) + (org-tag-beautify . [(20220820 102) ((emacs (26 1)) (org-pretty-tags (0 2 2)) (all-the-icons (4 0 0))) "Beautify Org Mode tags" single ((:commit . "28eceb17131a4a5de79ce06f867bce49d1a04b78") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-tag-beautify.git"))]) (org-tanglesync . [(20200127 1616) ((emacs (24 4))) "Syncing org src blocks with tangled external files" single ((:commit . "af83a73ae542d5cb3c9d433cbf2ce1d4f4259117") (:authors ("Mehmet Tekman")) (:maintainer "Mehmet Tekman") (:keywords "outlines") (:url . "https://github.com/mtekman/org-tanglesync.el"))]) (org-tfl . [(20170923 1218) ((org (0 16 2)) (cl-lib (0 5)) (emacs (24 1))) "Transport for London meets Orgmode" tar ((:commit . "f0d7d39106a1de5457f5160cddd98ab892b61066") (:authors ("storax (David Zuber), ")) (:maintainer "storax (David Zuber), ") (:keywords "org" "tfl") (:url . "https://github.com/storax/org-tfl"))]) (org-themis . [(20160122 404) ((cl-lib (0 4))) "Experimental project management mode for org-mode" single ((:commit . "78aadbbe22b1993be5c4accd0d3f91a4e85c9a3c") (:maintainer "Zachary Elliott" . "contact@zell.io") (:keywords "org-mode" "elisp" "project") (:url . "http://github.com/zellio/org-themis"))]) @@ -3592,22 +3603,22 @@ (org-wild-notifier . [(20220701 1147) ((alert (1 2)) (async (1 9 3)) (dash (2 18 0)) (emacs (24 4))) "Customizable org-agenda notifications" single ((:commit . "9392b06d20b2f88e45a41bea17bb2f10f24fd19c") (:authors ("Artem Khramov" . "akhramov+emacs@pm.me")) (:maintainer "Artem Khramov" . "akhramov+emacs@pm.me") (:keywords "notification" "alert" "org" "org-agenda" "agenda") (:url . "https://github.com/akhramov/org-wild-notifier.el"))]) (org-working-set . [(20220414 1402) ((org (9 3)) (dash (2 12)) (s (1 12)) (emacs (26 3))) "Manage and visit a small set of org-nodes." single ((:commit . "6af54ed3a5d9bf90629223157803c42f5d3b152c") (:authors ("Marc Ihm" . "1@2484.de")) (:maintainer "Marc Ihm" . "1@2484.de") (:url . "https://github.com/marcIhm/org-working-set"))]) (org-wunderlist . [(20191017 1917) ((request-deferred (0 2 0)) (alert (1 1)) (emacs (24)) (cl-lib (0 5)) (org (8 2 4)) (s (1 9 0))) "Org sync with Wunderlist" single ((:commit . "1a084bb49be4b5a1066db9cd9b7da2f8efab293f") (:authors ("myuhe ")) (:maintainer "myuhe") (:keywords "convenience") (:url . "https://github.com/myuhe/org-wunderlist.el"))]) - (org-zettelkasten . [(20220727 859) ((emacs (24 3)) (org (9 0))) "Helper functions to use Zettelkasten in org-mode" single ((:commit . "edba7bcfdc054ad0ff1952bb525f5709a687db25") (:authors ("Yann Herklotz" . "yann@ymhg.org")) (:maintainer "Yann Herklotz" . "yann@ymhg.org") (:keywords "files" "hypermedia" "org" "notes") (:url . "https://github.com/ymherklotz/emacs-zettelkasten"))]) - (org2blog . [(20210929 17) ((htmlize (1 54)) (hydra (0 15 0)) (xml-rpc (1 6 12)) (metaweblog (1 1 1))) "Blog from Org mode to WordPress" tar ((:commit . "68695ed0e012379556d57f9564ac5ad8cd68fbb8") (:authors ("Puneeth Chaganti" . "punchagan+org2blog@gmail.com")) (:maintainer "Grant Rettke" . "grant@wisdomandwonder.com") (:keywords "comm" "convenience" "outlines" "wp") (:url . "https://github.com/org2blog/org2blog"))]) + (org-zettelkasten . [(20220819 2335) ((emacs (24 3)) (org (9 0))) "Helper functions to use Zettelkasten in org-mode" single ((:commit . "505fd41dea012e743962c3a376c1e63e7a1e127e") (:authors ("Yann Herklotz" . "yann@ymhg.org")) (:maintainer "Yann Herklotz" . "yann@ymhg.org") (:keywords "files" "hypermedia" "org" "notes") (:url . "https://github.com/ymherklotz/emacs-zettelkasten"))]) + (org2blog . [(20220824 148) ((htmlize (1 54)) (hydra (0 15 0)) (xml-rpc (1 6 12)) (metaweblog (1 1 12))) "Blog from Org mode to WordPress" tar ((:commit . "b641fbcf33ac2b8a0de7b80536b42ce035428625") (:authors ("Puneeth Chaganti" . "punchagan+org2blog@gmail.com")) (:maintainer "Grant Rettke" . "grant@wisdomandwonder.com") (:keywords "comm" "convenience" "outlines" "wp") (:url . "https://github.com/org2blog/org2blog"))]) (org2ctex . [(20200331 550) ((emacs (24 4))) "Export org to ctex (a latex macro for Chinese)" single ((:commit . "2e40aa5e78b0562516f46f689e7b74cdf451cc2a") (:authors ("Feng Shu" . "tumashu@163.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:url . "https://github.com/tumashu/org2ctex"))]) (org2elcomment . [(20170324 945) ((org (8 3 4))) "Convert Org file to Elisp comments" single ((:commit . "c88a75d9587c484ead18f7adf08592b09c1cceb0") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) (org2issue . [(20190531 941) ((org (8 0)) (emacs (24 4)) (ox-gfm (0 1)) (gh (0 1)) (s (20160405 920))) "export org to github issue" single ((:commit . "910b98c858762fd14b11d261626c5e979dde0833") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "github" "org") (:url . "https://github.com/lujun9972/org2issue"))]) (org2jekyll . [(20210829 1113) ((dash (2 18 0)) (s (1 9 0))) "Minor mode to publish org-mode post to jekyll without specific yaml" tar ((:commit . "32f6cfc7265cf24ebb5361264e8c1b61a07e74df") (:authors ("Antoine R. Dumont (@ardumont)" . "antoine.romain.dumont@gmail.com")) (:maintainer "Antoine R. Dumont (@ardumont)" . "antoine.romain.dumont@gmail.com") (:keywords "org-mode" "jekyll" "blog" "publish") (:url . "https://github.com/ardumont/org2jekyll"))]) (org2web . [(20210203 324) ((cl-lib (1 0)) (ht (1 5)) (mustache (0 22)) (htmlize (1 47)) (org (8 0)) (dash (2 0 0)) (el2org (0 10)) (simple-httpd (0 1))) "A static site generator based on org mode." tar ((:commit . "6f5c5f0cc5c877ac3a383782bbe8751264d807b6") (:authors ("Feng Shu ") ("Jorge Javier Araya Navarro ") ("Kelvin Hu ")) (:maintainer "Feng Shu ") (:keywords "org-mode" "convenience" "beautify") (:url . "https://github.com/tumashu/org2web"))]) (organic-green-theme . [(20201216 2240) nil "Low-contrast green color theme." single ((:commit . "0ed99a9c0cf14be0a1f491518821f0e9b7e88b88"))]) - (organize-imports-java . [(20220704 657) ((emacs (25 1)) (f (0 20 0)) (s (1 12 0)) (dash (2 14 1)) (ht (2 2))) "Automatically organize imports in Java code" tar ((:commit . "1f422f6f3fa569a6c238ac34aec0320934f06208") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "organize" "imports" "java" "eclipse") (:url . "https://github.com/jcs-elpa/organize-imports-java"))]) + (organize-imports-java . [(20220704 657) ((emacs (25 1)) (f (0 20 0)) (s (1 12 0)) (dash (2 14 1)) (ht (2 2))) "Automatically organize imports in Java code" tar ((:commit . "9a35d8d3d660ac9d4f0a8d15b78cca4d7ed35f74") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "organize" "imports" "java" "eclipse") (:url . "https://github.com/jcs-elpa/organize-imports-java"))]) (orgbox . [(20180827 218) ((org (8 0)) (cl-lib (0 5))) "Mailbox-like task scheduling Org." single ((:commit . "3982f56efd67ec016389cad82ce5a44f619b36a9") (:authors ("Yasuhito Takamiya" . "yasuhito@gmail.com")) (:maintainer "Yasuhito Takamiya" . "yasuhito@gmail.com") (:keywords "org") (:url . "https://github.com/yasuhito/orgbox"))]) (orgit . [(20220425 1157) ((emacs (25 1)) (compat (28 1 1 0)) (magit (3 0)) (org (9 4))) "Support for Org links to Magit buffers" single ((:commit . "b33b916915db5f91d2c9da4cb1a2457ccbb09332") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "hypermedia" "vc") (:url . "https://github.com/magit/orgit"))]) (orgit-forge . [(20220422 1625) ((emacs (25 1)) (compat (28 1 1 0)) (forge (0 3)) (magit (3 3)) (org (9 5)) (orgit (1 8))) "Org links to Forge issue buffers" single ((:commit . "8baf1dee795f026d4555687022487fab89c9bcdf") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "hypermedia" "vc") (:url . "https://github.com/magit/orgit-forge"))]) (orglink . [(20220422 1626) ((emacs (25 1)) (compat (28 1 1 0)) (org (9 5)) (seq (2 23))) "Use Org Mode links in other modes" single ((:commit . "59bec36eb91e78d508e290b69c4383b27466513f") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "hypermedia") (:url . "https://github.com/tarsius/orglink"))]) (orglue . [(20200411 311) ((org (9 3)) (epic (0 2))) "more functionality to org-mode." tar ((:commit . "9d5a8e24be9acb8c55bb4d6aa8b98e30e2677401") (:authors ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainer "Yoshinari Nomura" . "nom@quickhack.net") (:keywords "org"))]) (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")) (:maintainer "Facet Framer" . "facet@facetframer.com") (:keywords "convenience" "outlines") (:url . "http://github.com/facetframer/orgnav"))]) - (orgstrap . [(20220715 16) ((emacs (24 4))) "Bootstrap an Org file using file local variables" single ((:commit . "f4df67e94926f9d389f4a456a9cbf721c9b22b89") (:authors ("Tom Gillespie")) (:maintainer "Tom Gillespie") (:keywords "lisp" "org" "org-mode" "bootstrap") (:url . "https://github.com/tgbugs/orgstrap"))]) + (orgstrap . [(20220715 16) ((emacs (24 4))) "Bootstrap an Org file using file local variables" single ((:commit . "f72980693976648cfdaef32b51b78f1209683d0e") (:authors ("Tom Gillespie")) (:maintainer "Tom Gillespie") (:keywords "lisp" "org" "org-mode" "bootstrap") (:url . "https://github.com/tgbugs/orgstrap"))]) (orgtbl-aggregate . [(20220726 1241) nil "Create an aggregated Org table from another one" single ((:commit . "cfdddd6700c7c0e8850aecfaae6f0bb4345ea5d0") (:keywords "org" "table" "aggregation" "filtering"))]) (orgtbl-ascii-plot . [(20200411 711) nil "ascii-art bar plots in org-mode tables" single ((:commit . "59618630205fc8c0fcc74fb34c4581d9712a5181") (:authors ("Thierry Banel tbanelwebmin at free dot fr") ("Michael Brand")) (:maintainer "Thierry Banel tbanelwebmin at free dot fr") (:keywords "org" "table" "ascii" "plot"))]) (orgtbl-join . [(20220726 1235) ((cl-lib (0 5))) "join columns from another table" single ((:commit . "4b09436de15545ce73dd40e938176a98254109f8") (:authors ("Thierry Banel tbanelwebmin at free dot fr")) (:maintainer "Thierry Banel tbanelwebmin at free dot fr") (:keywords "org" "table" "join" "filtering"))]) @@ -3630,6 +3641,7 @@ (osx-trash . [(20210419 2229) ((emacs (24 1))) "System trash for OS X" tar ((:commit . "af74a2055a15bf4182d8196600f7decd66eec634") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Sebastian Wiesner" . "swiesner@lunaryorn.com") (:keywords "files" "convenience" "tools" "unix") (:url . "https://github.com/lunaryorn/osx-trash.el"))]) (otama . [(20160404 1032) nil "Org-table Manipulator" single ((:commit . "c114fd8006762f891bc120a7c0ea213872e7ab31") (:authors ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainer "Yoshinari Nomura" . "nom@quickhack.net") (:keywords "database" "org-mode"))]) (other-emacs-eval . [(20180408 1348) ((emacs (25 1)) (async (1 9 2))) "Evaluate the Emacs Lisp expression in other Emacs" single ((:commit . "8ace5acafef65daabf0c6619eff60733d7f5d792") (:authors ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainer "Xu Chunyang" . "mail@xuchunyang.me") (:keywords "tools") (:url . "https://github.com/xuchunyang/other-emacs-eval"))]) + (ouroboros . [(20220810 1617) ((emacs (27 1)) (dash (2 19 0)) (cbor (0 2 2)) (bech32 (0 2 0))) "Ouroboros network mini-protocol" single ((:commit . "5e1bf8b8ffa4c75bece7a93feab9858f0e7d676e") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) (outline-magic . [(20180619 1819) nil "outline mode extensions for Emacs" single ((:commit . "2a5f07417b696cf7541d435c43bafcc64817636b") (:authors ("Carsten Dominik" . "dominik@science.uva.nl")) (:maintainer "Thorsten Jolitz ") (:keywords "outlines"))]) (outline-minor-faces . [(20220720 1144) ((emacs (25 1)) (compat (28 1 1 0))) "Headings faces for outline-minor-mode" single ((:commit . "9cc3fed195e0a1f960a971880287856c148b4861") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "faces" "outlines") (:url . "https://github.com/tarsius/outline-minor-faces"))]) (outline-toc . [(20200401 1208) nil "Sidebar showing a \"table of contents\"." single ((:commit . "81d373633b40628cc3a6b6fb534fd7730076bcdb") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:keywords "convenience" "outlines") (:url . "https://github.com/abingham/outline-toc.el"))]) @@ -3638,7 +3650,7 @@ (outrespace . [(20220218 1936) ((emacs (24 4))) "Some c++ namespace utility functions" single ((:commit . "4b6f8a103b2ce76bb0638eac9356c462402b0665") (:authors ("Dan Harms" . "danielrharms@gmail.com")) (:maintainer "Dan Harms" . "danielrharms@gmail.com") (:keywords "tools" "c++" "namespace") (:url . "https://github.com/articuluxe/outrespace.git"))]) (outshine . [(20220326 540) ((outorg (2 0)) (cl-lib (0 5))) "outline with outshine outshines outline" tar ((:commit . "bf1eed10dd7a89b63d0fc014944033db397c1e23") (:authors ("Thorsten Jolitz")) (:maintainer "Thibault Polge" . "thibault@thb.lt") (:keywords "convenience" "outlines" "org") (:url . "https://github.com/alphapapa/outshine"))]) (ov . [(20200326 1042) ((emacs (24 3))) "Overlay library for Emacs Lisp" single ((:commit . "c5b9aa4e1b00d702eb2caedd61c69a22a5fa1fab") (:authors ("Shingo Fukuyama - http://fukuyama.co")) (:maintainer "Shingo Fukuyama - http://fukuyama.co") (:keywords "convenience" "overlay") (:url . "https://github.com/ShingoFukuyama/ov.el"))]) - (overcast-theme . [(20200425 1601) ((emacs (24))) "A dark but vibrant color theme for Emacs" single ((:commit . "e02b835a08919ead079d7221d513348ac02ba92e") (:authors ("Mohammed Ismail Ansari" . "team.terminal@gmail.com")) (:maintainer "Mohammed Ismail Ansari" . "team.terminal@gmail.com") (:keywords "theme") (:url . "http://ismail.teamfluxion.com"))]) + (overcast-theme . [(20200425 1601) ((emacs (24))) "A dark but vibrant color theme for Emacs" single ((:commit . "4fea116272aa16593d4bf23cdbd2998074a593a7") (:authors ("Mohammed Ismail Ansari" . "team.terminal@gmail.com")) (:maintainer "Mohammed Ismail Ansari" . "team.terminal@gmail.com") (:keywords "theme") (:url . "http://ismail.teamfluxion.com"))]) (overseer . [(20180226 619) ((emacs (24)) (dash (2 10 0)) (pkg-info (0 4)) (f (0 18 1))) "Ert-runner Integration Into Emacs" single ((:commit . "02d49f582e80e36b4334c9187801c5ecfb027789") (:authors ("Samuel Tonini" . "tonini.samuel@gmail.com")) (:maintainer "Samuel Tonini" . "tonini.samuel@gmail.com") (:url . "http://www.github.com/tonini/overseer.el"))]) (ovpn-mode . [(20210403 440) ((emacs (25)) (cl-lib (0 5))) "an openvpn management mode" single ((:commit . "4492098c771d094dd0661a5bc6906f65fb530825") (:authors ("Bas Alberts" . "bas@anti.computer")) (:maintainer "Bas Alberts" . "bas@anti.computer") (:keywords "comm") (:url . "https://github.com/anticomputer/ovpn-mode"))]) (owcmd . [(20200517 2039) ((emacs (26 3))) "Run a single command in the other window" single ((:commit . "05fb8f8f81838b5888fdec8b3947096dd2222e61") (:authors ("Jacob First" . "jacob.first@member.fsf.org")) (:maintainer "Jacob First" . "jacob.first@member.fsf.org") (:keywords "convenience") (:url . "https://github.com/fishyfriend/owcmd"))]) @@ -3692,7 +3704,7 @@ (pacfiles-mode . [(20200915 1815) ((emacs (26)) (cl-lib (0 5))) "pacnew and pacsave merging tool" tar ((:commit . "8d06f64abc98c3f3338560c8d6eb47719e034069") (:authors ("Carlos G. Cordero ")) (:maintainer "Carlos G. Cordero" . "pacfiles@binarycharly.com") (:keywords "files" "pacman" "arch" "pacnew" "pacsave" "update" "linux") (:url . "https://github.com/UndeadKernel/pacfiles-mode"))]) (pack . [(20191017 456) ((emacs (24)) (cl-lib (0 5))) "Pack and unpack archive files" single ((:commit . "85cd856fdc00a2365e88b50373b99f1b3d2227be") (:authors ("10sr" . "8.slashes@gmail.com")) (:maintainer "10sr" . "8.slashes@gmail.com") (:keywords "files" "dired") (:url . "https://github.com/10sr/pack-el"))]) (package+ . [(20210124 640) ((emacs (24 3))) "Extensions for the package library." tar ((:commit . "079da78f3be8364e964f5861a5f433ad61b6f654") (:authors ("Ryan Davis" . "ryand-ruby@zenspider.com")) (:maintainer "Ryan Davis" . "ryand-ruby@zenspider.com") (:keywords "extensions" "tools") (:url . "https://github.com/zenspider/package"))]) - (package-build . [(20220625 1438) ((emacs (25 1))) "Tools for assembling a package archive" tar ((:commit . "0a47905f0cefac5a4043c923efe6d80b03a79551") (:authors ("Donald Ephraim Curtis" . "dcurtis@milkbox.net") ("Steve Purcell" . "steve@sanityinc.com") ("Jonas Bernoulli" . "jonas@bernoul.li") ("Phil Hagelberg" . "technomancy@gmail.com")) (:maintainer "Donald Ephraim Curtis" . "dcurtis@milkbox.net") (:keywords "maint" "tools") (:url . "https://github.com/melpa/package-build"))]) + (package-build . [(20220815 1311) ((emacs (25 1))) "Tools for assembling a package archive" tar ((:commit . "b137f76ae76dd7c2e9648a352a679099f1b0f101") (:authors ("Donald Ephraim Curtis" . "dcurtis@milkbox.net") ("Steve Purcell" . "steve@sanityinc.com") ("Jonas Bernoulli" . "jonas@bernoul.li") ("Phil Hagelberg" . "technomancy@gmail.com")) (:maintainer "Donald Ephraim Curtis" . "dcurtis@milkbox.net") (:keywords "maint" "tools") (:url . "https://github.com/melpa/package-build"))]) (package-filter . [(20161122 719) nil "package archive whitelist and blacklist" single ((:commit . "bc73b41aea1d65ca44ef1593ca13126df9bbb39e") (:authors ("Donald Ephraim Curtis" . "dcurtis@milkbox.net")) (:maintainer "Donald Ephraim Curtis" . "dcurtis@milkbox.net") (:url . "https://github.com/milkypostman/package-filter"))]) (package-lint . [(20220607 738) ((cl-lib (0 5)) (emacs (24 1)) (let-alist (1 0 6))) "A linting library for elisp package authors" tar ((:commit . "70529b2ecba5f3a037be8b2c6ecbca769c64b00e") (:authors ("Steve Purcell" . "steve@sanityinc.com") ("Fanael Linithien" . "fanael4@gmail.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "lisp") (:url . "https://github.com/purcell/package-lint"))]) (package-lint-flymake . [(20210530 319) ((emacs (26 1)) (package-lint (0 5))) "A package-lint Flymake backend" single ((:commit . "70529b2ecba5f3a037be8b2c6ecbca769c64b00e") (:url . "https://github.com/purcell/package-lint"))]) @@ -3717,7 +3729,7 @@ (pandoc-mode . [(20220519 2008) ((hydra (0 10 0)) (dash (2 10 0))) "Minor mode for interacting with Pandoc" tar ((:commit . "2a4e726a29d38e7c2379787cad619e5392ad2da0") (:authors ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainer "Joost Kremers" . "joostkremers@fastmail.fm") (:keywords "text" "pandoc") (:url . "http://joostkremers.github.io/pandoc-mode/"))]) (pangu-spacing . [(20190823 401) nil "Minor-mode to add space between Chinese and English characters." single ((:commit . "f92898949ba3bf991fd229416f3bbb54e9c6c223") (:authors ("coldnew" . "coldnew.tw@gmail.com")) (:maintainer "coldnew" . "coldnew.tw@gmail.com") (:url . "http://github.com/coldnew/pangu-spacing"))]) (paper-theme . [(20200510 5) ((emacs (24))) "A minimal Emacs colour theme." single ((:commit . "4359c640c7822a23976e9a5ca4ce63452d796912") (:authors ("Göktuğ Kayaalp")) (:maintainer "Göktuğ Kayaalp") (:keywords "theme" "paper") (:url . "https://dev.gkayaalp.com/elisp/index.html#paper"))]) - (paperless . [(20201130 1241) ((emacs (24 4)) (f (0 11 0)) (s (1 10 0)) (cl-lib (0 6 1))) "A major mode for sorting and filing PDF documents." tar ((:commit . "2db39586a2914f78f345379511d0e8cea4c96b86") (:authors ("Anthony Green" . "green@moxielogic.com")) (:maintainer "Anthony Green" . "green@moxielogic.com") (:keywords "pdf" "convenience") (:url . "http://github.com/atgreen/paperless"))]) + (paperless . [(20220819 226) ((emacs (24 4)) (f (0 11 0)) (s (1 10 0)) (cl-lib (0 6 1))) "A major mode for sorting and filing PDF documents." tar ((:commit . "f230acbc01588bb2ec99426832099b7fb7bad6c0") (:authors ("Anthony Green" . "green@moxielogic.com")) (:maintainer "Anthony Green" . "green@moxielogic.com") (:keywords "pdf" "convenience") (:url . "http://github.com/atgreen/paperless"))]) (paradox . [(20191011 1111) ((emacs (24 4)) (seq (1 7)) (let-alist (1 0 3)) (spinner (1 7 3)) (hydra (0 13 2))) "A modern Packages Menu. Colored, with package ratings, and customizable." tar ((:commit . "339fe3518d1d102b2295670340e75caf4f01a29a") (:authors ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "emacs@endlessparentheses.com") (:keywords "package" "packages") (:url . "https://github.com/Malabarba/paradox"))]) (parchment-theme . [(20200910 2310) ((autothemer (0 2))) "Light theme inspired by Acme and Leuven" single ((:commit . "b11d380f1105407d7985b876f3cf021ed3c0a33b") (:authors ("Alex Griffin" . "a@ajgrf.com")) (:maintainer "Alex Griffin" . "a@ajgrf.com") (:url . "https://github.com/ajgrf/parchment"))]) (paredit . [(20220709 849) nil "minor mode for editing parentheses" single ((:commit . "d0b1a2f42fb47efc8392763d6487fd027e3a2955") (:authors ("Taylor R. Campbell" . "campbell+paredit@mumble.net")) (:maintainer "Taylor R. Campbell" . "campbell+paredit@mumble.net") (:keywords "lisp"))]) @@ -3762,9 +3774,9 @@ (pcomplete-extension . [(20190928 519) ((emacs (24)) (cl-lib (0 5))) "additional completion for pcomplete" single ((:commit . "bc5eb204fee659e0980056009409b44bc7655716") (:authors ("Thierry Volpiatto" . "thierry.volpiatto@gmail.com")) (:maintainer "Thierry Volpiatto" . "thierry.volpiatto@gmail.com") (:url . "https://github.com/thierryvolpiatto/pcomplete-extension"))]) (pcre2el . [(20161120 2103) ((emacs (24)) (cl-lib (0 3))) "regexp syntax converter" single ((:commit . "0b5b2a2c173aab3fd14aac6cf5e90ad3bf58fa7d") (:authors ("joddie ")) (:maintainer "joddie ") (:url . "https://github.com/joddie/pcre2el"))]) (pcsv . [(20150220 1131) nil "Parser of csv" single ((:commit . "798e0933f8d0818beb17aebf3b1056bbf74e03d0") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "data") (:url . "https://github.com/mhayashi1120/Emacs-pcsv/raw/master/pcsv.el"))]) - (pdb-capf . [(20200419 1237) ((emacs (25 1))) "Completion-at-point function for python debugger" single ((:commit . "2f4099aa1330f87df4e9cd526de057ee9b71de6c") (:authors ("Andrii Kolomoiets" . "andreyk.mad@gmail.com")) (:maintainer "Andrii Kolomoiets" . "andreyk.mad@gmail.com") (:keywords "languages" "abbrev" "convenience") (:url . "https://github.com/muffinmad/emacs-pdb-capf"))]) + (pdb-capf . [(20200419 1237) ((emacs (25 1))) "Completion-at-point function for python debugger" single ((:commit . "31602ccab53aa7dcf26a1af222c7da2bcc1390ed") (:authors ("Andrii Kolomoiets" . "andreyk.mad@gmail.com")) (:maintainer "Andrii Kolomoiets" . "andreyk.mad@gmail.com") (:keywords "languages" "abbrev" "convenience") (:url . "https://github.com/muffinmad/emacs-pdb-capf"))]) (pdb-mode . [(20150128 1751) nil "Major mode for editing Protein Data Bank files" single ((:commit . "855fb18ebb73b5df30c8d7677c2bcd0f361b138a") (:authors (nil . "charles.bond@uwa.edu.au")) (:maintainer nil . "aix.bing@gmail.com") (:keywords "data" "pdb") (:url . "http://bondxray.org/software/pdb-mode/"))]) - (pdf-tools . [(20220723 2329) ((emacs (24 3)) (nadvice (0 3)) (tablist (1 0)) (let-alist (1 0 4))) "Support library for PDF documents" tar ((:commit . "bb0b71f5bafd81d0b5647c4ec48fafa0bb6f6c21") (:authors ("Andreas Politz" . "mail@andreas-politz.de")) (:maintainer "Vedang Manerikar" . "vedang.manerikar@gmail.com") (:keywords "files" "multimedia") (:url . "http://github.com/vedang/pdf-tools/"))]) + (pdf-tools . [(20220823 513) ((emacs (24 3)) (nadvice (0 3)) (tablist (1 0)) (let-alist (1 0 4))) "Support library for PDF documents" tar ((:commit . "1a0a30c54dc3effdba4781a2983115d4b6993260") (:authors ("Andreas Politz" . "mail@andreas-politz.de")) (:maintainer "Vedang Manerikar" . "vedang.manerikar@gmail.com") (:keywords "files" "multimedia") (:url . "http://github.com/vedang/pdf-tools/"))]) (pdf-view-restore . [(20190904 1708) ((pdf-tools (0 90)) (emacs (26 0))) "Support for opening last known pdf position in pdfview mode" single ((:commit . "5a1947c01a3edecc9e0fe7629041a2f53e0610c9") (:authors ("Kevin Kim" . "kevinkim1991@gmail.com")) (:maintainer "Kevin Kim" . "kevinkim1991@gmail.com") (:keywords "files" "convenience") (:url . "https://github.com/007kevin/pdf-view-restore"))]) (pdfgrep . [(20210203 1730) ((emacs (24 4))) "run `pdfgrep' and display the results." single ((:commit . "a4ca0a1e6521de93f28bb6736a5344b4974d144c") (:authors ("Jérémy Compostella" . "jeremy.compostella@gmail.com")) (:maintainer "Jérémy Compostella" . "jeremy.compostella@gmail.com") (:keywords "extensions" "mail" "pdf" "grep") (:url . "https://github.com/jeremy-compostella/pdfgrep"))]) (peacock-theme . [(20170808 1320) ((emacs (24 0))) "an Emacs 24 theme based on Peacock (tmTheme)" single ((:commit . "9e46fbfb562b6e26c6e3d6d618b044b3694da4c8") (:authors ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) @@ -3827,7 +3839,7 @@ (pinboard-popular . [(20180511 1726) ((loop (1 4))) "Displays links from the pinboard.in popular page." single ((:commit . "c0bc76cd35f8ecf34723c64a702b82eec2751318") (:keywords "pinboard") (:url . "https://github.com/asimpson/pinboard-popular"))]) (pine-script-mode . [(20210629 1257) ((emacs (24))) "Trading View Pine Script major mode" single ((:commit . "d8ce5dc595a053e80debf6c1e330995c739a8b05") (:authors ("Eric Crosson" . "eric.s.crosson@utexas.edu")) (:maintainer "Eric Crosson" . "eric.s.crosson@utexas.edu") (:keywords "extensions") (:url . "https://github.com/ericcrosson/pine-script-mode"))]) (pinot . [(20140211 2026) nil "Emacs interface to pinot-search" tar ((:commit . "67fda555a155b22bb2ce44ba618b4bd6fc5f144a") (:authors ("Takafumi Arakaki ")) (:maintainer "Takafumi Arakaki "))]) - (pinyin . [(20180620 1241) ((cl-lib (0 5)) (emacs (24))) "Convert Hanzi to Pinyin (汉字转拼音)" tar ((:commit . "e5508e5aa1ad4cfa05a7f4d299e5a155b288ec4c") (:authors ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainer "Xu Chunyang" . "mail@xuchunyang.me") (:keywords "extensions") (:url . "https://github.com/xuchunyang/pinyin.el"))]) + (pinyin . [(20220815 1239) ((cl-lib (0 5)) (emacs (24))) "Convert Hanzi to Pinyin (汉字转拼音)" tar ((:commit . "b7a0aad8ff35e50d1c536df4c0e73fc7e9d06700") (:authors ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainer "Xu Chunyang" . "mail@xuchunyang.me") (:keywords "extensions") (:url . "https://github.com/xuchunyang/pinyin.el"))]) (pinyin-search . [(20160515 358) ((pinyinlib (0 1 0))) "Search Chinese by Pinyin" single ((:commit . "2e877a76851009d41bde66eb33182a03a7f04262") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "chinese" "search") (:url . "https://github.com/xuchunyang/pinyin-search.el"))]) (pinyinlib . [(20200911 1723) nil "Convert first letter of Pinyin to Simplified/Traditional Chinese characters" single ((:commit . "1772c79b6f319b26b6a394a8dda065be3ea4498d") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) (pip-frame . [(20220802 1914) ((emacs (25 1))) "Display and manage a PIP frame" single ((:commit . "8c396a11f532a1beb594b65e99e594f1e9f1c2c8") (:authors ("Milan Zamazal" . "pdm@zamazal.org")) (:maintainer "Milan Zamazal" . "pdm@zamazal.org") (:keywords "frames") (:url . "https://git.zamazal.org/pdm/pip-frame"))]) @@ -3888,7 +3900,7 @@ (polybar-sesman . [(20210901 1336) ((emacs (25 1)) (dash (2 19 1)) (sesman (0 3 0))) "Display active sesman connections in polybar" single ((:commit . "5175b8d641aad9576519717f69f858621892d5c7") (:authors ("Mark Dawson" . "markgdawson@gmail.com")) (:maintainer "Mark Dawson" . "markgdawson@gmail.com") (:keywords "project" "convenience") (:url . "https://github.com/markgdawson/polybar-sesman.el"))]) (polymode . [(20220322 824) ((emacs (25))) "Extensible framework for multiple major modes" tar ((:commit . "2094c92403fe395dfb2b8b2521da1012a966e9ab") (:authors ("Vitalie Spinu")) (:maintainer "Vitalie Spinu") (:keywords "languages" "multi-modes" "processes") (:url . "https://github.com/polymode/polymode"))]) (pomidor . [(20220714 1932) ((emacs (24 3)) (alert (1 2)) (dash (2 17 0))) "Simple and cool pomodoro timer" tar ((:commit . "394a52f95587b1d10d0c3bdca503d2cc876db35b") (:authors ("TatriX" . "tatrics@gmail.com")) (:maintainer "TatriX" . "tatrics@gmail.com") (:keywords "tools" "time" "applications" "pomodoro technique") (:url . "https://github.com/TatriX/pomidor"))]) - (pomm . [(20220315 2038) ((emacs (27 1)) (alert (1 2)) (seq (2 22)) (transient (0 3 0))) "Yet another Pomodoro timer implementation" tar ((:commit . "2a2673bdc8e2c2af99040b14e97b39271806bf79") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/pomm.el"))]) + (pomm . [(20220815 824) ((emacs (27 1)) (alert (1 2)) (seq (2 22)) (transient (0 3 0))) "Pomodoro and Third Time timers" tar ((:commit . "42f03d6ff29109038b31a8647f1acdc80fb867be") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/pomm.el"))]) (pomodoro . [(20210225 2018) nil "A timer for the Pomodoro Technique" single ((:commit . "ed888b24d0b89a5dec6f5278b1064c530c827321") (:authors ("David Kerschner" . "dkerschner@gmail.com")) (:maintainer "David Kerschner" . "dkerschner@gmail.com"))]) (pony-mode . [(20170807 1522) nil "Minor mode for working with Django Projects" tar ((:commit . "760684d30b6c234d1b88c9a4673a808f36f7f341") (:authors ("David Miller" . "david@deadpansincerity.com")) (:maintainer "David Miller" . "david@deadpansincerity.com") (:keywords "python" "django") (:url . "https://github.com/davidmiller/pony-mode"))]) (pony-snippets . [(20200418 354) ((yasnippet (0 8 0))) "Yasnippets for Pony" tar ((:commit . "81a1348f81b0d8a3097d1ca3f2fb2f57964d56d6") (:keywords "snippets" "pony") (:url . "https://github.com/seantallen/pony-snippets"))]) @@ -3896,7 +3908,7 @@ (pophint . [(20200420 1429) ((log4e (0 3 3)) (yaxception (0 3))) "Provide navigation using pop-up tips, like Firefox's Vimperator Hint Mode" tar ((:commit . "5e13da4578ae7ba00e6f7bae31eb546d713cc19d") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "popup") (:url . "https://github.com/aki2o/emacs-pophint"))]) (poporg . [(20170403 751) nil "Pop a comment or string to an empty buffer for text editing" single ((:commit . "2c58d68c81ecca4140bf179f19ed153ec804b65a") (:authors ("François Pinard" . "pinard@iro.umontreal.ca") ("Joseph Rabinoff" . "rabinoff@post.harvard.edu")) (:maintainer "Joseph Rabinoff" . "rabinoff@post.harvard.edu") (:keywords "outlines" "tools") (:url . "https://github.com/QBobWatson/poporg"))]) (popper . [(20220711 836) ((emacs (26 1))) "Summon and dismiss buffers as popups" tar ((:commit . "d7560f18350faaee8362aee16481268de3cc6457") (:authors ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "convenience") (:url . "https://github.com/karthink/popper"))]) - (popup . [(20211231 1823) ((emacs (24 3))) "Visual Popup User Interface" single ((:commit . "c65905aa1a3ac32d1dbc8c1060605621e6143f80") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "lisp") (:url . "https://github.com/auto-complete/popup-el"))]) + (popup . [(20211231 1823) ((emacs (24 3))) "Visual Popup User Interface" single ((:commit . "34c2684a6fb9a98683951b4a68f542622d17952f") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "lisp") (:url . "https://github.com/auto-complete/popup-el"))]) (popup-complete . [(20141109 308) ((popup (0 5 0))) "completion with popup" single ((:commit . "caa655a6d8472e9a4bfa1311126d90d7d1b07fca") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-popup-complete"))]) (popup-edit-menu . [(20170404 1425) ((emacs (24))) "a popup context edit menu package" single ((:commit . "925600a6e29183841199e866cf55e566a6a1b002") (:authors ("Debugfan Chin" . "debugfanchin@gmail.com")) (:maintainer "Debugfan Chin" . "debugfanchin@gmail.com") (:keywords "lisp" "pop-up" "context" "edit" "menu"))]) (popup-imenu . [(20210404 1153) ((dash (2 12 1)) (popup (0 5 3)) (flx-ido (0 6 1))) "imenu index popup" single ((:commit . "b00c4d503cbbaf01c136b1647329e6a6257d012c") (:authors ("Igor Shymko" . "igor.shimko@gmail.com")) (:maintainer "Igor Shymko" . "igor.shimko@gmail.com") (:keywords "popup" "imenu") (:url . "https://github.com/ancane/popup-imenu"))]) @@ -3911,14 +3923,15 @@ (postcss-sorting . [(20180211 956) ((emacs (24))) "postcss-sorting interface" single ((:commit . "deb0c935d2904c11a965758a9aee5a0e905f21fc") (:authors ("Peiwen Lu" . "hi@peiwen.lu")) (:maintainer "Peiwen Lu" . "hi@peiwen.lu") (:url . "https://github.com/P233/postcss-sorting.el"))]) (pov-mode . [(20161115 743) nil "Major mode for editing POV-Ray scene files." tar ((:commit . "9fc1db3aab7c27155674dd1a87ec62606035d074") (:authors ("Peter Boettcher" . "pwb@andrew.cmu.edu")) (:maintainer "Marco Pessotto" . "melmothx@gmail.com") (:keywords "pov" "povray"))]) (pow . [(20140420 806) ((emacs (24)) (cl-lib (0 5))) "pow (http://pow.cx/) manager for emacs" tar ((:commit . "ea83986b8ca8e27cb996290d6463b111ec0966ce") (:authors ("yukihiro hara" . "yukihr@gmail.com")) (:maintainer "yukihiro hara" . "yukihr@gmail.com") (:keywords "develop" "web" "pow") (:url . "http://github.com/yukihr/emacs-pow"))]) + (power-mode . [(20220817 429) ((emacs (26 1))) "Imbue Emacs with power!" single ((:commit . "1641a01ecca254db4094e166893a073b7f9288d0") (:authors ("Eliza Velasquez")) (:maintainer "Eliza Velasquez") (:keywords "games") (:url . "https://github.com/elizagamedev/power-mode.el"))]) (powerline . [(20220122 1904) ((cl-lib (0 2))) "Rewrite of Powerline" tar ((:commit . "566c77844f053cb39fa7acdfbc143a855450f0b5") (:authors ("Donald Ephraim Curtis" . "dcurtis@milkbox.net")) (:maintainer "Donald Ephraim Curtis" . "dcurtis@milkbox.net") (:keywords "mode-line") (:url . "http://github.com/milkypostman/powerline/"))]) (powerline-evil . [(20190603 340) ((evil (1 0 8)) (powerline (2 3))) "Utilities for better Evil support for Powerline" tar ((:commit . "b77e2cf571e9990734f2b30d826f3a362b559fd1") (:authors ("Chris Johnson" . "chris@christophermjohnson.net")) (:maintainer "Chris Johnson" . "chris@christophermjohnson.net") (:keywords "evil" "mode-line" "powerline") (:url . "http://github.com/johnson-christopher/powerline-evil/"))]) - (powershell . [(20220402 643) ((emacs (24))) "Mode for editing PowerShell scripts" single ((:commit . "77b27faf8a292f1dc9f54c872241dc53b6791bf1") (:authors ("Frédéric Perrin ")) (:maintainer "Frédéric Perrin ") (:keywords "powershell" "languages") (:url . "http://github.com/jschaf/powershell.el"))]) + (powershell . [(20220805 1712) ((emacs (24))) "Mode for editing PowerShell scripts" single ((:commit . "f2da15857e430206e215a3c65289b4058ae3c976") (:authors ("Frédéric Perrin ")) (:maintainer "Frédéric Perrin ") (:keywords "powershell" "languages") (:url . "http://github.com/jschaf/powershell.el"))]) (powerthesaurus . [(20220414 1453) ((emacs (24)) (request (0 3 0)) (s (1 12 0))) "Powerthesaurus integration" single ((:commit . "88bc5229cba1604c8f74db0a1456d99259d538cc") (:keywords "convenience" "writing") (:url . "http://github.com/SavchenkoValeriy/emacs-powerthesaurus"))]) (ppcompile . [(20220619 1535) ((emacs (25 1))) "Ping-pong compile projects on remote machines" single ((:commit . "ff54435af0ea970ec89b48afe050a61e721eb39f") (:authors ("Guangwang Huang" . "whatacold@gmail.com")) (:maintainer "Guangwang Huang") (:keywords "tools") (:url . "https://github.com/whatacold/ppcompile"))]) (ppd-sr-speedbar . [(20151108 1224) ((sr-speedbar (20140914 2339)) (project-persist-drawer (0 0 4))) "Sr Speedbar adaptor for project-persist-drawer." tar ((:commit . "d88d7f63f695824c435dd996405454d1e46d2aa3") (:authors ("Robert Dallas Gray")) (:maintainer "Robert Dallas Gray") (:keywords "projects" "drawer") (:url . "https://github.com/rdallasgrayppd-sr-speedbar"))]) (ppp . [(20220211 1529) ((emacs (25 1))) "Extended pretty printer for Emacs Lisp" single ((:commit . "d5d854c3006dfd268e62c7f91c2aad6f86a505b5") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "tools") (:url . "https://github.com/conao3/ppp.el"))]) - (pr-review . [(20220629 1556) ((emacs (27 1)) (magit-section (3 2)) (magit (3 2)) (markdown-mode (2 5)) (ghub (3 5))) "Review github PR" tar ((:commit . "e4db206f2ca9baf65ca7757234bc2baf5634d8cb") (:authors ("Yikai Zhao" . "yikai@z1k.dev")) (:maintainer "Yikai Zhao" . "yikai@z1k.dev") (:keywords "tools") (:url . "https://github.com/blahgeek/emacs-pr-review"))]) + (pr-review . [(20220815 1610) ((emacs (27 1)) (magit-section (3 2)) (magit (3 2)) (markdown-mode (2 5)) (ghub (3 5))) "Review github PR" tar ((:commit . "8033aed0f6c16464b822489b137ba2f3ffe787de") (:authors ("Yikai Zhao" . "yikai@z1k.dev")) (:maintainer "Yikai Zhao" . "yikai@z1k.dev") (:keywords "tools") (:url . "https://github.com/blahgeek/emacs-pr-review"))]) (prassee-theme . [(20180709 1004) ((emacs (24))) "A high contrast color theme for Emacs." single ((:commit . "81126f69cdbaab836c00ae7a49aaf89d4229fde1") (:authors ("Prassee " . "prassee.sathian@gmail.com")) (:maintainer "Prassee " . "prassee.sathian@gmail.com") (:keywords "dark" "high-contrast" "faces") (:url . "https://github.com/prassee/prassee-emacs-theme"))]) (prefab . [(20220605 1310) ((emacs (27 1)) (f (0 2 0)) (transient (0 3 7))) "Integration for project generation tools like cookiecutter" single ((:commit . "5ca61a420d8de5e3707a5c2f01153f4ab2ea3ec1") (:authors ("Laurence Warne")) (:maintainer "Laurence Warne") (:url . "https://github.com/laurencewarne/prefab.el"))]) (preproc-font-lock . [(20151107 2018) nil "Highlight C-style preprocessor directives." single ((:commit . "565fda9f5fdeb0598986174a07e9fb09f7604397") (:authors ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "c" "languages" "faces") (:url . "https://github.com/Lindydancer/preproc-font-lock"))]) @@ -3949,14 +3962,14 @@ (prog-fill . [(20180607 132) ((emacs (25 1)) (cl-lib (0 6 1))) "Smartly format lines to use vertical space." single ((:commit . "3fbf7da6dd826e95c9077d659566ee29814a31d8") (:authors ("Matthew Carter" . "m@ahungry.com")) (:maintainer "Matthew Carter" . "m@ahungry.com") (:keywords "ahungry" "convenience" "c" "formatting" "editing") (:url . "https://github.com/ahungry/prog-fill"))]) (prognth . [(20130920 1759) nil "Extend prog1 to arbitrary index" single ((:commit . "2f1ca4d34b1fd581163e1df122c85418137e8e62") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "lisp"))]) (programmer-dvorak . [(20150427 137) nil "Input method for Programmer Dvorak." single ((:commit . "3288a8f058eca4cab390a564babbbcb17cfa0350") (:authors ("Chenyun Yang" . "yangchenyun@gmail.com")) (:maintainer "Chenyun Yang" . "yangchenyun@gmail.com") (:keywords "dvorak" "programmer-dvorak" "input-method") (:url . "https://github.com/yangchenyun/programmer-dvorak"))]) - (project-abbrev . [(20220704 658) ((emacs (25 1))) "Customize abbreviation expansion in the project" single ((:commit . "5c35af3e456f1300f0e170d74da87e563b6cc222") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "abbrev" "abbreviation" "customizable" "shortcut") (:url . "https://github.com/jcs-elpa/project-abbrev"))]) + (project-abbrev . [(20220704 658) ((emacs (25 1))) "Customize abbreviation expansion in the project" single ((:commit . "8ce8f7cf2b228795dc76af0128cc66c7d7aac3be") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "abbrev" "abbreviation" "customizable" "shortcut") (:url . "https://github.com/jcs-elpa/project-abbrev"))]) (project-explorer . [(20150504 14) ((cl-lib (0 3)) (es-lib (0 3)) (es-windows (0 1)) (emacs (24))) "A project explorer sidebar" single ((:commit . "589a09008706f5f4ef91393dc4306eede0d15ca9") (:authors ("sabof")) (:maintainer "sabof") (:url . "https://github.com/sabof/project-explorer"))]) - (project-mode-line-tag . [(20220720 2110) ((emacs (25 1))) "Display a buffer's project in its mode line" single ((:commit . "509ac9a01cd344ee9bfa1bfebed6565dd4cfedd7") (:authors ("Fritz Grabo" . "hello@fritzgrabo.com")) (:maintainer "Fritz Grabo" . "hello@fritzgrabo.com") (:keywords "convenience") (:url . "https://github.com/fritzgrabo/project-mode-line-tag"))]) + (project-mode-line-tag . [(20220720 2110) ((emacs (25 1))) "Display a buffer's project in its mode line" single ((:commit . "a8809cc1a50cfdedaf7bed2810249ae262884716") (:authors ("Fritz Grabo" . "hello@fritzgrabo.com")) (:maintainer "Fritz Grabo" . "hello@fritzgrabo.com") (:keywords "convenience") (:url . "https://github.com/fritzgrabo/project-mode-line-tag"))]) (project-persist . [(20180906 1302) nil "A minor mode to allow loading and saving of project settings." tar ((:commit . "26d9435bef44da2a1b0892eba822f9f487b98eec") (:authors ("Robert Dallas Gray")) (:maintainer "Robert Dallas Gray") (:keywords "project" "persistence") (:url . "https://github.com/rdallasgray/project-persist"))]) (project-persist-drawer . [(20151108 1222) ((project-persist (0 3))) "Use a project drawer with project-persist." tar ((:commit . "35bbe132a4fab6a0fec15ce6c0fd2fe6a4aa9626") (:authors ("Robert Dallas Gray" . "mail@robertdallasgray.com")) (:maintainer "Robert Dallas Gray" . "mail@robertdallasgray.com") (:keywords "defaults") (:url . "https://github.com/rdallasgray/project-persist-drawer.git"))]) (project-rootfile . [(20220708 1403) ((emacs (27 1))) "Extension of project.el to detect project with root file" single ((:commit . "db981a9d270e438b669aa0772d0ea406689d42ad") (:authors ("Taiki Sugawara" . "buzz.taiki@gmail.com")) (:maintainer "Taiki Sugawara" . "buzz.taiki@gmail.com") (:url . "https://github.com/buzztaiki/project-rootfile.el"))]) (project-shells . [(20210625 647) ((emacs (24 3)) (seq (2 19))) "Manage the shell buffers of each project" single ((:commit . "900369828f1a213c60a2207a71d46bc43fd5405c") (:authors ("\"Huang, Ying\"" . "huang.ying.caritas@gmail.com")) (:maintainer "\"Huang, Ying\"" . "huang.ying.caritas@gmail.com") (:keywords "processes" "terminals") (:url . "https://github.com/hying-caritas/project-shells"))]) - (project-tab-groups . [(20220720 2109) ((emacs (28 1))) "Support a \"one tab group per project\" workflow" single ((:commit . "2d348279876f3073176048d903f9672f3c933ca5") (:authors ("Fritz Grabo" . "hello@fritzgrabo.com")) (:maintainer "Fritz Grabo" . "hello@fritzgrabo.com") (:keywords "convenience") (:url . "https://github.com/fritzgrabo/project-tab-groups"))]) + (project-tab-groups . [(20220720 2109) ((emacs (28 1))) "Support a \"one tab group per project\" workflow" single ((:commit . "2bc01068d532ca338d3e24b1793e7ba69fadf419") (:authors ("Fritz Grabo" . "hello@fritzgrabo.com")) (:maintainer "Fritz Grabo" . "hello@fritzgrabo.com") (:keywords "convenience") (:url . "https://github.com/fritzgrabo/project-tab-groups"))]) (projectile . [(20220804 1530) ((emacs (25 1))) "Manage and navigate projects in Emacs easily" single ((:commit . "94273611c95b6718c41018be2657f6982a325f60") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "project" "convenience") (:url . "https://github.com/bbatsov/projectile"))]) (projectile-codesearch . [(20180508 1522) ((codesearch (20171122 431)) (projectile (20150405 126))) "Integration of codesearch into projectile" single ((:commit . "f6eb96f034a925444412cfa03e45e0ccbbafe3f2") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:keywords "tools" "development" "search") (:url . "https://github.com/abingham/emacs-codesearch"))]) (projectile-git-autofetch . [(20200820 2028) ((emacs (25 1)) (projectile (0 14 0))) "automatically fetch git repositories" single ((:commit . "423ed5fa6508c4edc0a837bb585c7e77e99876be") (:authors ("Andreas Müller" . "code@0x7.ch")) (:maintainer "Andreas Müller" . "code@0x7.ch") (:keywords "tools" "vc") (:url . "https://github.com/andrmuel/projectile-git-autofetch"))]) @@ -3978,7 +3991,7 @@ (propfont-mixed . [(20150113 2211) ((emacs (24)) (cl-lib (0 5))) "Use proportional fonts with space-based indentation." single ((:commit . "0b461ef4754a469610dba71874a34b6da42176bf") (:authors ("Kirill Ignatiev ")) (:maintainer "Kirill Ignatiev ") (:keywords "faces") (:url . "https://github.com/ikirill/propfont-mixed"))]) (proportional . [(20200309 1556) ((emacs (25 1))) "use a proportional font everywhere" single ((:commit . "0e4537af7ba2bc9dbb449c38350bce012b382f51") (:authors ("Johannes Goslar")) (:maintainer "Johannes Goslar") (:keywords "faces") (:url . "https://github.com/ksjogo/proportional"))]) (prosjekt . [(20151127 1416) ((dash (2 8 0))) "a software project tool for emacs" tar ((:commit . "a864a8be5842223043702395f311e3350c28e9db") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:url . "https://github.com/abingham/prosjekt"))]) - (protobuf-mode . [(20220303 1716) nil "major mode for editing protocol buffers." single ((:commit . "a744c223a47d747d98a9b5355ad250a9c72af4e5") (:authors ("Alexandre Vassalotti" . "alexandre@peadrop.com")) (:maintainer "Alexandre Vassalotti" . "alexandre@peadrop.com") (:keywords "google" "protobuf" "languages"))]) + (protobuf-mode . [(20220303 1716) nil "major mode for editing protocol buffers." single ((:commit . "cde4f478772edd5e8f0d80255d7d85067ed7c515") (:authors ("Alexandre Vassalotti" . "alexandre@peadrop.com")) (:maintainer "Alexandre Vassalotti" . "alexandre@peadrop.com") (:keywords "google" "protobuf" "languages"))]) (protocols . [(20170802 1132) ((cl-lib (0 5))) "Protocol database access functions." single ((:commit . "d0f7c4acb05465f1a0d4be54363bbd2802647e77") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "convenience" "net" "protocols") (:url . "https://github.com/davep/protocols.el"))]) (proxy-mode . [(20220210 1410) ((emacs (25))) "A minor mode to toggle proxy." single ((:commit . "620e48c6afaf760d0ee9f5bdf583fd91cd9d0ec6") (:keywords "comm" "proxy") (:url . "https://repo.or.cz/proxy-mode.git"))]) (psalm . [(20211002 1552) ((emacs (24 3)) (php-mode (1 22 3))) "Interface to Psalm" single ((:commit . "28d546a79cb865a78b94cd7e929d66d720505faa") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "tools" "php") (:url . "https://github.com/emacs-php/psalm.el"))]) @@ -4017,15 +4030,15 @@ (pyenv-mode . [(20200518 1521) ((pythonic (0 1 0))) "Integrate pyenv with python-mode" single ((:commit . "b818901b8eac0e260ced66a6a5acabdbf6f5ba99") (:authors ("Artem Malyshev" . "proofit404@gmail.com")) (:maintainer "Artem Malyshev" . "proofit404@gmail.com") (:url . "https://github.com/proofit404/pyenv-mode"))]) (pygen . [(20161121 506) ((elpy (1 12 0)) (python-mode (6 2 2)) (dash (2 13 0))) "Python code generation using Elpy and Python-mode." single ((:commit . "9019ff44ba49d7295b1476530feab91fdadb084b") (:authors ("Jack Crawley ")) (:maintainer "Jack Crawley ") (:keywords "python" "code generation") (:url . "https://github.com/JackCrawley/pygen/"))]) (pygn-mode . [(20220531 1422) ((emacs (26 1)) (tree-sitter (0 15 2)) (tree-sitter-langs (0 10 7)) (uci-mode (0 5 4)) (nav-flash (1 0 0)) (ivy (0 10 0))) "Major-mode for chess PGN files, powered by Python" tar ((:commit . "9a56e701cfcdf9024dda15175e0d0fc645446019") (:authors ("Dodge Coates and Roland Walker")) (:maintainer "Dodge Coates and Roland Walker") (:keywords "data" "games" "chess") (:url . "https://github.com/dwcoates/pygn-mode"))]) - (pyim . [(20220724 1211) ((emacs (25 1)) (async (1 6)) (xr (1 13))) "A Chinese input method support quanpin, shuangpin, wubi, cangjie and rime." tar ((:commit . "ae16cef16913999c08a35da1991514210e55be07") (:authors ("Ye Wenbin" . "wenbinye@163.com") ("Feng Shu" . "tumashu@163.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "convenience" "chinese" "pinyin" "input-method") (:url . "https://github.com/tumashu/pyim"))]) + (pyim . [(20220724 1211) ((emacs (25 1)) (async (1 6)) (xr (1 13))) "A Chinese input method support quanpin, shuangpin, wubi, cangjie and rime." tar ((:commit . "4d6323389665c0c30e737143832d17feb71e9199") (:authors ("Ye Wenbin" . "wenbinye@163.com") ("Feng Shu" . "tumashu@163.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "convenience" "chinese" "pinyin" "input-method") (:url . "https://github.com/tumashu/pyim"))]) (pyim-basedict . [(20220614 1108) nil "The default pinyin dict of pyim" tar ((:commit . "d61af27686f7a39e6c138b7261a686e7ea7a0ef7") (:authors ("Feng Shu" . "tumashu@163.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "convenience" "chinese" "pinyin" "input-method" "complete") (:url . "https://github.com/tumashu/pyim-basedict"))]) (pyim-cangjiedict . [(20210617 934) ((pyim (3 7))) "Some cangjie dicts for pyim" tar ((:commit . "d17e3d32a6480939b350a91a915ebe8e6efad819") (:authors ("Yuanchen Xie" . "yuanchen.gm@gmail.com")) (:maintainer "Yuanchen Xie" . "yuanchen.gm@gmail.com") (:keywords "convenience" "chinese" "pinyin" "input-method" "complete") (:url . "https://github.com/p1uxtar/pyim-cangjiedict"))]) (pyim-smzmdict . [(20210505 1445) ((pyim (3 7))) "Sanma(triple) Zhengma dict for pyim" tar ((:commit . "fcddbde17a04d174c7353548056524687f7be8d2") (:authors ("Yue Shi (Zhizhi)")) (:maintainer "Yuanchen Xie") (:keywords "convenience" "i18n" "pyim" "chinese" "zhengma") (:url . "https://github.com/p1uxtar/pyim-smzmdict"))]) (pyim-wbdict . [(20220604 1340) ((pyim (3 7))) "Some wubi dicts for pyim" tar ((:commit . "e3b128cfcf218e4a0ca04189b0bd46909761227e") (:authors ("Feng Shu" . "tumashu@163.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "convenience" "chinese" "pinyin" "input-method" "complete") (:url . "https://github.com/tumashu/pyim-wbdict"))]) (pyimport . [(20180308 1752) ((dash (2 8 0)) (s (1 9 0)) (shut-up (0 3 2))) "Manage Python imports!" single ((:commit . "a6f63cf7ed93f0c0f7c207e6595813966f8852b9") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk"))]) (pyimpsort . [(20160130 453) ((emacs (24 3))) "Sort python imports." tar ((:commit . "d5c61d70896b642646dfd3c809c06174ae086c1a") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "convenience") (:url . "https://github.com/emacs-pe/pyimpsort.el"))]) - (pyinspect . [(20211102 1415) ((emacs (27 1))) "Python object inspector" tar ((:commit . "36cf624236c8b4cce852dd52b64d058d4d4a32fd") (:authors ("Maor Kadosh" . "git@avocadosh.xyz")) (:maintainer "Maor Kadosh" . "git@avocadosh.xyz") (:keywords "tools") (:url . "https://github.com/it-is-wednesday/pyinspect.el"))]) - (pylint . [(20210411 1931) nil "minor mode for running `pylint'" single ((:commit . "20a2f129e82349f8a216c288905dee748ba55423") (:authors ("Ian Eure" . "ian.eure@gmail.com")) (:maintainer "Jonathan Kotta" . "jpkotta@gmail.com") (:keywords "languages" "python"))]) + (pyinspect . [(20220805 918) ((emacs (27 1))) "Python object inspector" tar ((:commit . "df5959e699157d757c16ce11efdf3045a5b58d23") (:authors ("Maor Kadosh" . "git@avocadosh.xyz")) (:maintainer "Maor Kadosh" . "git@avocadosh.xyz") (:keywords "tools") (:url . "https://github.com/it-is-wednesday/pyinspect.el"))]) + (pylint . [(20210411 1931) nil "minor mode for running `pylint'" single ((:commit . "32116ae72cff8cec0d57160c6505a1c6ec43fc7e") (:authors ("Ian Eure" . "ian.eure@gmail.com")) (:maintainer "Jonathan Kotta" . "jpkotta@gmail.com") (:keywords "languages" "python"))]) (pynt . [(20180710 726) ((emacs (24 4)) (ein (0 13 1)) (epc (0 1 1)) (deferred (0 5 1))) "Generate and scroll EIN buffers from python code" single ((:commit . "86cf9ce78d34f92bfd0764c9cbb75427ebd429e6") (:authors ("Edward Banner" . "edward.banner@gmail.com")) (:maintainer "Edward Banner" . "edward.banner@gmail.com") (:keywords "convenience") (:url . "https://github.com/ebanner/pynt"))]) (pyramid . [(20210427 1032) ((emacs (25 2)) (pythonic (0 1 1)) (tablist (0 70))) "Minor mode for working with pyramid projects" tar ((:commit . "66f54f4a9cc9fa81edf768ab433d5b3c5517363c") (:authors ("Daniel Kraus" . "daniel@kraus.my")) (:maintainer "Daniel Kraus" . "daniel@kraus.my") (:keywords "python" "pyramid" "pylons" "convenience" "tools" "processes") (:url . "https://github.com/dakra/pyramid.el"))]) (pytest . [(20200330 41) ((s (1 9 0))) "Easy Python test running in Emacs" single ((:commit . "6934047242db79b1c53e9fe3e0734cc9719ed1c4") (:keywords "pytest" "python" "testing") (:url . "https://github.com/ionrock/pytest-el"))]) @@ -4038,9 +4051,9 @@ (python-environment . [(20150310 853) ((deferred (0 3 1))) "virtualenv API for Emacs Lisp" tar ((:commit . "401006584e32864a10c69d29f14414828909362e") (:authors ("Takafumi Arakaki ")) (:maintainer "Takafumi Arakaki ") (:keywords "applications" "tools"))]) (python-info . [(20151228 1852) nil "Python info manual for Emacs" tar ((:commit . "306f15441b54b25757cdfd3b327b84024ea21ed7"))]) (python-insert-docstring . [(20211127 1232) ((emacs (25 1))) "Python Google docstring inserter" single ((:commit . "cd6419b74c99c06d5c48c1b289572acce1fd193b") (:authors ("Marco Vocialta" . "macurovc@tutanota.com")) (:maintainer "Marco Vocialta" . "macurovc@tutanota.com") (:url . "https://github.com/macurovc/insert-docstring"))]) - (python-isort . [(20210603 2153) ((emacs (26)) (reformatter (0 6))) "Reformat python-mode buffer with isort" single ((:commit . "339814df22b87eebca02137e581f65d6283fce97") (:authors ("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) (:maintainer "Jimmy Yuen Ho Wong" . "wyuenho@gmail.com") (:keywords "languages") (:url . "https://github.com/wyuenho/emacs-python-isort"))]) + (python-isort . [(20210603 2153) ((emacs (26)) (reformatter (0 6))) "Reformat python-mode buffer with isort" single ((:commit . "8b4948b7fcad90fc9b72f69f4653260bd21f62c3") (:authors ("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) (:maintainer "Jimmy Yuen Ho Wong" . "wyuenho@gmail.com") (:keywords "languages") (:url . "https://github.com/wyuenho/emacs-python-isort"))]) (python-mls . [(20220528 1502) ((emacs (27 1))) "Multi-line shell for (i)Python" single ((:commit . "bbfe9a8b2ea081c032eccfd541dac2bc46aa54a6") (:authors ("J.D. Smith")) (:maintainer "J.D. Smith") (:keywords "languages" "processes") (:url . "https://github.com/jdtsmith/python-mls"))]) - (python-mode . [(20220726 1741) nil "Python major mode" tar ((:commit . "23f8f55d3e5ce34b19f74c78928a43914df38696") (:authors ("2015-2021 https://gitlab.com/groups/python-mode-devs") ("2003-2014 https://launchpad.net/python-mode") ("1995-2002 Barry A. Warsaw") ("1992-1994 Tim Peters")) (:maintainer nil . "python-mode@python.org") (:keywords "languages" "processes" "python" "oop") (:url . "https://gitlab.com/groups/python-mode-devs"))]) + (python-mode . [(20220817 2017) nil "Python major mode" tar ((:commit . "765af4569eaf93c07c6aecdf7f134022677f9620") (:authors ("2015-2021 https://gitlab.com/groups/python-mode-devs") ("2003-2014 https://launchpad.net/python-mode") ("1995-2002 Barry A. Warsaw") ("1992-1994 Tim Peters")) (:maintainer nil . "python-mode@python.org") (:keywords "languages" "processes" "python" "oop") (:url . "https://gitlab.com/groups/python-mode-devs"))]) (python-pytest . [(20220720 1918) ((emacs (24 4)) (dash (2 18 0)) (transient (0 3 7)) (projectile (0 14 0)) (s (1 12 0))) "helpers to run pytest" single ((:commit . "9bf8db38bf18feb0484931877210cecfaa96bfc6") (:authors ("wouter bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "wouter bolsterlee" . "wouter@bolsterl.ee") (:keywords "pytest" "test" "python" "languages" "processes" "tools") (:url . "https://github.com/wbolster/emacs-python-pytest"))]) (python-switch-quotes . [(20161228 809) ((emacs (24 3))) "cycle between ' and \" quotes in python strings" single ((:commit . "93f1e9b40e061a6cea480139e8b1362b6404abd0") (:authors ("Vladimir Lagunov" . "lagunov.vladimir@gmail.com")) (:maintainer "Vladimir Lagunov" . "lagunov.vladimir@gmail.com") (:keywords "python" "tools" "convenience") (:url . "https://github.com/werehuman/python-switch-quotes"))]) (python-test . [(20181018 29) ((emacs (25 1))) "Python testing integration" single ((:commit . "f899975b133539e19ba822e4b0bfd1a28572967e") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "convenience" "tools" "processes") (:url . "https://github.com/emacs-pe/python-test.el"))]) @@ -4057,7 +4070,7 @@ (quarto-mode . [(20220802 2041) ((emacs (25 1)) (polymode (0 2 2)) (poly-markdown (0 2 2)) (markdown-mode (2 3)) (request (0 3 2))) "A (poly)mode for https://quarto.org" single ((:commit . "769a4ec178f8ad3e0c87b1ee23e64616ee161b02") (:authors ("Carlos Scheidegger")) (:maintainer "Carlos Scheidegger") (:keywords "languages" "multi-modes") (:url . "https://github.com/quarto-dev/quarto-emacs"))]) (quasi-monochrome-theme . [(20200415 705) nil "Quasi Monochrome theme" tar ((:commit . "b38d71860fdea945e10e8a766ac9dfa1410ade67") (:authors ("Lorenzo Bolla" . "lbolla@gmail.com")) (:maintainer "Lorenzo Bolla" . "lbolla@gmail.com") (:keywords "color-theme" "monochrome" "high contrast") (:url . "https://github.com/lbolla/emacs-quasi-monochrome"))]) (quelpa . [(20220730 230) ((emacs (25 1))) "Emacs Lisp packages built directly from source" tar ((:commit . "59bd9bf760f2fdf70c81c220f2875dbee0c29d5c") (:authors ("steckerhalter")) (:maintainer "steckerhalter") (:keywords "tools" "package" "management" "build" "source" "elpa") (:url . "https://github.com/quelpa/quelpa"))]) - (quelpa-leaf . [(20220704 635) ((emacs (25 1)) (quelpa (1 0)) (leaf (4 1 0))) "Quelpa handler for leaf" single ((:commit . "bd306aed20349ac96472a05da096c584f9c17408") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "package" "managment" "elpa" "leaf") (:url . "https://github.com/quelpa/quelpa-leaf"))]) + (quelpa-leaf . [(20220704 635) ((emacs (25 1)) (quelpa (1 0)) (leaf (4 1 0))) "Quelpa handler for leaf" single ((:commit . "e7e610662018fe187697b3be3b4fe3a0aae49e73") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "package" "managment" "elpa" "leaf") (:url . "https://github.com/quelpa/quelpa-leaf"))]) (quelpa-use-package . [(20201022 746) ((emacs (25 1)) (quelpa (1 0)) (use-package (2))) "quelpa handler for use-package" single ((:commit . "d985c0326b66aa19581918deccdc5edcacccf953") (:authors ("steckerhalter")) (:maintainer "steckerhalter") (:keywords "package" "management" "elpa" "use-package") (:url . "https://github.com/quelpa/quelpa-use-package"))]) (quick-buffer-switch . [(20201027 2307) nil "Quick switch to file or dir buffers." single ((:commit . "da82555f286588f171eed1de151325bbdd8cbd91") (:authors ("Sebastien Gross ")) (:maintainer "Sebastien Gross ") (:keywords "emacs" "configuration"))]) (quick-peek . [(20200130 2059) ((emacs (24 3))) "Inline quick-peek windows" single ((:commit . "03a276086795faad46a142454fc3e28cab058b70") (:authors ("Clément Pit-Claudel" . "clement.pitclaudel@live.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "tools" "help" "doc" "convenience"))]) @@ -4070,7 +4083,7 @@ (quiz . [(20190525 1206) ((cl-lib (0 5)) (emacs (25))) "Multiple choice quiz game" single ((:commit . "570bf53926d89282cdb9653bd5aa8fe968f92bbd") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "games" "trivia" "quiz") (:url . "https://github.com/davep/quiz.el"))]) (r-autoyas . [(20140101 1510) ((ess (0)) (yasnippet (0 8 0))) "Provides automatically created yasnippets for R function argument lists." tar ((:commit . "b4020ee7f5f895e0065b8b26da8a49c51432d530") (:authors ("Sven Hartenstein & Matthew Fidler")) (:maintainer "Matthew Fidler") (:keywords "r" "yasnippet") (:url . "https://github.com/mlf176f2/r-autoyas.el"))]) (racer . [(20210307 243) ((emacs (25 1)) (rust-mode (0 2 0)) (dash (2 13 0)) (s (1 10 0)) (f (0 18 2)) (pos-tip (0 4 6))) "code completion, goto-definition and docs browsing for Rust via racer" single ((:commit . "1e63e98626737ea9b662d4a9b1ffd6842b1c648c") (:authors ("Phil Dawes")) (:maintainer "Phil Dawes") (:keywords "abbrev" "convenience" "matching" "rust" "tools") (:url . "https://github.com/racer-rust/emacs-racer"))]) - (racket-mode . [(20220803 1542) ((emacs (25 1))) "Racket editing, REPL, and more" tar ((:commit . "e86ff2c865f1cd28bc047542eedfb55b32588b04") (:authors ("Greg Hendershott")) (:maintainer "Greg Hendershott") (:url . "https://www.racket-mode.com/"))]) + (racket-mode . [(20220818 1323) ((emacs (25 1))) "Racket editing, REPL, and more" tar ((:commit . "56df0bdf81070a45a524c94cbfe6354ad613dcbe") (:authors ("Greg Hendershott")) (:maintainer "Greg Hendershott") (:url . "https://www.racket-mode.com/"))]) (rails-i18n . [(20220126 1643) ((emacs (27 2)) (yaml (0 1 0)) (dash (2 19 1))) "Seach and insert i18n on ruby code" single ((:commit . "8e87e4e48e31902b8259ded28a208c2e7efea6e9") (:authors ("Otávio Schwanck dos Santos" . "otavioschwanck@gmail.com")) (:maintainer "Otávio Schwanck dos Santos" . "otavioschwanck@gmail.com") (:keywords "tools" "languages") (:url . "https://github.com/otavioschwanck/rails-i18n.el"))]) (rails-log-mode . [(20140408 425) nil "Major mode for viewing Rails log files" single ((:commit . "ff440003ad7d47cb0ac3300f2a632f4cfd36a446") (:authors ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainer "Anantha kumaran" . "ananthakumaran@gmail.com") (:keywords "rails" "log"))]) (rails-routes . [(20220126 1631) ((emacs (27 2)) (inflections (1 1))) "Search for and insert rails routes" single ((:commit . "eab995a9297ca5bd9bd4f4c2737f2fecfc36def0") (:authors ("Otávio Schwanck" . "otavioschwanck@gmail.com")) (:maintainer "Otávio Schwanck" . "otavioschwanck@gmail.com") (:keywords "tools" "languages") (:url . "https://github.com/otavioschwanck/rails-routes"))]) @@ -4103,10 +4116,10 @@ (react-snippets . [(20210430 1510) ((yasnippet (0 7 0))) "Yasnippets for React" tar ((:commit . "969c21734dab638057fe9e284f6a51edcc3407c9") (:authors ("John Mastro" . "john.b.mastro@gmail.com")) (:maintainer "John Mastro" . "john.b.mastro@gmail.com") (:keywords "snippets"))]) (read-aloud . [(20160923 500) ((emacs (24 4))) "A simple interface to TTS engines" single ((:commit . "c662366226abfb07204ab442b4f853ed85438d8a") (:authors ("Alexander Gromnitsky" . "alexander.gromnitsky@gmail.com")) (:maintainer "Alexander Gromnitsky" . "alexander.gromnitsky@gmail.com") (:keywords "multimedia") (:url . "https://github.com/gromnitsky/read-aloud.el"))]) (read-only-cfg . [(20210717 205) ((emacs (24 3))) "Make files read-only based on user config" single ((:commit . "a4e50d4fbf48970e98b2464e13f46e51a4c43c37") (:authors ("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" single ((:commit . "40d04a0baf5c3d1087b18cc03595c573a1b5891d") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) + (readable-numbers . [(20220711 911) ((emacs (24 1))) "Visually separate long integers" single ((:commit . "5e1bf8b8ffa4c75bece7a93feab9858f0e7d676e") (:authors ("Oscar Najera ")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) (readline-complete . [(20150708 1437) nil "offers completions in shell mode" single ((:commit . "30c020c37b2741160cc37e656e13c85d826a0ebf") (:authors ("Christopher Monsanto" . "chris@monsan.to")) (:maintainer "Christopher Monsanto" . "chris@monsan.to"))]) (real-auto-save . [(20200505 1537) ((emacs (24 4))) "Automatically save your buffers/files at regular intervals" single ((:commit . "481a2d1460ab5a9b6df3721dda76ad515923bfd1") (:authors ("Chaoji Li ") ("Anand Reddy Pandikunta ")) (:maintainer "Chaoji Li ") (:url . "https://github.com/ChillarAnand/real-auto-save"))]) - (realgud . [(20211107 2210) ((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 . "45f7e4409470abf964c7cb1d526248e4fa7078e0") (:authors ("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 . [(20220818 1750) ((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 . "aff03aeef1e40d2abb244240bab9787f4b3e6035") (:authors ("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") (:authors ("Rocky Bernstein")) (:maintainer "Rocky Bernstein") (:url . "http://github.com/rocky/realgud-byebug"))]) (realgud-ipdb . [(20200722 1116) ((realgud (1 5 0)) (load-relative (1 3 1)) (emacs (25))) "Realgud front-end to ipdb" tar ((:commit . "f18f907aa4ddd3e59dc19ca296d4ee2dc5e436b0") (:authors ("Rocky Bernstein" . "rocky@gnu.org")) (:maintainer "Rocky Bernstein" . "rocky@gnu.org") (:url . "https://github.com/realgud/realgud-ipdb"))]) (realgud-jdb . [(20200722 1120) ((realgud (1 5 0)) (load-relative (1 3 1)) (emacs (25))) "Realgud front-end to Java's jdb debugger\"" tar ((:commit . "1c183b2f8aae0de60942ea01444b896bf182c66a") (:authors ("Rocky Bernstein" . "rocky@gnu.org")) (:maintainer "Rocky Bernstein" . "rocky@gnu.org") (:url . "https://github.com/realgud/realgud-jdb"))]) @@ -4165,10 +4178,10 @@ (replace-pairs . [(20160207 1251) ((emacs (24 4))) "Query-replace pairs of things" single ((:commit . "acfb254dddffcee4250092fab9394ef2b42ffbc0") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:url . "https://github.com/davidshepherd7/replace-pairs"))]) (replace-symbol . [(20160518 12) nil "Rename symbols in expressions or buffers" single ((:commit . "baf949e528aee1881f455f9c84e67718bedcb3f6") (:authors ("Brian Mastenbrook" . "brian@mastenbrook.net")) (:maintainer "Brian Mastenbrook" . "brian@mastenbrook.net") (:url . "https://github.com/bmastenbrook/replace-symbol-el"))]) (replace-with-inflections . [(20180831 635) ((cl-lib (0 5)) (string-inflection (1 0 10)) (inflections (1 1))) "Inflection aware `query-replace'" single ((:commit . "d9201e047856492f282da65459b28aba25998dbb") (:authors ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainer "Akinori MUSHA" . "knu@iDaemons.org") (:keywords "matching") (:url . "https://github.com/knu/replace-with-inflections.el"))]) - (repo . [(20191201 38) ((emacs (24 3))) "Running repo from Emacs" single ((:commit . "7b3ce731f1209d74113cb65a2d6aa6f54ce8ed27") (:authors ("Damien Merenne")) (:maintainer "Damien Merenne") (:keywords "convenience") (:url . "https://github.com/canatella/repo-el"))]) + (repo . [(20220820 1554) ((emacs (24 3))) "Running repo from Emacs" single ((:commit . "e504aa831bfa38ddadce293face28b3c9d9ff9b7") (:authors ("Damien Merenne")) (:maintainer "Damien Merenne") (:keywords "convenience") (:url . "https://github.com/canatella/repo-el"))]) (req-package . [(20180605 1141) ((use-package (1 0)) (dash (2 7 0)) (log4e (0 2 0)) (ht (0))) "A use-package wrapper for package runtime dependencies management" tar ((:commit . "a77da72931914ac5f3f64dc61fe9dc3522b2817e") (:authors ("Edward Knyshov" . "edvorg@gmail.com")) (:maintainer "Edward Knyshov" . "edvorg@gmail.com") (:keywords "dotemacs" "startup" "speed" "config" "package") (:url . "https://github.com/edvorg/req-package"))]) - (request . [(20220614 1604) ((emacs (24 4))) "Compatible layer for URL request" single ((:commit . "38ed1d2e64138eb16a9d8ed2987cff2e01b4a93b") (:authors ("Takafumi Arakaki ")) (:maintainer "Takafumi Arakaki ") (:url . "https://github.com/tkf/emacs-request"))]) - (request-deferred . [(20220614 1604) ((emacs (24 1)) (deferred (0 3 1)) (request (0 3))) "Wrap request.el by deferred" single ((:commit . "38ed1d2e64138eb16a9d8ed2987cff2e01b4a93b") (:authors ("Takafumi Arakaki ")) (:maintainer "Takafumi Arakaki ") (:url . "https://github.com/tkf/emacs-request"))]) + (request . [(20220814 2158) ((emacs (24 4))) "Compatible layer for URL request" single ((:commit . "91313f1e00302b7e60d2043d8104bccf72aae80b") (:authors ("Takafumi Arakaki ")) (:maintainer "Takafumi Arakaki ") (:url . "https://github.com/tkf/emacs-request"))]) + (request-deferred . [(20220614 1604) ((emacs (24 1)) (deferred (0 3 1)) (request (0 3))) "Wrap request.el by deferred" single ((:commit . "91313f1e00302b7e60d2043d8104bccf72aae80b") (:authors ("Takafumi Arakaki ")) (:maintainer "Takafumi Arakaki ") (:url . "https://github.com/tkf/emacs-request"))]) (requirejs . [(20151204 719) ((js2-mode (20150713)) (popup (0 5 3)) (s (1 9 0)) (cl-lib (0 5)) (yasnippet (20151011 1823))) "Requirejs import manipulation and source traversal." tar ((:commit . "4ea2a5fcbc76e4cbb6a7461e6f05f019b75865b1") (:authors ("Joe Heyming" . "joeheyming@gmail.com")) (:maintainer "Joe Heyming" . "joeheyming@gmail.com") (:keywords "javascript" "requirejs") (:url . "https://github.com/joeheyming/requirejs-emacs"))]) (requirejs-mode . [(20130215 2104) nil "Improved AMD module management" single ((:commit . "bbb0c09f8eb2d6a33c17319be8137f68bb16bc92") (:authors ("Marc-Olivier Ricard" . "marco.ricard@gmail.com")) (:maintainer "Marc-Olivier Ricard" . "marco.ricard@gmail.com") (:keywords "javascript" "amd" "requirejs"))]) (rescript-mode . [(20220613 1246) ((emacs (26 1))) "A major mode for editing ReScript" tar ((:commit . "2aae2fbd4971dff965c758ec19688780ed7bff21") (:authors ("Karl Landstrom" . "karl.landstrom@brgeight.se") ("Daniel Colascione" . "dancol@dancol.org") ("John Lee" . "jjl@pobox.com")) (:maintainer "John Lee" . "jjl@pobox.com") (:keywords "languages" "rescript") (:url . "https://github.com/jjlee/rescript-mode"))]) @@ -4180,12 +4193,12 @@ (restclient-test . [(20210422 1815) ((emacs (26 1)) (restclient (0))) "Run tests with restclient.el" single ((:commit . "3c6661d087526510a04ea9de421c5869a1a1d061") (:authors ("Simen Heggestøyl" . "simenheg@runbox.com")) (:maintainer "Simen Heggestøyl" . "simenheg@runbox.com") (:url . "https://github.com/simenheg/restclient-test.el"))]) (retrie . [(20200519 551) ((emacs (24 5))) "Refactoring Haskell code with retrie" single ((:commit . "976d6f01a3e214917f16b82e750d825cb9bfcc59") (:authors ("Junyoung Clare Jang" . "jjc9310@gmail.com")) (:maintainer "Junyoung Clare Jang" . "jjc9310@gmail.com") (:keywords "files" "languages" "tools") (:url . "https://github.com/Ailrun/emacs-retrie"))]) (revbufs . [(20200907 2223) nil "Reverts all out-of-date buffers safely" single ((:commit . "df3c02d3063951582c693ae12547993cec8256e2") (:authors ("Neil Van Dyke" . "neil@neilvandyke.org")) (:maintainer "Sam Kleinman" . "sam@tychoish.com") (:keywords "convenience" "buffers") (:url . "http://www.neilvandyke.org/revbufs/"))]) - (reveal-in-folder . [(20220704 659) ((emacs (24 3)) (f (0 20 0)) (s (1 12 0))) "Reveal current file in folder" single ((:commit . "ccc5d8c61d386bb817863624fea0dba1273e7255") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "folder" "finder" "reveal" "file" "explorer") (:url . "https://github.com/jcs-elpa/reveal-in-folder"))]) + (reveal-in-folder . [(20220704 659) ((emacs (24 3)) (f (0 20 0)) (s (1 12 0))) "Reveal current file in folder" single ((:commit . "3d9a05c0bd3a80600a0544f420b77eba9a1f86c4") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "folder" "finder" "reveal" "file" "explorer") (:url . "https://github.com/jcs-elpa/reveal-in-folder"))]) (reveal-in-osx-finder . [(20150802 1657) nil "Reveal file associated with buffer in OS X Finder" single ((:commit . "5710e5936e47139a610ec9a06899f72e77ddc7bc") (:authors ("Kazuki YOSHIDA")) (:maintainer "Kazuki YOSHIDA") (:keywords "os x" "finder") (:url . "https://github.com/kaz-yos/reveal-in-osx-finder"))]) - (reverse-im . [(20220309 1919) ((emacs (25 1)) (seq (2 23))) "Reverse mapping for non-default system layouts" single ((:commit . "50b8376f152916bc200635a112db9439bc3cc9b5") (:keywords "i18n") (:url . "https://github.com/a13/reverse-im.el"))]) + (reverse-im . [(20220309 1919) ((emacs (25 1)) (seq (2 23))) "Reverse mapping for non-default system layouts" single ((:commit . "41f47d5ccab77d42cc2e1a89a09d0dc2410e9eb4") (:keywords "i18n") (:url . "https://github.com/a13/reverse-im.el"))]) (reverse-theme . [(20141205 145) nil "Reverse theme for Emacs" single ((:commit . "8319d0d5342890a3530ffa4daafdb7c35feda1ca") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-reverse-theme"))]) (revert-buffer-all . [(20220731 2351) ((emacs (24 3))) "Revert all open buffers" single ((:commit . "c07996fcf3e8f7ee156055327522586f32582ce1") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-buffer-revert-all"))]) - (review-mode . [(20220725 1305) nil "major mode for ReVIEW" single ((:commit . "ff87e8221740b3ea17de99291f3b29bf867ff6bb") (:authors ("Kenshi Muto" . "kmuto@kmuto.jp")) (:maintainer "Kenshi Muto" . "kmuto@kmuto.jp") (:url . "https://github.com/kmuto/review-el"))]) + (review-mode . [(20220817 1010) nil "major mode for ReVIEW" single ((:commit . "2b24db8d85a1c40dbd67be195caa79c9df1e0f4b") (:authors ("Kenshi Muto" . "kmuto@kmuto.jp")) (:maintainer "Kenshi Muto" . "kmuto@kmuto.jp") (:url . "https://github.com/kmuto/review-el"))]) (reykjavik-theme . [(20201219 947) ((emacs (24))) "Theme with a dark background." single ((:commit . "f6d8e83946633603234cd1dac725e17447f40bce") (:authors ("martin haesler")) (:maintainer "martin haesler"))]) (rfc-mode . [(20210615 1721) ((emacs (25 1))) "RFC document browser and viewer" single ((:commit . "3ef663203b157e7c5b2cd3c425ec8fbe7977a24c") (:authors ("Nicolas Martyanoff" . "khaelin@gmail.com")) (:maintainer "Nicolas Martyanoff" . "khaelin@gmail.com") (:url . "https://github.com/galdor/rfc-mode"))]) (rg . [(20220521 1653) ((emacs (25 1)) (transient (0 3 0)) (wgrep (2 1 10))) "A search tool based on ripgrep" tar ((:commit . "51596cb516d7af1737d964ffdbb4c4d416d6dfaf") (:authors ("David Landell" . "david.landell@sunnyhill.email") ("Roland McGrath" . "roland@gnu.org")) (:maintainer "David Landell" . "david.landell@sunnyhill.email") (:keywords "matching" "tools") (:url . "https://github.com/dajva/rg.el"))]) @@ -4218,10 +4231,10 @@ (roy-mode . [(20121208 1158) nil "Roy major mode" single ((:commit . "0416f561edbc6b4a29fced8be84d2527a9613d65") (:authors ("Georgii Leontiev")) (:maintainer "Georgii Leontiev") (:keywords "extensions") (:url . "https://github.com/folone/roy-mode"))]) (rpm-spec-mode . [(20160710 1136) nil "RPM spec file editing commands for Emacs/XEmacs" single ((:commit . "c1c38050c48ea330c7cea632b8785d66daeefb2b") (:authors ("Stig Bjørlykke," . "stig@bjorlykke.org")) (:maintainer "Stig Bjørlykke," . "stig@bjorlykke.org") (:keywords "unix" "languages"))]) (rpn-calc . [(20210306 426) ((popup (0 4))) "quick RPN calculator for hackers" single ((:commit . "320123ede874a8fc6cde542baa0d106950318071") (:authors ("zk_phi")) (:maintainer "zk_phi") (:url . "https://github.com/zk-phi/rpn-calc"))]) - (rspec-mode . [(20220622 909) ((ruby-mode (1 0)) (cl-lib (0 4))) "Enhance ruby-mode for RSpec" tar ((:commit . "778c76a6a4bd93faf137d5ca47e7823e1665051c") (:authors ("Peter Williams, et al.")) (:maintainer "Peter Williams, et al.") (:keywords "rspec" "ruby") (:url . "http://github.com/pezra/rspec-mode"))]) + (rspec-mode . [(20220809 150) ((ruby-mode (1 0)) (cl-lib (0 4))) "Enhance ruby-mode for RSpec" tar ((:commit . "484f0bab468674852aaf3e0ad0c3b3d4335d4316") (:authors ("Peter Williams, et al.")) (:maintainer "Peter Williams, et al.") (:keywords "rspec" "ruby") (:url . "http://github.com/pezra/rspec-mode"))]) (rsync-mode . [(20210911 0) ((emacs (27 1)) (spinner (1 7 1))) "Rsync projects to remote machines" single ((:commit . "2bc76aa8c2d82bb08ef70e23813a653d66bf3195") (:authors ("Ryan Pilgrim" . "ryan.z.pilgrim@gmail.com")) (:maintainer "Ryan Pilgrim" . "ryan.z.pilgrim@gmail.com") (:keywords "comm") (:url . "https://github.com/r-zip/rsync-mode.el"))]) - (rtags . [(20211101 2149) ((emacs (24 3))) "A front-end for rtags" single ((:commit . "c628efc9b485470a48aec2692d79f7c140bc5b92") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) - (rtags-xref . [(20210721 2314) ((emacs (25 1)) (rtags (2 37))) "RTags backend for xref.el" single ((:commit . "c628efc9b485470a48aec2692d79f7c140bc5b92") (:authors ("Jörg Walter")) (:maintainer "RTags Team") (:url . "https://github.com/Andersbakken/rtags"))]) + (rtags . [(20220818 1535) ((emacs (24 3))) "A front-end for rtags" single ((:commit . "b9c680e7ca003c103687e790f740d86daa6b4b17") (:authors ("Jan Erik Hanssen" . "jhanssen@gmail.com") ("Anders Bakken" . "agbakken@gmail.com")) (:maintainer "Jan Erik Hanssen" . "jhanssen@gmail.com") (:url . "https://github.com/Andersbakken/rtags"))]) + (rtags-xref . [(20210721 2314) ((emacs (25 1)) (rtags (2 37))) "RTags backend for xref.el" single ((:commit . "b9c680e7ca003c103687e790f740d86daa6b4b17") (:authors ("Jörg Walter")) (:maintainer "RTags Team") (:url . "https://github.com/Andersbakken/rtags"))]) (rtm . [(20180329 1508) ((cl-lib (1 0))) "An elisp implementation of the Remember The Milk API" single ((:commit . "3e3d09387cb84801343ecca8fb02e82f213e7bbe") (:authors ("Friedrich Delgado Friedrichs" . "frie...@nomaden.org")) (:maintainer "Friedrich Delgado Friedrichs" . "frie...@nomaden.org") (:keywords "remember" "the" "milk" "productivity" "todo") (:url . "https://github.com/pmiddend/emacs-rtm"))]) (rubik . [(20180222 2014) ((cl-lib (1 0)) (emacs (25 3))) "Rubik's Cube" single ((:commit . "c8dab1726463dbc9042a0b00186e4a8df02eb868") (:authors ("Ivan 'Kurvivor' Truskov" . "trus19@gmail.com")) (:maintainer "Ivan 'Kurvivor' Truskov" . "trus19@gmail.com") (:keywords "games") (:url . "https://github.com/Kurvivor19/rubik-mode"))]) (rubocop . [(20210309 1241) ((emacs (24))) "An Emacs interface for RuboCop" single ((:commit . "f5fd18aa810c3d3269188cbbd731ddc09006f8f5") (:authors ("Bozhidar Batsov")) (:maintainer "Bozhidar Batsov") (:keywords "project" "convenience") (:url . "https://github.com/rubocop/rubocop-emacs"))]) @@ -4240,26 +4253,27 @@ (rufo . [(20170718 1416) ((emacs (24 3))) "use rufo to automatically format ruby files" single ((:commit . "020b02ed6e9ab49e79d2ddf63e4ee2684c1728f4") (:authors ("Daniel Ma" . "danielhgma@gmail.com")) (:maintainer "Daniel Ma" . "danielhgma@gmail.com") (:url . "https://github.com/danielma/rufo.el"))]) (ruled-switch-buffer . [(20211205 635) ((emacs (24 3))) "Rule based buffer switching" single ((:commit . "4ae1a722750f7ecb4db93c062ffdbe353e706bf0") (:authors ("Kazuki Nishikawa" . "kzkn@hey.com")) (:maintainer "Kazuki Nishikawa" . "kzkn@hey.com") (:keywords "convenience") (:url . "https://github.com/kzkn/ruled-switch-buffer"))]) (rum-mode . [(20180127 22) ((emacs (24))) "Major mode for Rum programming language" single ((:commit . "b69a3866e0299cae8c9c805d644e69b2c17b64de") (:keywords "rum" "languages" "lisp") (:url . "https://github.com/rumlang/rum-mode"))]) - (run-command . [(20210529 1505) ((emacs (27 1))) "Run an external command from a context-dependent list" single ((:commit . "ce2d69feeffb9ef9815ef5b5e32f236763197a10") (:authors ("Massimiliano Mirra" . "hyperstruct@gmail.com")) (:maintainer "Massimiliano Mirra" . "hyperstruct@gmail.com") (:keywords "processes") (:url . "https://github.com/bard/emacs-run-command"))]) + (run-command . [(20220821 1421) ((emacs (27 1))) "Run an external command from a context-dependent list" single ((:commit . "0ac25bc1acae652ffb58c48a3853cd1dcd7b40a4") (:authors ("Massimiliano Mirra" . "hyperstruct@gmail.com")) (:maintainer "Massimiliano Mirra" . "hyperstruct@gmail.com") (:keywords "processes") (:url . "https://github.com/bard/emacs-run-command"))]) (run-command-recipes . [(20220801 1851) ((emacs (25 1)) (dash (2 18 0)) (f (0 20 0)) (run-command (0 1 0)) (s (1 12 0)) (ht (2 4))) "This is collection of recipes to `run-command'" tar ((:commit . "4e7846ea4174fa6f56f5c1173e72fc9743812245") (:authors ("semenInRussia" . "hrams205@gmail.com")) (:maintainer "semenInRussia" . "hrams205@gmail.com") (:keywords "extensions" "run-command") (:url . "https://github.com/semenInRussia/emacs-run-command-recipes"))]) (run-stuff . [(20220710 1035) ((emacs (25 1))) "Context based command execution" single ((:commit . "1c51c273ab2f1750278284a15bf6cff464bddc2a") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "files" "lisp" "files" "convenience" "hypermedia") (:url . "https://codeberg.org/ideasman42/emacs-run-stuff"))]) (runner . [(20160524 743) nil "Improved \"open with\" suggestions for dired" single ((:commit . "a211d57ddc600410d07a8b534920ba905b093d87") (:authors ("Thamer Mahmoud" . "thamer.mahmoud@gmail.com")) (:maintainer "Thamer Mahmoud" . "thamer.mahmoud@gmail.com") (:keywords "shell command" "dired" "file extension" "open with") (:url . "https://github.com/thamer/runner"))]) (runtests . [(20150807 831) nil "Run unit tests from Emacs" single ((:commit . "ed90249f24cc48290018df48b9b9b7172440be3e") (:authors ("Sune Simonsen" . "sune@we-knowhow.dk")) (:maintainer "Sune Simonsen" . "sune@we-knowhow.dk") (:keywords "test") (:url . "https://github.com/sunesimonsen/emacs-runtests"))]) (russian-holidays . [(20170109 2140) nil "Russian holidays for the calendar" single ((:commit . "b285a30f29d85c48e3ea4eb93972d34a090c167b") (:authors ("Alexander I.Grafov" . "siberian@laika.name")) (:maintainer "Alexander I.Grafov" . "siberian@laika.name") (:url . "https://github.com/grafov/russian-holidays"))]) (rust-auto-use . [(20200608 1359) nil "Utility to automatically insert Rust use statements" single ((:commit . "d5205f7b9b9eae0f7d0893f87d3391464719f9c0") (:authors ("Rotem Yaari" . "rotemy@MBP.local")) (:maintainer "Rotem Yaari" . "rotemy@MBP.local") (:keywords "languages"))]) - (rust-mode . [(20220626 1126) ((emacs (25 1))) "A major-mode for editing Rust source code" tar ((:commit . "01ba44166cf16d9b78d99f2fa0c3c54c0f206894") (:authors ("Mozilla")) (:maintainer "Mozilla") (:keywords "languages") (:url . "https://github.com/rust-lang/rust-mode"))]) + (rust-mode . [(20220819 1203) ((emacs (25 1))) "A major-mode for editing Rust source code" tar ((:commit . "894487d44c1664a9005cafd625fa99b54ff66c85") (:authors ("Mozilla")) (:maintainer "Mozilla") (:keywords "languages") (:url . "https://github.com/rust-lang/rust-mode"))]) (rust-playground . [(20200116 1043) ((emacs (24 3))) "Local Rust playground for short code snippets." single ((:commit . "5a117781dcb66065bea7830dd73618008fc34949") (:authors ("Alexander I.Grafov" . "grafov@gmail.com")) (:maintainer "Alexander I.Grafov" . "grafov@gmail.com") (:keywords "tools" "rust") (:url . "https://github.com/grafov/rust-playground"))]) - (rustic . [(20220715 1010) ((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)) (seq (2 3)) (spinner (1 7 3)) (xterm-color (1 6))) "Rust development environment" tar ((:commit . "baef1faba8c73587455f8582ee6ba3ef32077a69") (:authors ("Mozilla")) (:maintainer "Mozilla") (:keywords "languages"))]) + (rustic . [(20220824 858) ((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)) (seq (2 3)) (spinner (1 7 3)) (xterm-color (1 6))) "Rust development environment" tar ((:commit . "e279b7d60fa87b54264a8ba6c05604b8178e0d01") (:authors ("Mozilla")) (:maintainer "Mozilla") (: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")) (:maintainer "Shuguang Sun" . "shuguang79@qq.com") (:keywords "convenience") (:url . "https://github.com/ShuguangSun/rutils.el"))]) (rvm . [(20201222 17) nil "Emacs integration for rvm" single ((:commit . "c1f2642434b0f68d9baa0687127079ecd884ba12") (:authors ("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 . [(20220103 940) ((emacs (25 1))) "Roll your own modal mode" single ((:commit . "0a61eed4d2917422d6401b6abe2037c26dab658a") (:authors ("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"))]) - (s . [(20210616 619) nil "The long lost Emacs string manipulation library." single ((:commit . "08661efb075d1c6b4fa812184c1e5e90c08795a9") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "strings"))]) + (s . [(20220816 956) nil "The long lost Emacs string manipulation library." single ((:commit . "07c57d3562da534d1b18a0fb21e12cd6ae6ff4d7") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "strings"))]) (s-buffer . [(20130605 2124) ((s (1 6 0)) (noflet (0 0 3))) "s operations for buffers" single ((:commit . "f95d234282377f00a2c3a9846681080cb95bb1df") (:authors ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp") (:url . "http://github.com/nicferrier/emacs-s-buffer"))]) (s12cpuv2-mode . [(20171013 2051) ((emacs (24 3))) "Major-mode for S12CPUV2 assembly" single ((:commit . "b17d4cf848dec1e20e66458e5c7ff77a2c051a8c") (:authors ("Adam Niederer" . "adam.niederer@gmail.com")) (:maintainer "Adam Niederer" . "adam.niederer@gmail.com") (:keywords "s12cpuv2" "assembly" "languages") (:url . "https://github.com/AdamNiederer/s12cpuv2-mode"))]) (s3ed . [(20200929 1317) ((emacs (25 1)) (dash (2 17 0)) (s (1 12 0))) "Tramp-like access to s3" tar ((:commit . "2234444ead6c4c6fc3fea548958b36d2c29a9938") (:authors ("Matt Usifer" . "mattusifer@gmail.com")) (:maintainer "Matt Usifer" . "mattusifer@gmail.com") (:keywords "s3" "tools") (:url . "https://github.com/mattusifer/s3ed"))]) (sackspace . [(20130719 956) nil "A better backspace" single ((:commit . "fd0480eaaf6d3d11fd30ac5feb2da2f4f7572708") (:authors ("Michael Markert" . "markert.michael@googlemail.com")) (:maintainer "Michael Markert" . "markert.michael@googlemail.com") (:keywords "delete" "convenience") (:url . "http://github.com/cofi/sackspace.el"))]) (sage-shell-mode . [(20201225 1011) ((cl-lib (0 6 1)) (emacs (24 4)) (let-alist (1 0 5)) (deferred (0 5 1))) "A front-end for Sage Math" tar ((:commit . "7fc47d5eab0efac009d5a9316e3dfa223595ab5a") (:authors ("Sho Takemori" . "stakemorii@gmail.com")) (:maintainer "Sho Takemori" . "stakemorii@gmail.com") (:keywords "sage" "math") (:url . "https://github.com/sagemath/sage-shell-mode"))]) (sailfish-scratchbox . [(20171202 1332) nil "Sailfish OS scratchbox inside the emacs." single ((:commit . "65c6b04abadd2cdeb4cc2dc2a8b96b06e0f27ed8") (:authors ("V. V. Polevoy" . "fx@thefx.co")) (:maintainer "V. V. Polevoy" . "fx@thefx.co") (:keywords "sb2" "mb2" "building" "scratchbox" "sailfish") (:url . "https://github.com/vityafx/sailfish-scratchbox.el"))]) + (sakura-theme . [(20220822 254) nil "Filled with cherry blossoms" single ((:commit . "49f6fbb9f0f5d0129c2fc23c09be2c6bfc7ada0a") (:url . "http://github.com/emacsfodder/emacs-theme-sakura"))]) (salesforce-utils . [(20160814 154) ((cl-lib (0 5))) "simple utilities for Salesforce" single ((:commit . "73328baf0fb94ac0d0de645a8f6d42e5ae27f773") (:authors ("Sean McAfee")) (:maintainer "Sean McAfee") (:url . "https://github.com/grimnebulin/emacs-salesforce"))]) (salt-mode . [(20200210 1200) ((emacs (24 4)) (yaml-mode (0 0 12)) (mmm-mode (0 5 4)) (mmm-jinja2 (0 1))) "Major mode for Salt States" single ((:commit . "c46b24e7fdf4a46df5507dc9c533bbc0064a46fa") (:authors ("Ben Hayden" . "hayden767@gmail.com")) (:maintainer "Glynn Forrest" . "me@glynnforrest.com") (:keywords "languages") (:url . "https://github.com/glynnforrest/salt-mode"))]) (sass-mode . [(20190502 53) ((haml-mode (3 0 15)) (cl-lib (0 5))) "Major mode for editing Sass files" single ((:commit . "247a0d4b509f10b28e4687cd8763492bca03599b") (:authors ("Natalie Weizenbaum")) (:maintainer "Natalie Weizenbaum") (:keywords "markup" "language" "css") (:url . "http://github.com/nex3/haml/tree/master"))]) @@ -4302,7 +4316,7 @@ (sdcv . [(20220210 1412) ((emacs (24 3)) (popup (0 5 3)) (showtip (0 1)) (pos-tip (0 4 6)) (cl-lib (0 3))) "Interface for sdcv (StartDict console version)." single ((:commit . "98e239c7380c63282845d5bc55ea6d605f5a33b8") (:authors ("Andy Stewart" . "lazycat.manatee@gmail.com")) (:maintainer "Andy Stewart" . "lazycat.manatee@gmail.com") (:keywords "startdict" "sdcv") (:url . "https://repo.or.cz/sdcv.el.git"))]) (sdlang-mode . [(20161201 711) ((emacs (24 3))) "Major mode for Simple Declarative Language files." single ((:commit . "7fdcf4ead88d451c0a4a6425b2e730818eaf610e") (:authors ("Vladimir Panteleev")) (:maintainer "Vladimir Panteleev") (:keywords "languages") (:url . "https://github.com/CyberShadow/sdlang-mode"))]) (search-web . [(20150312 1103) nil "Post web search queries using `browse-url'." single ((:commit . "c4ae86ac1acfc572b81f3d78764bd9a54034c331") (:authors ("Tomoya Otake" . "tomoya.ton@gmail.com")) (:maintainer "Tomoya Otake" . "tomoya.ton@gmail.com"))]) - (searcher . [(20220704 758) ((emacs (25 1)) (dash (2 10)) (f (0 20 0))) "Searcher in pure elisp" single ((:commit . "6debf99431a5cb4d1db55e5379453c6a181a133f") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "search" "searcher" "string") (:url . "https://github.com/jcs-elpa/searcher"))]) + (searcher . [(20220704 758) ((emacs (25 1)) (dash (2 10)) (f (0 20 0))) "Searcher in pure elisp" single ((:commit . "54330eabcc5c47019b9ab51d6b3fc035deebbab0") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "search" "searcher" "string") (:url . "https://github.com/jcs-elpa/searcher"))]) (searchq . [(20150829 1211) ((emacs (24 3))) "Framework of queued search tasks using GREP, ACK, AG and more." tar ((:commit . "dd510d55ad66a82c6ef022cfe7c4a73ad5365f82") (:authors ("boyw165")) (:maintainer "boyw165"))]) (secretaria . [(20191128 250) ((emacs (24 4)) (alert (1 2)) (s (1 12)) (f (0 20 0)) (org (9))) "A personal assistant based on org-mode" single ((:commit . "03986130a2ada1fa952d45e83536729f20230fcf") (:authors ("Jorge Araya Navarro" . "jorge@esavara.cr")) (:maintainer "Jorge Araya Navarro" . "jorge@esavara.cr") (:keywords "org" "convenience") (:url . "https://gitlab.com/shackra/secretaria"))]) (see-mode . [(20180511 41) ((emacs (24 4)) (language-detection (0 1 0))) "Edit string in a separate buffer" single ((:commit . "b6e72ea90105b03816c334be9e43bb41dcc79abf") (:authors ("Marcelo Muñoz" . "ma.munoz.araya@gmail.com")) (:maintainer "Marcelo Muñoz" . "ma.munoz.araya@gmail.com") (:keywords "convenience") (:url . "https://github.com/marcelino-m/see-mode"))]) @@ -4347,7 +4361,7 @@ (shadowenv . [(20210512 1625) ((emacs (24 3))) "Shadowenv integration." single ((:commit . "bcdce40b906c848727dbb55176262a9f03f8abb4") (:authors ("Dante Catalfamo" . "dante.catalfamo@shopify.com")) (:maintainer "Dante Catalfamo" . "dante.catalfamo@shopify.com") (:keywords "shadowenv" "tools") (:url . "https://github.com/Shopify/shadowenv.el"))]) (shakespeare-mode . [(20180704 2138) nil "A major mode for editing Shakespearean templates." single ((:commit . "c442eeea9d585e1b1fbb8813e33d47feec348a57") (:authors ("Cody Reichert")) (:maintainer "Cody Reichert") (:keywords "shakespeare" "hamlet" "lucius" "julius" "mode") (:url . "http://github.com/CodyReichert/shakespeare-mode"))]) (shampoo . [(20131230 1019) nil "A remote Smalltalk development mode" tar ((:commit . "bc193c39636c30182159c5c91c37a9a4cb50fedf"))]) - (shanty-themes . [(20220623 1528) ((emacs (27 2))) "The themes for digital workers" tar ((:commit . "906104f6a6b185efc19285fcc3bc943f094fb594") (:authors ("Philip Gaber" . "phga@posteo.de")) (:maintainer "Philip Gaber" . "phga@posteo.de") (:keywords "faces" "theme" "blue" "yellow" "gold" "dark" "light") (:url . "https://github.com/qhga/shanty-themes"))]) + (shanty-themes . [(20220816 1830) ((emacs (24 5 1))) "The themes for digital workers" tar ((:commit . "9ddd8ccd94b51ad5b19ee7097dcfccb3588b5eef") (:authors ("Philip Gaber" . "phga@posteo.de")) (:maintainer "Philip Gaber" . "phga@posteo.de") (:keywords "faces" "theme" "blue" "yellow" "gold" "dark" "light") (:url . "https://github.com/qhga/shanty-themes"))]) (share2computer . [(20200316 31) ((emacs (25 1))) "Elisp helper of android ShareToComputer" single ((:commit . "15da47625a800e3310b8dc714bd4e41e32966d6a") (:authors ("Feng Shu" . "tumashu@163.com")) (:maintainer "Feng Shu" . "tumashu@163.com") (:keywords "convenience" "comm") (:url . "https://github.com/tumashu/share2computer"))]) (sharper . [(20220510 2001) ((emacs (27 1)) (transient (0 2 0))) "A dotnet CLI wrapper, using Transient" single ((:commit . "8020a5da0327f9a18b4bdab474bf8d81c1b38ea7") (:authors ("Sebastian Monia" . "smonia@outlook.com")) (:maintainer "Sebastian Monia" . "smonia@outlook.com") (:keywords "maint" "tool") (:url . "https://github.com/sebasmonia/sharper"))]) (shell-current-directory . [(20140101 2354) nil "create new shell based on buffer directory" single ((:commit . "bf843771bf9a4aa05e054ade799eb8862f3be89a") (:authors ("Daniel Polani")) (:maintainer "Daniel Polani") (:keywords "shell" "comint"))]) @@ -4362,15 +4376,15 @@ (shelldon . [(20220325 1305) ((emacs (27 1))) "An enhanced shell interface" single ((:commit . "8d073ce580e7782ed863fc6e19dc33b4f73c0d79") (:authors ("overdr0ne" . "scmorris.dev@gmail.com")) (:maintainer "overdr0ne" . "scmorris.dev@gmail.com") (:keywords "tools" "convenience") (:url . "https://github.com/Overdr0ne/shelldon"))]) (shelltest-mode . [(20180501 141) nil "Major mode for shelltestrunner" single ((:commit . "5fea8c9394380e822971a171905b6b5ab9be812d") (:authors ("Dustin Fechner" . "dfe@rtrn.io")) (:maintainer "Dustin Fechner" . "dfe@rtrn.io") (:keywords "languages") (:url . "https://github.com/rtrn/shelltest-mode"))]) (shen-elisp . [(20210530 349) ((emacs (24 4))) "Shen implementation in Elisp" tar ((:commit . "dabb829d0d86f454ceb3b0846cdfc11af1f91cc7") (:authors ("Aditya Siram" . "aditya.siram@gmail.com")) (:maintainer "Aditya Siram" . "aditya.siram@gmail.com") (:url . "https://github.com/deech/shen-elisp"))]) - (shenshou . [(20220802 1232) ((emacs (27 1))) "Download&Extract subtitles from opensubtitles" single ((:commit . "8152f6f6ee975a7f32913d54ae9223a655c3b82c") (:authors ("Chen Bin ")) (:maintainer "Chen Bin ") (:keywords "convenience" "tools") (:url . "http://github.com/redguardtoo/shenshou"))]) + (shenshou . [(20220808 604) ((emacs (27 1))) "Download&Extract subtitles from opensubtitles.org" single ((:commit . "25903d642e81f33abea84573979af2a373f5b5b0") (:authors ("Chen Bin ")) (:maintainer "Chen Bin ") (:keywords "convenience" "tools") (:url . "http://github.com/redguardtoo/shenshou"))]) (shfmt . [(20220602 1535) ((emacs (24)) (reformatter (0 3))) "Reformat shell scripts using shfmt" single ((:commit . "279a51defa3e0d97dc40b8a26e078699d4e22e90") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "languages") (:url . "https://github.com/purcell/emacs-shfmt"))]) (shift-number . [(20170301 1459) nil "Increase/decrease the number at point" single ((:commit . "cd099a5582fc996b800ac7607f6c38a004ce9740") (:authors ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "convenience") (:url . "https://github.com/alezost/shift-number.el"))]) (shift-text . [(20130831 1655) ((cl-lib (1 0)) (es-lib (0 3))) "Move the region in 4 directions, in a way similar to Eclipse's" single ((:commit . "1be9cbf994000022172ceb746fe1d597f57ea8ba") (:authors ("sabof")) (:maintainer "sabof") (:url . "https://github.com/sabof/shift-text"))]) - (shimbun . [(20220707 40) nil "interfacing with web newspapers" tar ((:commit . "db02ce0fde628a8eb9a1a0c95939c41981fdd931") (:authors ("TSUCHIYA Masatoshi" . "tsuchiya@namazu.org") ("Akihiro Arisawa " . "ari@mbf.sphere.ne.jp") ("Yuuichi Teranishi " . "teranisi@gohome.org") ("Katsumi Yamaoka " . "yamaoka@jpl.org")) (:maintainer "TSUCHIYA Masatoshi" . "tsuchiya@namazu.org") (:keywords "news"))]) + (shimbun . [(20220823 543) nil "interfacing with web newspapers" tar ((:commit . "e185b54d13fb7eac7d045daf6a83731d9ef79231") (:authors ("TSUCHIYA Masatoshi" . "tsuchiya@namazu.org") ("Akihiro Arisawa " . "ari@mbf.sphere.ne.jp") ("Yuuichi Teranishi " . "teranisi@gohome.org") ("Katsumi Yamaoka " . "yamaoka@jpl.org")) (:maintainer "TSUCHIYA Masatoshi" . "tsuchiya@namazu.org") (:keywords "news"))]) (shm . [(20180327 57) nil "Structured Haskell Mode" tar ((:commit . "7f9df73f45d107017c18ce4835bbc190dfe6782e") (:authors ("Chris Done" . "chrisdone@gmail.com")) (:maintainer "Chris Done" . "chrisdone@gmail.com") (:keywords "development" "haskell" "structured"))]) (shoulda . [(20140616 1833) ((cl-lib (0 5))) "Shoulda test support for ruby" single ((:commit . "fbe8eb8efc6cfcca1713283a290882cfcdc8725e") (:authors ("Marcwebbie" . "marcwebbie@gmail.com")) (:maintainer "Marcwebbie" . "marcwebbie@gmail.com") (:keywords "ruby" "tests" "shoulda"))]) (show-css . [(20160210 1408) ((doom (1 3)) (s (1 10 0))) "Show the css of the html attribute the cursor is on" tar ((:commit . "771daeddd4df7a7c10f66419a837145649bab63b") (:authors ("Sheldon McGrandle" . "developer@rednemesis.com")) (:maintainer "Sheldon McGrandle" . "developer@rednemesis.com") (:keywords "hypermedia") (:url . "https://github.com/smmcg/showcss-mode"))]) - (show-eol . [(20220704 659) ((emacs (24 4))) "Show end of line symbol in buffer" single ((:commit . "1a332666192f377dca933b0da71e6528b288fc09") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "end" "eol" "line") (:url . "https://github.com/jcs-elpa/show-eol"))]) + (show-eol . [(20220704 659) ((emacs (24 4))) "Show end of line symbol in buffer" single ((:commit . "61e8b5f867b3b139bdaa8a0079fe11447b5ca886") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "end" "eol" "line") (:url . "https://github.com/jcs-elpa/show-eol"))]) (show-font-mode . [(20201225 2217) ((emacs (25 1))) "Show font at point on mode line" single ((:commit . "8503be7966d3bd8316039b5f49d3c37c7b97d10c") (:authors ("Melissa Boiko" . "melissa@namakajiri.net")) (:maintainer "Melissa Boiko" . "melissa@namakajiri.net") (:keywords "faces" "i18n" "unicode" "fonts" "fontsets") (:url . "https://github.com/melissaboiko/show-font-mode"))]) (showtip . [(20090830 1040) nil "Show tip at cursor" single ((:commit . "930da302809a4257e8d69425455b29e1cc91949b") (:authors ("Ye Wenbin" . "wenbinye@gmail.com")) (:maintainer "Ye Wenbin" . "wenbinye@gmail.com") (:keywords "help"))]) (shpec-mode . [(20150530 922) nil "Minor mode for shpec specification" single ((:commit . "146adc8281d0f115df39a3a3f982ac59ab61b754") (:authors ("AdrieanKhisbe" . "adriean.khisbe@live.fr")) (:maintainer "AdrieanKhisbe" . "adriean.khisbe@live.fr") (:keywords "languages" "tools") (:url . "http://github.com/shpec/shpec-mode"))]) @@ -4386,11 +4400,11 @@ (side-hustle . [(20210627 701) ((emacs (24 4)) (seq (2 20))) "Hustle through Imenu in a side window" single ((:commit . "1f4cd5e7cfbabb00c6d87e913770f21e3d16c957") (:authors ("Paul W. Rankin" . "pwr@bydasein.com")) (:maintainer "Paul W. Rankin" . "pwr@bydasein.com") (:keywords "convenience") (:url . "https://github.com/rnkn/side-hustle"))]) (side-notes . [(20210709 1403) ((emacs (24 4))) "Easy access to a directory notes file" single ((:commit . "41fe8544661a772f764a0924e04080f258053955") (:authors ("Paul W. Rankin" . "pwr@bydasein.com")) (:maintainer "Paul W. Rankin" . "pwr@bydasein.com") (:keywords "convenience") (:url . "https://github.com/rnkn/side-notes"))]) (sidecar-locals . [(20220710 1040) ((emacs (27 1))) "A flexible alternative to built-in dir-locals" single ((:commit . "3aa9c890ebc38800ab26f5f877da32a79ce87d18") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "convenience") (:url . "https://codeberg.org/ideasman42/emacs-sidecar-locals"))]) - (sideline . [(20220706 720) ((emacs (27 1))) "Show information on the side" single ((:commit . "f1bad8c6aa954a65cb3a61a116c97155daa45c96") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience") (:url . "https://github.com/emacs-sideline/sideline"))]) - (sideline-blame . [(20220629 801) ((emacs (27 1)) (sideline (0 1 0)) (vc-msg (1 1 1))) "Show blame messages with sideline" single ((:commit . "574592a8ecc171112d4ac2575f9cac51ff5eb184") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "blame") (:url . "https://github.com/emacs-sideline/sideline-blame"))]) - (sideline-flycheck . [(20220629 752) ((emacs (27 1)) (sideline (0 1 1)) (flycheck (0 14)) (ht (2 4))) "Show flycheck errors with sideline" single ((:commit . "7f11c14c938a88a35728b04add991903486ddd31") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "flycheck") (:url . "https://github.com/emacs-sideline/sideline-flycheck"))]) - (sideline-flymake . [(20220629 802) ((emacs (27 1)) (sideline (0 1 0))) "Show flymake errors with sideline" single ((:commit . "a6c6e9e4e64a12040adadaf351f3e83a0e2954e6") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "flymake") (:url . "https://github.com/emacs-sideline/sideline-flymake"))]) - (sideline-lsp . [(20220629 757) ((emacs (27 1)) (sideline (0 1 0)) (lsp-mode (6 0)) (dash (2 18 0)) (ht (2 4)) (s (1 12 0))) "Show lsp information with sideline" single ((:commit . "b340c1e9a6d26ad4b3afe6aa660d62e8cebd66c8") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp") (:url . "https://github.com/emacs-sideline/sideline-lsp"))]) + (sideline . [(20220806 401) ((emacs (27 1))) "Show information on the side" single ((:commit . "c76ed684e36f1a81914cd8dfcfe7efe2d061468e") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience") (:url . "https://github.com/emacs-sideline/sideline"))]) + (sideline-blame . [(20220629 801) ((emacs (27 1)) (sideline (0 1 0)) (vc-msg (1 1 1))) "Show blame messages with sideline" single ((:commit . "b0db4abe5c1c74e15c0844f60a94e8bcb1e29d11") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "blame") (:url . "https://github.com/emacs-sideline/sideline-blame"))]) + (sideline-flycheck . [(20220629 752) ((emacs (27 1)) (sideline (0 1 1)) (flycheck (0 14)) (ht (2 4))) "Show flycheck errors with sideline" single ((:commit . "72e94b34c58e9497abb4f997d2de9f5978b50fa3") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "flycheck") (:url . "https://github.com/emacs-sideline/sideline-flycheck"))]) + (sideline-flymake . [(20220629 802) ((emacs (27 1)) (sideline (0 1 0))) "Show flymake errors with sideline" single ((:commit . "d105be829dc621bbc50158da3dbed7a5e304f8a8") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "flymake") (:url . "https://github.com/emacs-sideline/sideline-flymake"))]) + (sideline-lsp . [(20220629 757) ((emacs (27 1)) (sideline (0 1 0)) (lsp-mode (6 0)) (dash (2 18 0)) (ht (2 4)) (s (1 12 0))) "Show lsp information with sideline" single ((:commit . "4495fd9c7d926b0caf2d041c8deb7833f4be06be") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp") (:url . "https://github.com/emacs-sideline/sideline-lsp"))]) (sift . [(20200421 1423) nil "Front-end for sift, a fast and powerful grep alternative" single ((:commit . "cdddba2d183146c340915003f1b5d09d13712c22") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "sift" "ack" "pt" "ag" "grep" "search") (:url . "https://github.com/nlamirault/sift.el"))]) (signal . [(20160816 1438) ((emacs (24)) (cl-lib (0 5))) "Advanced hook" single ((:commit . "aa58327e2297df921d72a0370468b48663efd438") (:authors ("Mola-T" . "Mola@molamola.xyz")) (:maintainer "Mola-T" . "Mola@molamola.xyz") (:keywords "internal" "lisp" "processes" "tools") (:url . "https://github.com/mola-T/signal"))]) (silkworm-theme . [(20210215 1120) ((emacs (24))) "Light theme with pleasant, low contrast colors." single ((:commit . "ff80e9294da0fb093e15097ac62153ef4a64a889") (:authors ("Martin Haesler")) (:maintainer "Martin Haesler"))]) @@ -4431,7 +4445,7 @@ (slime-volleyball . [(20190701 1624) nil "An SVG Slime Volleyball Game" tar ((:commit . "f36a84f3c503c46ba0d011874d387a34b01c6bf6") (:authors ("Thomas Fitzsimmons" . "fitzsim@fitzsim.org")) (:maintainer "Thomas Fitzsimmons" . "fitzsim@fitzsim.org") (:keywords "games"))]) (slirm . [(20160201 1425) ((emacs (24 4))) "Systematic Literature Review Mode for Emacs." single ((:commit . "9adfbe1fc67580e7d0d90f7e927a25d63a797464") (:authors ("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" single ((:commit . "bedd26dd45ca497c0028a11e94a905560fcdb2f1") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "calendar"))]) - (slow-keys . [(20180831 459) ((emacs (24 1))) "Slow keys mode to avoid RSI" single ((:commit . "b89b4fbddb4b6b95fcc7301ec543ea535b2cc4d7") (:authors ("Manuel Uberti" . "manuel.uberti@inventati.org")) (:maintainer "Manuel Uberti" . "manuel.uberti@inventati.org") (:keywords "convenience") (:url . "https://github.com/manuel-uberti/slow-keys"))]) + (slow-keys . [(20220807 1425) ((emacs (24 1))) "Slow keys mode to avoid RSI" single ((:commit . "ec69a853a40bc5650c1b30cd866acb16e0a2dd8a") (:authors ("Manuel Uberti" . "manuel.uberti@inventati.org")) (:maintainer "Manuel Uberti" . "manuel.uberti@inventati.org") (:keywords "convenience") (:url . "https://github.com/manuel-uberti/slow-keys"))]) (slstats . [(20170823 849) ((cl-lib (0 5)) (emacs (24))) "Acquire and display stats about Second Life" single ((:commit . "e9696066abf3f2b7b818a57c062530dfd9377033") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "games") (:url . "https://github.com/davep/slstats.el"))]) (slurm-mode . [(20210519 1109) nil "Interaction with the SLURM job scheduling system" tar ((:commit . "589826fbb07f625b23c322df1cc16377c4fe6f66") (:url . "https://github.com/ffevotte/slurm.el"))]) (sly . [(20220713 1433) ((emacs (24 3))) "Sylvester the Cat's Common Lisp IDE" tar ((:commit . "ef5211456a59d639c98b2ed42428726d32728ff8") (:keywords "languages" "lisp" "sly") (:url . "https://github.com/joaotavora/sly"))]) @@ -4482,7 +4496,7 @@ (smtpmail-multi . [(20160218 2349) nil "Use different smtp servers for sending mail" single ((:commit . "83fa9d7a02e000be95cb282c8b48446646896ea1") (:authors ("Joe Bloggs" . "vapniks@yahoo.com")) (:maintainer "Joe Bloggs" . "vapniks@yahoo.com") (:keywords "comm") (:url . "https://github.com/vapniks/smtpmail-multi"))]) (smudge . [(20210326 2222) ((emacs (27 1)) (simple-httpd (1 5)) (request (0 3)) (oauth2 (0 16))) "Control the Spotify app" tar ((:commit . "9e3488f485b7d7f3c97ebaad34ed552bb0cc228a") (:keywords "multimedia" "music" "spotify" "smudge") (:url . "https://github.com/danielfm/smudge"))]) (smyx-theme . [(20141127 828) nil "smyx Color Theme" single ((:commit . "6263f6b401bbabaed388c8efcfc0be2e58c51401") (:authors ("Uriel G Maldonado" . "uriel781@gmail.com")) (:maintainer "Uriel G Maldonado" . "uriel781@gmail.com") (:keywords "color" "theme" "smyx"))]) - (snakemake-mode . [(20220223 218) ((emacs (26 1)) (transient (0 3 0))) "Major mode for editing Snakemake files" tar ((:commit . "0dfeaff6079558c39081c2c078c41369da01903b") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "tools") (:url . "https://git.kyleam.com/snakemake-mode/about"))]) + (snakemake-mode . [(20220223 218) ((emacs (26 1)) (transient (0 3 0))) "Major mode for editing Snakemake files" tar ((:commit . "849a74c24597c72818f194cf410087fc7c07f3d1") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "tools") (:url . "https://git.kyleam.com/snakemake-mode/about"))]) (snapshot-timemachine . [(20161221 929) ((emacs (24 4))) "Step through (Btrfs, ZFS, ...) snapshots of files" single ((:commit . "99efcebab309b11ed512a8dc62555d3834df5efb") (:authors ("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" single ((:commit . "72b0b700d80f1a0442e62bbbb6a0c8c59182f97f") (:authors ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr"))]) (snazzy-theme . [(20170823 1832) ((emacs (24)) (base16-theme (2 1))) "An elegant syntax theme with bright colors" single ((:commit . "57a1763b49b4a776084c16bc70c219246fa5b412") (:keywords "faces" "theme" "color" "snazzy") (:url . "https://github.com/weijiangan/emacs-snazzy/"))]) @@ -4503,7 +4517,7 @@ (solo-jazz-theme . [(20220117 2009) ((emacs (24 1))) "The Solo-Jazz color theme" single ((:commit . "51d63d8a2c855f4ea79eef9fc9c8a5c9702642c4") (:authors ("Carl Steib")) (:maintainer "Carl Steib") (:url . "https://github.com/cstby/solo-jazz-emacs-theme"))]) (somafm . [(20220402 2131) ((emacs (26 1)) (dash (2 12 0)) (request (0 3 2)) (cl-lib (0 6 1))) "A simple soma.fm interface" single ((:commit . "90b661fb1abc652feb6508eb61735919d02e9687") (:authors ("Arte Ebrahimi <>")) (:maintainer "Arte Ebrahimi <>") (:keywords "multimedia") (:url . "https://github.com/artenator/somafm.el"))]) (sonic-pi . [(20211214 1242) ((cl-lib (0 5)) (osc (0 1)) (dash (2 2 0)) (emacs (24)) (highlight (0))) "A Emacs client for SonicPi" tar ((:commit . "9ae16d0fd4cba77ae0bedac83f2cb46569be6ade") (:authors ("Joseph Wilk" . "joe@josephwilk.net")) (:maintainer "Joseph Wilk" . "joe@josephwilk.net") (:keywords "sonicpi" "ruby") (:url . "http://www.github.com/repl-electric/sonic-pi.el"))]) - (soothe-theme . [(20141027 1441) ((emacs (24 1))) "a dark colorful theme for Emacs24." single ((:commit . "0786fe70c6c1b4ddcfb932fdc6862b9611cfc09b") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "https://github.com/jasonm23/emacs-soothe-theme"))]) + (soothe-theme . [(20220825 729) ((emacs (24 3)) (autothemer (0 2))) "A dark colorful theme" tar ((:commit . "36c3a8be287d59a1514e59674ed2ebc9c936eb1d") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "https://github.com/emacsfodder/emacs-soothe-theme"))]) (sorcery-theme . [(20210101 1352) ((autothemer (0 2))) "A D&D (Dark and Dusty) Theme" single ((:commit . "5a1c4445b9e6e09589a299a9962a6973272a0c2f") (:authors ("Maxime Tréca" . "maxime@gmail.com")) (:maintainer "Maxime Tréca" . "maxime@gmail.com") (:url . "http://github.com/vxid/emacs-theme-sorcery"))]) (soria-theme . [(20220127 1004) ((emacs (25 1))) "A xoria256 theme with some colors from openSUSE" tar ((:commit . "2db1859743fe9fc58eab4e6f6c1e37825ad7b69c") (:authors ("Miquel Sabaté Solà" . "mikisabate@gmail.com")) (:maintainer "Miquel Sabaté Solà" . "mikisabate@gmail.com") (:keywords "faces") (:url . "https://github.com/mssola/soria"))]) (sort-words . [(20160929 1335) nil "Sort words in a selected region" single ((:commit . "7b6e108f80237363faf7ec28b2c58dec270b8601") (:authors ("\"Aleksandar Simic\"" . "asimic@gmail.com")) (:maintainer "\"Aleksandar Simic\"" . "asimic@gmail.com") (:keywords "tools") (:url . "http://github.org/dotemacs/sort-words.el"))]) @@ -4520,19 +4534,19 @@ (spacegray-theme . [(20150719 1931) ((emacs (24 1))) "A Hyperminimal UI Theme" single ((:commit . "9826265c2bceb2ebc1c5e16a45021da0253ace97") (:authors ("Bruce Williams" . "brwcodes@gmail.com")) (:maintainer "Bruce Williams" . "brwcodes@gmail.com") (:keywords "themes") (:url . "http://github.com/bruce/emacs-spacegray-theme"))]) (spaceline . [(20211120 1636) ((emacs (24 4)) (cl-lib (0 5)) (powerline (2 3)) (dash (2 11 0)) (s (1 10 0))) "Modeline configuration library for powerline" tar ((:commit . "9a81afa52738544ad5e8b71308a37422ca7e25ba") (:authors ("Eivind Fonn" . "evfonn@gmail.com")) (:maintainer "Eivind Fonn" . "evfonn@gmail.com") (:keywords "mode-line" "powerline" "spacemacs") (:url . "https://github.com/TheBB/spaceline"))]) (spaceline-all-the-icons . [(20190325 1602) ((emacs (24 4)) (all-the-icons (2 6 0)) (spaceline (2 0 0)) (memoize (1 0 1))) "A Spaceline theme using All The Icons" tar ((:commit . "5afd48c10f1bd42d9b9648c5e64596b72f3e9042") (:authors ("Dominic Charlesworth" . "dgc336@gmail.com")) (:maintainer "Dominic Charlesworth" . "dgc336@gmail.com") (:keywords "convenience" "lisp" "tools") (:url . "https://github.com/domtronn/spaceline-all-the-icons.el"))]) - (spacemacs-theme . [(20220430 2248) nil "Color theme with a dark and light versions" tar ((:commit . "bd376f705d6eb7afd9a1dfa0c1bd407e869d1e9f"))]) + (spacemacs-theme . [(20220817 1546) nil "Color theme with a dark and light versions" tar ((:commit . "e04d1f21107a1565861625209bb9c46a7aa43cc5"))]) (spaces . [(20170809 2208) nil "Create and switch between named window configurations." single ((:commit . "6bdb51e9a346907d60a9625f6180bddd06be6674") (:authors ("Steven Thomas")) (:maintainer "Steven Thomas") (:keywords "frames" "convenience") (:url . "https://github.com/chumpage/chumpy-windows"))]) (spark . [(20211021 1832) ((emacs (24 3))) "sparkline generation" single ((:commit . "c9af24a169b1f1aeba175f1f8d51abda113639af") (:authors ("Alvin Francis Dumalus")) (:maintainer "Alvin Francis Dumalus") (:keywords "lisp" "data") (:url . "https://github.com/alvinfrancis/spark"))]) (sparkline . [(20150101 1319) ((cl-lib (0 3))) "Make sparkline images from a list of numbers" single ((:commit . "a2b5d817d272d6363b67ed8f8cc75499a19fa8d2") (:authors ("Willem Rein Oudshoorn" . "woudshoo@xs4all.nl")) (:maintainer "Willem Rein Oudshoorn" . "woudshoo@xs4all.nl") (:keywords "extensions"))]) - (sparql-mode . [(20210701 1202) ((cl-lib (0 5)) (emacs (24 3))) "Edit and interactively evaluate SPARQL queries." tar ((:commit . "ceb370b3879841f8809cc3f9b1b87e898f10562f") (:authors ("Craig Andera ")) (:maintainer "Bjarte Johansen ") (:url . "https://github.com/ljos/sparql-mode"))]) + (sparql-mode . [(20220824 1323) ((cl-lib (0 5)) (emacs (24 3))) "Edit and interactively evaluate SPARQL queries." tar ((:commit . "15960092e8ce8ebe6a6afd82202ccf47cb306e76") (:authors ("Craig Andera ")) (:maintainer "Bjarte Johansen ") (:url . "https://github.com/ljos/sparql-mode"))]) (spatial-navigate . [(20220708 211) ((emacs (26 2))) "Directional navigation between white-space blocks" single ((:commit . "6840ca694d281d39de76c1af0cb2ec526b40820a") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-spatial-navigate"))]) - (spdx . [(20220803 148) ((emacs (24 4))) "Insert SPDX license and copyright headers" tar ((:commit . "cefebe6215b4065827553f2e0cb87c31c5893e5e") (:authors ("Zhiwei Chen" . "condy0919@gmail.com")) (:maintainer "Zhiwei Chen" . "condy0919@gmail.com") (:keywords "license" "tools") (:url . "https://github.com/condy0919/spdx.el"))]) + (spdx . [(20220824 154) ((emacs (24 4))) "Insert SPDX license and copyright headers" tar ((:commit . "3e9b59dbdb81a8ade36f21ae57690df5922a67b5") (:authors ("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")) (:maintainer "Danny McClanahan" . "danieldmcclanahan@gmail.com") (:keywords "speech" "tag" "nlp" "language" "corenlp" "parsing" "natural") (:url . "https://github.com/cosmicexplorer/speech-tagger"))]) (speechd-el . [(20220608 1422) nil "Client to speech synthesizers and Braille displays." tar ((:commit . "7e30c439729d5635ddd341ad5ab16f832a4619ea") (:authors ("Milan Zamazal" . "pdm@zamazal.org")) (:maintainer "Milan Zamazal" . "pdm@zamazal.org"))]) - (speed-type . [(20220723 1839) ((emacs (25 1))) "Practice touch and speed typing" single ((:commit . "bd292bd375bcf8702fadd73e604f3e251560c4c3") (:authors ("Gunther Hagleitner")) (:maintainer "Julien Pagès" . "j.parkouss@gmail.com") (:keywords "games") (:url . "https://github.com/parkouss/speed-type"))]) + (speed-type . [(20220815 1458) ((emacs (25 1))) "Practice touch and speed typing" tar ((:commit . "304cb8cd6c30d07577d7d864fd32858a29a73dba") (:authors ("Gunther Hagleitner")) (:maintainer "Daniel Kraus" . "daniel@kraus.my") (:keywords "games") (:url . "https://github.com/dakra/speed-type"))]) (speedbar-git-respect . [(20200901 246) ((f (0 8 0)) (emacs (25 1))) "Particular respect git repo in speedbar" single ((:commit . "dd8f0849fc1dd21b42380e1a8c28a9a29acd9511") (:authors ("Muromi Ukari" . "chendianbuji@gmail.com")) (:maintainer "Muromi Ukari" . "chendianbuji@gmail.com") (:url . "https://github.com/ukari/speedbar-git-respect"))]) (speeddating . [(20180319 723) ((emacs (25))) "Increase date and time at point" single ((:commit . "df69db0560f19636a66a74f3d88c793bbb18b21e") (:authors ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainer "Xu Chunyang" . "mail@xuchunyang.me") (:keywords "date" "time") (:url . "https://github.com/xuchunyang/emacs-speeddating"))]) - (spell-fu . [(20220802 1151) ((emacs (26 2))) "Fast & light spelling highlighter" single ((:commit . "bfd017fd1985418a1b101c1e497510ce24bb0348") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "convenience") (:url . "https://codeberg.org/ideasman42/emacs-spell-fu"))]) + (spell-fu . [(20220822 2347) ((emacs (26 2))) "Fast & light spelling highlighter" single ((:commit . "2288af944af74f03f4fdb298d0aa7e62ed805c04") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "convenience") (:url . "https://codeberg.org/ideasman42/emacs-spell-fu"))]) (sphinx-doc . [(20210213 1250) ((s (1 9 0)) (cl-lib (0 5)) (dash (2 10 0))) "Sphinx friendly docstrings for Python functions" single ((:commit . "1eda612a44ef027e5229895daa77db99a21b8801") (:authors ("Vineet Naik" . "naikvin@gmail.com")) (:maintainer "Vineet Naik" . "naikvin@gmail.com") (:keywords "sphinx" "python") (:url . "https://github.com/naiquevin/sphinx-doc.el"))]) (sphinx-frontend . [(20161025 758) nil "Launch build process for rst documents via sphinx." single ((:commit . "0cbb03361c245382d3e679dded30c4fc1713c252") (:authors ("Kostafey" . "kostafey@gmail.com")) (:maintainer "Kostafey" . "kostafey@gmail.com") (:keywords "compile" "sphinx" "restructuredtext") (:url . "https://github.com/kostafey/sphinx-frontend"))]) (sphinx-mode . [(20220417 1552) ((f (0 20 0)) (dash (2 14 1))) "Minor mode providing sphinx support." tar ((:commit . "77ca51adf9ee877f3a8f43e744f59e650772f121") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "languages"))]) @@ -4560,7 +4574,7 @@ (sr-speedbar . [(20161025 831) nil "Same frame speedbar" single ((:commit . "77a83fb50f763a465c021eca7343243f465b4a47") (:authors ("Sebastian Rose" . "sebastian_rose@gmx.de")) (:maintainer "Sebastian Rose" . "sebastian_rose@gmx.de") (:keywords "speedbar" "sr-speedbar.el") (:url . "http://www.emacswiki.org/emacs/download/sr-speedbar.el"))]) (srcery-theme . [(20210601 1247) ((emacs (24))) "Dark color theme" single ((:commit . "58dd21cd63e4a2eed15e0082c2547069363f107b") (:authors ("Daniel Berg")) (:maintainer "Daniel Berg") (:keywords "faces") (:url . "https://github.com/srcery-colors/srcery-emacs"))]) (srefactor . [(20180703 1810) ((emacs (24 4))) "A refactoring tool based on Semantic parser framework" tar ((:commit . "6f2c97d17fb70f4ca2112f5a2b99a8ec162004f5") (:authors ("Tu, Do Hoang" . "tuhdo1710@gmail.com")) (:maintainer "Tu, Do Hoang") (:keywords "c" "languages" "tools") (:url . "https://github.com/tuhdo/semantic-refactor"))]) - (srfi . [(20220724 11) ((emacs (25 1))) "Scheme Requests for Implementation browser" tar ((:commit . "10c36b101cadfdded4d7b78d920fc2919a7d674e") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/srfi-explorations/emacs-srfi"))]) + (srfi . [(20220812 2051) ((emacs (25 1))) "Scheme Requests for Implementation browser" tar ((:commit . "a17dabd0c45c4481de48e00ce390f5968d95111d") (:authors ("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" single ((:commit . "714387d5a5cf34d8d8cd96bdb1f9cb8ded823ff7") (:authors ("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" single ((:commit . "96f557887ad97a0066a60c54f92b7234b8407016") (:authors ("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." single ((:commit . "812e27409d01c38d74906a1816640506d6e7e3ef") (:authors ("Noah Friedman" . "friedman@splode.com")) (:maintainer "Ian Eure" . "ian.eure@gmail.com") (:keywords "unix" "comm"))]) @@ -4581,10 +4595,10 @@ (stem . [(20131102 1109) nil "Routines for stemming" single ((:commit . "d74e6611d6ba5025e0276a2cc7c8a90f46bfa9ac") (:authors ("Tsuchiya Masatoshi" . "tsuchiya@pine.kuee.kyoto-u.ac.jp")) (:maintainer "Tsuchiya Masatoshi" . "tsuchiya@pine.kuee.kyoto-u.ac.jp") (:keywords "stemming") (:url . "https://github.com/yuutayamada/stem"))]) (stem-english . [(20180109 358) ((emacs (24 3))) "- routines for stemming English word" single ((:commit . "c9fc4c6ed6bf82382e479dae80912f4ae17d31f4") (:authors ("Tsuchiya Masatoshi" . "tsuchiya@pine.kuee.kyoto-u.ac.jp")) (:maintainer "KAWABATA, Taichi ") (:keywords "text") (:url . "http://github.com/kawabata/stem-english"))]) (stem-reading-mode . [(20220522 1053) ((emacs (25 1))) "Highlight word stems for speed-reading" single ((:commit . "6efc9962e3a19a452c7ab9636cf1e2566a51bd38") (:authors ("Yuri D'Elia" . "wavexx@thregr.org")) (:maintainer "Yuri D'Elia" . "wavexx@thregr.org") (:keywords "convenience" "wp") (:url . "https://gitlab.com/wavexx/stem-reading-mode.el"))]) - (stgit . [(20200606 1308) nil "major mode for StGit interaction" single ((:commit . "246279055ff01e7df2af2247eaa261f5f84952da") (:authors ("David Kågedal" . "davidk@lysator.liu.se")) (:maintainer "David Kågedal" . "davidk@lysator.liu.se") (:url . "http://stacked-git.github.io"))]) + (stgit . [(20220822 1637) nil "major mode for StGit interaction" single ((:commit . "af04cf8247f7b2adc71e618dbff6006faca1af5a") (:authors ("David Kågedal" . "davidk@lysator.liu.se")) (:maintainer "David Kågedal" . "davidk@lysator.liu.se") (:url . "http://stacked-git.github.io"))]) (sticky . [(20170926 36) nil "Sticky key for capital letters" single ((:commit . "fec4e1af38f17f5cd80eca361d8e8ef8772db366") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "convenience") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/sticky.el"))]) (stickyfunc-enhance . [(20150429 1814) ((emacs (24 3))) "An enhancement to stock `semantic-stickyfunc-mode'" single ((:commit . "13bdba51fcd83ccbc3267959d23afc94d458dcb0") (:authors ("Tu, Do Hoang" . "tuhdo1710@gmail.com")) (:maintainer "Tu, Do Hoang") (:keywords "c" "languages" "tools") (:url . "https://github.com/tuhdo/semantic-stickyfunc-enhance"))]) - (stimmung-themes . [(20220705 1627) ((emacs (25))) "Themes tuned to inner harmonies" tar ((:commit . "d09863f13a1a32906d962e55abd5b13ca7e844a5") (:authors ("Love Lagerkvist")) (:maintainer "Love Lagerkvist") (:keywords "faces") (:url . "https://github.com/motform/stimmung-themes"))]) + (stimmung-themes . [(20220823 919) ((emacs (25))) "Themes tuned to inner harmonies" tar ((:commit . "fc9f685fee717f52a249a72189bbdccb225bc122") (:authors ("Love Lagerkvist")) (:maintainer "Love Lagerkvist") (:keywords "faces") (:url . "https://github.com/motform/stimmung-themes"))]) (stock-ticker . [(20150204 1052) ((s (1 9 0)) (request (0 2 0))) "Show stock prices in mode line" single ((:commit . "f2e564142c9de84232839a5b01979cf95b04d6a9") (:authors ("Gunther Hagleitner")) (:maintainer "Gunther Hagleitner") (:keywords "comms") (:url . "https://github.com/hagleitn/stock-ticker"))]) (stock-tracker . [(20220523 1424) ((emacs (27 1)) (dash (2 16 0)) (async (1 9 5))) "Track stock price" single ((:commit . "14fe70fcce24a045f34e42617432a2d830906b98") (:authors ("Huming Chen" . "chenhuming@gmail.com")) (:maintainer "Huming Chen" . "chenhuming@gmail.com") (:keywords "convenience" "stock" "finance") (:url . "https://github.com/beacoder/stock-tracker"))]) (strace-mode . [(20171116 2039) nil "strace output syntax highlighting" single ((:commit . "2901baa968d5180ab985ac40ca22cc20914d01f5") (:authors ("Preston Moore" . "prestonkmoore@gmail.com")) (:maintainer "Preston Moore" . "prestonkmoore@gmail.com") (:keywords "languages"))]) @@ -4592,7 +4606,7 @@ (streamlink . [(20210811 1429) ((s (1 12 0))) "A major mode for streamlink output" single ((:commit . "c265dc61c02ad29ec01dfd8b5cbe3bac60fbf097") (:keywords "multimedia" "streamlink") (:url . "https://github.com/BenediktBroich/streamlink"))]) (strie . [(20160211 2222) ((cl-lib (0 5))) "A simple trie data structure implementation" single ((:commit . "eb7efb0cccc127c414f6a64db11454869d9c10a8") (:authors ("James Atwood" . "jatwood@cs.umass.edu")) (:maintainer "James Atwood" . "jatwood@cs.umass.edu"))]) (string-edit . [(20220604 2128) ((dash (1 2 0))) "Avoid escape nightmares by editing string in separate buffer" single ((:commit . "d7c4b9db6c4987b5c022a9858e6302a4c53aff5f") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com"))]) - (string-inflection . [(20210918 419) nil "underscore -> UPCASE -> CamelCase -> lowerCamelCase conversion of names" single ((:commit . "fd7926ac17293e9124b31f706a4e8f38f6a9b855") (:authors ("akicho8" . "akicho8@gmail.com")) (:maintainer "akicho8" . "akicho8@gmail.com") (:keywords "elisp"))]) + (string-inflection . [(20220812 1259) nil "underscore -> UPCASE -> CamelCase -> lowerCamelCase conversion of names" single ((:commit . "f848c68221f0c474a4f060884d8f7a86f44f42d4") (:authors ("akicho8" . "akicho8@gmail.com")) (:maintainer "akicho8" . "akicho8@gmail.com") (:keywords "elisp"))]) (string-utils . [(20140508 2041) ((list-utils (0 4 2))) "String-manipulation utilities" single ((:commit . "c2232d691617973ecf12a970c6008a161c21da14") (:authors ("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" single ((:commit . "c252080f55cb78c951b19ebab9687f6d00237baf") (:authors ("Andy Stewart" . "lazycat.manatee@gmail.com")) (:maintainer "sabof" . "esabof@gmail.com") (:url . "https://github.com/sabof/stripe-buffer"))]) (stripes . [(20220310 2237) ((emacs (24 3))) "highlight alternating lines differently" single ((:commit . "618e40e0a9cf80decea32c8daecb1c9f6eae2991") (:authors ("Michael Schierl" . "schierlm-public@gmx.de") ("Štěpán Němec" . "stepnem@gmail.com")) (:maintainer "Štěpán Němec" . "stepnem@gmail.com") (:keywords "convenience" "faces") (:url . "https://gitlab.com/stepnem/stripes-el"))]) @@ -4640,7 +4654,7 @@ (swiper-helm . [(20180131 1744) ((emacs (24 1)) (swiper (0 1 0)) (helm (1 5 3))) "Helm version of Swiper." single ((:commit . "93fb6db87bc6a5967898b5fd3286954cc72a0008") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "matching") (:url . "https://github.com/abo-abo/swiper-helm"))]) (swiss-holidays . [(20200526 822) nil "Swiss holidays for the calendar" single ((:commit . "0995c9685033a09466f5b2dceb7316362bde997a") (:authors ("Christian Egli" . "christian.egli@alumni.ethz.ch")) (:maintainer "Christian Egli" . "christian.egli@alumni.ethz.ch") (:keywords "calendar") (:url . "https://github.com/egli/swiss-holidays"))]) (switch-buffer-functions . [(20200127 409) nil "Hook run when current buffer changed" single ((:commit . "95a846baa93bac4c3b3c028b9d53507f1042b23a") (:authors ("10sr <8slashes+el [at] gmail [dot] com>")) (:maintainer "10sr <8slashes+el [at] gmail [dot] com>") (:keywords "hook" "utility") (:url . "https://github.com/10sr/switch-buffer-functions-el"))]) - (switch-window . [(20210808 742) ((emacs (24))) "A *visual* way to switch window" tar ((:commit . "8d9fe251d8d38b223d643df975876356ddfc1b98") (:authors ("Dimitri Fontaine" . "dim@tapoueh.org") ("Feng Shu" . "tumashu@163.com")) (:maintainer "Dimitri Fontaine" . "dim@tapoueh.org") (:keywords "convenience") (:url . "https://github.com/dimitri/switch-window"))]) + (switch-window . [(20220812 2137) ((emacs (24))) "A *visual* way to switch window" tar ((:commit . "71ef2f54c97f3fd2e7ff7964d82e6562eb6282f7") (:authors ("Dimitri Fontaine" . "dim@tapoueh.org") ("Feng Shu" . "tumashu@163.com")) (:maintainer "Dimitri Fontaine" . "dim@tapoueh.org") (:keywords "convenience") (:url . "https://github.com/dimitri/switch-window"))]) (swoop . [(20200618 905) ((emacs (24 3)) (ht (2 0)) (pcre2el (1 5)) (async (1 1))) "Peculiar buffer navigation" tar ((:commit . "0c737a961970a2e61735545320367bafaa8dfc49") (:authors ("Shingo Fukuyama - http://fukuyama.co")) (:maintainer "Shingo Fukuyama - http://fukuyama.co") (:keywords "tools" "swoop" "inner" "buffer" "search" "navigation") (:url . "https://github.com/ShingoFukuyama/emacs-swoop"))]) (sws-mode . [(20210908 2121) nil "(S)ignificant (W)hite(S)pace mode" single ((:commit . "1ad7c51f3c6a6ae64550d9510c5e4e8470014375") (:authors ("Brian M. Carlson and other contributors")) (:maintainer "Brian M. Carlson and other contributors") (:keywords "languages") (:url . "https://github.com/brianc/jade-mode"))]) (sx . [(20220804 1419) ((emacs (24 1)) (cl-lib (0 5)) (json (1 3)) (markdown-mode (2 0)) (let-alist (1 0 3))) "StackExchange client. Ask and answer questions on Stack Overflow, Super User, and the likes" tar ((:commit . "c58405f9ff27b9740997ea837a1f6fd173d1edc5") (:authors ("Sean Allred" . "code@seanallred.com")) (:maintainer "Sean Allred" . "code@seanallred.com") (:keywords "help" "hypermedia" "tools") (:url . "https://github.com/vermiculus/sx.el/"))]) @@ -4649,7 +4663,7 @@ (symbol-overlay . [(20220304 917) ((emacs (24 3)) (seq (2 2))) "Highlight symbols with keymap-enabled overlays" single ((:commit . "c439b73a5f9713bb3dce98986b589bb901e22130") (:authors ("wolray" . "wolray@foxmail.com")) (:maintainer "wolray" . "wolray@foxmail.com") (:keywords "faces" "matching") (:url . "https://github.com/wolray/symbol-overlay/"))]) (symbolist . [(20211107 1615) ((emacs (24 5))) "List and interactively unbind Emacs Lisp symbols" single ((:commit . "92b712734941a45da7d47fd61b95e4013ff53481") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "lisp" "maint") (:url . "https://github.com/lassik/emacs-symbolist"))]) (symbolword-mode . [(20180401 1427) ((emacs (24)) (f (0 19 0))) "modify word split" single ((:commit . "c254ec56e83a5d9de04df0856248723cf6d4be50") (:authors ("ncaq" . "ncaq@ncaq.net")) (:maintainer "ncaq" . "ncaq@ncaq.net") (:url . "https://github.com/ncaq/symbolword-mode"))]) - (symex . [(20220705 1558) ((emacs (25 1)) (lispy (0 26 0)) (paredit (24)) (evil-cleverparens (20170718 413)) (evil (1 2 14)) (evil-surround (1 0 4)) (hydra (0 15 0)) (seq (2 22)) (undo-tree (0 7 5))) "An evil way to edit Lisp symbolic expressions as trees" tar ((:commit . "533eb8ad0ce43c6a7667a2c8a9a8fd6ae275c820") (:authors ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainer "Siddhartha Kasivajhula" . "sid@countvajhula.com") (:keywords "lisp" "convenience" "languages") (:url . "https://github.com/countvajhula/symex.el"))]) + (symex . [(20220811 235) ((emacs (25 1)) (lispy (0 26 0)) (paredit (24)) (evil-cleverparens (20170718 413)) (evil (1 2 14)) (evil-surround (1 0 4)) (hydra (0 15 0)) (seq (2 22)) (undo-tree (0 7 5))) "An evil way to edit Lisp symbolic expressions as trees" tar ((:commit . "adebb7aa1dedac88585bd1077ac81f97df3bd7d5") (:authors ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainer "Siddhartha Kasivajhula" . "sid@countvajhula.com") (:keywords "lisp" "convenience" "languages") (:url . "https://github.com/countvajhula/symex.el"))]) (symon . [(20170224 833) nil "tiny graphical system monitor" single ((:commit . "8dd8b6df49b03cd7d31b85aedbe9dd08fb922335") (:authors ("zk_phi")) (:maintainer "zk_phi") (:url . "http://hins11.yu-yake.com/"))]) (symon-lingr . [(20150719 1342) ((symon (1 1 2)) (cl-lib (0 5))) "A notification-based Lingr client powered by symon.el" single ((:commit . "056d1a473e36992ff5881e5ce6fdc331cead975f") (:authors ("zk_phi")) (:maintainer "zk_phi") (:url . "http://hins11.yu-yake.com/"))]) (sync-recentf . [(20160326 2001) nil "Synchronize the recent files list between Emacs instances" single ((:commit . "0052561d5c5b5c2684faedc3eead776aec06c3ed") (:authors ("François Févotte" . "fevotte@gmail.com")) (:maintainer "François Févotte" . "fevotte@gmail.com") (:keywords "recentf") (:url . "https://github.com/ffevotte/sync-recentf"))]) @@ -4683,7 +4697,7 @@ (talonscript-mode . [(20220204 1441) ((emacs (24 3))) "Major mode for Talon Voice's .talon files" single ((:commit . "b6eb61f56349e0d47276270163ec611c2d5b188e") (:authors ("Jcaw" . "toastedjcaw@gmail.com")) (:maintainer "Jcaw" . "toastedjcaw@gmail.com") (:keywords "languages") (:url . "https://github.com/jcaw/talonscript-mode"))]) (tango-2-theme . [(20120312 2025) nil "Tango 2 color theme for GNU Emacs 24" single ((:commit . "64e44c98e41ebbe3b827d54280e3b9615787daaa") (:authors ("Nick Parker")) (:maintainer "Nick Parker"))]) (tango-plus-theme . [(20220525 1311) nil "A color theme based on the tango palette" single ((:commit . "bdf1dd6ea9c43d07b22dfa15fec0dcfd03544c63") (:authors ("Titus von der Malsburg" . "malsburg@posteo.de")) (:maintainer "Titus von der Malsburg" . "malsburg@posteo.de") (:url . "https://github.com/tmalsburg/tango-plus-theme"))]) - (tangonov-theme . [(20220801 528) ((emacs (25))) "A 256 color dark theme featuring bright pastels" single ((:commit . "20b59b96ef57f7531525951989cef0e9f849371b") (:authors ("Trevor Richards" . "trev@trevdev.ca")) (:maintainer "Trevor Richards" . "trev@trevdev.ca") (:keywords "faces" "theme" "dark") (:url . "https://github.com/trev-dev/tangonov-theme"))]) + (tangonov-theme . [(20220816 2316) ((emacs (25))) "A 256 color dark theme featuring bright pastels" single ((:commit . "18c0c07b03a963b0c625f66c77f1f0755a0ca22a") (:authors ("Trevor Richards" . "trev@trevdev.ca")) (:maintainer "Trevor Richards" . "trev@trevdev.ca") (:keywords "faces" "theme" "dark" "fringe") (:url . "https://sr.ht/~trevdev/tangonov-theme/"))]) (tangotango-theme . [(20220714 2034) nil "Tango Palette color theme for Emacs 24." single ((:commit . "dbefd8638c36595467efbce5a26da208320ac30a") (:authors ("Julien Barnier" . "julien@nozav.org")) (:maintainer "Julien Barnier" . "julien@nozav.org") (:keywords "tango" "palette" "color" "theme" "emacs") (:url . "https://github.com/juba/color-theme-tangotango"))]) (tao-theme . [(20220414 354) nil "This package provides two parametrized uncoloured color themes for Emacs: tao-yin and tao-yang." tar ((:commit . "d6fe980783e22df310df1ae51ac249c28c83ac53") (:authors ("Peter Kosov" . "11111000000@email.com")) (:maintainer "Peter Kosov" . "11111000000@email.com") (:url . "http://github.com/11111000000/tao-theme-emacs"))]) (taskpaper-mode . [(20220410 1953) nil "Major mode for working with TaskPaper files" single ((:commit . "12c2f7e01a0e5cc9a57c9d8a8f3fecc8f8ddecb2") (:authors ("Dmitry Safronov" . "saf.dmitry@gmail.com")) (:maintainer "Dmitry Safronov" . "saf.dmitry@gmail.com") (:keywords "outlines" "notetaking" "task management" "productivity" "taskpaper") (:url . "https://github.com/saf-dmitry/taskpaper-mode"))]) @@ -4696,7 +4710,7 @@ (tea-time . [(20120331 820) nil "Simple timer package, useful to make perfect tea." single ((:commit . "1f6cf0bdd27c5eb3508989c5095427781f858eca") (:authors ("konsty" . "antipin.konstantin@googlemail.com")) (:maintainer "Gabriel Saldana" . "gsaldana@gmail.com") (:keywords "timer" "tea-time"))]) (teacode-expand . [(20181231 640) ((emacs (24 4))) "Expansion of text by TeaCode program." single ((:commit . "2122e4b32ed4edd2d7ebc0ff8ebf407e29d6e910") (:authors ("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" single ((:commit . "61caf8f419659a0567a269f290c90427a215d77b") (:authors ("Dale R. Worley" . "worley@alum.mit.edu")) (:maintainer "Mark T. Kennedy" . "mtk@acm.org") (:keywords "convenience" "emulations" "files") (:url . "https://github.com/mtk/teco.git"))]) - (telega . [(20220713 2342) ((emacs (26 1)) (visual-fill-column (1 9)) (rainbow-identifiers (0 2 2))) "Telegram client (unofficial)" tar ((:commit . "11c0c785ed9e479b65514a10022d5c34984aab59") (:authors ("Zajcev Evgeny" . "zevlg@yandex.ru")) (:maintainer "Zajcev Evgeny" . "zevlg@yandex.ru") (:keywords "comm") (:url . "https://github.com/zevlg/telega.el"))]) + (telega . [(20220819 506) ((emacs (26 1)) (visual-fill-column (1 9)) (rainbow-identifiers (0 2 2))) "Telegram client (unofficial)" tar ((:commit . "42a0dd0e30a82a8e34eaccf6e7a1366f4621d49d") (:authors ("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" single ((:commit . "211d785b02a29ddc254422fdcc3db45262582f8c") (:authors ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainer "Nicolas Petton" . "petton.nicolas@gmail.com") (:keywords "telepathy" "tools"))]) (telephone-line . [(20220424 400) ((emacs (24 4)) (cl-lib (0 5)) (cl-generic (0 2)) (seq (1 8))) "Rewrite of Powerline" tar ((:commit . "6f3455a365912e8f0c45a2240ea79507dee45ade") (:authors ("Daniel Bordak" . "dbordak@fastmail.fm")) (:maintainer "Daniel Bordak" . "dbordak@fastmail.fm") (:keywords "mode-line") (:url . "https://github.com/dbordak/telephone-line"))]) (teletext . [(20211203 1111) ((emacs (24 3))) "Teletext broadcast viewer" single ((:commit . "6b003e9dab9bd0c27d188a81f5fff740d66a2282") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "comm" "help" "hypermedia") (:url . "https://github.com/lassik/emacs-teletext"))]) @@ -4716,10 +4730,10 @@ (term-run . [(20200128 702) nil "Run arbitrary command in terminal buffer" single ((:commit . "0fd135d55fcf864598b1fb8dd880833a1a322910") (:authors ("10sr <8slashes+el [at] gmail [dot] com>")) (:maintainer "10sr <8slashes+el [at] gmail [dot] com>") (:keywords "utility" "shell" "command" "term-mode") (:url . "https://github.com/10sr/term-run-el"))]) (termbright-theme . [(20151031 235) ((emacs (24 1))) "a more usable theme for white-on-black terminals" single ((:commit . "bec6ab14336c0611e85f45486276004f16d20607") (:authors ("Brian Mastenbrook" . "brian@mastenbrook.net")) (:maintainer "Brian Mastenbrook" . "brian@mastenbrook.net") (:keywords "themes") (:url . "https://github.com/bmastenbrook/termbright-theme-el"))]) (terminal-focus-reporting . [(20180830 719) ((emacs (24 4))) "Minor mode for terminal focus reporting." single ((:commit . "6b1dbb2e96b3ff680dbe88153c4c569adbbd64ce") (:authors ("Vitalii Elenhaupt")) (:maintainer "Vitalii Elenhaupt") (:keywords "convenience") (:url . "https://github.com/veelenga/terminal-focus-reporting.el"))]) - (terminal-here . [(20220519 552) ((emacs (25 1))) "Run an external terminal in current directory" single ((:commit . "c16a500926416c09cd2faee6ab9541686b51e34f") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:keywords "tools" "frames") (:url . "https://github.com/davidshepherd7/terminal-here"))]) + (terminal-here . [(20220814 2056) ((emacs (25 1))) "Run an external terminal in current directory" single ((:commit . "3cc7c57599630036714183b973b47d559fa46a9a") (:authors ("David Shepherd" . "davidshepherd7@gmail.com")) (:maintainer "David Shepherd" . "davidshepherd7@gmail.com") (:keywords "tools" "frames") (:url . "https://github.com/davidshepherd7/terminal-here"))]) (terminal-toggle . [(20190226 1510) ((emacs (24)) (popwin (1 0 0))) "simple pop-up terminal" single ((:commit . "f824d634aef3600cb7a8e2ddf9e8444c6607c160") (:authors ("Mehmet Tekman")) (:maintainer "Mehmet Tekman") (:keywords "outlines") (:url . "https://github.com/mtekman/terminal-toggle.el"))]) - (tern . [(20181108 722) ((json (1 2)) (cl-lib (0 5)) (emacs (24))) "Tern-powered JavaScript integration" single ((:commit . "ef50c6f0269a6fd9ce742d0a87647d60a0ef850f") (:authors ("Marijn Haverbeke")) (:maintainer "Marijn Haverbeke") (:url . "http://ternjs.net/"))]) - (tern-auto-complete . [(20170521 1935) ((tern (0 0 1)) (auto-complete (1 4)) (cl-lib (0 5)) (emacs (24))) "Tern Completion by auto-complete.el" single ((:commit . "ef50c6f0269a6fd9ce742d0a87647d60a0ef850f") (:authors ("")) (:maintainer ""))]) + (tern . [(20181108 722) ((json (1 2)) (cl-lib (0 5)) (emacs (24))) "Tern-powered JavaScript integration" single ((:commit . "5f1f83b1689d4954d62e794d9a89c57709099c27") (:authors ("Marijn Haverbeke")) (:maintainer "Marijn Haverbeke") (:url . "http://ternjs.net/"))]) + (tern-auto-complete . [(20170521 1935) ((tern (0 0 1)) (auto-complete (1 4)) (cl-lib (0 5)) (emacs (24))) "Tern Completion by auto-complete.el" single ((:commit . "5f1f83b1689d4954d62e794d9a89c57709099c27") (:authors ("")) (:maintainer ""))]) (tern-context-coloring . [(20161218 747) ((emacs (24 3)) (context-coloring (8 1 0)) (tern (0 0 1))) "Use Tern for context coloring" single ((:commit . "3a8e979d6cc83aabcb3dda3f5f31a6422532efba") (:authors ("Jackson Ray Hamilton" . "jackson@jacksonrayhamilton.com")) (:maintainer "Jackson Ray Hamilton" . "jackson@jacksonrayhamilton.com") (:keywords "convenience" "faces" "tools") (:url . "https://github.com/jacksonrayhamilton/tern-context-coloring"))]) (terraform-doc . [(20211003 1333) ((emacs (24 4))) "Look up terraform documentation on the fly" single ((:commit . "16179e57ce290190c222b27961900657a1981330") (:authors ("Giap Tran" . "txgvnn@gmail.com")) (:maintainer "Giap Tran" . "txgvnn@gmail.com") (:keywords "comm") (:url . "https://github.com/TxGVNN/terraform-doc"))]) (terraform-mode . [(20210621 1953) ((emacs (24 3)) (hcl-mode (0 3)) (dash (2 17 0))) "Major mode for terraform configuration file" single ((:commit . "e560caaa9d9a11b0868adf6d9dcae5ebb5055730") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-terraform-mode"))]) @@ -4740,28 +4754,29 @@ (the-matrix-theme . [(20220622 1214) ((emacs (26 1))) "Green-on-black dark theme inspired by \"The Matrix\" movie" single ((:commit . "289ed872003708ef1595e5e6765b50ca53e34ac8") (:authors ("Dan Dee" . "monkeyjunglejuice@pm.me")) (:maintainer "Dan Dee" . "monkeyjunglejuice@pm.me") (:keywords "faces" "theme") (:url . "https://github.com/monkeyjunglejuice/matrix-emacs-theme"))]) (theme-anchor . [(20220204 321) ((emacs (26))) "Apply theme in current buffer only" single ((:commit . "c6f715d4ccd30e83922e39cab856578ce19224bb") (:authors ("Liāu, Kiong-Gē" . "gliao.tw@pm.me")) (:maintainer "Liāu, Kiong-Gē" . "gliao.tw@pm.me") (:keywords "extensions" "lisp" "theme") (:url . "https://github.com/GongYiLiao/theme-anchor"))]) (theme-changer . [(20201226 2256) nil "Sunrise/Sunset Theme Changer for Emacs" single ((:commit . "57b8c579f134374a45bec9043feff6b29bb4f108") (:authors ("Joshua B. Griffith" . "josh.griffith@gmail.com")) (:maintainer "Joshua B. Griffith" . "josh.griffith@gmail.com") (:keywords "color-theme" "deftheme" "solar" "sunrise" "sunset") (:url . "https://github.com/hadronzoo/theme-changer"))]) - (theme-looper . [(20210827 424) ((emacs (24)) (cl-lib (0 5))) "A package for switching themes in Emacs interactively" single ((:commit . "e6e8efd740df0b68db89805ba72492818dba61ab") (:authors ("Mohammed Ismail Ansari" . "team.terminal@gmail.com")) (:maintainer "Mohammed Ismail Ansari" . "team.terminal@gmail.com") (:keywords "convenience" "color-themes") (:url . "http://ismail.teamfluxion.com"))]) + (theme-looper . [(20210827 424) ((emacs (24)) (cl-lib (0 5))) "A package for switching themes in Emacs interactively" single ((:commit . "617c3674193c38d34e5b01d5d936318cc421eedd") (:authors ("Mohammed Ismail Ansari" . "team.terminal@gmail.com")) (:maintainer "Mohammed Ismail Ansari" . "team.terminal@gmail.com") (:keywords "convenience" "color-themes") (:url . "http://ismail.teamfluxion.com"))]) (theme-magic . [(20190711 2034) ((emacs (25)) (seq (1 8))) "Apply your Emacs theme to the rest of Linux" tar ((:commit . "844c4311bd26ebafd4b6a1d72ddcc65d87f074e3") (:authors ("GitHub user \"jcaw\"" . "40725916+jcaw@users.noreply.github.com")) (:maintainer "GitHub user \"jcaw\"" . "40725916+jcaw@users.noreply.github.com") (:keywords "unix" "faces" "terminals" "extensions") (:url . "https://github.com/jcaw/theme-magic.el"))]) (therapy . [(20151113 1953) ((emacs (24))) "Hooks for managing multiple Python major versions" single ((:commit . "775a92bb7b6b0fcc5b38c0b5198a9d0a1bef788a") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:url . "https://github.com/abingham/therapy"))]) (thingopt . [(20160520 2318) nil "Thing at Point optional utilities" single ((:commit . "5679815852652479f3b3c9f3a98affc927384b2c") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Tomohiro Matsuyama" . "m2ym.pub@gmail.com") (:keywords "convenience"))]) (thinks . [(20170802 1128) ((cl-lib (0 5))) "Insert text in a think bubble." single ((:commit . "c02f236abc8c2025d9f01460b09b89ebdc96e28d") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "convenience" "quoting") (:url . "https://github.com/davep/thinks.el"))]) (thread-dump . [(20170816 1850) nil "Java thread dump viewer" single ((:commit . "204c9600242756d4b514bb5ff6293e052bf4b49d") (:authors ("Dmitry Neverov")) (:maintainer "Dmitry Neverov") (:url . "http://github.com/nd/thread-dump.el"))]) (threes . [(20160820 1242) ((emacs (24)) (seq (1 11))) "A clone of Threes (a tiny puzzle game)" single ((:commit . "6981acb30b856c77cba6aba63fefbf102cbdfbb2") (:authors ("Chunyang Xu" . "xuchunyang.me@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang.me@gmail.com") (:keywords "games") (:url . "https://github.com/xuchunyang/threes.el"))]) - (thrift . [(20200212 1903) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" single ((:commit . "c8deb3fa7265ee3252e33e12754e4eb0dde32a8c") (:keywords "languages"))]) + (thrift . [(20200212 1903) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" single ((:commit . "35478e765468b97850b8d32be535908f2a89ddc5") (:keywords "languages"))]) (thumb-through . [(20120119 534) nil "Plain text reader of HTML documents" single ((:commit . "08d8fb720f93c6172653e035191a8fa9c3305e63") (:keywords "html"))]) (tickscript-mode . [(20171219 203) ((emacs (24 1))) "A major mode for Tickscript files" single ((:commit . "f0579f38ff14954df5002ce30ae6d4a2c978d461") (:authors ("Marc Sherry" . "msherry@gmail.com")) (:maintainer "Marc Sherry" . "msherry@gmail.com") (:keywords "languages") (:url . "https://github.com/msherry/tickscript-mode"))]) - (tidal . [(20220804 906) ((haskell-mode (16)) (emacs (25 1))) "Interact with TidalCycles for live coding patterns" single ((:commit . "ddb3b073c3b9e1a5a4856c61d961dd32f2291235") (:authors (nil . "alex@slab.org")) (:maintainer nil . "alex@slab.org") (:keywords "tools") (:url . "https://github.com/tidalcycles/Tidal"))]) + (tidal . [(20220808 1553) ((haskell-mode (16)) (emacs (25 1))) "Interact with TidalCycles for live coding patterns" single ((:commit . "22cb3d3ebd7fb7acec349e040f977495e44aec8b") (:authors (nil . "alex@slab.org")) (:maintainer nil . "alex@slab.org") (:keywords "tools") (:url . "https://github.com/tidalcycles/Tidal"))]) (tide . [(20220514 614) ((emacs (25 1)) (dash (2 10 0)) (s (1 11 0)) (flycheck (27)) (typescript-mode (0 1)) (cl-lib (0 5))) "Typescript Interactive Development Environment" tar ((:commit . "4cf6a0d89da7f946565a425a632ee2410a40c7da") (:authors ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainer "Anantha kumaran" . "ananthakumaran@gmail.com") (:keywords "typescript") (:url . "http://github.com/ananthakumaran/tide"))]) (tikz . [(20220526 521) ((emacs (24 1))) "A minor mode to edit TikZ pictures" tar ((:commit . "4b205afc5c88f050639135d1d57f1276db323842") (:authors ("Emilio Torres-Manzanera" . "torres@uniovi.es")) (:maintainer "Emilio Torres-Manzanera" . "torres@uniovi.es") (:keywords "tex") (:url . "https://github.com/emiliotorres/tikz"))]) (tile . [(20161225 357) ((emacs (25 1)) (s (1 9 0)) (dash (2 12 0)) (stream (2 2 3))) "Tile windows with layouts" single ((:commit . "22660f21f6e95de5aba55cd5d293d4841e9a4661") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "tile" "tiling" "window" "manager" "dynamic" "frames") (:url . "https://github.com/IvanMalison/tile"))]) + (time-block . [(20220821 1431) ((emacs (25 1)) (ts (0 1))) "Block running commands using time" single ((:commit . "4ac663b5196567326b82fe76c9930c116bed5596") (:authors ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Samuel W. Flint" . "swflint@flintfam.org") (:keywords "tools" "productivity" "convenience") (:url . "https://git.sr.ht/~swflint/time-block"))]) (time-ext . [(20170126 1215) nil "more function for time/date" single ((:commit . "d128becf660fe3f30178eb1b05cd266741f4784a") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "lisp") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/time-ext.el"))]) (timecop . [(20160520 1052) ((cl-lib (0 5)) (datetime-format (0 0 1))) "Freeze Time for testing" single ((:commit . "e6427538b547cbe02e1bd6ed4b765c73620bdae8") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "datetime" "testing") (:url . "https://github.com/zonuexe/emacs-datetime"))]) (timer-revert . [(20150122 2032) nil "minor mode to revert buffer for a given time interval." tar ((:commit . "615c91dec8b440d2b9b7c725dd733d7432564e45") (:authors ("Yagnesh Raghava Yakkala. http://yagnesh.org")) (:maintainer nil . "hi@yagnesh.org") (:keywords "timer" "revert" "auto-revert.") (:url . "http://github.com/yyr/timer-revert"))]) (timesheet . [(20191024 151) ((s (1)) (org (7)) (auctex (11))) "Timesheet management add-on for org-mode" tar ((:commit . "5098dc87d3d4f289b6c1b6532070dacbfe6de9fd") (:authors ("Tom Marble")) (:maintainer "Tom Marble") (:keywords "org" "timesheet") (:url . "https://github.com/tmarble/timesheet.el"))]) (timonier . [(20170411 800) ((emacs (24 4)) (s (1 11 0)) (f (0 19 0)) (dash (2 12 0)) (pkg-info (0 5 0)) (hydra (0 13 6)) (request (0 2 0)) (all-the-icons (2 0 0))) "Manage Kubernetes Applications" tar ((:commit . "0a150ea87bf695b43cf1740dfd7e553e0ae7601c") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "kubernetes" "docker") (:url . "https://github.com/nlamirault/timonier"))]) (timp . [(20160618 803) ((emacs (24 4)) (cl-lib (0 5)) (fifo-class (1 0)) (signal (1 0))) "Multithreading library" tar ((:commit . "66b21934b1eb8ee428c06dd64b3562ad44776a35") (:authors ("Mola-T" . "Mola@molamola.xyz")) (:maintainer "Mola-T" . "Mola@molamola.xyz") (:keywords "internal" "lisp" "processes" "tools") (:url . "https://github.com/mola-T/timp"))]) - (timu-rouge-theme . [(20220525 1239) ((emacs (27 1))) "Color theme inspired by the Rouge Theme for VSCode" single ((:commit . "5c6f406bf5815e6b72b09093b651b09c7c6769fc") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-rouge-theme"))]) - (timu-spacegrey-theme . [(20220604 1042) ((emacs (25 1))) "Color theme inspired by the Spacegray theme in Sublime Text" single ((:commit . "1051753f371535c0af2008a372a456b3769c7f69") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-spacegrey-theme"))]) + (timu-rouge-theme . [(20220717 2158) ((emacs (27 1))) "Color theme inspired by the Rouge Theme for VSCode" single ((:commit . "bbfc8bbba01e5caa9d11628f0bc2276605c75901") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-rouge-theme"))]) + (timu-spacegrey-theme . [(20220808 2051) ((emacs (25 1))) "Color theme inspired by the Spacegray theme in Sublime Text" single ((:commit . "65fd97f36cfcf64be721297ee596fc9c5adf5c9b") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-spacegrey-theme"))]) (tinkerer . [(20200914 1756) ((s (1 2 0))) "Elisp wrapper for Tinkerer Blogging Engine." single ((:commit . "7cedeb264a44cd62bcd9c778dca52316d09e07e5") (:authors ("Yagnesh Raghava Yakkala" . "hi@yagnesh.org")) (:maintainer "Yagnesh Raghava Yakkala" . "hi@yagnesh.org") (:keywords "tinkerer" "blog" "wrapper") (:url . "https://github.com/yyr/tinkerer.el"))]) (tiny . [(20190722 1212) nil "Quickly generate linear ranges in Emacs" single ((:commit . "fd8a6b0b0c564d8242259e20e557ee6041f40908") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "convenience") (:url . "https://github.com/abo-abo/tiny"))]) (tiny-menu . [(20220725 1748) ((emacs (24 4))) "Display tiny menus." single ((:commit . "17eacfd1d44cd4d5482d32eac63229230c3cd3fc") (:authors ("Aaron Bieber" . "aaron@aaronbieber.com")) (:maintainer "Aaron Bieber" . "aaron@aaronbieber.com") (:keywords "menu" "tools") (:url . "https://github.com/aaronbieber/tiny-menu.el"))]) @@ -4783,13 +4798,13 @@ (toggle-test . [(20140723 537) nil "Toggle between source and test files in various programming languages" single ((:commit . "e969321f274903d705995a7d0345a257576ec5ff") (:authors ("Raghunandan Rao" . "r.raghunandan@gmail.com")) (:maintainer "Raghunandan Rao" . "r.raghunandan@gmail.com") (:keywords "tdd" "test" "toggle" "productivity") (:url . "https://github.com/rags/toggle-test"))]) (toggle-window . [(20141207 1548) nil "toggle current window size between half and full" single ((:commit . "e82c60e543933880402ede11e9423e48a17dde53") (:authors ("Kenny Liu")) (:maintainer "Kenny Liu") (:keywords "hide" "window") (:url . "https://github.com/deadghost/toggle-window"))]) (tok-theme . [(20220718 1215) ((emacs (26 1))) "Comfy light theme" single ((:commit . "f6d036b3213f277b0da86eb0e0f333f469eca643") (:authors ("Topi Kettunen" . "topi@topikettunen.com")) (:maintainer "Topi Kettunen" . "topi@topikettunen.com") (:url . "https://github.com/topikettunen/tok-theme"))]) - (tokei . [(20220422 2234) ((emacs (27 1)) (magit-section (3 3 0))) "Display codebase statistics" single ((:commit . "181021cd881eecd604a546d4a717866a81c7a511") (:authors ("Daniel Nagy ")) (:maintainer "Daniel Nagy" . "danielnagy@posteo.de") (:url . "https://github.com/nagy/tokei.el"))]) + (tokei . [(20220823 2058) ((emacs (27 1)) (magit-section (3 3 0))) "Display codebase statistics" single ((:commit . "86fbca422f580a95eb30247e46891184f3ac5c18") (:authors ("Daniel Nagy ")) (:maintainer "Daniel Nagy" . "danielnagy@posteo.de") (:url . "https://github.com/nagy/tokei.el"))]) (tomatinho . [(20180621 1748) nil "Simple and beautiful pomodoro timer" tar ((:commit . "b53354b9b9f496c0388d6a573b06b7d6fc53d0bd") (:authors ("Konrad Scorciapino" . "scorciapino@gmail.com")) (:maintainer "Konrad Scorciapino" . "scorciapino@gmail.com") (:keywords "time" "productivity" "pomodoro technique"))]) (toml . [(20130903 1255) nil "TOML (Tom's Obvious, Minimal Language) parser" single ((:commit . "994644f9e68c383071eeee23389a7989b228c2d2") (:authors ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainer "Wataru MIYAGUNI" . "gonngo@gmail.com") (:keywords "toml" "parser") (:url . "https://github.com/gongo/emacs-toml"))]) (toml-mode . [(20161107 1800) ((emacs (24)) (cl-lib (0 5))) "Major mode for editing TOML files" single ((:commit . "f6c61817b00f9c4a3cab1bae9c309e0fc45cdd06") (:authors ("Felix Chern" . "idryman@gmail.com")) (:maintainer "Felix Chern" . "idryman@gmail.com") (:keywords "data" "toml") (:url . "https://github.com/dryman/toml-mode.el"))]) (tommyh-theme . [(20131004 2330) nil "A bright, bold-colored theme for emacs" single ((:commit . "46d1c69ee0a1ca7c67b569b891a2f28fed89e7d5") (:authors ("William Glass" . "william.glass@gmail.com")) (:maintainer "William Glass" . "william.glass@gmail.com"))]) (tongbu . [(20200414 507) ((emacs (25 1)) (web-server (0 1 2))) "A web server to share text or files between two devices" single ((:commit . "6f6e5c5446f0c5735357ab520b249ab97295653e") (:authors ("Xu Chunyang")) (:maintainer "Xu Chunyang") (:keywords "tools") (:url . "https://github.com/xuchunyang/tongbu.el"))]) - (topspace . [(20220803 2258) ((emacs (25 1))) "Recenter line 1 with scrollable upper margin/padding" single ((:commit . "fa67b67e1ae41adb44de0e5180a6cab922da6bb0") (:authors ("Trevor Edwin Pogue" . "trevor.pogue@gmail.com")) (:maintainer "Trevor Edwin Pogue" . "trevor.pogue@gmail.com") (:keywords "convenience" "scrolling" "center" "cursor" "margin" "padding") (:url . "https://github.com/trevorpogue/topspace"))]) + (topspace . [(20220824 134) ((emacs (25 1))) "Recenter line 1 with scrollable upper margin/padding" single ((:commit . "4f090520174cab0fa752765b7c18885efb6cce11") (:authors ("Trevor Edwin Pogue" . "trevor.pogue@gmail.com")) (:maintainer "Trevor Edwin Pogue" . "trevor.pogue@gmail.com") (:keywords "convenience" "scrolling" "center" "cursor" "margin" "padding") (:url . "https://github.com/trevorpogue/topspace"))]) (topsy . [(20210831 133) ((emacs (26 3))) "Simple sticky header" single ((:commit . "8ae0976dfdbe4461c33ed44cf1dedc2c903b0bb0") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "convenience") (:url . "https://github.com/alphapapa/topsy.el"))]) (tornado-template-mode . [(20141128 1008) nil "A major mode for editing tornado templates" single ((:commit . "667c0663dbbd279b6c345446b9f2bc50eb52b747") (:authors ("Florian Mounier aka paradoxxxzero")) (:maintainer "Florian Mounier aka paradoxxxzero"))]) (torus . [(20190325 753) ((emacs (26))) "A buffer groups manager" single ((:commit . "b309da8c2eaee573a2e2572f25a08ce5da9e9990") (:authors ("Chimay")) (:maintainer "Chimay") (:keywords "files" "buffers" "groups" "persistent" "history" "layout" "tabs") (:url . "https://github.com/chimay/torus"))]) @@ -4807,14 +4822,14 @@ (tramp-hdfs . [(20210526 339) ((emacs (24 4))) "Tramp extension to access hadoop/hdfs file system in Emacs" single ((:commit . "aa93bdbb3d5619c262ce53af1981edcd2a0705e5") (:authors ("Raghav Kumar Gautam" . "raghav@apache.org")) (:maintainer "Raghav Kumar Gautam" . "raghav@apache.org") (:keywords "tramp" "emacs" "hdfs" "hadoop" "webhdfs" "rest"))]) (tramp-term . [(20220725 1441) nil "Automatic setup of directory tracking in ssh sessions" single ((:commit . "ed75189122737d301f716a30a8013205aa3736f1") (:authors ("Randy Morris" . "randy.morris@archlinux.us")) (:maintainer "Randy Morris" . "randy.morris@archlinux.us") (:keywords "comm" "terminals") (:url . "https://github.com/randymorris/tramp-term.el"))]) (transfer-sh . [(20200601 1708) ((emacs (24 3)) (async (1 0))) "Simple interface for sending buffer contents to transfer.sh" single ((:commit . "0621a66d00ec91a209a542c10b158095088bd44d") (:keywords "comm" "convenience" "files") (:url . "https://gitlab.com/tuedachu/transfer-sh.el"))]) - (transient . [(20220803 1000) ((emacs (25 1)) (compat (28 1 1 0))) "Transient commands" tar ((:commit . "389d2bffff1d7ada561688861ca67b7a360cf352") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "extensions") (:url . "https://github.com/magit/transient"))]) + (transient . [(20220806 2224) ((emacs (25 1)) (compat (28 1 1 0))) "Transient commands" tar ((:commit . "6b9c93af9c1d1646be4445656c46ee9390c9f129") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "extensions") (:url . "https://github.com/magit/transient"))]) (transient-dwim . [(20220425 1331) ((emacs (26 1)) (transient (0 1))) "Useful preset transient commands" single ((:commit . "7b6e70fb49b9d18106748202011863ebc39b864a") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "tools") (:url . "https://github.com/conao3/transient-dwim.el"))]) (transient-posframe . [(20210102 130) ((emacs (26 0)) (posframe (0 4 3)) (transient (0 2 0))) "Using posframe to show transient" single ((:commit . "dcd898d1d35183a7d4f2c8f0ebcb43b4f8e70ebe") (:authors ("Yanghao Xie")) (:maintainer "Yanghao Xie" . "yhaoxie@gmail.com") (:keywords "convenience" "bindings" "tooltip") (:url . "https://github.com/yanghaoxie/transient-posframe"))]) (translate-mode . [(20220511 1357) ((emacs (24 3))) "Paragraph-oriented side-by-side doc translation workflow" single ((:commit . "e1940b333241a4d0c224b7b875962736ca2b693b") (:authors ("Ray Wang" . "rayw.public@gmail.com")) (:maintainer "Ray Wang" . "rayw.public@gmail.com") (:keywords "translate" "convenience" "editing") (:url . "https://github.com/rayw000/translate-mode"))]) (transmission . [(20210705 2152) ((emacs (24 4)) (let-alist (1 0 5))) "Interface to a Transmission session" single ((:commit . "a03a6f5c7b133e0a37896b6d993dd6d6d4532cc2") (:authors ("Mark Oteiza" . "mvoteiza@udel.edu")) (:maintainer "Mark Oteiza" . "mvoteiza@udel.edu") (:keywords "comm" "tools"))]) (transpose-frame . [(20200307 2119) nil "Transpose windows arrangement in a frame" single ((:commit . "12e523d70ff78cc8868097b56120848befab5dbc") (:authors ("S. Irie")) (:maintainer "S. Irie") (:keywords "window"))]) (transpose-mark . [(20150405 716) nil "Transpose data using the Emacs mark" single ((:commit . "667327602004794de97214cf336ac61650ef75b7") (:authors ("Kevin W. van Rooijen" . "kevin.van.rooijen@attichacker.com")) (:maintainer "Kevin W. van Rooijen" . "kevin.van.rooijen@attichacker.com") (:keywords "transpose" "convenience"))]) - (transwin . [(20220704 640) ((emacs (24 3))) "Make window/frame transparent" single ((:commit . "9f3c5ef4029f992fe94b70d54b8d00f0a4cbce1c") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "frames" "window" "transparent") (:url . "https://github.com/jcs-elpa/transwin"))]) + (transwin . [(20220704 640) ((emacs (24 3))) "Make window/frame transparent" single ((:commit . "7a8dc6ac88536e4ce7bdaf47bd2da06a77336f5a") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "frames" "window" "transparent") (:url . "https://github.com/jcs-elpa/transwin"))]) (trashed . [(20220106 1358) ((emacs (25 1))) "Viewing/editing system trash can" single ((:commit . "ddf5830730544435a068f2dc9ac75a81ea69df1d") (:authors ("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 . "754ef07c17fed17ab03664ad11e2b0b2ef5e78ed") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "travis") (:url . "https://github.com/nlamirault/emacs-travis"))]) (tray . [(20220422 1628) ((emacs (27 1)) (compat (28 1 1 0)) (transient (0 3 0))) "Various transient menus" single ((:commit . "1292530acd05956a2f1bd19c94ef6ab59f05ad8a") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "https://git.sr.ht/~tarsius/tray"))]) @@ -4824,17 +4839,17 @@ (tree-sitter-ess-r . [(20220801 1453) ((emacs (26 1)) (ess (18 10 1)) (tree-sitter (0 12 1)) (tree-sitter-langs (0 12 0))) "R with tree-sitter" single ((:commit . "52fcf9a83dc3ec1cbd0b662794a59cfc23eaa204") (:authors ("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" single ((:commit . "4ef246db3e4ff99f672fe5e4b416c890f885c09e") (:authors ("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 . [(20220704 340) ((emacs (25 1)) (tree-sitter (0 15 0))) "Run ispell on tree-sitter text nodes" single ((:commit . "2efe943dd62096a819b7c2d6b61c93a4f18aeb22") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/tree-sitter-ispell.el"))]) - (tree-sitter-langs . [(20220508 636) ((emacs (25 1)) (tree-sitter (0 15 0))) "Grammar bundle for tree-sitter" tar ((:commit . "deb2d8674be8f777ace50e15c7c041aeddb1d0b2") (:authors ("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 . [(20220821 1313) ((emacs (25 1)) (tree-sitter (0 15 0))) "Grammar bundle for tree-sitter" tar ((:commit . "5c6900a66a6b3a5d4ae6bde5199b5288c09af43b") (:authors ("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"))]) (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" single ((:commit . "75357757022a4399ab772ff0d92065bd114dabe9") (:authors ("Leo Littlebook" . "Leo.Littlebook@gmail.com")) (:maintainer "Leo Littlebook" . "Leo.Littlebook@gmail.com") (:keywords "outlines" "files" "convenience") (:url . "https://github.com/cyberthal/treefactor"))]) - (treemacs . [(20220801 1910) ((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 . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-all-the-icons . [(20220425 1124) ((emacs (26 1)) (all-the-icons (4 0 1)) (treemacs (0 0))) "all-the-icons integration for treemacs" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Eric Dallo" . "ercdll1337@gmail.com")) (:maintainer "Eric Dallo" . "ercdll1337@gmail.com") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-evil . [(20220616 1202) ((emacs (26 1)) (evil (1 2 12)) (treemacs (0 0))) "Evil mode integration for treemacs" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-icons-dired . [(20220622 801) ((treemacs (0 0)) (emacs (26 1))) "Treemacs icons for dired" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-magit . [(20220616 1202) ((emacs (26 1)) (treemacs (0 0)) (pfuture (1 3)) (magit (2 90 0))) "Magit integration for treemacs" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-persp . [(20220209 2117) ((emacs (26 1)) (treemacs (0 0)) (persp-mode (2 9 7)) (dash (2 11 0))) "Persp-mode integration for treemacs" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-perspective . [(20220209 2117) ((emacs (26 1)) (treemacs (0 0)) (perspective (2 8)) (dash (2 11 0))) "Perspective integration for treemacs" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de") ("Jason Dufair" . "jase@dufair.org")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-projectile . [(20211223 1454) ((emacs (26 1)) (projectile (0 14 0)) (treemacs (0 0))) "Projectile integration for treemacs" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) - (treemacs-tab-bar . [(20220221 2038) ((emacs (27 1)) (treemacs (0 0)) (dash (2 11 0))) "Tab bar integration for treemacs" single ((:commit . "39bc43bcd24afe206b96aa7364e8b6a0907984d3") (:authors ("Alexander Miller" . "alexanderm@web.de") ("Jason Dufair" . "jase@dufair.org") ("Aaron Jensen" . "aaronjensen@gmail.com")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs . [(20220801 1910) ((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 . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-all-the-icons . [(20220425 1124) ((emacs (26 1)) (all-the-icons (4 0 1)) (treemacs (0 0))) "all-the-icons integration for treemacs" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Eric Dallo" . "ercdll1337@gmail.com")) (:maintainer "Eric Dallo" . "ercdll1337@gmail.com") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-evil . [(20220616 1202) ((emacs (26 1)) (evil (1 2 12)) (treemacs (0 0))) "Evil mode integration for treemacs" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-icons-dired . [(20220622 801) ((treemacs (0 0)) (emacs (26 1))) "Treemacs icons for dired" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-magit . [(20220616 1202) ((emacs (26 1)) (treemacs (0 0)) (pfuture (1 3)) (magit (2 90 0))) "Magit integration for treemacs" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-persp . [(20220209 2117) ((emacs (26 1)) (treemacs (0 0)) (persp-mode (2 9 7)) (dash (2 11 0))) "Persp-mode integration for treemacs" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-perspective . [(20220209 2117) ((emacs (26 1)) (treemacs (0 0)) (perspective (2 8)) (dash (2 11 0))) "Perspective integration for treemacs" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de") ("Jason Dufair" . "jase@dufair.org")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-projectile . [(20211223 1454) ((emacs (26 1)) (projectile (0 14 0)) (treemacs (0 0))) "Projectile integration for treemacs" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) + (treemacs-tab-bar . [(20220221 2038) ((emacs (27 1)) (treemacs (0 0)) (dash (2 11 0))) "Tab bar integration for treemacs" single ((:commit . "89ade54c0e96f1c6f6abe5c5373c46c3355c91be") (:authors ("Alexander Miller" . "alexanderm@web.de") ("Jason Dufair" . "jase@dufair.org") ("Aaron Jensen" . "aaronjensen@gmail.com")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) (treepy . [(20191108 2217) ((emacs (25 1))) "Generic tree traversal tools" single ((:commit . "3ac940e97f3d03e48ca9d7fcd74916a9b01c72f3") (:authors ("Daniel Barreto" . "daniel.barreto.n@gmail.com")) (:maintainer "Daniel Barreto" . "daniel.barreto.n@gmail.com") (:keywords "lisp" "maint" "tools") (:url . "https://github.com/volrath/treepy.el"))]) (treeview . [(20210723 2256) ((emacs (24 4))) "A generic tree navigation library" single ((:commit . "09c8c1d045c7c8eace61b10b6df9d2f9079de78e") (:authors ("Tilman Rassy" . "tilman.rassy@googlemail.com")) (:maintainer "Tilman Rassy" . "tilman.rassy@googlemail.com") (:keywords "lisp" "tools" "internal" "convenience") (:url . "https://github.com/tilmanrassy/emacs-treeview"))]) (trident-mode . [(20190410 2036) ((emacs (24)) (slime (20130526)) (skewer-mode (1 5 0)) (dash (1 0 3))) "Live Parenscript interaction" single ((:commit . "109a1bc10bd0c4b47679a6ca5c4cd27c7c8d4ccb") (:authors ("John Mastro" . "john.b.mastro@gmail.com")) (:maintainer "John Mastro" . "john.b.mastro@gmail.com") (:keywords "languages" "lisp" "processes" "tools") (:url . "https://github.com/johnmastro/trident-mode.el"))]) @@ -4843,7 +4858,7 @@ (trr . [(20191019 1403) nil "a type-writing training program on GNU Emacs." tar ((:commit . "f841173e11213ac6916b2d3394b28fb202543871") (:authors ("YAMAMOTO Hirotaka" . "ymmt@is.s.u-tokyo.ac.jp") ("KATO Kenji" . "kato@suri.co.jp") (" *Original Author") ("INAMURA You" . "inamura@icot.or.jp") (" *Original Author")) (:maintainer "YAMAMOTO Hirotaka" . "ymmt@is.s.u-tokyo.ac.jp") (:keywords "games" "faces"))]) (truthy . [(20140508 2041) ((list-utils (0 4 2))) "Test the content of a value" single ((:commit . "8ed8d07772aa8457554547eb17e264b5df2b4a69") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "extensions") (:url . "http://github.com/rolandwalker/truthy"))]) (try . [(20181204 236) ((emacs (24))) "Try out Emacs packages." single ((:commit . "8831ded1784df43a2bd56c25ad3d0650cdb9df1d") (:authors ("Lars Tveito" . "larstvei@ifi.uio.no")) (:maintainer "Lars Tveito" . "larstvei@ifi.uio.no") (:keywords "packages") (:url . "http://github.com/larstvei/try"))]) - (ts . [(20210813 1617) ((emacs (26 1)) (dash (2 14 1)) (s (1 12 0))) "Timestamp and date/time library" single ((:commit . "3fee71ceefac71ba55eb34829d7e94bb3df37cee") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "calendar" "lisp") (:url . "http://github.com/alphapapa/ts.el"))]) + (ts . [(20220822 2313) ((emacs (26 1)) (dash (2 14 1)) (s (1 12 0))) "Timestamp and date/time library" single ((:commit . "552936017cfdec89f7fc20c254ae6b37c3f22c5b") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "calendar" "lisp") (:url . "http://github.com/alphapapa/ts.el"))]) (ts-comint . [(20181219 719) nil "Run a Typescript interpreter in an inferior process window." single ((:commit . "786b88fffc553e122868a1c4883f14136a040df6") (:authors ("Paul Huff" . "paul.huff@gmail.com")) (:maintainer "Paul Huff" . "paul.huff@gmail.com") (:keywords "typescript" "node" "inferior-mode" "convenience") (:url . "https://github.com/josteink/ts-comint"))]) (tsc . [(20220212 1632) ((emacs (25 1))) "Core Tree-sitter APIs" tar ((:commit . "3cfab8a0e945db9b3df84437f27945746a43cc71") (:authors ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") ("Jorge Javier Araya Navarro" . "jorgejavieran@yahoo.com.mx")) (:maintainer "Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") (:keywords "languages" "tools" "parsers" "dynamic-modules" "tree-sitter") (:url . "https://github.com/emacs-tree-sitter/elisp-tree-sitter"))]) (tss . [(20150913 1408) ((auto-complete (1 4 0)) (json-mode (1 1 0)) (log4e (0 2 0)) (yaxception (0 1))) "provide a interface for auto-complete.el/flymake.el on typescript-mode." tar ((:commit . "81ac6351a2ae258fd0ebf916dae9bd5a179fefd0") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "typescript" "completion") (:url . "https://github.com/aki2o/emacs-tss"))]) @@ -4863,7 +4878,7 @@ (twitch-api . [(20220420 1547) ((emacs (27 1)) (dash (2 19 0))) "An elisp interface for the Twitch.tv API" single ((:commit . "181681097d1fc8d7b78928f8a5b38c61d0e20ef5") (:keywords "multimedia" "twitch-api") (:url . "https://github.com/BenediktBroich/twitch-api"))]) (twittering-mode . [(20181121 1402) nil "Major mode for Twitter" single ((:commit . "114891e8fdb4f06b1326a6cf795e49c205cf9e29") (:authors ("Tadashi MATSUO" . "tad@mymail.twin.ne.jp") ("Y. Hayamizu" . "y.hayamizu@gmail.com") ("Tsuyoshi CHO" . "Tsuyoshi.CHO+develop@Gmail.com") ("Alberto Garcia" . "agarcia@igalia.com") ("Xavier Maillard" . "xavier@maillard.im")) (:maintainer "Tadashi MATSUO" . "tad@mymail.twin.ne.jp") (:keywords "twitter" "web") (:url . "http://twmode.sf.net/"))]) (twtxt . [(20220628 309) ((emacs (25 1)) (request (0 2 0))) "A twtxt client for Emacs" single ((:commit . "eb9efa19086fcae343353f6a5e88c3377fd06dd4") (:authors ("DEADBLACKCLOVER" . "deadblackclover@protonmail.com")) (:maintainer "DEADBLACKCLOVER" . "deadblackclover@protonmail.com") (:url . "https://github.com/deadblackclover/twtxt-el"))]) - (typescript-mode . [(20220730 1035) ((emacs (24 3))) "Major mode for editing typescript" tar ((:commit . "acd8d7995204c1faf14383b8236e57f4da380ecf") (:keywords "typescript" "languages") (:url . "http://github.com/ananthakumaran/typescript.el"))]) + (typescript-mode . [(20220815 1954) ((emacs (24 3))) "Major mode for editing typescript" tar ((:commit . "d79551c67ff5f2bd5f651eb411cdc66ceeb787e3") (:keywords "typescript" "languages") (:url . "http://github.com/ananthakumaran/typescript.el"))]) (typing . [(20180830 2203) nil "The Typing Of Emacs" single ((:commit . "a2ef25dde2d8eb91bd9c0c6164cb5208208647fa") (:authors ("Alex Schroeder" . "alex@gnu.org")) (:maintainer "Alex Schroeder" . "alex@gnu.org") (:keywords "games") (:url . "http://www.emacswiki.org/emacs/TypingOfEmacs"))]) (typing-game . [(20160426 1220) nil "a simple typing game" single ((:commit . "616435a5270274f4c7b698697674dbb2039049a4") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "lisp" "game"))]) (typit . [(20220616 2033) ((emacs (24 4)) (f (0 18)) (mmt (0 1 1))) "Typing game similar to tests on 10 fast fingers" tar ((:commit . "eb67151f0693103bd7ef09a4a121c0f18b53c395") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "games") (:url . "https://github.com/mrkkrp/typit"))]) @@ -4882,7 +4897,7 @@ (uncrustify-mode . [(20130707 1359) nil "Minor mode to automatically uncrustify." single ((:commit . "73893d000361e95784911e5ec268ad0ab2a1473c") (:authors ("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" single ((:commit . "1d3587f1fad66a747688f36636b67b33b73447d3") (:authors ("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"))]) (underline-with-char . [(20191128 2309) ((emacs (24))) "Underline with a char" single ((:commit . "36577e72aa4fbfa7f1abad01842359209f543751") (:maintainer nil . "marcowahlsoft@gmail.com") (:keywords "convenience") (:url . "https://gitlab.com/marcowahl/underline-with-char"))]) - (undersea-theme . [(20220616 1950) ((emacs (24 3))) "Theme styled after undersea imagery" single ((:commit . "6bc351c4cb49ccc7210801e6b54ecc2993289b92") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "theme" "sea") (:url . "https://github.com/jcs-elpa/undersea-theme"))]) + (undersea-theme . [(20220616 1950) ((emacs (24 3))) "Theme styled after undersea imagery" single ((:commit . "ee8a3cff8abadbfcd1b3c51e8622d32a104c99c4") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "theme" "sea") (:url . "https://github.com/jcs-elpa/undersea-theme"))]) (underwater-theme . [(20131118 2) nil "A gentle, deep blue color theme" single ((:commit . "4eb9ef014f580adc135d91d1cd68d37a310640b6") (:authors ("Jon-Michael Deldin" . "dev@jmdeldin.com")) (:maintainer "Jon-Michael Deldin" . "dev@jmdeldin.com") (:keywords "faces"))]) (undo-fu . [(20220731 2326) ((emacs (25 1))) "Undo helper with redo" single ((:commit . "b0d6eba024ac87a0aaf7fa66ae76d76f6c764d46") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-undo-fu"))]) (undo-fu-session . [(20220731 2356) ((emacs (28 1))) "Persistent undo, available between sessions" single ((:commit . "48544cb102fd3d761acf92598076b20bbb4075f9") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "convenience") (:url . "https://codeberg.org/ideasman42/emacs-undo-fu-session"))]) @@ -4917,13 +4932,13 @@ (urlenc . [(20140116 1456) nil "URL encoding/decoding utility for Emacs." single ((:commit . "835a6dcb783bbe84714bae87a3464aa0b128bfac") (:authors ("Taiki SUGAWARA" . "buzz.taiki@gmail.com")) (:maintainer "Taiki SUGAWARA" . "buzz.taiki@gmail.com") (:keywords "url") (:url . "https://github.com/buzztaiki/urlenc-el"))]) (urscript-mode . [(20190219 1604) ((emacs (24 4))) "major mode for editing URScript." single ((:commit . "b341f96b129ead8fb74d680cb4f546985bf110a9") (:authors ("Guido Schmidt" . "git@guidoschmidt.cc")) (:maintainer "Guido Schmidt" . "git@guidoschmidt.cc") (:keywords "languages") (:url . "https://github.com/guidoschmidt/urscript-mode"))]) (usage-memo . [(20170926 37) nil "integration of Emacs help system and memo" single ((:commit . "88e15a9942a3e0a6e36e9c3e51e3edb746067b1a") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "convenience" "languages" "lisp" "help" "tools" "docs") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/usage-memo.el"))]) - (use-package . [(20210207 1926) ((emacs (24 3)) (bind-key (2 4))) "A configuration macro for simplifying your .emacs" tar ((:commit . "0ad5d9d5d8a61517a207ab04bf69e71c081149eb") (:authors ("John Wiegley" . "johnw@newartisans.com")) (:maintainer "John Wiegley" . "johnw@newartisans.com") (:keywords "dotemacs" "startup" "speed" "config" "package") (:url . "https://github.com/jwiegley/use-package"))]) - (use-package-chords . [(20181024 2322) ((use-package (2 1)) (bind-key (1 0)) (bind-chord (0 2)) (key-chord (0 6))) "key-chord keyword for use-package" single ((:commit . "0ad5d9d5d8a61517a207ab04bf69e71c081149eb") (:authors ("Justin Talbott" . "justin@waymondo.com")) (:maintainer "Justin Talbott" . "justin@waymondo.com") (:keywords "convenience" "tools" "extensions") (:url . "https://github.com/waymondo/use-package-chords"))]) + (use-package . [(20220819 553) ((emacs (24 3)) (bind-key (2 4))) "A configuration macro for simplifying your .emacs" tar ((:commit . "e2d173b1200865a9efd5c2066831a230497582c0") (:authors ("John Wiegley" . "johnw@newartisans.com")) (:maintainer "John Wiegley" . "johnw@newartisans.com") (:keywords "dotemacs" "startup" "speed" "config" "package") (:url . "https://github.com/jwiegley/use-package"))]) + (use-package-chords . [(20220807 1556) ((use-package (2 1)) (bind-key (1 0)) (bind-chord (0 2)) (key-chord (0 6))) "key-chord keyword for use-package" single ((:commit . "e2d173b1200865a9efd5c2066831a230497582c0") (:authors ("Justin Talbott" . "justin@waymondo.com")) (:maintainer "Justin Talbott" . "justin@waymondo.com") (:keywords "convenience" "tools" "extensions") (:url . "https://github.com/jwiegley/use-package"))]) (use-package-el-get . [(20180131 505) ((use-package (1 0))) "el-get support for use package" single ((:commit . "cba87c4e9a3a66b7c10962e3aefdf11c83d737bc") (:authors ("Edward Knyshov" . "edvorg@gmail.com")) (:maintainer "Edward Knyshov" . "edvorg@gmail.com") (:keywords "dotemacs" "startup" "speed" "config" "package" "tools") (:url . "https://github.com/edvorg/use-package-el-get"))]) - (use-package-ensure-system-package . [(20180913 1501) ((use-package (2 1)) (system-packages (1 0 4))) "auto install system packages" single ((:commit . "0ad5d9d5d8a61517a207ab04bf69e71c081149eb") (:authors ("Justin Talbott" . "justin@waymondo.com")) (:maintainer "Justin Talbott" . "justin@waymondo.com") (:keywords "convenience" "tools" "extensions") (:url . "https://github.com/waymondo/use-package-ensure-system-package"))]) + (use-package-ensure-system-package . [(20220807 1558) ((use-package (2 1)) (system-packages (1 0 4))) "auto install system packages" single ((:commit . "e2d173b1200865a9efd5c2066831a230497582c0") (:authors ("Justin Talbott" . "justin@waymondo.com")) (:maintainer "Justin Talbott" . "justin@waymondo.com") (:keywords "convenience" "tools" "extensions") (:url . "https://github.com/waymondo/use-package-ensure-system-package"))]) (use-package-hydra . [(20181228 745) ((emacs (24 3)) (use-package (2 4))) "Adds :hydra keyword to use-package macro" single ((:commit . "8cd55a1128fbdf6327bb38a199d206225896d146") (:authors ("Toon Claes" . "toon@iotcl.com")) (:maintainer "Toon Claes" . "toon@iotcl.com") (:keywords "convenience" "extensions" "tools") (:url . "https://gitlab.com/to1ne/use-package-hydra"))]) (use-proxy . [(20201209 853) ((exec-path-from-shell (1 12)) (emacs (26 2))) "Enable/Disable proxies respecting your HTTP/HTTPS env" single ((:commit . "b2995563f41c162a082cd4823a499887f807176e") (:authors ("Ray Wang" . "ray.hackmylife@gmail.com")) (:maintainer "Ray Wang" . "ray.hackmylife@gmail.com") (:keywords "proxy" "comm") (:url . "https://github.com/rayw000/use-proxy"))]) - (use-ttf . [(20220704 700) ((emacs (24 4))) "Keep font consistency across different OSs" single ((:commit . "6473227e4e26896db099d54bd2ba1d05619395bc") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "customize" "font" "install" "ttf") (:url . "https://github.com/jcs-elpa/use-ttf"))]) + (use-ttf . [(20220704 700) ((emacs (24 4))) "Keep font consistency across different OSs" single ((:commit . "9fd4d37c3fce9d0f3743172f50ed7c4cb17b822d") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "customize" "font" "install" "ttf") (:url . "https://github.com/jcs-elpa/use-ttf"))]) (utimeclock . [(20220710 1024) ((emacs (24 4))) "Simple utility for manual time tracking" single ((:commit . "32994d27771f77a384b246e69a621c4d26e27f4f") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-utimeclock"))]) (utop . [(20220719 2111) ((emacs (26)) (tuareg (2 2 0))) "Universal toplevel for OCaml" single ((:commit . "bbd9a6ed45c8de8d50adcd5d4d845bdba212db63") (:authors ("Jeremie Dimino" . "jeremie@dimino.org")) (:maintainer "Jeremie Dimino" . "jeremie@dimino.org") (:keywords "ocaml" "languages") (:url . "https://github.com/ocaml-community/utop"))]) (uuid . [(20120910 851) nil "UUID's for EmacsLisp" single ((:commit . "1519bfeb0e31602b840bc8dd35d7c7e732c159fe") (:authors ("James Mastros")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp"))]) @@ -4959,7 +4974,8 @@ (vdm-snippets . [(20190313 1122) ((emacs (24)) (yasnippet (0 13 0))) "YASnippets for VDM mode" tar ((:commit . "56336930555df91787f196acac15680498d17d5e") (:authors ("Peter W. V. Tran-Jørgensen" . "peter.w.v.jorgensen@gmail.com")) (:maintainer "Peter W. V. Tran-Jørgensen" . "peter.w.v.jorgensen@gmail.com") (:keywords "languages") (:url . "https://github.com/peterwvj/vdm-mode"))]) (vector-utils . [(20140508 2041) nil "Vector-manipulation utility functions" single ((:commit . "c38ca1c6a23b2b51a6ac36c2c64e50e21cbe9d21") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "extensions") (:url . "http://github.com/rolandwalker/vector-utils"))]) (vega-view . [(20210401 1115) ((emacs (25)) (cider (0 24 0)) (parseedn (0 1))) "Vega visualization viewer" single ((:commit . "3793025a523a86acc6255b4183b12ebfc95e1116") (:authors ("Jack Rusher" . "jack@appliedscience.studio")) (:maintainer "Jack Rusher" . "jack@appliedscience.studio") (:keywords "multimedia") (:url . "https://www.github.com/applied-science/emacs-vega-view"))]) - (verb . [(20220727 1923) ((emacs (25 1))) "Organize and send HTTP requests" tar ((:commit . "aa12067ba3c34749c41bf5247f4bda1c68625110") (:authors ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainer "Federico Tedin" . "federicotedin@gmail.com") (:keywords "tools") (:url . "https://github.com/federicotdn/verb"))]) + (vegetative-theme . [(20220822 353) ((autothemer (0 2)) (emacs (24))) "A Theme based on green CRT terminals" single ((:commit . "db60ce0fe327ae7e4371545179ed94483b1132a8") (:url . "http://github.com/emacsfodder/emacs-theme-vegetative"))]) + (verb . [(20220727 1923) ((emacs (25 1))) "Organize and send HTTP requests" tar ((:commit . "e3b3c146d7bf8fb12295338aded6a96ff4fb1752") (:authors ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainer "Federico Tedin" . "federicotedin@gmail.com") (:keywords "tools") (:url . "https://github.com/federicotdn/verb"))]) (veri-kompass . [(20200213 934) ((emacs (25)) (cl-lib (0 5)) (org (8 2 0))) "verilog codebase navigation facility" single ((:commit . "271903cdf92db05898ee7cffb65641f30fa08280") (:maintainer nil . "andrea_corallo@yahoo.it") (:keywords "languages" "extensions" "verilog" "hardware" "rtl") (:url . "https://gitlab.com/koral/veri-kompass"))]) (verify-url . [(20160426 1228) ((cl-lib (0 5))) "find out invalid urls in the buffer or region" single ((:commit . "d6f3623cda8cd526a2d198619b137059cb1ba1ab") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "usability" "url") (:url . "https://github.com/lujun9972/verify-url"))]) (verona-mode . [(20200823 536) ((emacs (25 1)) (dash (2 17 0)) (hydra (0 15 0))) "A major mode for the Verona programming language" single ((:commit . "72dd31ef847344d79409503f3c42169041eb3da4") (:keywords "languages" "programming") (:url . "https://github.com/damon-kwok/verona-mode"))]) @@ -4993,13 +5009,13 @@ (voca-builder . [(20161101 1645) ((popup (0 5 2))) "Helps you build up your vocabulary" single ((:commit . "51573beec8cd8308477b0faf453aad93e17f57c5") (:authors ("Yi Tang" . "yi.tang.uk@me.com")) (:maintainer "Yi Tang" . "yi.tang.uk@me.com") (:keywords "english" "vocabulary") (:url . "https://github.com/yitang/voca-builder"))]) (volatile-highlights . [(20160612 155) nil "Minor mode for visual feedback on some operations." single ((:commit . "9a20091f0ce7fc0a6b3e641a6a46d5f3ac4d8392") (:authors ("K-talo Miyazaki ")) (:maintainer "K-talo Miyazaki ") (:keywords "emulations" "convenience" "wp") (:url . "http://www.emacswiki.org/emacs/download/volatile-highlights.el"))]) (volume . [(20201002 1022) nil "tweak your sound card volume from Emacs" single ((:commit . "afb75a5f7fe41eb28c8dbb1378e80d103eea05c7") (:authors ("Daniel Brockman" . "daniel@brockman.se")) (:maintainer "Daniel Brockman" . "daniel@brockman.se") (:url . "http://www.brockman.se/software/volume-el/"))]) - (vs-dark-theme . [(20220703 809) ((emacs (24 1))) "Visual Studio IDE dark theme" single ((:commit . "87b1b300375bb9f06f92e253c6941113b9bce5b4") (:authors ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-dark-theme"))]) - (vs-light-theme . [(20220703 806) ((emacs (24 1))) "Visual Studio IDE light theme" single ((:commit . "af9c8ee7e99ec4ffb20d15512efafc49d509f5db") (:authors ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-light-theme"))]) + (vs-dark-theme . [(20220817 733) ((emacs (24 1))) "Visual Studio IDE dark theme" single ((:commit . "bf0e9d763ccff2add31f441261903986f9cbc702") (:authors ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-dark-theme"))]) + (vs-light-theme . [(20220817 737) ((emacs (24 1))) "Visual Studio IDE light theme" single ((:commit . "38c45512dc74b1221ad3364c560c2eb65ced8ff4") (:authors ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-light-theme"))]) (vscdark-theme . [(20191212 107) ((emacs (24 1))) "VS Code Dark+ like theme" single ((:commit . "8eba74059e8a9db974e4056ee024e52fe54da485") (:authors ("Alexander L. Belikoff")) (:maintainer "Alexander L. Belikoff") (:url . "https://github.com/abelikoff/vscdark-theme"))]) (vscode-dark-plus-theme . [(20220320 530) nil "Default Visual Studio Code Dark+ theme" single ((:commit . "d247fcf2b0c4f21398276e12faf3f2a4c26b2306") (:authors ("Ian Y.E. Pan")) (:maintainer "Ian Y.E. Pan") (:url . "https://github.com/ianpan870102/vscode-dark-plus-emacs-theme"))]) (vscode-icon . [(20201214 2227) ((emacs (25 1))) "Utility package to provide Vscode style icons" tar ((:commit . "909151c8105861aa300f5601e333909d36d0ebf5") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "files" "tools") (:url . "https://github.com/jojojames/vscode-icon-emacs"))]) - (vterm . [(20220613 1614) ((emacs (25 1))) "Fully-featured terminal emulator" tar ((:commit . "3155a477b43c1567d754768f5be79296440ebaf8") (:authors ("Lukas Fürmetz" . "fuermetz@mailbox.org")) (:maintainer "Lukas Fürmetz" . "fuermetz@mailbox.org") (:keywords "terminals") (:url . "https://github.com/akermu/emacs-libvterm"))]) - (vterm-toggle . [(20220606 1524) ((emacs (25 1)) (vterm (0 0 1))) "Toggles between the vterm buffer and other buffers." single ((:commit . "02519323aa0a2e6af641cd205b230f48a04a5ca3") (:authors (nil . "jixiuf jixiuf@qq.com")) (:maintainer nil . "jixiuf jixiuf@qq.com") (:keywords "vterm" "terminals") (:url . "https://github.com/jixiuf/vterm-toggle"))]) + (vterm . [(20220822 1158) ((emacs (25 1))) "Fully-featured terminal emulator" tar ((:commit . "f104e3a11c9ff33ccc0e086cffaadc9549e9e8b1") (:authors ("Lukas Fürmetz" . "fuermetz@mailbox.org")) (:maintainer "Lukas Fürmetz" . "fuermetz@mailbox.org") (:keywords "terminals") (:url . "https://github.com/akermu/emacs-libvterm"))]) + (vterm-toggle . [(20220820 1608) ((emacs (25 1)) (vterm (0 0 1))) "Toggles between the vterm buffer and other buffers." single ((:commit . "b94522e3a4ddaae098f4711aadce675e891cdec8") (:authors (nil . "jixiuf jixiuf@qq.com")) (:maintainer nil . "jixiuf jixiuf@qq.com") (:keywords "vterm" "terminals") (:url . "https://github.com/jixiuf/vterm-toggle"))]) (vtm . [(20200921 338) nil "Manages vterm buffers with configuration files" tar ((:commit . "d770fd8cff7c24688199392ad93c01485c6a9569") (:keywords "convenience") (:url . "https://github.com/laishulu/emacs-vterm-manager"))]) (vue-html-mode . [(20180428 2035) nil "Major mode for editing Vue.js templates" single ((:commit . "1514939804bad558584feeb6298b38d22eadf64e") (:authors ("Adam Niederer" . "adam.niederer@gmail.com")) (:maintainer "Adam Niederer" . "adam.niederer@gmail.com") (:keywords "languages" "vue" "template") (:url . "http://github.com/AdamNiederer/vue-html-mode"))]) (vue-mode . [(20190415 231) ((mmm-mode (0 5 5)) (vue-html-mode (0 2)) (ssass-mode (0 2)) (edit-indirect (0 1 4))) "Major mode for vue component based on mmm-mode" single ((:commit . "031edd1f97db6e7d8d6c295c0e6d58dd128b9e71") (:authors ("codefalling" . "code.falling@gmail.com")) (:maintainer "codefalling" . "code.falling@gmail.com") (:keywords "languages"))]) @@ -5009,7 +5025,7 @@ (vyper-mode . [(20180707 1935) ((emacs (24 3))) "Major mode for the Vyper programming language" single ((:commit . "323dfddfc38f0b11697e9ebaf04d1b53297e54e5") (:authors ("Alex Stokes" . "r.alex.stokes@gmail.com")) (:maintainer "Alex Stokes" . "r.alex.stokes@gmail.com") (:keywords "languages") (:url . "https://github.com/ralexstokes/vyper-mode"))]) (w32-browser . [(20170101 1954) nil "Run Windows application associated with a file." single ((:commit . "e5c60eafd8f8d3546a0fa295ad5af2414d36b4e6") (:authors ("Emacs Wiki, Drew Adams")) (:maintainer nil . "Drew Adams (concat \"drew.adams\" \"@\" \"oracle\" \".com\")") (:keywords "mouse" "dired" "w32" "explorer") (:url . "http://www.emacswiki.org/w32-browser.el"))]) (w32-ime . [(20201107 143) ((emacs (24 4))) "Windows IME UI/UX controler" single ((:commit . "9c62273dce0ba685a591577885b1e216ba832ec1") (:authors ("H.Miyashita") ("MIYOSHI Masanori") ("KOBAYASHI Yasuhiro") ("NTEmacsJP") ("ksugita (gnupack)") ("rzl24ozi") ("TANE") ("Masamichi Hosoda" . "trueroad@trueroad.jp") ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Masamichi Hosoda" . "trueroad@trueroad.jp") (:url . "https://github.com/trueroad/w32-ime.el"))]) - (w3m . [(20220729 42) nil "an Emacs interface to w3m" tar ((:commit . "db02ce0fde628a8eb9a1a0c95939c41981fdd931") (:keywords "w3m" "www" "hypermedia"))]) + (w3m . [(20220823 543) nil "an Emacs interface to w3m" tar ((:commit . "e185b54d13fb7eac7d045daf6a83731d9ef79231") (:keywords "w3m" "www" "hypermedia"))]) (wacspace . [(20180311 2350) ((dash (1 2 0)) (cl-lib (0 2))) "The WACky WorkSPACE manager for emACS" tar ((:commit . "54d19aab6fd2bc5945b7ffc58104e695064927e2") (:authors ("Emanuel Evans" . "emanuel.evans@gmail.com")) (:maintainer "Emanuel Evans" . "emanuel.evans@gmail.com") (:keywords "workspace") (:url . "http://github.com/shosti/wacspace.el"))]) (waf-mode . [(20170403 1940) nil "Waf integration for Emacs" single ((:commit . "20c75eabd1d54fbce8e0dbef785c9fb68577ee4f") (:authors ("Denys Valchuk" . "dvalchuk@gmail.com")) (:maintainer "Denys Valchuk" . "dvalchuk@gmail.com") (:url . "https://bitbucket.org/dvalchuk/waf-mode"))]) (waher-theme . [(20141115 1230) ((emacs (24 1))) "Emacs 24 theme based on waher for st2 by dduckster" single ((:commit . "60d31519fcfd8e797723d47961b255ae2f2e2c0a") (:authors ("Jasonm23" . "jasonm23@gmail.com")) (:maintainer "Jasonm23" . "jasonm23@gmail.com") (:url . "https://github.com/jasonm23/emacs-waher-theme"))]) @@ -5034,7 +5050,7 @@ (web . [(20141231 2001) ((dash (2 9 0)) (s (1 5 0))) "useful HTTP client" single ((:commit . "483188dac4bc6b409b985c9dae45f3324a425efd") (:authors ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp" "http" "hypermedia") (:url . "http://github.com/nicferrier/emacs-web"))]) (web-beautify . [(20161115 2247) nil "Format HTML, CSS and JavaScript/JSON" single ((:commit . "e1b45321d8c11b404b12c8e55afe55eaa7c84ee9") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainer "Yasuyuki Oka" . "yasuyk@gmail.com") (:url . "https://github.com/yasuyk/web-beautify"))]) (web-completion-data . [(20160318 848) nil "Shared completion data for ac-html and company-web" tar ((:commit . "c272c94e8a71b779c29653a532f619acad433a4f") (:authors ("Olexandr Sydorchuk" . "olexandr.syd@gmail.com")) (:maintainer "Olexandr Sydorchuk" . "olexandr.syd@gmail.com") (:keywords "html" "auto-complete" "company") (:url . "https://github.com/osv/web-completion-data"))]) - (web-mode . [(20220615 602) ((emacs (23 1))) "major mode for editing web templates" single ((:commit . "7b5459f58c381f31eed257480b000a9a46209094") (:authors ("François-Xavier Bois")) (:maintainer "François-Xavier Bois" . "fxbois@gmail.com") (:keywords "languages") (:url . "https://web-mode.org"))]) + (web-mode . [(20220820 1935) ((emacs (23 1))) "major mode for editing web templates" single ((:commit . "de9dfa3ac3cd54e6acb4f9d9d1343f8d4eabd363") (:authors ("François-Xavier Bois")) (:maintainer "François-Xavier Bois" . "fxbois@gmail.com") (:keywords "languages") (:url . "https://web-mode.org"))]) (web-mode-edit-element . [(20190531 852) ((emacs (24 4)) (web-mode (14))) "Helper-functions for attribute- and element-handling" tar ((:commit . "ad5d7e4dc2420bdd00ce65d9adffbd38a5904afa") (:authors ("Julian T. Knabenschuh" . "jtkdevelopments@gmail.com")) (:maintainer "Julian T. Knabenschuh" . "jtkdevelopments@gmail.com") (:keywords "languages" "convenience") (:url . "https://github.com/jtkDvlp/web-mode-edit-element"))]) (web-narrow-mode . [(20170407 210) ((web-mode (14 0 27))) "quick narrow code block in web-mode" single ((:commit . "73bdcb7d0701abe65dab4fc295d944885e05ae33") (:authors ("Qquanwei" . "quanwei9958@126.com")) (:maintainer "Johan Andersson" . "quanwei9958@126.com") (:keywords "web-mode" "react" "narrow" "web") (:url . "https://github.com/Qquanwei/web-narrow-mode"))]) (web-search . [(20190620 602) ((emacs (24 3))) "Open a web search" tar ((:commit . "a22cbdc663a1895d5a5b69de91e1e3b9eb64b92f") (:authors ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainer "Xu Chunyang" . "mail@xuchunyang.me") (:keywords "web" "search") (:url . "https://github.com/xuchunyang/web-search.el"))]) @@ -5044,6 +5060,7 @@ (weblogger . [(20110926 1618) ((xml-rpc (1 6 8))) "Weblog maintenance via XML-RPC APIs" single ((:commit . "b3dd4aead9d3a87e6d85e7fef4f4f3bd40d87b53") (:keywords "weblog" "blogger" "cms" "movable" "type" "openweblog" "blog") (:url . "http://launchpad.net/weblogger-el"))]) (weblorg . [(20220312 2008) ((templatel (0 1 6)) (emacs (26 1))) "Static Site Generator for org-mode" tar ((:commit . "b2bb79ed2c532cad5b03455d8cae887ddb803db3") (:authors ("Lincoln Clarete" . "lincoln@clarete.li")) (:maintainer "Lincoln Clarete" . "lincoln@clarete.li") (:url . "https://emacs.love/weblorg"))]) (webpaste . [(20220524 1745) ((emacs (24 4)) (request (0 2 0)) (cl-lib (0 5))) "Paste to pastebin-like services" single ((:commit . "d96da58fe42988d5c433c71ee9f8e6fb75d595a9") (:authors ("Elis \"etu\" Hirwing" . "elis@hirwing.se")) (:maintainer "Elis \"etu\" Hirwing" . "elis@hirwing.se") (:keywords "convenience" "comm" "paste") (:url . "https://github.com/etu/webpaste.el"))]) + (websearch . [(20220823 42) ((emacs (24 4))) "Query search engines" tar ((:commit . "9336601462ce29822e6aa14db01d923a4bd1c6ef") (:authors ("Maciej Barć" . "xgqt@riseup.net")) (:maintainer "Maciej Barć" . "xgqt@riseup.net") (:keywords "convenience" "hypermedia") (:url . "https://gitlab.com/xgqt/emacs-websearch/"))]) (websocket . [(20210110 17) ((cl-lib (0 5))) "Emacs WebSocket client and server" single ((:commit . "82b370602fa0158670b1c6c769f223159affce9b") (:authors ("Andrew Hyatt" . "ahyatt@gmail.com")) (:maintainer "Andrew Hyatt" . "ahyatt@gmail.com") (:keywords "communication" "websocket" "server") (:url . "https://github.com/ahyatt/emacs-websocket"))]) (wedge-ws . [(20140714 2149) nil "Wedge whitespace between columns in text" single ((:commit . "4669115f02d9c6fee067cc5369bb38c0f9db88b2") (:authors ("Anders Eurenius" . "aes@spotify.com")) (:maintainer "Anders Eurenius" . "aes@spotify.com") (:keywords "formatting" "indentation"))]) (weechat . [(20190520 1551) ((s (1 3 1)) (cl-lib (0 2)) (emacs (24)) (tracking (1 2))) "Chat via WeeChat's relay protocol in Emacs" tar ((:commit . "d9a13306ea8be27367f92e9202d116a88fa1f441") (:authors ("Moritz Ulrich" . "moritz@tarn-vedra.de") ("Rüdiger Sonderfeld" . "ruediger@c-plusplus.de") ("Aristid Breitkreuz" . "aristidb@gmail.com")) (:maintainer "Moritz Ulrich" . "moritz@tarn-vedra.de") (:keywords "irc" "chat" "network" "weechat") (:url . "https://github.com/the-kenny/weechat.el"))]) @@ -5056,7 +5073,7 @@ (wgrep-helm . [(20210322 2148) ((wgrep (2 1 1))) "Writable helm-grep-mode buffer and apply the changes to files" single ((:commit . "f9687c28bbc2e84f87a479b6ce04407bb97cfb23") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "grep" "edit" "extensions") (:url . "http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep-helm.el"))]) (wgrep-pt . [(20200128 109) ((wgrep (2 1 5))) "Writable pt buffer and apply the changes to files" single ((:commit . "f9687c28bbc2e84f87a479b6ce04407bb97cfb23") (:authors ("Masahiro Hayashi , Bailey Ling" . "bling@live.ca")) (:maintainer "Masahiro Hayashi , Bailey Ling" . "bling@live.ca") (:keywords "grep" "edit" "extensions") (:url . "http://github.com/mhayashi1120/Emacs-wgrep/raw/master/wgrep-pt.el"))]) (what-the-commit . [(20150901 1316) nil "Random commit message generator" single ((:commit . "868c80a1b8614bcbd2225cd0290142c72f2a7956") (:authors ("Dan Barbarito" . "dan@barbarito.me")) (:maintainer "Dan Barbarito" . "dan@barbarito.me") (:keywords "git" "commit" "message") (:url . "http://barbarito.me/"))]) - (which-key . [(20220518 1941) ((emacs (24 4))) "Display available keybindings in popup" single ((:commit . "1ab1d0cc88843c9a614ed3226c5a1070e32e4823") (:authors ("Justin Burkett" . "justin@burkett.cc")) (:maintainer "Justin Burkett" . "justin@burkett.cc") (:url . "https://github.com/justbur/emacs-which-key"))]) + (which-key . [(20220811 1616) ((emacs (24 4))) "Display available keybindings in popup" single ((:commit . "8093644032854b1cdf3245ce4e3c7b6673f741bf") (:authors ("Justin Burkett" . "justin@burkett.cc")) (:maintainer "Justin Burkett" . "justin@burkett.cc") (:url . "https://github.com/justbur/emacs-which-key"))]) (which-key-posframe . [(20210615 944) ((emacs (26 0)) (posframe (0 4 3)) (which-key (3 3 2))) "Using posframe to show which-key" single ((:commit . "90e85d74899fc23d95798048cc0bbdb4bab9c1b7") (:authors ("Yanghao Xie")) (:maintainer "Yanghao Xie" . "yhaoxie@gmail.com") (:keywords "convenience" "bindings" "tooltip") (:url . "https://github.com/yanghaoxie/which-key-posframe"))]) (whiley-mode . [(20220501 2219) ((emacs (24 1))) "Major mode for Whiley language" single ((:commit . "69eb67cf41dad029f1456079aea62a4b61ca9b46") (:authors ("David J. Pearce" . "dave01001110@gmail.com")) (:maintainer "David J. Pearce" . "dave01001110@gmail.com") (:keywords "languages") (:url . "http://github.com/Whiley/WhileyEmacsMode"))]) (whitaker . [(20210203 1149) ((emacs (25))) "Comint interface for Whitaker's Words" single ((:commit . "a6fda24ccb69a18c0706633326d5cc4fcfaed83a") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "processes"))]) @@ -5092,8 +5109,8 @@ (winum . [(20190911 1607) ((cl-lib (0 5)) (dash (2 13 0))) "Navigate windows and frames using numbers." single ((:commit . "c5455e866e8a5f7eab6a7263e2057aff5f1118b9") (:authors ("Thomas de Beauchêne" . "thomas.de.beauchene@gmail.com")) (:maintainer "Thomas de Beauchêne" . "thomas.de.beauchene@gmail.com") (:keywords "convenience" "frames" "windows" "multi-screen") (:url . "http://github.com/deb0ch/winum.el"))]) (wisp-mode . [(20220529 1522) ((emacs (24 4))) "Tools for wisp: the Whitespace-to-Lisp preprocessor" single ((:commit . "1a01003d400db8a42838cabcb26c06d627246a17") (:authors ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainer "Arne Babenhauserheide" . "arne_bab@web.de") (:keywords "languages" "lisp" "scheme") (:url . "http://www.draketo.de/english/wisp"))]) (wispjs-mode . [(20170720 1919) ((clojure-mode (0))) "Major mode for Wisp code." single ((:commit . "60f9f5fd9d1556e2d008939f67eb1b1d0f325fa8") (:authors ("Kris Jenkins" . "krisajenkins@gmail.com")) (:maintainer "Kris Jenkins" . "krisajenkins@gmail.com") (:url . "https://github.com/krisajenkins/wispjs-mode"))]) - (with-editor . [(20220608 1017) ((emacs (25 1)) (compat (28 1 1 0))) "Use the Emacsclient as $EDITOR" tar ((:commit . "cfcbc2731e402b9169c0dc03e89b5b57aa988502") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "processes" "terminals") (:url . "https://github.com/magit/with-editor"))]) - (with-emacs . [(20200210 1543) ((emacs (24 4))) "Evaluate Emacs Lisp expressions in a separate Emacs process" single ((:commit . "9f99bec56f87e53deb9f33b364eda77677a17eb9") (:authors ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainer "Gong Qijian" . "gongqijian@gmail.com") (:keywords "tools") (:url . "https://github.com/twlz0ne/with-emacs.el"))]) + (with-editor . [(20220810 1159) ((emacs (25 1)) (compat (28 1 1 0))) "Use the Emacsclient as $EDITOR" tar ((:commit . "1d5860cfd05d6805018bd071b8f9b56493ba11c6") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "processes" "terminals") (:url . "https://github.com/magit/with-editor"))]) + (with-emacs . [(20220814 444) ((emacs (24 4))) "Evaluate Emacs Lisp expressions in a separate Emacs process" single ((:commit . "fb9ef454a4bb2d6de3415807b4858a20a9cc0dad") (:authors ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainer "Gong Qijian" . "gongqijian@gmail.com") (:keywords "tools") (:url . "https://github.com/twlz0ne/with-emacs.el"))]) (with-namespace . [(20130407 1822) ((dash (1 1 0)) (loop (1 1))) "interoperable elisp namespaces" single ((:commit . "8ac52da3a09cf46087720e30cf730d00f140cde6") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "namespaces"))]) (with-proxy . [(20200510 414) ((emacs (24 4))) "Evaluate expressions with proxy" single ((:commit . "93b1ed2f3060f305009fa71f4fb5bb10173a10e3") (:authors ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainer "Gong Qijian" . "gongqijian@gmail.com") (:keywords "comm") (:url . "https://github.com/twlz0ne/with-proxy.el"))]) (with-shell-interpreter . [(20200828 1217) ((emacs (25 1)) (cl-lib (0 6 1))) "Helper for shell command APIs" single ((:commit . "3fd1ea892e44f7fe6f86df2b5c0a0a1e0f3913fa") (:keywords "processes" "terminals") (:url . "https://github.com/p3r7/with-shell-interpreter"))]) @@ -5106,7 +5123,7 @@ (wordel . [(20220508 1745) ((emacs (27 1))) "An Elisp implementation of \"Wordle\" (aka \"Lingo\")" tar ((:commit . "d37187bb5abb2fe4a8ba120fad9e52dd74cc220e") (:authors ("Nicholas Vollmer" . "iarchivedmywholelife@gmail.com")) (:maintainer "Nicholas Vollmer" . "iarchivedmywholelife@gmail.com") (:keywords "games") (:url . "https://github.com/progfolio/wordel"))]) (wordgen . [(20170803 1820) ((emacs (24)) (cl-lib (0 5))) "Random word generator" single ((:commit . "aacad928ae99a953e034a831dfd0ebdf7d52ac1d") (:authors ("Fanael Linithien" . "fanael4@gmail.com")) (:maintainer "Fanael Linithien" . "fanael4@gmail.com") (:url . "https://github.com/Fanael/wordgen.el"))]) (wordnut . [(20180313 443) ((emacs (24 4))) "Major mode interface to WordNet" tar ((:commit . "feac531404041855312c1a046bde7ea18c674915"))]) - (wordreference . [(20220804 946) ((emacs (27 1)) (s (1 12 0))) "Interface for wordreference.com" single ((:commit . "6bb88a37895b62350650447fff124ce06c7e1ec8") (:authors ("Marty Hiatt ")) (:maintainer "Marty Hiatt ") (:keywords "convenience" "translate" "wp" "dictionary") (:url . "https://codeberg.org/martianh/wordreference.el"))]) + (wordreference . [(20220806 1022) ((emacs (27 1)) (s (1 12 0))) "Interface for wordreference.com" single ((:commit . "fd46c30ddc3abd6124b9057110fb0cbdc242937a") (:authors ("Marty Hiatt ")) (:maintainer "Marty Hiatt ") (:keywords "convenience" "translate" "wp" "dictionary") (:url . "https://codeberg.org/martianh/wordreference.el"))]) (wordsmith-mode . [(20210715 1517) nil "Syntax analysis and NLP text-processing in Emacs (OSX-only)" single ((:commit . "5d40ceaa2b8d41ab3634ca377ceb6a74deeb2287") (:authors ("istib" . "istib@thebati.net")) (:maintainer "istib" . "istib@thebati.net"))]) (worf . [(20220102 835) ((swiper (0 11 0)) (ace-link (0 1 0)) (hydra (0 13 0)) (zoutline (0 1 0))) "A warrior does not press so many keys! (in org-mode)" tar ((:commit . "8681241e118585824cd256e5b026978bf06c7e58") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "lisp") (:url . "https://github.com/abo-abo/worf"))]) (workgroups . [(20110726 1641) nil "workgroups for windows (for Emacs)" single ((:commit . "9572b3492ee09054dc329f64ed846c962b395e39") (:authors ("tlh" . "thunkout@gmail.com")) (:maintainer "tlh" . "thunkout@gmail.com") (:keywords "session" "management" "window-configuration" "persistence"))]) @@ -5126,13 +5143,13 @@ (wwtime . [(20151122 1610) nil "Insert a time of day with appropriate world-wide localization" single ((:commit . "d04d8fa814b5d3644efaeb28f25520ada69acbbd") (:authors ("Norman Walsh" . "ndw@nwalsh.com")) (:maintainer "Norman Walsh" . "ndw@nwalsh.com") (:keywords "time"))]) (www-synonyms . [(20170128 2251) ((request (0 2 0)) (cl-lib (0 5))) "insert synonym for a word" single ((:commit . "7e37ea35064ff31c9945f0198a653647d408c936") (:authors ("Bernhard Specht" . "bernhard@specht.net")) (:maintainer "Bernhard Specht" . "bernhard@specht.net") (:keywords "lisp"))]) (x-path-walker . [(20220714 1056) ((helm-core (3 6 0))) "Navigation feature for JSON/XML/HTML based on path (imenu like)" tar ((:commit . "c91deaaba0d5cc9018008a39c96222deacba3868") (:authors (nil . "")) (:maintainer nil . "") (:keywords "convenience"))]) - (x509-mode . [(20220705 757) ((emacs (24 3))) "View certificates, CRLs and keys using OpenSSL" tar ((:commit . "49fbab6166d03e5d4c8b856de6ef04b99ba16eb4") (:authors ("Fredrik Axelsson" . "f.axelsson@gmai.com")) (:maintainer "Fredrik Axelsson" . "f.axelsson@gmai.com") (:url . "https://github.com/jobbflykt/x509-mode"))]) + (x509-mode . [(20220819 541) ((emacs (24 3))) "View certificates, CRLs and keys using OpenSSL" tar ((:commit . "a2ec552b454c22f027da5acc44f20f51a531e2e5") (:authors ("Fredrik Axelsson" . "f.axelsson@gmai.com")) (:maintainer "Fredrik Axelsson" . "f.axelsson@gmai.com") (:url . "https://github.com/jobbflykt/x509-mode"))]) (x86-lookup . [(20210412 2022) ((emacs (24 3)) (cl-lib (0 3))) "jump to x86 instruction documentation" single ((:commit . "1573d61cc4457737b94624598a891c837fb52c16") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/x86-lookup"))]) (xbm-life . [(20210508 1640) ((emacs (24 1))) "A XBM version of Conway's Game of Life" single ((:commit . "ec6abb0182068294a379cb49ad5346b1d757457d") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "games") (:url . "https://depp.brause.cc/xbm-life"))]) (xcode-mode . [(20160907 1208) ((emacs (24 4)) (s (1 10 0)) (dash (2 11 0)) (multiple-cursors (1 0 0))) "A minor mode for emacs to perform Xcode like actions." single ((:commit . "2ae4f512d6c601ea39d5ab785c2b5288eac24b59") (:authors ("Nickolas Lanasa" . "nick@nytekproductions.com")) (:maintainer "Nickolas Lanasa" . "nick@nytekproductions.com") (:keywords "conveniences"))]) (xcode-project . [(20200810 2010) ((emacs (25))) "A package for reading Xcode project files." tar ((:commit . "11743f0a2212c840a108e1b905b1f20afcff8156") (:authors ("John Buckley" . "john@olivetoast.com")) (:maintainer "John Buckley" . "john@olivetoast.com") (:keywords "languages" "tools") (:url . "https://github.com/nhojb/xcode-project.git"))]) (xcscope . [(20210719 828) nil "cscope interface for (X)Emacs" single ((:commit . "d228d7593d762e457340f678d14b663ef66d7cee") (:authors ("Darryl Okahata" . "darrylo@sonic.net") ("Dima Kogan" . "dima@secretsauce.net")) (:maintainer "Dima Kogan" . "dima@secretsauce.net") (:keywords "languages" "c") (:url . "https://github.com/dkogan/xcscope.el"))]) - (xenops . [(20220421 1320) ((emacs (26 1)) (aio (1 0)) (auctex (12 2 0)) (avy (0 5 0)) (dash (2 18 0)) (f (0 20 0)) (s (1 12 0))) "A LaTeX editing environment for mathematical documents" tar ((:commit . "a2c685b3bb2257da49af771caa02325aa41fa699") (:authors ("Dan Davison" . "dandavison7@gmail.com")) (:maintainer "Dan Davison" . "dandavison7@gmail.com") (:url . "https://github.com/dandavison/xenops"))]) + (xenops . [(20220821 1111) ((emacs (26 1)) (aio (1 0)) (auctex (12 2 0)) (avy (0 5 0)) (dash (2 18 0)) (f (0 20 0)) (s (1 12 0))) "A LaTeX editing environment for mathematical documents" tar ((:commit . "4d75c1cd5ee7afba62af3a39a1f43432b295c29c") (:authors ("Dan Davison" . "dandavison7@gmail.com")) (:maintainer "Dan Davison" . "dandavison7@gmail.com") (:url . "https://github.com/dandavison/xenops"))]) (xhair . [(20210801 222) ((emacs (24 3)) (vline (1 0))) "Highlight the current line and column" single ((:commit . "c7bd7c501c3545aa99dadac386c882fe7c5edd9c") (:keywords "convenience" "faces" "maint") (:url . "https://github.com/Boruch-Baum/emacs-xhair"))]) (xkcd . [(20220503 1109) ((json (1 3))) "View xkcd from Emacs" single ((:commit . "80011da2e7def8f65233d4e0d790ca60d287081d") (:authors ("Vibhav Pant" . "vibhavp@gmail.com")) (:maintainer "Vibhav Pant" . "vibhavp@gmail.com") (:keywords "xkcd" "webcomic") (:url . "https://github.com/vibhavp/emacs-xkcd"))]) (xmind-org . [(20201202 1605) ((emacs (27 1)) (org-ml (5 3)) (dash (2 12))) "Import XMind mindmaps into Org" single ((:commit . "ee09e382b3fefb67ccf3cd4db96a8dd2acc34045") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "outlines" "wp" "files") (:url . "https://github.com/akirak/xmind-org-el"))]) @@ -5166,12 +5183,12 @@ (yaml . [(20220720 2359) ((emacs (25 1))) "YAML parser for Elisp" single ((:commit . "73fde9d8fbbaf2596449285df9eb412ae9dd74d9") (:authors ("Zachary Romero" . "zkry@posteo.org")) (:maintainer "Zachary Romero" . "zkry@posteo.org") (:keywords "tools") (:url . "https://github.com/zkry/yaml.el"))]) (yaml-imenu . [(20220406 1703) ((emacs (24 4)) (yaml-mode (0))) "Enhancement of the imenu support in yaml-mode." tar ((:commit . "c1fbba8b03a7bef4fc2b87404914fa9c6eb67b55") (:authors ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainer "Akinori MUSHA" . "knu@iDaemons.org") (:keywords "outlining" "convenience" "imenu") (:url . "https://github.com/knu/yaml-imenu.el"))]) (yaml-mode . [(20220104 1503) ((emacs (24 1))) "Major mode for editing YAML files" single ((:commit . "535273d5a1eb76999d20afbcf4d9f056d8ffd2da") (:authors ("Yoshiki Kurihara" . "clouder@gmail.com") ("Marshall T. Vandegrift" . "llasram@gmail.com")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "data" "yaml") (:url . "https://github.com/yoshiki/yaml-mode"))]) - (yaml-pro . [(20220722 334) ((emacs (26 1)) (yaml (0 5 1))) "Parser-aided YAML editing features" tar ((:commit . "3e698c625c716a1f85e64b9b839241cb56f0db92") (:authors ("Zachary Romero")) (:maintainer "Zachary Romero") (:keywords "tools") (:url . "https://github.com/zkry/yaml-pro"))]) + (yaml-pro . [(20220722 334) ((emacs (26 1)) (yaml (0 5 1))) "Parser-aided YAML editing features" tar ((:commit . "4f2b032a75871b1ece2c465ca41fd54e615e4d25") (:authors ("Zachary Romero")) (:maintainer "Zachary Romero") (:keywords "tools") (:url . "https://github.com/zkry/yaml-pro"))]) (yaml-tomato . [(20151123 753) ((s (1 9))) "copy or show the yaml path currently under cursor." single ((:commit . "f9df1c9bdfcec629b03031b2d2032f9dc533cb14") (:authors ("qrczeno")) (:maintainer "qrczeno") (:keywords "yaml"))]) (yang-mode . [(20190507 724) nil "major mode for editing YANG files" single ((:commit . "4b4ab4d4a79d37d6c31c6ea7cccbc425e0b1eded") (:authors ("Martin Bjorklund" . "mbj4668@gmail.com")) (:maintainer "Martin Bjorklund" . "mbj4668@gmail.com"))]) (yankpad . [(20220201 2104) ((emacs (25 1))) "Paste snippets from an org-mode file" single ((:commit . "927e6d26956ac7219b8a69d641acf486854fba16") (:authors ("Erik Sjöstrand")) (:maintainer "Erik Sjöstrand") (:keywords "abbrev" "convenience") (:url . "http://github.com/Kungsgeten/yankpad"))]) (yapfify . [(20210914 634) nil "(automatically) format python buffers using YAPF." single ((:commit . "c9347e3b1dec5fc8d34883e206fcdc8500d22368") (:authors ("Joris Engbers" . "info@jorisengbers.nl")) (:maintainer "Joris Engbers" . "info@jorisengbers.nl") (:url . "https://github.com/JorisE/yapfify"))]) - (yara-mode . [(20220317 935) ((emacs (24))) "Major mode for editing yara rule file" single ((:commit . "4c959b300ce52665c92e04e524dda5ed051c34f3") (:authors (nil . "binjo.cn@gmail.com")) (:maintainer nil . "binjo.cn@gmail.com") (:keywords "yara") (:url . "not distributed yet"))]) + (yara-mode . [(20220317 935) ((emacs (24))) "Major mode for editing yara rule file" single ((:commit . "8a9738f2d4b3454a01c755ca690cdef881f12843") (:authors (nil . "binjo.cn@gmail.com")) (:maintainer nil . "binjo.cn@gmail.com") (:keywords "yara") (:url . "not distributed yet"))]) (yard-mode . [(20170817 1237) nil "Minor mode for Ruby YARD comments" single ((:commit . "ba74a47463b0320ae152bd42a7dd7aeecd7b5748") (:authors ("Kyle Hargraves")) (:maintainer "Kyle Hargraves") (:url . "https://github.com/pd/yard-mode.el"))]) (yari . [(20151128 739) nil "Yet Another RI interface for Emacs" single ((:commit . "a2cb9656ee5dfe1fc2ee3854f3079a1c8e85dbe9") (:authors ("Aleksei Gusev" . "aleksei.gusev@gmail.com")) (:maintainer "Aleksei Gusev" . "aleksei.gusev@gmail.com") (:keywords "tools"))]) (yarn-mode . [(20200208 2332) ((emacs (24 3))) "Major mode for yarn.lock files." single ((:commit . "8239d4dc7d8a52fa1e3fa81bd32c904a359fcfc1") (:authors ("Nicolás Salas V." . "nikosalas@gmail.com")) (:maintainer "Nicolás Salas V." . "nikosalas@gmail.com") (:keywords "convenience") (:url . "https://github.com/anachronic/yarn-mode"))]) @@ -5199,7 +5216,7 @@ (zeal-at-point . [(20180131 2354) nil "Search the word at point with Zeal" single ((:commit . "0fc3263f44e95acd3e9d91057677621ce4d297ee") (:authors ("Jinzhu" . "wosmvp@gmail.com")) (:maintainer "Jinzhu" . "wosmvp@gmail.com") (:url . "https://github.com/jinzhu/zeal-at-point"))]) (zen-and-art-theme . [(20120622 1437) nil "zen and art color theme for GNU Emacs 24" single ((:commit . "a7226cbce0bca2501d69a620cb2aeabfc396c232") (:authors ("Nick Parker")) (:maintainer "Nick Parker"))]) (zen-mode . [(20200609 822) ((emacs (24 3))) "A major mode for the Zen programming language" single ((:commit . "c1b1806358f3cce6c04b30699987d82dc7d42559") (:authors ("Andrea Orru , Andrew Kelley , kristopher tate , Yoshitaka Takemoto" . "yt.3b8@connectfree.co.jp")) (:maintainer "Andrea Orru , Andrew Kelley , kristopher tate , Yoshitaka Takemoto" . "yt.3b8@connectfree.co.jp") (:keywords "zen" "languages") (:url . "https://github.com/zenlang/zen-mode"))]) - (zenburn-theme . [(20220710 623) nil "A low contrast color theme for Emacs." single ((:commit . "cff73bfea8deef2c97cc1ceac4b03702702c4a83") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.com")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.com") (:url . "http://github.com/bbatsov/zenburn-emacs"))]) + (zenburn-theme . [(20220823 442) nil "A low contrast color theme for Emacs." single ((:commit . "2db3a34f50ec4dd6e2cae92bab639ccfc742b3cc") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.com")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.com") (:url . "http://github.com/bbatsov/zenburn-emacs"))]) (zencoding-mode . [(20140213 822) nil "Unfold CSS-selector-like expressions to markup" single ((:commit . "58e42af182c98cb9941d27cd042d227fbf4e146c") (:authors ("Chris Done" . "chrisdone@gmail.com")) (:maintainer "Chris Done" . "chrisdone@gmail.com") (:keywords "convenience") (:url . "https://github.com/rooney/zencoding"))]) (zenity-color-picker . [(20160302 1154) ((emacs (24 4))) "Insert and adjust colors using Zenity" single ((:commit . "4f4f46676a461ebc881487fb70c8c181e323db5e") (:authors ("Samuel Laurén" . "samuel.lauren@iki.fi")) (:maintainer "Samuel Laurén" . "samuel.lauren@iki.fi") (:keywords "colors") (:url . "https://bitbucket.org/Soft/zenity-color-picker.el"))]) (zeno-theme . [(20211205 2148) ((emacs (24))) "A dark theme using different shades of blue" single ((:commit . "70fa7b7442f24ea25eab538b5a22da690745fef5") (:authors ("Bharat Joshi" . "jbharat@outlook.com")) (:maintainer "Bharat Joshi" . "jbharat@outlook.com") (:keywords "faces" "theme" "dark" "blue") (:url . "https://github.com/jbharat/zeno-theme"))]) @@ -5213,14 +5230,14 @@ (zetteldesk-kb . [(20220703 1648) ((zetteldesk (1 0 1)) (hydra (0 15)) (major-mode-hydra (0 2)) (emacs (24 1))) "Keybindings for zetteldesk.el" single ((:commit . "b9367a738628dbb569ab878b65240a567eadaaf6") (:authors ("Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com")) (:maintainer "Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com") (:url . "https://github.com/Vidianos-Giannitsis/zetteldesk-kb.el"))]) (zetteldesk-ref . [(20220619 2028) ((zetteldesk (1 0)) (bibtex-completion (1 0)) (emacs (26 1))) "A zetteldesk extension for interfacing with literature nodes" single ((:commit . "b9367a738628dbb569ab878b65240a567eadaaf6") (:authors ("Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com")) (:maintainer "Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com") (:url . "https://github.com/Vidianos-Giannitsis/zetteldesk-ref.el"))]) (zetteldesk-remark . [(20220626 1100) ((zetteldesk (1 0)) (org-remark (1 0)) (emacs (27 2))) "Org-Remark integration for zetteldesk.el" single ((:commit . "b9367a738628dbb569ab878b65240a567eadaaf6") (:authors ("Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com")) (:maintainer "Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com") (:url . "https://github.com/Vidianos-Giannitsis/zetteldesk-remark.el"))]) - (zettelkasten . [(20220727 1137) ((emacs (25 1)) (s (1 10 0))) "Helper functions to organise notes in a Zettelkasten style" single ((:commit . "edba7bcfdc054ad0ff1952bb525f5709a687db25") (:authors ("Yann Herklotz" . "yann@ymhg.org")) (:maintainer "Yann Herklotz" . "yann@ymhg.org") (:keywords "files" "hypermedia" "notes") (:url . "https://github.com/ymherklotz/emacs-zettelkasten"))]) + (zettelkasten . [(20220819 2351) ((emacs (25 1)) (s (1 10 0))) "Helper functions to organise notes in a Zettelkasten style" single ((:commit . "505fd41dea012e743962c3a376c1e63e7a1e127e") (:authors ("Yann Herklotz" . "yann@ymhg.org")) (:maintainer "Yann Herklotz" . "yann@ymhg.org") (:keywords "files" "hypermedia" "notes") (:url . "https://github.com/ymherklotz/emacs-zettelkasten"))]) (zetz-mode . [(20200823 536) ((emacs (25 1)) (dash (2 17 0)) (hydra (0 15 0))) "A major mode for the ZetZ programming language" single ((:commit . "04da33f4ffa9db5b3556f423276f4fd1db13ec67") (:keywords "languages" "programming") (:url . "https://github.com/damon-kwok/zetz-mode"))]) (zig-mode . [(20220521 1148) ((emacs (24 3))) "A major mode for the Zig programming language" single ((:commit . "dbc648f5bca8f3b9ca2cc7827f326f5530115144") (:authors ("Andrea Orru , Andrew Kelley" . "superjoe30@gmail.com")) (:maintainer "Andrea Orru , Andrew Kelley" . "superjoe30@gmail.com") (:keywords "zig" "languages") (:url . "https://github.com/zig-lang/zig-mode"))]) (zim-wiki-mode . [(20211117 2000) ((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" single ((:commit . "aa906931f22c34d77c65bed31121edfef714e4e2") (:authors ("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" single ((:commit . "76cf76bdc871cb0454a6fc555aeb1aa94f1b6e57") (:url . "https://github.com/schmir/zimports.el"))]) - (zk . [(20220722 1626) ((emacs (24 4))) "Functions for working with Zettelkasten-style linked notes" single ((:commit . "d997b13e7a03f7c3c7411183641bd0dc89526ec9") (:authors ("Grant Rosson ")) (:maintainer "Grant Rosson ") (:url . "https://github.com/localauthor/zk"))]) - (zk-index . [(20220723 1824) ((emacs (27 1)) (zk (0 3))) "Index and Desktop for zk" single ((:commit . "d997b13e7a03f7c3c7411183641bd0dc89526ec9") (:authors ("Grant Rosson ")) (:maintainer "Grant Rosson ") (:url . "https://github.com/localauthor/zk"))]) - (zk-luhmann . [(20220713 1653) ((emacs (24 4)) (zk (0 4)) (zk-index (0 6))) "Support for Luhmann-style IDs in zk" single ((:commit . "566fa6a5933455a3f42f700bc91f9f9958878861") (:authors ("Grant Rosson ")) (:maintainer "Grant Rosson ") (:url . "https://github.com/localauthor/zk-luhmann"))]) + (zk . [(20220820 1139) ((emacs (25 1))) "Functions for working with Zettelkasten-style linked notes" single ((:commit . "843e33acaa8e0a2caa8cd5fbbcc7ab66693efe06") (:authors ("Grant Rosson ")) (:maintainer "Grant Rosson ") (:url . "https://github.com/localauthor/zk"))]) + (zk-index . [(20220821 1327) ((emacs (27 1)) (zk (0 3))) "Index and Desktop for zk" single ((:commit . "843e33acaa8e0a2caa8cd5fbbcc7ab66693efe06") (:authors ("Grant Rosson ")) (:maintainer "Grant Rosson ") (:url . "https://github.com/localauthor/zk"))]) + (zk-luhmann . [(20220820 1643) ((emacs (25 1)) (zk (0 4)) (zk-index (0 6))) "Support for Luhmann-style IDs in zk" single ((:commit . "304536486ccae8129dd681265d199062f2026891") (:authors ("Grant Rosson ")) (:maintainer "Grant Rosson ") (:url . "https://github.com/localauthor/zk-luhmann"))]) (zlc . [(20151011 157) nil "Provides zsh like completion system to Emacs" single ((:commit . "4dd2ba267ecdeac845a7cbb3147294ee7daa25f4") (:authors ("mooz" . "stillpedant@gmail.com")) (:maintainer "mooz" . "stillpedant@gmail.com") (:keywords "matching" "convenience"))]) (zmq . [(20220510 1820) ((cl-lib (0 5)) (emacs (26))) "ZMQ bindings in elisp" tar ((:commit . "af5299d80715b1083a18145e9c84ef9563020676") (:authors ("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" single ((:commit . "6f0949c393b7778a96033716787d152ada32f705") (:authors ("Yaroslav Shirokov")) (:maintainer "Yaroslav Shirokov") (:url . "https://github.com/sshirokov/ZNC.el"))]) diff --git a/code/elpa/doom-modeline-20220816.1627/doom-modeline-autoloads.el b/code/elpa/doom-modeline-20220816.1627/doom-modeline-autoloads.el new file mode 100644 index 0000000..97a3a40 --- /dev/null +++ b/code/elpa/doom-modeline-20220816.1627/doom-modeline-autoloads.el @@ -0,0 +1,132 @@ +;;; doom-modeline-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "doom-modeline" "doom-modeline.el" (0 0 0 0)) +;;; Generated autoloads from doom-modeline.el + +(autoload 'doom-modeline-set-main-modeline "doom-modeline" "\ +Set main mode-line. +If DEFAULT is non-nil, set the default mode-line for all buffers. + +\(fn &optional DEFAULT)" nil nil) + +(autoload 'doom-modeline-set-minimal-modeline "doom-modeline" "\ +Set minimal mode-line." nil nil) + +(autoload 'doom-modeline-set-special-modeline "doom-modeline" "\ +Set special mode-line." nil nil) + +(autoload 'doom-modeline-set-project-modeline "doom-modeline" "\ +Set project mode-line." nil nil) + +(autoload 'doom-modeline-set-dashboard-modeline "doom-modeline" "\ +Set dashboard mode-line." nil nil) + +(autoload 'doom-modeline-set-vcs-modeline "doom-modeline" "\ +Set vcs mode-line." nil nil) + +(autoload 'doom-modeline-set-info-modeline "doom-modeline" "\ +Set Info mode-line." nil nil) + +(autoload 'doom-modeline-set-package-modeline "doom-modeline" "\ +Set package mode-line." nil nil) + +(autoload 'doom-modeline-set-media-modeline "doom-modeline" "\ +Set media mode-line." nil nil) + +(autoload 'doom-modeline-set-message-modeline "doom-modeline" "\ +Set message mode-line." nil nil) + +(autoload 'doom-modeline-set-pdf-modeline "doom-modeline" "\ +Set pdf mode-line." nil nil) + +(autoload 'doom-modeline-set-org-src-modeline "doom-modeline" "\ +Set org-src mode-line." nil nil) + +(autoload 'doom-modeline-set-helm-modeline "doom-modeline" "\ +Set helm mode-line. + +\(fn &rest _)" nil nil) + +(autoload 'doom-modeline-set-timemachine-modeline "doom-modeline" "\ +Set timemachine mode-line." nil nil) + +(defvar doom-modeline-mode nil "\ +Non-nil if Doom-Modeline mode is enabled. +See the `doom-modeline-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `doom-modeline-mode'.") + +(custom-autoload 'doom-modeline-mode "doom-modeline" nil) + +(autoload 'doom-modeline-mode "doom-modeline" "\ +Toggle `doom-modeline' on or off. + +This is a minor mode. If called interactively, toggle the +`Doom-Modeline mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='doom-modeline-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "doom-modeline" '("doom-modeline-mode-map")) + +;;;*** + +;;;### (autoloads nil "doom-modeline-core" "doom-modeline-core.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from doom-modeline-core.el + +(register-definition-prefixes "doom-modeline-core" '("doom-modeline")) + +;;;*** + +;;;### (autoloads nil "doom-modeline-env" "doom-modeline-env.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from doom-modeline-env.el + (autoload 'doom-modeline-env-setup-python "doom-modeline-env") + (autoload 'doom-modeline-env-setup-ruby "doom-modeline-env") + (autoload 'doom-modeline-env-setup-perl "doom-modeline-env") + (autoload 'doom-modeline-env-setup-go "doom-modeline-env") + (autoload 'doom-modeline-env-setup-elixir "doom-modeline-env") + (autoload 'doom-modeline-env-setup-rust "doom-modeline-env") + +(register-definition-prefixes "doom-modeline-env" '("doom-modeline-")) + +;;;*** + +;;;### (autoloads nil "doom-modeline-segments" "doom-modeline-segments.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from doom-modeline-segments.el + +(register-definition-prefixes "doom-modeline-segments" '("doom-modeline-")) + +;;;*** + +;;;### (autoloads nil nil ("doom-modeline-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; doom-modeline-autoloads.el ends here diff --git a/code/elpa/doom-modeline-20220816.1627/doom-modeline-core.el b/code/elpa/doom-modeline-20220816.1627/doom-modeline-core.el new file mode 100644 index 0000000..b732a21 --- /dev/null +++ b/code/elpa/doom-modeline-20220816.1627/doom-modeline-core.el @@ -0,0 +1,1378 @@ +;;; doom-modeline-core.el --- The core libraries for doom-modeline -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2020 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 . +;; + +;;; Commentary: +;; +;; The core libraries for doom-modeline. +;; + +;;; Code: + +(require 'cl-lib) +(require 'subr-x) + +(require 'compat) +(require 'shrink-path) + +(require 'all-the-icons nil t) + + +;; +;; Externals +;; + +(declare-function all-the-icons--function-name "ext:all-the-icons") + + +;; +;; Optimization +;; + +;; Don’t compact font caches during GC. +(when (eq system-type 'windows-nt) + (setq inhibit-compacting-font-caches t)) + +;; WORKAROUND: `string-pixel-width' is introduced in 29, +;; and is able to calculate the accurate string width. +;; Below is the workaround for backward compatibility +;; since `window-font-width' consumes a lot. +(defvar doom-modeline--font-width-cache nil) +(defun doom-modeline--font-width () + "Cache the font width for better performance." + (if (display-graphic-p) + (let ((attributes (face-all-attributes 'mode-line))) + (or (cdr (assoc attributes doom-modeline--font-width-cache)) + (let ((width (window-font-width nil 'mode-line))) + (push (cons attributes width) doom-modeline--font-width-cache) + width))) + 1)) + +;; Refresh the font width after setting frame parameters +;; to ensure the font width is correct. +(defun doom-modeline-refresh-font-width-cache (&rest _) + "Refresh the font width cache." + (setq doom-modeline--font-width-cache nil) + (doom-modeline--font-width)) + +(unless (fboundp 'string-pixel-width) + (add-hook 'window-setup-hook #'doom-modeline-refresh-font-width-cache) + (add-hook 'after-make-frame-functions #'doom-modeline-refresh-font-width-cache) + (add-hook 'after-setting-font-hook #'doom-modeline-refresh-font-width-cache) + (add-hook 'server-after-make-frame-hook #'doom-modeline-refresh-font-width-cache)) + + +;; +;; 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 25 + "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 + buffer-name => comint.el<2> (uniquify buffer name)" + :type '(choice (const auto) + (const truncate-upto-project) + (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 buffer-name)) + :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 variable `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 `all-the-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 variable `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 variable `doom-modeline-icon' and `doom-modeline-buffer-state-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-time-icon t + "Whether display the time icon. + +It respects variable `doom-modeline-icon'." + :type 'boolean + :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-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) + +;; 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-checker-simple-format t + "If non-nil, only display one number for checker 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-vcs-max-length 12 + "The maximum displayed length of the branch name of version control." + :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." + :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-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-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 'identity + "Function to stylize the irc buffer names." + :type 'function + :group 'doom-modeline) + +(defcustom doom-modeline-time t + "Whether display the time. + +It respects `display-time-mode'." + :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-emphasis + '((t (:inherit mode-line-emphasis))) + "Face used for emphasis." + :group 'doom-modeline-faces) + +(defface doom-modeline-highlight + '((t (:inherit 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 (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 (error bold) :background nil))) + "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 font-lock-doc-face :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 (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 (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 italic))) + "Face for remote hosts in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-input-method + '((t (:inherit (doom-modeline-emphasis bold)))) + "Face for input method in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-input-method-alt + '((t (:inherit (font-lock-doc-face bold) :slant normal))) + "Alternative face for input method in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-debug + '((t (:inherit (font-lock-doc-face bold) :slant normal))) + "Face for debug-level messages in the mode-line. Used by vcs, checker, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-info + '((t (:inherit (success bold)))) + "Face for info-level messages in the mode-line. Used by vcs, checker, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-warning + '((t (:inherit (warning bold)))) + "Face for warnings in the mode-line. Used by vcs, checker, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-urgent + '((t (:inherit (error bold)))) + "Face for errors in the mode-line. Used by vcs, checker, 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 (:slant italic :weight normal))) + "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 (:background ,(face-foreground 'mode-line-inactive)))) + "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") + (((background dark)) :foreground "#915B2D")) + "Face to use for the mode-line while debugging." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-emacs-state + '((t (:inherit (font-lock-builtin-face bold)))) + "Face for the Emacs state tag in evil state indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-insert-state + '((t (:inherit (font-lock-keyword-face bold)))) + "Face for the insert state tag in evil state indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-motion-state + '((t :inherit (font-lock-doc-face bold) :slant normal)) + "Face for the motion state tag in evil state indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-normal-state + '((t (:inherit doom-modeline-info))) + "Face for the normal state tag in evil state indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-operator-state + '((t (:inherit doom-modeline-buffer-file))) + "Face for the operator state tag in evil state indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-visual-state + '((t (:inherit doom-modeline-warning))) + "Face for the visual state tag in evil state indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-replace-state + '((t (:inherit doom-modeline-urgent))) + "Face for the replace state tag in evil state indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-persp-name + '((t (:inherit (font-lock-comment-face italic)))) + "Face for the persp name." + :group 'doom-modeline-faces) + +(defface doom-modeline-persp-buffer-not-in-persp + '((t (:inherit (font-lock-doc-face bold italic)))) + "Face for the buffers which are not in the persp." + :group 'doom-modeline-faces) + +(defface doom-modeline-repl-success + '((t (:inherit success :weight normal))) + "Face for REPL success state." + :group 'doom-modeline-faces) + +(defface doom-modeline-repl-warning + '((t (:inherit warning :weight normal))) + "Face for REPL warning state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-success + '((t (:inherit success :weight normal))) + "Face for LSP success state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-warning + '((t (:inherit warning :weight normal))) + "Face for LSP warning state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-error + '((t (:inherit error :weight normal))) + "Face for LSP error state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-running + '((t (:inherit compilation-mode-line-run :weight normal :slant normal))) + "Face for LSP running state." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-charging + '((t (:inherit success :weight normal))) + "Face for battery charging status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-full + '((t (:inherit success :weight normal))) + "Face for battery full status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-normal + '((t (:inherit mode-line :weight normal))) + "Face for battery normal status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-warning + '((t (:inherit warning :weight normal))) + "Face for battery warning status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-critical + '((t (:inherit error :weight normal))) + "Face for battery critical status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-error + '((t (:inherit error :weight normal))) + "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 (mode-line-buffer-id bold)))) + "Face for display time." + :group 'doom-modeline-faces) + +;; +;; Externals +;; + +(declare-function face-remap-remove-relative "face-remap") +(declare-function project-root "project") +(declare-function ffip-get-project-root-directory "ext:find-file-in-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 +(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))) +(unless (>= emacs-major-version 29) + (advice-add #'fit-window-to-buffer :before #'doom-modeline-redisplay)) + +(defun doom-modeline-icon-displayable-p () + "Return non-nil if icons are displayable." + (and doom-modeline-icon + (display-graphic-p) + (featurep 'all-the-icons))) + +;; Keep `doom-modeline-current-window' up-to-date +(defun doom-modeline--get-current-window (&optional frame) + "Get the current window but should exclude the child windows. + +If FRAME is nil, it means the current frame." + (if (and (fboundp 'frame-parent) (frame-parent frame)) + (frame-selected-window (frame-parent frame)) + (frame-selected-window frame))) + +(defvar doom-modeline-current-window (doom-modeline--get-current-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--get-current-window) doom-modeline-current-window)))) + +(defun doom-modeline-set-selected-window (&rest _) + "Set `doom-modeline-current-window' appropriately." + (let ((win (doom-modeline--get-current-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 + 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)) +(dolist (face (face-list)) + (let ((f (symbol-name face))) + (and + (string-match-p "^\\(doom-modeline\\|all-the-icons\\)" f) + (not (string-match-p "\\(-inactive\\|-dired\\|-ivy\\|-ibuffer\\)" f)) + (add-to-list 'doom-modeline--remap-faces 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)) + (doom-modeline-focus) + (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))) + `(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body)))) + (t + (add-to-list 'doom-modeline-fn-alist (cons name sym)) + `(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 (cdr (assq seg doom-modeline-fn-alist))) + (push (list :eval (list it)) forms)) + ((setq it (cdr (assq 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 + (propertize + " " + 'display `((space + :align-to + (- (+ right right-margin scroll-bar) + ,(let ((rhs-str (format-mode-line (cons "" rhs-forms))) + (char-width (frame-char-width))) + (if (fboundp 'string-pixel-width) + ;; Accurate calculations in 29+ + (/ (string-pixel-width + (propertize rhs-str 'face 'mode-line)) + char-width + 1.0) + ;; Backward compatibility + (* (/ (doom-modeline--font-width) + char-width + 1.0) + (string-width rhs-str)))))))) + 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) + (buffer-local-value 'mode-line-format (current-buffer))) + (list "%e" modeline)))) + + +;; +;; Helpers +;; + +(defconst doom-modeline-spc " " "Whitespace.") +(defconst doom-modeline-wspc " " "Wide whitespace.") +(defconst doom-modeline-vspc + (propertize " " 'display '((space :relative-width 0.5))) + "Thin whitespace.") + +(defconst doom-modeline-ellipsis + (if (char-displayable-p ?…) "…" "...") + "Ellipsis.") + +(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) face) + (and (facep 'mode-line-active) 'mode-line-active) + 'mode-line) + (or (and (facep inactive-face) inactive-face) + 'mode-line-inactive))) + +;; Since 27, the calculation of char height was changed +;; @see https://github.com/seagle0128/doom-modeline/issues/271 +(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 + (* (pcase system-type + ('darwin (if doom-modeline-icon 1.7 1.0)) + ('windows-nt (if doom-modeline-icon 0.88 0.625)) + (_ (if (and doom-modeline-icon (< emacs-major-version 27)) 1.4 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." + (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 ,(or face inherit props 'mode-line) + :family ,(or family "") + :height ,(or height 1.0)))))) + (propertize icon 'face 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 `octicon', `faicon', `material', `alltheicons' and `fileicon', +etc. +UNICODE is the unicode char fallback. TEXT is the ASCII char fallback. +ARGS is same as `all-the-icons-octicon' and others." + (let ((face (or (plist-get args :face) 'mode-line))) + (cond + ;; Icon + ((and (doom-modeline-icon-displayable-p) + icon-name + (not (string-empty-p icon-name))) + (when-let* ((func (all-the-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-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 'mode-line-inactive))) + +(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 (display-graphic-p) + (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 :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 +(defvar-local doom-modeline--limited-width-p nil) +(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-get-project-root-directory)) + (let ((inhibit-message t)) + (ffip-get-project-root-directory))) + ((and (memq doom-modeline-project-detection '(auto projectile)) + (or (fboundp 'projectile-project-root) + (require 'projectile nil t))) + (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) + (cdr project))))))))) + +(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." + (or (doom-modeline--project-root) default-directory)) + +(defun doom-modeline-buffer-file-name () + "Propertize file name based on `doom-modeline-buffer-file-name-style'." + (let* ((buffer-file-name (file-local-name (or (buffer-file-name (buffer-base-buffer)) ""))) + (buffer-file-truename (file-local-name + (or buffer-file-truename (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 'shink 'hide)) + ('truncate-except-project + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shink)) + ('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)) + ((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. + +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 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/code/elpa/doom-modeline-20220816.1627/doom-modeline-env.el b/code/elpa/doom-modeline-20220816.1627/doom-modeline-env.el new file mode 100644 index 0000000..4a3c3f2 --- /dev/null +++ b/code/elpa/doom-modeline-20220816.1627/doom-modeline-env.el @@ -0,0 +1,275 @@ +;;; doom-modeline-env.el --- A environment parser for doom-modeline -*- lexical-binding: t -*- + +;; Copyright (C) 2019-2020 Justin Barclay, 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 . +;; +;;; Commentary: +;; +;; Parse programming environment. +;; + +;;; Code: + +(require 'subr-x) +(require 'doom-modeline-core) + + +;; Externals +(defvar python-shell-interpreter) + + +;; Customization + +(defgroup doom-modeline-env nil + "The environment parser for `doom-modeline'." + :group 'doom-modeline + :link '(url-link :tag "Homepage" "https://github.com/seagle0128/doom-modeline")) + +(defcustom doom-modeline-env-load-string doom-modeline-ellipsis + "What to display as the version while a new one is being loaded." + :type 'string + :group 'doom-modeline-env) + +(defcustom doom-modeline-before-update-env-hook nil + "Hooks that run before the modeline version string is updated." + :type 'hook + :group 'doom-modeline-env) + +(defcustom doom-modeline-after-update-env-hook nil + "Hooks that run after the modeline version string is updated." + :type 'hook + :group 'doom-modeline-env) + + +;; Variables + +;; Show version string for multi-version managers like rvm, rbenv, pyenv, etc. +(defvar-local doom-modeline-env--version nil + "The version to display with major-mode in mode-line. +Example: \"2.6.0\"") + +(defvar-local doom-modeline-env--command nil + "A program that we're looking to extract version information from. +Example: \"ruby\"") + +(defvar-local doom-modeline-env--command-args nil + "A list of arguments for the command to extract the version from. +Example: \\='(\"--version\")") + +(defvar-local doom-modeline-env--parser nil + "A function that returns version number from a command --version (or similar). +Example: \\='doom-modeline-env--ruby") + + +;; Functions & Macros + +(defun doom-modeline-update-env () + "Update environment info on mode-line." + (when (and doom-modeline-env-version + doom-modeline-env--command + (executable-find doom-modeline-env--command) + doom-modeline-env--command-args + doom-modeline-env--parser) + (let ((default-directory (doom-modeline-project-root)) + (buffer (current-buffer))) + (run-hooks 'doom-modeline-before-update-env-hook) + (setq doom-modeline-env--version doom-modeline-env-load-string) + (doom-modeline-env--get + doom-modeline-env--command + doom-modeline-env--command-args + (lambda (prog-version) + (with-current-buffer buffer + (setq doom-modeline-env--version + (funcall doom-modeline-env--parser prog-version)) + (run-hooks 'doom-modeline-after-update-env-hook))))))) + +(add-hook 'find-file-hook #'doom-modeline-update-env) +(with-no-warnings + (if (boundp 'after-focus-change-function) + (add-function + :after after-focus-change-function + (lambda () + (if (frame-focus-state) + (doom-modeline-update-env)))) + (add-hook 'focus-in-hook #'doom-modeline-update-env))) + +(defun doom-modeline-env--get (prog args callback) + "Start a sub process using PROG and apply the ARGS to the sub process. +Once it receives information from STDOUT, it closes off the subprocess and +passes on the information into the CALLBACK. +Example: + (doom-modeline-env--get + \"ruby\" + \\='(\"--version\") + (lambda (line) + (message (doom-modeline-parser--ruby line)))" + (let ((proc (apply 'start-process + ;; Flaten process-args into a single list so we can handle + ;; variadic length args + (append + (list "doom-modeline-env" nil prog) + args))) + (parser callback)) + (set-process-filter proc + (lambda (_proc line) + (ignore-errors + (funcall parser line)))))) + +(cl-defmacro doom-modeline-def-env (name &key hooks command parser) + "Define a handler for updating & displaying a version string for a language. + +NAME is an unquoted symbol representing the handler's unique ID. +HOOKS is a list of hook symbols where this handler should be triggered. +COMMAND should be a function that returns a shell command and its arguments (as + a list). It is run on HOOKS. It takes no arguments. +PARSER should be a function for parsing COMMAND's output line-by-line, to + extract the version string." + (declare (indent defun)) + (unless (and hooks command parser) + (error "'%s' env is missing either :hooks, :command or :parser" name)) + (let ((parse-fn (intern (format "doom-modeline-env--%s-parse" name))) + (action-fn (intern (format "doom-modeline-env--%s-args" name))) + (setup-fn (intern (format "doom-modeline-env-setup-%s" name))) + (update-fn (intern (format "doom-modeline-env-update-%s" name))) + (enable-var (intern (format "doom-modeline-env-enable-%s" name))) + (command-var (intern (format "doom-modeline-env-%s-command" name))) + (parser-var (intern (format "doom-modeline-env-%s-parser-fn" name))) + (exe-var (intern (format "doom-modeline-env-%s-executable" name)))) + (macroexp-progn + `((defcustom ,enable-var t + ,(format "Whether to display the version string for %s buffers." name) + :type 'boolean + :group 'doom-modeline-env) + (defvar ,command-var ',action-fn + ,(concat "A function that returns the shell command and arguments (as a list) to\n" + "produce a version string.")) + (defvar ,parser-var ',parse-fn + ,(format "The function to parse each line of `%s'\'s output." command-var)) + (defcustom ,exe-var nil + ,(format (concat "What executable to use for the version indicator in %s buffers.\n\n" + "If nil, the default binary for this language is used.") + name) + :type 'string + :group 'doom-modeline-env) + (defalias ',parse-fn ,parser + (format "The line parser for %s buffers.\n\nUsed by `%s'." + ',name ',update-fn)) + (defalias ',action-fn ,command + (format "The command resolver for %s buffers.\n\nUsed by `%s'." + ',name ',update-fn)) + (defalias ',setup-fn + (lambda () + (if enable-local-variables + (add-hook 'hack-local-variables-hook #',update-fn nil t) + (,update-fn))) + (format "Prepares the modeline to later display the %s version string." + ',name)) + (defalias ',update-fn + (lambda () + (when ,enable-var + (when-let* ((command-list (funcall ,command-var)) + (exe (executable-find (car command-list)))) + (setq doom-modeline-env--command exe + doom-modeline-env--command-args (cdr command-list) + doom-modeline-env--parser ,parser-var) + (doom-modeline-update-env)))) + (format "Updates the %s version string in the modeline." ',name)) + (let ((hooks ',(eval hooks))) + (dolist (hook (if (listp hooks) hooks (list hooks))) + (add-hook hook #',setup-fn))))))) + + +;; Bootstrap +;; Versions, support Python, Ruby, Perl and Golang, etc. + +;;;###autoload (autoload 'doom-modeline-env-setup-python "doom-modeline-env") +(doom-modeline-def-env python + :hooks 'python-mode-hook + :command (lambda () (cond ((and (fboundp 'pipenv-project-p) + (pipenv-project-p)) + (list "pipenv" "run" + (or doom-modeline-env-python-executable + python-shell-interpreter + "python") + "--version")) + ((executable-find "pyenv") (list "pyenv" "version-name")) + ((list (or doom-modeline-env-python-executable + python-shell-interpreter + "python") + "--version")))) + :parser (lambda (line) (let ((version (split-string line))) + (if (length> version 1) + (cadr version) + (car version))))) + +;;;###autoload (autoload 'doom-modeline-env-setup-ruby "doom-modeline-env") +(doom-modeline-def-env ruby + :hooks '(ruby-mode-hook enh-ruby-mode-hook) + :command (lambda () (list (or doom-modeline-env-ruby-executable "ruby") "--version")) + :parser (lambda (line) + (car (split-string + (cadr + (split-string line)) + "p")))) + +;;;###autoload (autoload 'doom-modeline-env-setup-perl "doom-modeline-env") +(doom-modeline-def-env perl + :hooks 'perl-mode-hook + :command (lambda () (list (or doom-modeline-env-perl-executable "perl") "--version")) + :parser (lambda (line) + (cadr + (split-string + (car + (split-string + (cadr + (split-string line "(")) + ")")) + "v")))) + +;;;###autoload (autoload 'doom-modeline-env-setup-go "doom-modeline-env") +(doom-modeline-def-env go + :hooks 'go-mode-hook + :command (lambda () (list (or doom-modeline-env-go-executable "go") "version")) + :parser (lambda (line) + (cadr + (split-string + (cadr + (cdr + (split-string line))) + "go")))) + +;;;###autoload (autoload 'doom-modeline-env-setup-elixir "doom-modeline-env") +(doom-modeline-def-env elixir + :hooks 'elixir-mode-hook + :command (lambda () (list (or doom-modeline-env-elixir-executable "elixir") "--version")) + :parser (lambda (line) (cadr (split-string line)))) + +;;;###autoload (autoload 'doom-modeline-env-setup-rust "doom-modeline-env") +(doom-modeline-def-env rust + :hooks 'rust-mode-hook + :command (lambda () (list (or doom-modeline-env-rust-executable "rustc") "--version")) + :parser (lambda (line) + (car + (split-string + (cadr + (split-string line)) + "-")))) + +(provide 'doom-modeline-env) + +;;; doom-modeline-env.el ends here diff --git a/code/elpa/doom-modeline-20220816.1627/doom-modeline-pkg.el b/code/elpa/doom-modeline-20220816.1627/doom-modeline-pkg.el new file mode 100644 index 0000000..736e3e2 --- /dev/null +++ b/code/elpa/doom-modeline-20220816.1627/doom-modeline-pkg.el @@ -0,0 +1,14 @@ +(define-package "doom-modeline" "20220816.1627" "A minimal and modern mode-line" + '((emacs "25.1") + (compat "28.1.1.1") + (shrink-path "0.2.0")) + :commit "acac2409e2debfeabcc81a17b6ae67f9622d72ae" :authors + '(("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/code/elpa/doom-modeline-20220816.1627/doom-modeline-segments.el b/code/elpa/doom-modeline-20220816.1627/doom-modeline-segments.el new file mode 100644 index 0000000..331016c --- /dev/null +++ b/code/elpa/doom-modeline-20220816.1627/doom-modeline-segments.el @@ -0,0 +1,2962 @@ +;;; doom-modeline-segments.el --- The segments for doom-modeline -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2020 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 . +;; + +;;; Commentary: +;; +;; The segments for doom-modeline. +;; Use `doom-modeline-def-segment' to create a new segment. +;; + +;;; Code: + +(require 'cl-lib) +(require 'seq) +(require 'subr-x) +(require 'doom-modeline-core) +(require 'doom-modeline-env) + + +;; +;; Externals +;; + +(defvar Info-current-file) +(defvar Info-current-node) +(defvar Info-mode-line-node-keymap) +(defvar anzu--cached-count) +(defvar anzu--current-position) +(defvar anzu--overflow-p) +(defvar anzu--state) +(defvar anzu--total-matched) +(defvar anzu-cons-mode-line-p) +(defvar aw-keys) +(defvar battery-echo-area-format) +(defvar battery-load-critical) +(defvar battery-mode-line-format) +(defvar battery-mode-line-limit) +(defvar battery-status-function) +(defvar boon-command-state) +(defvar boon-insert-state) +(defvar boon-off-state) +(defvar boon-special-state) +(defvar edebug-execution-mode) +(defvar eglot--managed-mode) +(defvar erc-modified-channels-alist) +(defvar evil-ex-active-highlights-alist) +(defvar evil-ex-argument) +(defvar evil-ex-range) +(defvar evil-mc-frozen) +(defvar evil-state) +(defvar evil-visual-beginning) +(defvar evil-visual-end) +(defvar evil-visual-selection) +(defvar flycheck--automatically-enabled-checkers) +(defvar flycheck-current-errors) +(defvar flycheck-mode-menu-map) +(defvar flymake--mode-line-format) +(defvar flymake--state) +(defvar flymake-menu) +(defvar gnus-newsrc-alist) +(defvar gnus-newsrc-hashtb) +(defvar grip--process) +(defvar helm--mode-line-display-prefarg) +(defvar iedit-occurrences-overlays) +(defvar meow--indicator) +(defvar minions-mode-line-lighter) +(defvar minions-mode-line-minor-modes-map) +(defvar mlscroll-minimum-current-width) +(defvar mlscroll-right-align) +(defvar mu4e-alert-mode-line) +(defvar mu4e-alert-modeline-formatter) +(defvar nyan-minimum-window-width) +(defvar objed--obj-state) +(defvar objed--object) +(defvar objed-modeline-setup-func) +(defvar persp-nil-name) +(defvar phi-replace--mode-line-format) +(defvar phi-search--overlays) +(defvar phi-search--selection) +(defvar phi-search-mode-line-format) +(defvar poke-line-minimum-window-width) +(defvar rcirc-activity) +(defvar sml-modeline-len) +(defvar symbol-overlay-keywords-alist) +(defvar symbol-overlay-temp-symbol) +(defvar text-scale-mode-amount) +(defvar tracking-buffers) +(defvar winum-auto-setup-mode-line) +(defvar xah-fly-insert-state-p) +(defvar display-time-string) + +(declare-function all-the-icons-icon-for-buffer "ext:all-the-icons") +(declare-function anzu--reset-status "ext:anzu") +(declare-function anzu--where-is-here "ext:anzu") +(declare-function async-inject-variables "ext:async") +(declare-function async-start "ext:async") +(declare-function avy-traverse "ext:avy") +(declare-function avy-tree "ext:avy") +(declare-function aw-update "ext:ace-window") +(declare-function aw-window-list "ext:ace-window") +(declare-function battery-format "battery") +(declare-function battery-update "battery") +(declare-function boon-modeline-string "ext:boon") +(declare-function boon-state-string "ext:boon") +(declare-function cider--connection-info "ext:cider") +(declare-function cider-connected-p "ext:cider") +(declare-function cider-current-repl "ext:cider") +(declare-function cider-jack-in "ext:cider") +(declare-function cider-quit "ext:cider") +(declare-function citre-mode "ext:citre-basic-tools") +(declare-function dap--cur-session "ext:dap-mode") +(declare-function dap--debug-session-name "ext:dap-mode") +(declare-function dap--debug-session-state "ext:dap-mode") +(declare-function dap--session-running "ext:dap-mode") +(declare-function dap-debug-recent "ext:dap-mode") +(declare-function dap-disconnect "ext:dap-mode") +(declare-function dap-hydra "ext:dap-hydra") +(declare-function edebug-help "edebug") +(declare-function edebug-next-mode "edebug") +(declare-function edebug-stop "edebug") +(declare-function eglot "ext:eglot") +(declare-function eglot--major-mode "ext:eglot" t t) +(declare-function eglot--project-nickname "ext:eglot" t t) +(declare-function eglot--spinner "ext:eglot" t t) +(declare-function eglot-clear-status "ext:eglot") +(declare-function eglot-current-server "ext:eglot") +(declare-function eglot-events-buffer "ext:eglot") +(declare-function eglot-forget-pending-continuations "ext:eglot") +(declare-function eglot-managed-p "ext:glot") +(declare-function eglot-reconnect "ext:eglot") +(declare-function eglot-shutdown "ext:eglot") +(declare-function eglot-stderr-buffer "ext:eglot") +(declare-function erc-switch-to-buffer "erc") +(declare-function erc-track-switch-buffer "erc-track") +(declare-function evil-delimited-arguments "ext:evil-common") +(declare-function evil-emacs-state-p "ext:evil-states" t t) +(declare-function evil-force-normal-state "ext:evil-commands" t t) +(declare-function evil-insert-state-p "ext:evil-states" t t) +(declare-function evil-motion-state-p "ext:evil-states" t t) +(declare-function evil-normal-state-p "ext:evil-states" t t) +(declare-function evil-operator-state-p "ext:evil-states" t t) +(declare-function evil-replace-state-p "ext:evil-states" t t) +(declare-function evil-state-property "ext:evil-common") +(declare-function evil-visual-state-p "ext:evil-states" t t) +(declare-function eyebrowse--get "ext:eyebrowse") +(declare-function face-remap-remove-relative "face-remap") +(declare-function fancy-narrow-active-p "ext:fancy-narrow") +(declare-function flycheck-buffer "ext:flycheck") +(declare-function flycheck-count-errors "ext:flycheck") +(declare-function flycheck-error-level-compilation-level "ext:flycheck") +(declare-function flycheck-list-errors "ext:flycheck") +(declare-function flycheck-next-error "ext:flycheck") +(declare-function flycheck-previous-error "ext:flycheck") +(declare-function flymake--diag-type "ext:flymake" t t) +(declare-function flymake--handle-report "ext:flymake") +(declare-function flymake--lookup-type-property "ext:flymake") +(declare-function flymake--state-diags "ext:flymake" t t) +(declare-function flymake-disabled-backends "ext:flymake") +(declare-function flymake-goto-next-error "ext:flymake") +(declare-function flymake-goto-prev-error "ext:flymake") +(declare-function flymake-reporting-backends "ext:flymake") +(declare-function flymake-running-backends "ext:flymake") +(declare-function flymake-show-buffer-diagnostics "ext:flymake") +(declare-function flymake-show-diagnostics-buffer "ext:flymake") +(declare-function flymake-start "ext:flymake") +(declare-function follow-all-followers "follow") +(declare-function gnus-demon-add-handler "gnus-demon") +(declare-function grip--preview-url "ext:grip-mode") +(declare-function grip-browse-preview "ext:grip-mode") +(declare-function grip-restart-preview "ext:grip-mode") +(declare-function grip-stop-preview "ext:grip-mode") +(declare-function helm-candidate-number-at-point "ext:helm-core") +(declare-function helm-get-candidate-number "ext:helm-core") +(declare-function iedit-find-current-occurrence-overlay "ext:iedit-lib") +(declare-function iedit-prev-occurrence "ext:iedit-lib") +(declare-function image-get-display-property "image-mode") +(declare-function jsonrpc--request-continuations "ext:jsonrpc" t t) +(declare-function jsonrpc-last-error "ext:jsonrpc" t t) +(declare-function lsp--workspace-print "ext:lsp-mode") +(declare-function lsp-describe-session "ext:lsp-mode") +(declare-function lsp-workspace-folders-open "ext:lsp-mode") +(declare-function lsp-workspace-restart "ext:lsp-mode") +(declare-function lsp-workspace-shutdown "ext:lsp-mode") +(declare-function lsp-workspaces "ext:lsp-mode") +(declare-function lv-message "ext:lv") +(declare-function mc/num-cursors "ext:multiple-cursors-core") +(declare-function minions--prominent-modes "ext:minions") +(declare-function mlscroll-mode-line "ext:mlscroll") +(declare-function mu4e-alert-default-mode-line-formatter "ext:mu4e-alert") +(declare-function mu4e-alert-enable-mode-line-display "ext:mu4e-alert") +(declare-function nyan-create "ext:nyan-mode") +(declare-function org-edit-src-save "ext:org-src") +(declare-function parrot-create "ext:parrot") +(declare-function pdf-cache-number-of-pages "ext:pdf-cache" t t) +(declare-function persp-add-buffer "ext:persp-mode") +(declare-function persp-contain-buffer-p "ext:persp-mode") +(declare-function persp-switch "ext:persp-mode") +(declare-function phi-search--initialize "ext:phi-search") +(declare-function poke-line-create "ext:poke-line") +(declare-function popup-create "ext:popup") +(declare-function popup-delete "ext:popup") +(declare-function rcirc-next-active-buffer "rcirc") +(declare-function rcirc-short-buffer-name "rcirc") +(declare-function rcirc-switch-to-server-buffer "rcirc") +(declare-function rcirc-window-configuration-change "rcirc") +(declare-function rime--should-enable-p "ext:rime") +(declare-function rime--should-inline-ascii-p "ext:rime") +(declare-function sml-modeline-create "ext:sml-modeline") +(declare-function symbol-overlay-assoc "ext:symbol-overlay") +(declare-function symbol-overlay-get-list "ext:symbol-overlay") +(declare-function symbol-overlay-get-symbol "ext:symbol-overlay") +(declare-function symbol-overlay-rename "ext:symbol-overlay") +(declare-function tab-bar--current-tab "tab-bar") +(declare-function tab-bar--current-tab-index "tab-bar") +(declare-function tracking-next-buffer "ext:tracking") +(declare-function tracking-previous-buffer "ext:tracking") +(declare-function tracking-shorten "ext:tracking") +(declare-function undo-tree-redo-1 "ext:undo-tree") +(declare-function undo-tree-undo-1 "ext:undo-tree") +(declare-function warning-numeric-level "warnings") +(declare-function window-numbering-clear-mode-line "ext:window-numbering") +(declare-function window-numbering-get-number-string "ext:window-numbering") +(declare-function window-numbering-install-mode-line "ext:window-numbering") +(declare-function winum--clear-mode-line "ext:winum") +(declare-function winum--install-mode-line "ext:winum") +(declare-function winum-get-number-string "ext:winum") + + + +;; +;; Buffer information +;; + +(defvar-local doom-modeline--buffer-file-icon nil) +(defun doom-modeline-update-buffer-file-icon (&rest _) + "Update file icon in mode-line." + (setq doom-modeline--buffer-file-icon + (when (and doom-modeline-major-mode-icon + (doom-modeline-icon-displayable-p)) + (let ((icon (all-the-icons-icon-for-buffer))) + (propertize (if (or (null icon) (symbolp icon)) + (doom-modeline-icon 'faicon "file-o" nil nil + :face 'all-the-icons-dsilver + :height 0.9 + :v-adjust 0.0) + icon) + 'help-echo (format "Major-mode: %s" (format-mode-line mode-name)) + 'display '(raise -0.135)))))) +(add-hook 'find-file-hook #'doom-modeline-update-buffer-file-icon) +(add-hook 'after-change-major-mode-hook #'doom-modeline-update-buffer-file-icon) +(add-hook 'clone-indirect-buffer-hook #'doom-modeline-update-buffer-file-icon) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-buffer-file-icon)))))) + +(defun doom-modeline-buffer-file-state-icon (icon unicode text face) + "Displays an ICON of buffer state with FACE. +UNICODE and TEXT are the alternatives if it is not applicable. +Uses `all-the-icons-material' to fetch the icon." + (doom-modeline-icon 'material icon unicode text + :face face + :height 1.1 + :v-adjust -0.225)) + +(defvar-local doom-modeline--buffer-file-state-icon nil) +(defun doom-modeline-update-buffer-file-state-icon (&rest _) + "Update the buffer or file state in mode-line." + (setq doom-modeline--buffer-file-state-icon + (when doom-modeline-buffer-state-icon + (ignore-errors + (concat + (cond (buffer-read-only + (doom-modeline-buffer-file-state-icon + "lock" "🔒" "%1*" `(:inherit doom-modeline-warning + :weight ,(if doom-modeline-icon + 'normal + 'bold)))) + ((and buffer-file-name (buffer-modified-p) + doom-modeline-buffer-modification-icon) + (doom-modeline-buffer-file-state-icon + "save" "💾" "%1*" `(:inherit doom-modeline-buffer-modified + :weight ,(if doom-modeline-icon + 'normal + 'bold)))) + ((and buffer-file-name + (not (file-remote-p buffer-file-name)) ; Avoid freezing while connection is lost + (not (file-exists-p buffer-file-name))) + (doom-modeline-buffer-file-state-icon + "do_not_disturb_alt" "🚫" "!" 'doom-modeline-urgent)) + (t "")) + (when (or (buffer-narrowed-p) + (and (bound-and-true-p fancy-narrow-mode) + (fancy-narrow-active-p)) + (bound-and-true-p dired-narrow-mode)) + (doom-modeline-buffer-file-state-icon + "vertical_align_center" "↕" "><" 'doom-modeline-warning))))))) + +(defvar-local doom-modeline--buffer-file-name nil) +(defun doom-modeline-update-buffer-file-name (&rest _) + "Update buffer file name in mode-line." + (setq doom-modeline--buffer-file-name + (ignore-errors + (save-match-data + (if buffer-file-name + (doom-modeline-buffer-file-name) + (propertize "%b" + 'face 'doom-modeline-buffer-file + 'mouse-face 'doom-modeline-highlight + 'help-echo "Buffer name +mouse-1: Previous buffer\nmouse-3: Next buffer" + 'local-map mode-line-buffer-identification-keymap)))))) +(add-hook 'find-file-hook #'doom-modeline-update-buffer-file-name) +(add-hook 'after-save-hook #'doom-modeline-update-buffer-file-name) +(add-hook 'clone-indirect-buffer-hook #'doom-modeline-update-buffer-file-name) +(add-hook 'evil-insert-state-exit-hook #'doom-modeline-update-buffer-file-name) +(advice-add #'not-modified :after #'doom-modeline-update-buffer-file-name) +(advice-add #'rename-buffer :after #'doom-modeline-update-buffer-file-name) +(advice-add #'set-visited-file-name :after #'doom-modeline-update-buffer-file-name) +(advice-add #'pop-to-buffer :after #'doom-modeline-update-buffer-file-name) +(advice-add #'undo :after #'doom-modeline-update-buffer-file-name) +(advice-add #'undo-tree-undo-1 :after #'doom-modeline-update-buffer-file-name) +(advice-add #'undo-tree-redo-1 :after #'doom-modeline-update-buffer-file-name) +(advice-add #'fill-paragraph :after #'doom-modeline-update-buffer-file-name) +(advice-add #'popup-create :after #'doom-modeline-update-buffer-file-name) +(advice-add #'popup-delete :after #'doom-modeline-update-buffer-file-name) +(advice-add #'org-edit-src-save :after #'doom-modeline-update-buffer-file-name) +(advice-add #'symbol-overlay-rename :after #'doom-modeline-update-buffer-file-name) + +(with-no-warnings + (if (boundp 'after-focus-change-function) + (progn + (advice-add #'handle-switch-frame :after #'doom-modeline-update-buffer-file-name) + (add-function :after after-focus-change-function #'doom-modeline-update-buffer-file-name)) + (progn + (add-hook 'focus-in-hook #'doom-modeline-update-buffer-file-name) + (add-hook 'focus-out-hook #'doom-modeline-update-buffer-file-name)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-buffer-file-name-style + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-buffer-file-name-style val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when buffer-file-name + (doom-modeline-update-buffer-file-name))))))) + +(defsubst doom-modeline--buffer-mode-icon () + "The icon of the current major mode." + (when (and doom-modeline-icon doom-modeline-major-mode-icon) + (when-let ((icon (or doom-modeline--buffer-file-icon + (doom-modeline-update-buffer-file-icon)))) + (unless (string-empty-p icon) + (concat + (if doom-modeline-major-mode-color-icon + (doom-modeline-display-icon icon) + (doom-modeline-propertize-icon + icon + (doom-modeline-face))) + doom-modeline-vspc))))) + +(defsubst doom-modeline--buffer-state-icon () + "The icon of the current buffer state." + (when doom-modeline-buffer-state-icon + (when-let ((icon (doom-modeline-update-buffer-file-state-icon))) + (unless (string-empty-p icon) + (concat + (doom-modeline-display-icon icon) + doom-modeline-vspc))))) + +(defsubst doom-modeline--buffer-simple-name () + "The buffer simple name." + (propertize "%b" + 'face (doom-modeline-face + (if (and buffer-file-name (buffer-modified-p)) + 'doom-modeline-buffer-modified + 'doom-modeline-buffer-file)) + 'mouse-face 'doom-modeline-highlight + 'help-echo "Buffer name +mouse-1: Previous buffer\nmouse-3: Next buffer" + 'local-map mode-line-buffer-identification-keymap)) + +(defsubst doom-modeline--buffer-name () + "The current buffer name." + (when doom-modeline-buffer-name + (if (and (not (eq doom-modeline-buffer-file-name-style 'file-name)) + doom-modeline--limited-width-p) + ;; Only display the buffer name if the window is small, and doesn't + ;; need to respect file-name style. + (doom-modeline--buffer-simple-name) + (when-let ((name (or doom-modeline--buffer-file-name + (doom-modeline-update-buffer-file-name)))) + ;; Check if the buffer is modified + (if (and buffer-file-name (buffer-modified-p)) + (propertize name 'face (doom-modeline-face 'doom-modeline-buffer-modified)) + (doom-modeline-display-text name)))))) + +(doom-modeline-def-segment buffer-info + "Combined information about the current buffer. + +Including the current working directory, the file name, and its state (modified, +read-only or non-existent)." + (concat + doom-modeline-spc + (doom-modeline--buffer-mode-icon) + (doom-modeline--buffer-state-icon) + (doom-modeline--buffer-name))) + +(doom-modeline-def-segment buffer-info-simple + "Display only the current buffer's name, but with fontification." + (concat + doom-modeline-spc + (doom-modeline--buffer-mode-icon) + (doom-modeline--buffer-state-icon) + (doom-modeline--buffer-simple-name))) + +(doom-modeline-def-segment buffer-default-directory + "Displays `default-directory' with the icon and state. + +This is for special buffers like the scratch buffer where knowing the current +project directory is important." + (let ((face (doom-modeline-face + (if (and buffer-file-name (buffer-modified-p)) + 'doom-modeline-buffer-modified + 'doom-modeline-buffer-path)))) + (concat doom-modeline-spc + (and doom-modeline-major-mode-icon + (concat (doom-modeline-icon + 'octicon "file-directory" "🖿" "" + :face face :v-adjust -0.05 :height 1.25) + doom-modeline-vspc)) + (doom-modeline--buffer-state-icon) + (propertize (abbreviate-file-name default-directory) 'face face)))) + +(doom-modeline-def-segment buffer-default-directory-simple + "Displays `default-directory'. + +This is for special buffers like the scratch buffer where knowing the current +project directory is important." + (let ((face (doom-modeline-face 'doom-modeline-buffer-path))) + (concat doom-modeline-spc + (and doom-modeline-major-mode-icon + (concat (doom-modeline-icon + 'octicon "file-directory" "🖿" "" + :face face :v-adjust -0.05 :height 1.25) + doom-modeline-vspc)) + (propertize (abbreviate-file-name default-directory) 'face face)))) + + +;; +;; Encoding +;; + +(doom-modeline-def-segment buffer-encoding + "Displays the eol and the encoding style of the buffer." + (when doom-modeline-buffer-encoding + (let ((mouse-face 'doom-modeline-highlight)) + (concat + doom-modeline-spc + + ;; eol type + (let ((eol (coding-system-eol-type buffer-file-coding-system))) + (when (or (eq doom-modeline-buffer-encoding t) + (and (eq doom-modeline-buffer-encoding 'nondefault) + (not (equal eol doom-modeline-default-eol-type)))) + (propertize + (pcase eol + (0 "LF ") + (1 "CRLF ") + (2 "CR ") + (_ "")) + 'mouse-face mouse-face + 'help-echo (format "End-of-line style: %s\nmouse-1: Cycle" + (pcase eol + (0 "Unix-style LF") + (1 "DOS-style CRLF") + (2 "Mac-style CR") + (_ "Undecided"))) + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] 'mode-line-change-eol) + map)))) + + ;; coding system + (let* ((sys (coding-system-plist buffer-file-coding-system)) + (cat (plist-get sys :category)) + (sym (if (memq cat + '(coding-category-undecided coding-category-utf-8)) + 'utf-8 + (plist-get sys :name)))) + (when (or (eq doom-modeline-buffer-encoding t) + (and (eq doom-modeline-buffer-encoding 'nondefault) + (not (eq cat 'coding-category-undecided)) + (not (eq sym doom-modeline-default-coding-system)))) + (propertize + (upcase (symbol-name sym)) + 'mouse-face mouse-face + 'help-echo 'mode-line-mule-info-help-echo + 'local-map mode-line-coding-system-map))) + + doom-modeline-spc)))) + + +;; +;; Indentation +;; + +(doom-modeline-def-segment indent-info + "Displays the indentation information." + (when doom-modeline-indent-info + (let ((do-propertize + (lambda (mode size) + (propertize + (format " %s %d " mode size))))) + (if indent-tabs-mode + (funcall do-propertize "TAB" tab-width) + (let ((lookup-var + (seq-find (lambda (var) + (and var (boundp var) (symbol-value var))) + (cdr (assoc major-mode doom-modeline-indent-alist)) nil))) + (funcall do-propertize "SPC" + (if lookup-var + (symbol-value lookup-var) + tab-width))))))) + +;; +;; Remote host +;; + +(doom-modeline-def-segment remote-host + "Hostname for remote buffers." + (when default-directory + (when-let ((host (file-remote-p default-directory 'host))) + (propertize + (concat "@" host) + 'face (doom-modeline-face 'doom-modeline-host))))) + + +;; +;; Major mode +;; + +(doom-modeline-def-segment major-mode + "The major mode, including environment and text-scale info." + (propertize + (concat + doom-modeline-spc + (propertize (format-mode-line + (or (and (boundp 'delighted-modes) + (cadr (assq major-mode delighted-modes))) + mode-name)) + 'help-echo "Major mode\n\ + mouse-1: Display major mode menu\n\ + mouse-2: Show help for major mode\n\ + mouse-3: Toggle minor modes" + 'mouse-face 'doom-modeline-highlight + 'local-map mode-line-major-mode-keymap) + (when (and doom-modeline-env-version doom-modeline-env--version) + (format "%s%s" doom-modeline-vspc doom-modeline-env--version)) + (and (boundp 'text-scale-mode-amount) + (/= text-scale-mode-amount 0) + (format + (if (> text-scale-mode-amount 0) + " (%+d)" + " (%-d)") + text-scale-mode-amount)) + doom-modeline-spc) + 'face (doom-modeline-face 'doom-modeline-buffer-major-mode))) + + +;; +;; Process +;; + +(doom-modeline-def-segment process + "The process info." + (format-mode-line mode-line-process)) + + +;; +;; Minor modes +;; + +(doom-modeline-def-segment minor-modes + (when doom-modeline-minor-modes + (let ((face (doom-modeline-face 'doom-modeline-buffer-minor-mode)) + (mouse-face 'doom-modeline-highlight) + (help-echo "Minor mode + mouse-1: Display minor mode menu + mouse-2: Show help for minor mode + mouse-3: Toggle minor modes")) + (if (bound-and-true-p minions-mode) + `((:propertize ("" ,(minions--prominent-modes)) + face ,face + mouse-face ,mouse-face + help-echo ,help-echo + local-map ,mode-line-minor-mode-keymap) + ,doom-modeline-spc + (:propertize ("" ,(doom-modeline-icon 'octicon "gear" "⚙" + minions-mode-line-lighter + :face face :v-adjust -0.05)) + mouse-face ,mouse-face + help-echo "Minions +mouse-1: Display minor modes menu" + local-map ,minions-mode-line-minor-modes-map) + ,doom-modeline-spc) + `((:propertize ("" minor-mode-alist) + face ,face + mouse-face ,mouse-face + help-echo ,help-echo + local-map ,mode-line-minor-mode-keymap) + ,doom-modeline-spc))))) + + +;; +;; VCS +;; + +(defun doom-modeline-vcs-icon (icon &optional unicode text face voffset) + "Displays the vcs ICON with FACE and VOFFSET. + +UNICODE and TEXT are fallbacks. +Uses `all-the-icons-octicon' to fetch the icon." + (doom-modeline-icon 'octicon icon unicode text + :face face :v-adjust (or voffset -0.1))) + +(defvar-local doom-modeline--vcs-icon nil) +(defun doom-modeline-update-vcs-icon (&rest _) + "Update icon of vcs state in mode-line." + (setq doom-modeline--vcs-icon + (when (and vc-mode buffer-file-name) + (let* ((backend (vc-backend buffer-file-name)) + (state (vc-state (file-local-name buffer-file-name) backend))) + (cond ((memq state '(edited added)) + (doom-modeline-vcs-icon "git-compare" "⇆" "*" 'doom-modeline-info -0.05)) + ((eq state 'needs-merge) + (doom-modeline-vcs-icon "git-merge" "⛙" "?" 'doom-modeline-info)) + ((eq state 'needs-update) + (doom-modeline-vcs-icon "arrow-down" "↓" "!" 'doom-modeline-warning)) + ((memq state '(removed conflict unregistered)) + (doom-modeline-vcs-icon "alert" "⚠" "!" 'doom-modeline-urgent)) + (t + (doom-modeline-vcs-icon "git-branch" "" "@" 'doom-modeline-info -0.05))))))) +(add-hook 'find-file-hook #'doom-modeline-update-vcs-icon) +(add-hook 'after-save-hook #'doom-modeline-update-vcs-icon) +(advice-add #'vc-refresh-state :after #'doom-modeline-update-vcs-icon) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-vcs-icon)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-vcs-icon)))))) + +(defvar-local doom-modeline--vcs-text nil) +(defun doom-modeline-update-vcs-text (&rest _) + "Update text of vcs state in mode-line." + (setq doom-modeline--vcs-text + (when (and vc-mode buffer-file-name) + (let* ((backend (vc-backend buffer-file-name)) + (state (vc-state (file-local-name buffer-file-name) backend)) + (str (if vc-display-status + (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) + ""))) + (propertize (if (length> str doom-modeline-vcs-max-length) + (concat + (substring str 0 (- doom-modeline-vcs-max-length 3)) + doom-modeline-ellipsis) + str) + 'mouse-face 'doom-modeline-highlight + 'face (cond ((eq state 'needs-update) + 'doom-modeline-warning) + ((memq state '(removed conflict unregistered)) + 'doom-modeline-urgent) + (t 'doom-modeline-info))))))) +(add-hook 'find-file-hook #'doom-modeline-update-vcs-text) +(add-hook 'after-save-hook #'doom-modeline-update-vcs-text) +(advice-add #'vc-refresh-state :after #'doom-modeline-update-vcs-text) + +(doom-modeline-def-segment vcs + "Displays the current branch, colored based on its state." + (when-let ((icon doom-modeline--vcs-icon) + (text doom-modeline--vcs-text)) + (concat + doom-modeline-spc + (propertize (concat + (doom-modeline-display-icon icon) + doom-modeline-vspc + (doom-modeline-display-text text)) + 'mouse-face 'doom-modeline-highlight + 'help-echo (get-text-property 1 'help-echo vc-mode) + 'local-map (get-text-property 1 'local-map vc-mode)) + doom-modeline-spc))) + + +;; +;; Checker +;; + +(defun doom-modeline-checker-icon (icon unicode text face) + "Displays the checker ICON with FACE. + +UNICODE and TEXT are fallbacks. +Uses `all-the-icons-material' to fetch the icon." + (doom-modeline-icon 'material icon unicode text + :face face :height 1.1 :v-adjust -0.225)) + +(defun doom-modeline-checker-text (text &optional face) + "Displays TEXT with FACE." + (propertize text 'face (or face 'mode-line))) + +;; Flycheck + +(defun doom-modeline--flycheck-count-errors () + "Count the number of ERRORS, grouped by level. + +Return an alist, where each ITEM is a cons cell whose `car' is an +error level, and whose `cdr' is the number of errors of that +level." + (let ((info 0) (warning 0) (error 0)) + (mapc + (lambda (item) + (let ((count (cdr item))) + (pcase (flycheck-error-level-compilation-level (car item)) + (0 (cl-incf info count)) + (1 (cl-incf warning count)) + (2 (cl-incf error count))))) + (flycheck-count-errors flycheck-current-errors)) + `((info . ,info) (warning . ,warning) (error . ,error)))) + +(defvar-local doom-modeline--flycheck-icon nil) +(defun doom-modeline-update-flycheck-icon (&optional status) + "Update flycheck icon via STATUS." + (setq doom-modeline--flycheck-icon + (when-let + ((icon + (pcase status + ('finished (if flycheck-current-errors + (let-alist (doom-modeline--flycheck-count-errors) + (doom-modeline-checker-icon + "block" "🚫" "!" + (cond ((> .error 0) 'doom-modeline-urgent) + ((> .warning 0) 'doom-modeline-warning) + (t 'doom-modeline-info)))) + (doom-modeline-checker-icon "check" "✓" "-" 'doom-modeline-info))) + ('running (doom-modeline-checker-icon "access_time" "⏱" "*" 'doom-modeline-debug)) + ('no-checker (doom-modeline-checker-icon "sim_card_alert" "⚠" "-" 'doom-modeline-debug)) + ('errored (doom-modeline-checker-icon "sim_card_alert" "⚠" "-" 'doom-modeline-urgent)) + ('interrupted (doom-modeline-checker-icon "pause" "⏸" "=" 'doom-modeline-debug)) + ('suspicious (doom-modeline-checker-icon "priority_high" "❗" "!" 'doom-modeline-urgent)) + (_ nil)))) + (propertize icon + 'help-echo (concat "Flycheck\n" + (pcase status + ('finished "mouse-1: Display minor mode menu +mouse-2: Show help for minor mode") + ('running "Running...") + ('no-checker "No Checker") + ('errored "Error") + ('interrupted "Interrupted") + ('suspicious "Suspicious"))) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line down-mouse-1] + flycheck-mode-menu-map) + (define-key map [mode-line mouse-2] + (lambda () + (interactive) + (describe-function 'flycheck-mode))) + map))))) +(add-hook 'flycheck-status-changed-functions #'doom-modeline-update-flycheck-icon) +(add-hook 'flycheck-mode-hook #'doom-modeline-update-flycheck-icon) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flycheck-mode) + (flycheck-buffer))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flycheck-mode) + (flycheck-buffer))))))) + +(defvar-local doom-modeline--flycheck-text nil) +(defun doom-modeline-update-flycheck-text (&optional status) + "Update flycheck text via STATUS." + (setq doom-modeline--flycheck-text + (when-let + ((text + (pcase status + ('finished (when flycheck-current-errors + (let-alist (doom-modeline--flycheck-count-errors) + (if doom-modeline-checker-simple-format + (doom-modeline-checker-text + (number-to-string (+ .error .warning .info)) + (cond ((> .error 0) 'doom-modeline-urgent) + ((> .warning 0) 'doom-modeline-warning) + (t 'doom-modeline-info))) + (format "%s/%s/%s" + (doom-modeline-checker-text (number-to-string .error) + 'doom-modeline-urgent) + (doom-modeline-checker-text (number-to-string .warning) + 'doom-modeline-warning) + (doom-modeline-checker-text (number-to-string .info) + 'doom-modeline-info)))))) + ('running nil) + ('no-checker nil) + ('errored (doom-modeline-checker-text "Error" 'doom-modeline-urgent)) + ('interrupted (doom-modeline-checker-text "Interrupted" 'doom-modeline-debug)) + ('suspicious (doom-modeline-checker-text "Suspicious" 'doom-modeline-urgent)) + (_ nil)))) + (propertize + text + 'help-echo (pcase status + ('finished + (concat + (when flycheck-current-errors + (let-alist (doom-modeline--flycheck-count-errors) + (format "error: %d, warning: %d, info: %d\n" .error .warning .info))) + "mouse-1: Show all errors +mouse-3: Next error" + (if (featurep 'mwheel) + "\nwheel-up/wheel-down: Previous/next error"))) + ('running "Running...") + ('no-checker "No Checker") + ('errored "Error") + ('interrupted "Interrupted") + ('suspicious "Suspicious")) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'flycheck-list-errors) + (define-key map [mode-line mouse-3] + #'flycheck-next-error) + (when (featurep 'mwheel) + (define-key map [mode-line mouse-wheel-down-event] + (lambda (event) + (interactive "e") + (with-selected-window (posn-window (event-start event)) + (flycheck-previous-error 1)))) + (define-key map [mode-line mouse-wheel-up-event] + (lambda (event) + (interactive "e") + (with-selected-window (posn-window (event-start event)) + (flycheck-next-error 1)))) + map)))))) +(add-hook 'flycheck-status-changed-functions #'doom-modeline-update-flycheck-text) +(add-hook 'flycheck-mode-hook #'doom-modeline-update-flycheck-text) + +;; Flymake + +;; Compatibility +;; @see https://github.com/emacs-mirror/emacs/commit/6e100869012da9244679696634cab6b9cac96303. +(with-eval-after-load 'flymake + (unless (boundp 'flymake--state) + (defvaralias 'flymake--state 'flymake--backend-state)) + (unless (fboundp 'flymake--state-diags) + (defalias 'flymake--state-diags 'flymake--backend-state-diags))) + +(defvar-local doom-modeline--flymake-icon nil) +(defun doom-modeline-update-flymake-icon (&rest _) + "Update flymake icon." + (setq flymake--mode-line-format nil) ; remove the lighter of minor mode + (setq doom-modeline--flymake-icon + (let* ((known (hash-table-keys flymake--state)) + (running (flymake-running-backends)) + (disabled (flymake-disabled-backends)) + (reported (flymake-reporting-backends)) + (all-disabled (and disabled (null running))) + (some-waiting (cl-set-difference running reported))) + (when-let + ((icon + (cond + (some-waiting (doom-modeline-checker-icon "access_time" "⏰" "*" 'doom-modeline-debug)) + ((null known) (doom-modeline-checker-icon "sim_card_alert" "❓" "?" 'doom-modeline-debug)) + (all-disabled (doom-modeline-checker-icon "sim_card_alert" "❗" "!" 'doom-modeline-urgent)) + (t (let ((.error 0) + (.warning 0) + (.note 0)) + (progn + (cl-loop + with warning-level = (warning-numeric-level :warning) + with note-level = (warning-numeric-level :debug) + for state being the hash-values of flymake--state + do (cl-loop + with diags = (flymake--state-diags state) + for diag in diags do + (let ((severity (flymake--lookup-type-property (flymake--diag-type diag) 'severity + (warning-numeric-level :error)))) + (cond ((> severity warning-level) (cl-incf .error)) + ((> severity note-level) (cl-incf .warning)) + (t (cl-incf .note)))))) + (if (> (+ .error .warning .note) 0) + (doom-modeline-checker-icon "do_not_disturb_alt" "🚫" "!" + (cond ((> .error 0) 'doom-modeline-urgent) + ((> .warning 0) 'doom-modeline-warning) + (t 'doom-modeline-info))) + (doom-modeline-checker-icon "check" "✔" "-" 'doom-modeline-info)))))))) + (propertize + icon + 'help-echo (concat "Flymake\n" + (cond + (some-waiting "Running...") + ((null known) "No Checker") + (all-disabled "All Checkers Disabled") + (t (format "%d/%d backends running +mouse-1: Display minor mode menu +mouse-2: Show help for minor mode" + (length running) (length known))))) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line down-mouse-1] + flymake-menu) + (define-key map [mode-line mouse-2] + (lambda () + (interactive) + (describe-function 'flymake-mode))) + map)))))) +(advice-add #'flymake--handle-report :after #'doom-modeline-update-flymake-icon) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flymake-mode) + (flymake-start))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flymake-mode) + (flymake-start))))))) + +(defvar-local doom-modeline--flymake-text nil) +(defun doom-modeline-update-flymake-text (&rest _) + "Update flymake text." + (setq flymake--mode-line-format nil) ; remove the lighter of minor mode + (setq doom-modeline--flymake-text + (let* ((known (hash-table-keys flymake--state)) + (running (flymake-running-backends)) + (disabled (flymake-disabled-backends)) + (reported (flymake-reporting-backends)) + (all-disabled (and disabled (null running))) + (some-waiting (cl-set-difference running reported)) + (warning-level (warning-numeric-level :warning)) + (note-level (warning-numeric-level :debug)) + (.error 0) + (.warning 0) + (.note 0)) + (maphash (lambda (_b state) + (cl-loop + with diags = (flymake--state-diags state) + for diag in diags do + (let ((severity (flymake--lookup-type-property (flymake--diag-type diag) 'severity + (warning-numeric-level :error)))) + (cond ((> severity warning-level) (cl-incf .error)) + ((> severity note-level) (cl-incf .warning)) + (t (cl-incf .note)))))) + flymake--state) + (when-let + ((text + (cond + (some-waiting doom-modeline--flymake-text) + ((null known) nil) + (all-disabled nil) + (t (let ((num (+ .error .warning .note))) + (when (> num 0) + (if doom-modeline-checker-simple-format + (doom-modeline-checker-text (number-to-string num) + (cond ((> .error 0) 'doom-modeline-urgent) + ((> .warning 0) 'doom-modeline-warning) + (t 'doom-modeline-info))) + (format "%s/%s/%s" + (doom-modeline-checker-text (number-to-string .error) + 'doom-modeline-urgent) + (doom-modeline-checker-text (number-to-string .warning) + 'doom-modeline-warning) + (doom-modeline-checker-text (number-to-string .note) + 'doom-modeline-info))))))))) + (propertize + text + 'help-echo (cond + (some-waiting "Running...") + ((null known) "No Checker") + (all-disabled "All Checkers Disabled") + (t (format "error: %d, warning: %d, note: %d +mouse-1: List all problems%s" + .error .warning .note + (if (featurep 'mwheel) + "\nwheel-up/wheel-down: Previous/next problem")))) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'flymake-show-diagnostics-buffer) + (when (featurep 'mwheel) + (define-key map (vector 'mode-line + mouse-wheel-down-event) + (lambda (event) + (interactive "e") + (with-selected-window (posn-window (event-start event)) + (flymake-goto-prev-error 1 nil t)))) + (define-key map (vector 'mode-line + mouse-wheel-up-event) + (lambda (event) + (interactive "e") + (with-selected-window (posn-window (event-start event)) + (flymake-goto-next-error 1 nil t)))) + map))))))) +(advice-add #'flymake--handle-report :after #'doom-modeline-update-flymake-text) + +(doom-modeline-def-segment checker + "Displays color-coded error status in the current buffer with pretty icons." + (let* ((seg (cond + ((and (bound-and-true-p flymake-mode) + (bound-and-true-p flymake--state)) ; only support 26+ + `(,doom-modeline--flymake-icon . ,doom-modeline--flymake-text)) + ((and (bound-and-true-p flycheck-mode) + (bound-and-true-p flycheck--automatically-enabled-checkers)) + `(,doom-modeline--flycheck-icon . ,doom-modeline--flycheck-text)))) + (icon (car seg)) + (text (cdr seg))) + (concat + (and (or icon text) doom-modeline-spc) + (and icon (doom-modeline-display-icon icon)) + (and text + (concat + (and icon doom-modeline-vspc) + (doom-modeline-display-text text))) + (and (or icon text) doom-modeline-spc)))) + + +;; +;; Word Count +;; + +(doom-modeline-def-segment word-count + "The buffer word count. +Displayed when in a major mode in `doom-modeline-continuous-word-count-modes'. +Respects `doom-modeline-enable-word-count'." + (when (and doom-modeline-enable-word-count + (member major-mode doom-modeline-continuous-word-count-modes)) + (format " %dW" (count-words (point-min) (point-max))))) + + +;; +;; Selection +;; + +(defsubst doom-modeline-column (pos) + "Get the column of the position `POS'." + (save-excursion (goto-char pos) + (current-column))) + +(doom-modeline-def-segment selection-info + "Information about the current selection. + +Such as how many characters and lines are selected, or the NxM dimensions of a +block selection." + (when (and (or mark-active (and (bound-and-true-p evil-local-mode) + (eq evil-state 'visual))) + (doom-modeline--active)) + (cl-destructuring-bind (beg . end) + (if (and (bound-and-true-p evil-local-mode) (eq evil-state 'visual)) + (cons evil-visual-beginning evil-visual-end) + (cons (region-beginning) (region-end))) + (propertize + (let ((lines (count-lines beg (min end (point-max))))) + (concat doom-modeline-spc + (cond ((or (bound-and-true-p rectangle-mark-mode) + (and (bound-and-true-p evil-visual-selection) + (eq 'block evil-visual-selection))) + (let ((cols (abs (- (doom-modeline-column end) + (doom-modeline-column beg))))) + (format "%dx%dB" lines cols))) + ((and (bound-and-true-p evil-visual-selection) + (eq evil-visual-selection 'line)) + (format "%dL" lines)) + ((> lines 1) + (format "%dC %dL" (- end beg) lines)) + (t + (format "%dC" (- end beg)))) + (when doom-modeline-enable-word-count + (format " %dW" (count-words beg end))) + doom-modeline-spc)) + 'face 'doom-modeline-emphasis)))) + + +;; +;; Matches (macro, anzu, evil-substitute, iedit, symbol-overlay and multi-cursors) +;; + +(defsubst doom-modeline--macro-recording () + "Display current Emacs or evil macro being recorded." + (when (and (doom-modeline--active) + (or defining-kbd-macro executing-kbd-macro)) + (let ((sep (propertize " " 'face 'doom-modeline-panel )) + (vsep (propertize " " 'face + '(:inherit (doom-modeline-panel variable-pitch))))) + (concat + sep + (doom-modeline-icon 'material "fiber_manual_record" "●" + (if (bound-and-true-p evil-this-macro) + (char-to-string evil-this-macro) + "Macro") + :face 'doom-modeline-panel + :v-adjust -0.225) + vsep + (doom-modeline-icon 'octicon "triangle-right" "▶" ">" + :face 'doom-modeline-panel + :v-adjust -0.05) + sep)))) + +;; `anzu' and `evil-anzu' expose current/total state that can be displayed in the +;; mode-line. +(defun doom-modeline-fix-anzu-count (positions here) + "Calulate anzu count via POSITIONS and HERE." + (cl-loop for (start . end) in positions + collect t into before + when (and (>= here start) (<= here end)) + return (length before) + finally return 0)) + +(advice-add #'anzu--where-is-here :override #'doom-modeline-fix-anzu-count) + +(setq anzu-cons-mode-line-p nil) ; manage modeline segment ourselves +;; Ensure anzu state is cleared when searches & iedit are done +(with-eval-after-load 'anzu + (add-hook 'isearch-mode-end-hook #'anzu--reset-status t) + (add-hook 'iedit-mode-end-hook #'anzu--reset-status) + (advice-add #'evil-force-normal-state :after #'anzu--reset-status) + ;; Fix matches segment mirroring across all buffers + (mapc #'make-variable-buffer-local + '(anzu--total-matched + anzu--current-position anzu--state anzu--cached-count + anzu--cached-positions anzu--last-command + anzu--last-isearch-string anzu--overflow-p))) + +(defsubst doom-modeline--anzu () + "Show the match index and total number thereof. +Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with +`evil-search'." + (when (and (bound-and-true-p anzu--state) + (not (bound-and-true-p iedit-mode))) + (propertize + (let ((here anzu--current-position) + (total anzu--total-matched)) + (cond ((eq anzu--state 'replace-query) + (format " %d replace " anzu--cached-count)) + ((eq anzu--state 'replace) + (format " %d/%d " here total)) + (anzu--overflow-p + (format " %s+ " total)) + (t + (format " %s/%d " here total)))) + 'face (doom-modeline-face 'doom-modeline-panel)))) + +(defsubst doom-modeline--evil-substitute () + "Show number of matches for evil-ex substitutions and highlights in real time." + (when (and (bound-and-true-p evil-local-mode) + (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist) + (assq 'evil-ex-global-match evil-ex-active-highlights-alist) + (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist))) + (propertize + (let ((range (if evil-ex-range + (cons (car evil-ex-range) (cadr evil-ex-range)) + (cons (line-beginning-position) (line-end-position)))) + (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2)))) + (if pattern + (format " %s matches " (how-many pattern (car range) (cdr range))) + " - ")) + 'face (doom-modeline-face 'doom-modeline-panel)))) + +(defun doom-modeline-themes--overlay-sort (a b) + "Sort overlay A and B." + (< (overlay-start a) (overlay-start b))) + +(defsubst doom-modeline--iedit () + "Show the number of iedit regions matches + what match you're on." + (when (and (bound-and-true-p iedit-mode) + (bound-and-true-p iedit-occurrences-overlays)) + (propertize + (let ((this-oc (or (let ((inhibit-message t)) + (iedit-find-current-occurrence-overlay)) + (save-excursion (iedit-prev-occurrence) + (iedit-find-current-occurrence-overlay)))) + (length (length iedit-occurrences-overlays))) + (format " %s/%d " + (if this-oc + (- length + (length (memq this-oc (sort (append iedit-occurrences-overlays nil) + #'doom-modeline-themes--overlay-sort))) + -1) + "-") + length)) + 'face (doom-modeline-face 'doom-modeline-panel)))) + +(defsubst doom-modeline--symbol-overlay () + "Show the number of matches for symbol overlay." + (when (and (doom-modeline--active) + (bound-and-true-p symbol-overlay-keywords-alist) + (not (bound-and-true-p symbol-overlay-temp-symbol)) + (not (bound-and-true-p iedit-mode))) + (let* ((keyword (symbol-overlay-assoc (symbol-overlay-get-symbol t))) + (symbol (car keyword)) + (before (symbol-overlay-get-list -1 symbol)) + (after (symbol-overlay-get-list 1 symbol)) + (count (length before))) + (if (symbol-overlay-assoc symbol) + (propertize + (format (concat " %d/%d " (and (cadr keyword) "in scope ")) + (+ count 1) + (+ count (length after))) + 'face (doom-modeline-face 'doom-modeline-panel)))))) + +(defsubst doom-modeline--multiple-cursors () + "Show the number of multiple cursors." + (cl-destructuring-bind (count . face) + (cond ((bound-and-true-p multiple-cursors-mode) + (cons (mc/num-cursors) + (doom-modeline-face 'doom-modeline-panel))) + ((bound-and-true-p evil-mc-cursor-list) + (cons (length evil-mc-cursor-list) + (doom-modeline-face (if evil-mc-frozen + 'doom-modeline-bar + 'doom-modeline-panel)))) + ((cons nil nil))) + (when count + (concat (propertize " " 'face face) + (or (doom-modeline-icon 'faicon "i-cursor" nil nil + :face face :v-adjust -0.0575) + (propertize "I" + 'face `(:inherit ,face :height 1.4 :weight normal) + 'display '(raise -0.1))) + (propertize doom-modeline-vspc + 'face `(:inherit (variable-pitch ,face))) + (propertize (format "%d " count) + 'face face))))) + +(defsubst doom-modeline--phi-search () + "Show the number of matches for `phi-search' and `phi-replace'." + (when (and (doom-modeline--active) + (bound-and-true-p phi-search--overlays)) + (let ((total (length phi-search--overlays)) + (selection phi-search--selection)) + (when selection + (propertize + (format " %d/%d " (1+ selection) total) + 'face (doom-modeline-face 'doom-modeline-panel)))))) + +(defun doom-modeline--override-phi-search-mode-line (orig-fun &rest args) + "Override the mode-line of `phi-search' and `phi-replace'." + (if (bound-and-true-p doom-modeline-mode) + (apply orig-fun mode-line-format (cdr args)) + (apply orig-fun args))) +(advice-add #'phi-search--initialize :around #'doom-modeline--override-phi-search-mode-line) + +(defsubst doom-modeline--buffer-size () + "Show buffer size." + (when size-indication-mode + (concat doom-modeline-spc + (propertize "%I" + 'help-echo "Buffer size +mouse-1: Display Line and Column Mode Menu" + 'mouse-face 'doom-modeline-highlight + 'local-map mode-line-column-line-number-mode-map) + doom-modeline-spc))) + +(doom-modeline-def-segment matches + "Displays matches. + +Including: +1. the currently recording macro, 2. A current/total for the +current search term (with `anzu'), 3. The number of substitutions being +conducted with `evil-ex-substitute', and/or 4. The number of active `iedit' +regions, 5. The current/total for the highlight term (with `symbol-overlay'), +6. The number of active `multiple-cursors'." + (let ((meta (concat (doom-modeline--macro-recording) + (doom-modeline--anzu) + (doom-modeline--phi-search) + (doom-modeline--evil-substitute) + (doom-modeline--iedit) + (doom-modeline--symbol-overlay) + (doom-modeline--multiple-cursors)))) + (or (and (not (string-empty-p meta)) meta) + (doom-modeline--buffer-size)))) + +(doom-modeline-def-segment buffer-size + "Display buffer size." + (doom-modeline--buffer-size)) + +;; +;; Media +;; + +(doom-modeline-def-segment media-info + "Metadata regarding the current file, such as dimensions for images." + ;; TODO Include other information + (cond ((eq major-mode 'image-mode) + (cl-destructuring-bind (width . height) + (when (fboundp 'image-size) + (image-size (image-get-display-property) :pixels)) + (format " %dx%d " width height))))) + + +;; +;; Bars +;; + +(defvar doom-modeline--bar-active nil) +(defvar doom-modeline--bar-inactive nil) + +(defsubst doom-modeline--bar () + "The default bar regulates the height of the mode-line in GUI." + (unless (and doom-modeline--bar-active doom-modeline--bar-inactive) + (let ((width doom-modeline-bar-width) + (height (max doom-modeline-height (doom-modeline--font-height)))) + (setq doom-modeline--bar-active + (doom-modeline--create-bar-image 'doom-modeline-bar width height) + doom-modeline--bar-inactive + (doom-modeline--create-bar-image + 'doom-modeline-bar-inactive width height)))) + (if (doom-modeline--active) + doom-modeline--bar-active + doom-modeline--bar-inactive)) + +(defun doom-modeline-refresh-bars () + "Refresh mode-line bars on next redraw." + (setq doom-modeline--bar-active nil + doom-modeline--bar-inactive nil)) + +(cl-defstruct doom-modeline--hud-cache active inactive top-margin bottom-margin) + +(defsubst doom-modeline--hud () + "Powerline's hud segment reimplemented in the style of Doom's bar segment." + (let* ((ws (window-start)) + (we (window-end)) + (bs (buffer-size)) + (height (max doom-modeline-height (doom-modeline--font-height))) + (top-margin (if (zerop bs) + 0 + (/ (* height (1- ws)) bs))) + (bottom-margin (if (zerop bs) + 0 + (max 0 (/ (* height (- bs we 1)) bs)))) + (cache (or (window-parameter nil 'doom-modeline--hud-cache) + (set-window-parameter + nil + 'doom-modeline--hud-cache + (make-doom-modeline--hud-cache))))) + (unless (and (doom-modeline--hud-cache-active cache) + (doom-modeline--hud-cache-inactive cache) + (= top-margin (doom-modeline--hud-cache-top-margin cache)) + (= bottom-margin + (doom-modeline--hud-cache-bottom-margin cache))) + (setf (doom-modeline--hud-cache-active cache) + (doom-modeline--create-hud-image + 'doom-modeline-bar 'default doom-modeline-bar-width + height top-margin bottom-margin) + (doom-modeline--hud-cache-inactive cache) + (doom-modeline--create-hud-image + 'doom-modeline-bar-inactive 'default doom-modeline-bar-width + height top-margin bottom-margin) + (doom-modeline--hud-cache-top-margin cache) top-margin + (doom-modeline--hud-cache-bottom-margin cache) bottom-margin)) + (if (doom-modeline--active) + (doom-modeline--hud-cache-active cache) + (doom-modeline--hud-cache-inactive cache)))) + +(defun doom-modeline-invalidate-huds () + "Invalidate all cached hud images." + (dolist (frame (frame-list)) + (dolist (window (window-list frame)) + (set-window-parameter window 'doom-modeline--hud-cache nil)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-height + (lambda (_sym val op _where) + (when (and (eq op 'set) (integerp val)) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-bar-width + (lambda (_sym val op _where) + (when (and (eq op 'set) (integerp val)) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym _val op _where) + (when (eq op 'set) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym _val op _where) + (when (eq op 'set) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(add-hook 'window-configuration-change-hook #'doom-modeline-refresh-bars) +(add-hook 'window-configuration-change-hook #'doom-modeline-invalidate-huds) + +(doom-modeline-def-segment bar + "The bar regulates the height of the `doom-modeline' in GUI." + (if doom-modeline-hud + (doom-modeline--hud) + (doom-modeline--bar))) + +(doom-modeline-def-segment hud + "Powerline's hud segment reimplemented in the style of Doom's bar segment." + (doom-modeline--hud)) + + +;; +;; Window number +;; + +;; HACK: `ace-window-display-mode' should respect the ignore buffers. +(defun doom-modeline-aw-update () + "Update ace-window-path window parameter for all windows. +Ensure all windows are labeled so the user can select a specific +one. The ignored buffers are excluded unless `aw-ignore-on' is nil." + (let ((ignore-window-parameters t)) + (avy-traverse + (avy-tree (aw-window-list) aw-keys) + (lambda (path leaf) + (set-window-parameter + leaf 'ace-window-path + (propertize + (apply #'string (reverse path)) + 'face 'aw-mode-line-face)))))) +(advice-add #'aw-update :override #'doom-modeline-aw-update) + +;; Remove original window number of `ace-window-display-mode'. +(add-hook 'ace-window-display-mode-hook + (lambda () + (setq-default mode-line-format + (assq-delete-all 'ace-window-display-mode + (default-value 'mode-line-format))))) + +(advice-add #'window-numbering-install-mode-line :override #'ignore) +(advice-add #'window-numbering-clear-mode-line :override #'ignore) +(advice-add #'winum--install-mode-line :override #'ignore) +(advice-add #'winum--clear-mode-line :override #'ignore) + +(doom-modeline-def-segment window-number + "The current window number." + (let ((num (cond + ((bound-and-true-p ace-window-display-mode) + (aw-update) + (window-parameter (selected-window) 'ace-window-path)) + ((bound-and-true-p winum-mode) + (setq winum-auto-setup-mode-line nil) + (winum-get-number-string)) + ((bound-and-true-p window-numbering-mode) + (window-numbering-get-number-string)) + (t "")))) + (if (and (length> num 0) + (length> (cl-mapcan + (lambda (frame) + ;; Exclude minibuffer and child frames + (unless (and (fboundp 'frame-parent) + (frame-parent frame)) + (window-list frame 'never))) + (visible-frame-list)) + 1)) + (propertize (format " %s " num) + 'face (doom-modeline-face 'doom-modeline-buffer-major-mode)) + doom-modeline-spc))) + + +;; +;; Workspace +;; + +(doom-modeline-def-segment workspace-name + "The current workspace name or number. +Requires `eyebrowse-mode' to be enabled or `tab-bar-mode' tabs to be created." + (when doom-modeline-workspace-name + (when-let + ((name (cond + ((and (bound-and-true-p eyebrowse-mode) + (length> (eyebrowse--get 'window-configs) 1)) + (assq-delete-all 'eyebrowse-mode mode-line-misc-info) + (when-let* + ((num (eyebrowse--get 'current-slot)) + (tag (nth 2 (assoc num (eyebrowse--get 'window-configs))))) + (if (length> tag 0) tag (int-to-string num)))) + ((and (fboundp 'tab-bar-mode) + (length> (frame-parameter nil 'tabs) 1)) + (let* ((current-tab (tab-bar--current-tab)) + (tab-index (tab-bar--current-tab-index)) + (explicit-name (alist-get 'explicit-name current-tab)) + (tab-name (alist-get 'name current-tab))) + (if explicit-name tab-name (+ 1 tab-index))))))) + (propertize (format " %s " name) + 'face (doom-modeline-face 'doom-modeline-buffer-major-mode))))) + + +;; +;; Perspective +;; + +(defvar-local doom-modeline--persp-name nil) +(defun doom-modeline-update-persp-name (&rest _) + "Update perspective name in mode-line." + (setq doom-modeline--persp-name + ;; Support `persp-mode', while not support `perspective' + (when (and doom-modeline-persp-name + (bound-and-true-p persp-mode) + (fboundp 'safe-persp-name) + (fboundp 'get-current-persp)) + (let* ((persp (get-current-persp)) + (name (safe-persp-name persp)) + (face (if (and persp + (not (persp-contain-buffer-p (current-buffer) persp))) + 'doom-modeline-persp-buffer-not-in-persp + 'doom-modeline-persp-name)) + (icon (doom-modeline-icon 'material "folder" "🖿" "#" + :face `(:inherit ,face :slant normal) + :height 1.1 + :v-adjust -0.225))) + (when (or doom-modeline-display-default-persp-name + (not (string-equal persp-nil-name name))) + (concat doom-modeline-spc + (propertize (concat (and doom-modeline-persp-icon + (concat icon doom-modeline-vspc)) + (propertize name 'face face)) + 'help-echo "mouse-1: Switch perspective +mouse-2: Show help for minor mode" + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'persp-switch) + (define-key map [mode-line mouse-2] + (lambda () + (interactive) + (describe-function 'persp-mode))) + map)) + doom-modeline-spc)))))) + +(add-hook 'buffer-list-update-hook #'doom-modeline-update-persp-name) +(add-hook 'find-file-hook #'doom-modeline-update-persp-name) +(add-hook 'persp-activated-functions #'doom-modeline-update-persp-name) +(add-hook 'persp-renamed-functions #'doom-modeline-update-persp-name) +(advice-add #'lv-message :after #'doom-modeline-update-persp-name) + +(doom-modeline-def-segment persp-name + "The current perspective name." + (when (and (doom-modeline--active) + (not doom-modeline--limited-width-p)) + doom-modeline--persp-name)) + + +;; +;; Misc info +;; + +(doom-modeline-def-segment misc-info + "Mode line construct for miscellaneous information. +By default, this shows the information specified by `global-mode-string'." + (unless doom-modeline--limited-width-p + '("" mode-line-misc-info))) + + +;; +;; Position +;; + +;; Be compatible with Emacs 25. +(defvar doom-modeline-column-zero-based + (if (boundp 'column-number-indicator-zero-based) + column-number-indicator-zero-based + t) + "When non-nil, mode line displays column numbers zero-based. +See `column-number-indicator-zero-based'.") + +(defvar doom-modeline-percent-position + (if (boundp 'mode-line-percent-position) + mode-line-percent-position + '(-3 "%p")) + "Specification of \"percentage offset\" of window through buffer. +See `mode-line-percent-position'.") + +(doom-modeline-add-variable-watcher + 'column-number-indicator-zero-based + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-column-zero-based val)))) + +(doom-modeline-add-variable-watcher + 'mode-line-percent-position + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-percent-position val)))) + +(doom-modeline-def-segment buffer-position + "The buffer position information." + (let ((active (doom-modeline--active)) + (lc '(line-number-mode + (column-number-mode + (doom-modeline-column-zero-based "%l:%c" "%l:%C") + "%l") + (column-number-mode (doom-modeline-column-zero-based ":%c" ":%C")))) + (mouse-face 'doom-modeline-highlight) + (local-map mode-line-column-line-number-mode-map)) + (concat + doom-modeline-wspc + + ;; Line and column + (propertize (format-mode-line lc) + 'help-echo "Buffer position\n\ +mouse-1: Display Line and Column Mode Menu" + 'mouse-face mouse-face + 'local-map local-map) + + ;; Position + (cond ((and active + (bound-and-true-p nyan-mode) + (not doom-modeline--limited-width-p) + (>= (window-width) nyan-minimum-window-width)) + (concat + doom-modeline-wspc + (propertize (nyan-create) 'mouse-face mouse-face))) + ((and active + (bound-and-true-p poke-line-mode) + (not doom-modeline--limited-width-p) + (>= (window-width) poke-line-minimum-window-width)) + (concat + doom-modeline-wspc + (propertize (poke-line-create) 'mouse-face mouse-face))) + ((and active + (bound-and-true-p mlscroll-mode) + (not doom-modeline--limited-width-p) + (>= (window-width) mlscroll-minimum-current-width)) + (concat + doom-modeline-wspc + (let ((mlscroll-right-align nil)) + (format-mode-line (mlscroll-mode-line))))) + ((and active + (bound-and-true-p sml-modeline-mode) + (not doom-modeline--limited-width-p) + (>= (window-width) sml-modeline-len)) + (concat + doom-modeline-wspc + (propertize (sml-modeline-create) 'mouse-face mouse-face))) + (t "")) + + ;; Percent position + (when doom-modeline-percent-position + (concat + doom-modeline-spc + (propertize (format-mode-line '("" doom-modeline-percent-position "%%")) + 'help-echo "Buffer percentage\n\ +mouse-1: Display Line and Column Mode Menu" + 'mouse-face mouse-face + 'local-map local-map))) + + (when (or line-number-mode column-number-mode doom-modeline-percent-position) + doom-modeline-spc)))) + +;; +;; Party parrot +;; +(doom-modeline-def-segment parrot + "The party parrot animated icon. Requires `parrot-mode' to be enabled." + (when (and (doom-modeline--active) + (not doom-modeline--limited-width-p) + (bound-and-true-p parrot-mode)) + (concat doom-modeline-wspc + (parrot-create) + doom-modeline-spc))) + +;; +;; Modals (evil, overwrite, god, ryo and xah-fly-keys, etc.) +;; + +(defun doom-modeline--modal-icon (text face help-echo) + "Display the model icon with FACE and HELP-ECHO. +TEXT is alternative if icon is not available." + (propertize (doom-modeline-icon + 'material + (when doom-modeline-modal-icon "fiber_manual_record") + "●" + text + :face (doom-modeline-face face) + :v-adjust -0.225) + 'help-echo help-echo)) + +(defsubst doom-modeline--evil () + "The current evil state. Requires `evil-mode' to be enabled." + (when (bound-and-true-p evil-local-mode) + (doom-modeline--modal-icon + (let ((tag (evil-state-property evil-state :tag t))) + (if (stringp tag) tag (funcall tag))) + (cond + ((evil-normal-state-p) 'doom-modeline-evil-normal-state) + ((evil-emacs-state-p) 'doom-modeline-evil-emacs-state) + ((evil-insert-state-p) 'doom-modeline-evil-insert-state) + ((evil-motion-state-p) 'doom-modeline-evil-motion-state) + ((evil-visual-state-p) 'doom-modeline-evil-visual-state) + ((evil-operator-state-p) 'doom-modeline-evil-operator-state) + ((evil-replace-state-p) 'doom-modeline-evil-replace-state) + (t 'doom-modeline-evil-normal-state)) + (evil-state-property evil-state :name t)))) + +(defsubst doom-modeline--overwrite () + "The current overwrite state which is enabled by command `overwrite-mode'." + (when (and (bound-and-true-p overwrite-mode) + (not (bound-and-true-p evil-local-mode))) + (doom-modeline--modal-icon " " 'doom-modeline-urgent "Overwrite mode"))) + +(defsubst doom-modeline--god () + "The current god state which is enabled by the command `god-mode'." + (when (bound-and-true-p god-local-mode) + (doom-modeline--modal-icon " " 'doom-modeline-evil-normal-state "God mode"))) + +(defsubst doom-modeline--ryo () + "The current ryo-modal state which is enabled by the command `ryo-modal-mode'." + (when (bound-and-true-p ryo-modal-mode) + (doom-modeline--modal-icon "" 'doom-modeline-evil-normal-state "Ryo modal"))) + +(defsubst doom-modeline--xah-fly-keys () + "The current `xah-fly-keys' state." + (when (bound-and-true-p xah-fly-keys) + (if xah-fly-insert-state-p + (doom-modeline--modal-icon " " + 'doom-modeline-evil-insert-state + (format "Xah-fly insert mode")) + (doom-modeline--modal-icon " " + 'doom-modeline-evil-normal-state + (format "Xah-fly command mode"))))) + +(defsubst doom-modeline--boon () + "The current Boon state. Requires `boon-mode' to be enabled." + (when (bound-and-true-p boon-local-mode) + (doom-modeline--modal-icon + (boon-state-string) + (cond + (boon-command-state 'doom-modeline-evil-normal-state) + (boon-insert-state 'doom-modeline-evil-insert-state) + (boon-special-state 'doom-modeline-evil-emacs-state) + (boon-off-state 'doom-modeline-evil-operator-state) + (t 'doom-modeline-evil-operator-state)) + (boon-modeline-string)))) + +(defsubst doom-modeline--meow () + "The current Meow state. Requires `meow-mode' to be enabled." + (when (bound-and-true-p meow-mode) + (if (doom-modeline--active) + meow--indicator + (propertize (substring-no-properties meow--indicator) + 'face + 'mode-line-inactive)))) + +(doom-modeline-def-segment modals + "Displays modal editing states. + +Including `evil', `overwrite', `god', `ryo' and `xha-fly-kyes', etc." + (let* ((evil (doom-modeline--evil)) + (ow (doom-modeline--overwrite)) + (god (doom-modeline--god)) + (ryo (doom-modeline--ryo)) + (xf (doom-modeline--xah-fly-keys)) + (boon (doom-modeline--boon)) + (vsep doom-modeline-vspc) + (meow (doom-modeline--meow)) + (sep (and (or evil ow god ryo xf boon) doom-modeline-spc))) + (concat sep + (and evil (concat evil (and (or ow god ryo xf boon meow) vsep))) + (and ow (concat ow (and (or god ryo xf boon meow) vsep))) + (and god (concat god (and (or ryo xf boon meow) vsep))) + (and ryo (concat ryo (and (or xf boon meow) vsep))) + (and xf (concat xf (and (or boon meow) vsep))) + (and boon (concat boon (and meow vsep))) + meow + sep))) + +;; +;; Objed state +;; + +(defvar doom-modeline--objed-active nil) + +(defun doom-modeline-update-objed (_ &optional reset) + "Update `objed' status, inactive when RESET is true." + (setq doom-modeline--objed-active (not reset))) + +(setq objed-modeline-setup-func #'doom-modeline-update-objed) + +(doom-modeline-def-segment objed-state () + "The current objed state." + (when (and doom-modeline--objed-active + (doom-modeline--active)) + (propertize (format " %s(%s) " + (symbol-name objed--object) + (char-to-string (aref (symbol-name objed--obj-state) 0))) + 'face 'doom-modeline-evil-emacs-state + 'help-echo (format "Objed object: %s (%s)" + (symbol-name objed--object) + (symbol-name objed--obj-state))))) + + +;; +;; Input method +;; + +(doom-modeline-def-segment input-method + "The current input method." + (propertize (cond (current-input-method + (concat doom-modeline-spc + current-input-method-title + doom-modeline-spc)) + ((and (bound-and-true-p evil-local-mode) + (bound-and-true-p evil-input-method)) + (concat + doom-modeline-spc + (nth 3 (assoc default-input-method input-method-alist)) + doom-modeline-spc)) + (t "")) + 'face (doom-modeline-face + (if (and (bound-and-true-p rime-mode) + (equal current-input-method "rime")) + (if (and (rime--should-enable-p) + (not (rime--should-inline-ascii-p))) + 'doom-modeline-input-method + 'doom-modeline-input-method-alt) + 'doom-modeline-input-method)) + 'help-echo (concat + "Current input method: " + current-input-method + "\n\ +mouse-2: Disable input method\n\ +mouse-3: Describe current input method") + 'mouse-face 'doom-modeline-highlight + 'local-map mode-line-input-method-map)) + + +;; +;; Info +;; + +(doom-modeline-def-segment info-nodes + "The topic and nodes in the Info buffer." + (concat + " (" + ;; topic + (propertize (if (stringp Info-current-file) + (replace-regexp-in-string + "%" "%%" + (file-name-sans-extension + (file-name-nondirectory Info-current-file))) + (format "*%S*" Info-current-file)) + 'face (doom-modeline-face 'doom-modeline-info)) + ") " + ;; node + (when Info-current-node + (propertize (replace-regexp-in-string + "%" "%%" Info-current-node) + 'face (doom-modeline-face 'doom-modeline-buffer-path) + 'help-echo + "mouse-1: scroll forward, mouse-3: scroll back" + 'mouse-face 'doom-modeline-highlight + 'local-map Info-mode-line-node-keymap)))) + + +;; +;; REPL +;; + +(defun doom-modeline-repl-icon (text face) + "Display REPL icon (or TEXT in terminal) with FACE." + (doom-modeline-icon 'faicon "terminal" "$" text + :face face :height 1.0 :v-adjust -0.0575)) + +(defvar doom-modeline--cider nil) + +(defun doom-modeline-update-cider () + "Update cider repl state." + (setq doom-modeline--cider + (let* ((connected (cider-connected-p)) + (face (if connected 'doom-modeline-repl-success 'doom-modeline-repl-warning)) + (repl-buffer (cider-current-repl nil nil)) + (cider-info (when repl-buffer + (cider--connection-info repl-buffer t))) + (icon (doom-modeline-repl-icon "REPL" face))) + (propertize icon + 'help-echo + (if connected + (format "CIDER Connected %s\nmouse-2: CIDER quit" cider-info) + "CIDER Disconnected\nmouse-1: CIDER jack-in") + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (if connected + (define-key map [mode-line mouse-2] + #'cider-quit) + (define-key map [mode-line mouse-1] + #'cider-jack-in)) + map))))) + +(add-hook 'cider-connected-hook #'doom-modeline-update-cider) +(add-hook 'cider-disconnected-hook #'doom-modeline-update-cider) +(add-hook 'cider-mode-hook #'doom-modeline-update-cider) + +(doom-modeline-def-segment repl + "The REPL state." + (when doom-modeline-repl + (when-let (icon (when (bound-and-true-p cider-mode) + doom-modeline--cider)) + (concat + doom-modeline-spc + (doom-modeline-display-icon icon) + doom-modeline-spc)))) + + +;; +;; LSP +;; + +(defun doom-modeline-lsp-icon (text face) + "Display LSP icon (or TEXT in terminal) with FACE." + (doom-modeline-icon 'faicon "rocket" "🚀" text + :face face :height 1.0 :v-adjust -0.0575)) + +(defvar-local doom-modeline--lsp nil) +(defun doom-modeline-update-lsp (&rest _) + "Update `lsp-mode' state." + (setq doom-modeline--lsp + (let* ((workspaces (lsp-workspaces)) + (face (if workspaces 'doom-modeline-lsp-success 'doom-modeline-lsp-warning)) + (icon (doom-modeline-lsp-icon "LSP" face))) + (propertize icon + 'help-echo + (if workspaces + (concat "LSP Connected " + (string-join + (mapcar (lambda (w) + (format "[%s]\n" (lsp--workspace-print w))) + workspaces)) + "C-mouse-1: Switch to another workspace folder +mouse-1: Describe current session +mouse-2: Quit server +mouse-3: Reconnect to server") + "LSP Disconnected +mouse-1: Reload to start server") + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (if workspaces + (progn + (define-key map [mode-line C-mouse-1] + #'lsp-workspace-folders-open) + (define-key map [mode-line mouse-1] + #'lsp-describe-session) + (define-key map [mode-line mouse-2] + #'lsp-workspace-shutdown) + (define-key map [mode-line mouse-3] + #'lsp-workspace-restart)) + (progn + (define-key map [mode-line mouse-1] + (lambda () + (interactive) + (ignore-errors (revert-buffer t t)))))) + map))))) +(add-hook 'lsp-before-initialize-hook #'doom-modeline-update-lsp) +(add-hook 'lsp-after-initialize-hook #'doom-modeline-update-lsp) +(add-hook 'lsp-after-uninitialized-functions #'doom-modeline-update-lsp) +(add-hook 'lsp-before-open-hook #'doom-modeline-update-lsp) +(add-hook 'lsp-after-open-hook #'doom-modeline-update-lsp) + +(defvar-local doom-modeline--eglot nil) +(defun doom-modeline-update-eglot () + "Update `eglot' state." + (setq doom-modeline--eglot + (pcase-let* ((server (and (eglot-managed-p) (eglot-current-server))) + (nick (and server (eglot--project-nickname server))) + (pending (and server (hash-table-count + (jsonrpc--request-continuations server)))) + (`(,_id ,doing ,done-p ,detail) (and server (eglot--spinner server))) + (last-error (and server (jsonrpc-last-error server))) + (face (cond (last-error 'doom-modeline-lsp-error) + ((and doing (not done-p)) 'doom-modeline-lsp-running) + ((and pending (cl-plusp pending)) 'doom-modeline-lsp-warning) + (nick 'doom-modeline-lsp-success) + (t 'doom-modeline-lsp-warning))) + (icon (doom-modeline-lsp-icon "EGLOT" face))) + (propertize icon + 'help-echo (cond + (last-error + (format "EGLOT\nAn error occured: %s +mouse-3: Clear this status" (plist-get last-error :message))) + ((and doing (not done-p)) + (format "EGLOT\n%s%s" doing + (if detail (format "%s" detail) ""))) + ((and pending (cl-plusp pending)) + (format "EGLOT\n%d outstanding requests" pending)) + (nick (format "EGLOT Connected (%s/%s) +C-mouse-1: Go to server errors +mouse-1: Go to server events +mouse-2: Quit server +mouse-3: Reconnect to server" nick (eglot--major-mode server))) + (t "EGLOT Disconnected +mouse-1: Start server")) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (cond (last-error + (define-key map [mode-line mouse-3] + #'eglot-clear-status)) + ((and pending (cl-plusp pending)) + (define-key map [mode-line mouse-3] + #'eglot-forget-pending-continuations)) + (nick + (define-key map [mode-line C-mouse-1] + #'eglot-stderr-buffer) + (define-key map [mode-line mouse-1] + #'eglot-events-buffer) + (define-key map [mode-line mouse-2] + #'eglot-shutdown) + (define-key map [mode-line mouse-3] + #'eglot-reconnect)) + (t (define-key map [mode-line mouse-1] + #'eglot))) + map))))) +(add-hook 'eglot-managed-mode-hook #'doom-modeline-update-eglot) + +(defvar-local doom-modeline--tags nil) +(defun doom-modeline-update-tags () + "Update tags state." + (setq doom-modeline--tags + (propertize + (doom-modeline-lsp-icon "LSP" 'doom-modeline-lsp-success) + 'help-echo "TAGS: Citre mode +mouse-1: Toggle citre mode" + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map 'mouse-1 #'citre-mode)))) +(add-hook 'citre-mode-hook #'doom-modeline-update-tags) + +(defun doom-modeline-update-lsp-icon () + "Update lsp icon." + (cond ((bound-and-true-p lsp-mode) + (doom-modeline-update-lsp)) + ((bound-and-true-p eglot--managed-mode) + (doom-modeline-update-eglot)) + ((bound-and-true-p citre-mode) + (doom-modeline-update-tags)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-lsp-icon)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-lsp-icon)))))) + +(doom-modeline-def-segment lsp + "The LSP server state." + (when doom-modeline-lsp + (let ((icon (cond ((bound-and-true-p lsp-mode) + doom-modeline--lsp) + ((bound-and-true-p eglot--managed-mode) + doom-modeline--eglot) + ((bound-and-true-p citre-mode) + doom-modeline--tags)))) + (when icon + (concat + doom-modeline-spc + (doom-modeline-display-icon icon) + doom-modeline-spc))))) + +(defun doom-modeline-override-eglot-modeline () + "Override `eglot' mode-line." + (if (bound-and-true-p doom-modeline-mode) + (setq mode-line-misc-info + (delq (assq 'eglot--managed-mode mode-line-misc-info) mode-line-misc-info)) + (add-to-list 'mode-line-misc-info + `(eglot--managed-mode (" [" eglot--mode-line-format "] "))))) +(add-hook 'eglot-managed-mode-hook #'doom-modeline-override-eglot-modeline) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-eglot-modeline) + + +;; +;; GitHub +;; + +(defvar doom-modeline--github-notification-number 0) +(defvar doom-modeline-before-github-fetch-notification-hook nil + "Hooks before fetching GitHub notifications. +Example: + (add-hook \\='doom-modeline-before-github-fetch-notification-hook + #\\='auth-source-pass-enable)") +(defun doom-modeline--github-fetch-notifications () + "Fetch GitHub notifications." + (when (and doom-modeline-github + (require 'async nil t)) + (async-start + `(lambda () + ,(async-inject-variables + "\\`\\(load-path\\|auth-sources\\|doom-modeline-before-github-fetch-notification-hook\\)\\'") + (run-hooks 'doom-modeline-before-github-fetch-notification-hook) + (when (require 'ghub nil t) + (with-timeout (10) + (ignore-errors + (when-let* ((username (ghub--username ghub-default-host)) + (token (ghub--token ghub-default-host username 'ghub t))) + (ghub-get "/notifications" nil + :query '((notifications . "true")) + :username username + :auth token + :noerror t)))))) + (lambda (result) + (message "") ; suppress message + (setq doom-modeline--github-notification-number (length result)))))) + +(defvar doom-modeline--github-timer nil) +(defun doom-modeline-github-timer () + "Start/Stop the timer for GitHub fetching." + (if (timerp doom-modeline--github-timer) + (cancel-timer doom-modeline--github-timer)) + (setq doom-modeline--github-timer + (and doom-modeline-github + (run-with-idle-timer 30 + doom-modeline-github-interval + #'doom-modeline--github-fetch-notifications)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-github + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-github val) + (doom-modeline-github-timer)))) + +(doom-modeline-github-timer) + +(doom-modeline-def-segment github + "The GitHub notifications." + (when (and doom-modeline-github + (doom-modeline--active) + (not doom-modeline--limited-width-p) + (numberp doom-modeline--github-notification-number) + (> doom-modeline--github-notification-number 0)) + (concat + doom-modeline-spc + (propertize + (concat + (doom-modeline-icon 'faicon "github" "🔔" "#" + :face 'doom-modeline-notification + :v-adjust -0.0575) + doom-modeline-vspc + ;; GitHub API is paged, and the limit is 50 + (propertize + (if (>= doom-modeline--github-notification-number 50) + "50+" + (number-to-string doom-modeline--github-notification-number)) + 'face '(:inherit + (doom-modeline-unread-number doom-modeline-notification)))) + 'help-echo "Github Notifications +mouse-1: Show notifications +mouse-3: Fetch notifications" + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + (lambda () + "Open GitHub notifications page." + (interactive) + (run-with-idle-timer 300 nil #'doom-modeline--github-fetch-notifications) + (browse-url "https://github.com/notifications"))) + (define-key map [mode-line mouse-3] + (lambda () + "Fetching GitHub notifications." + (interactive) + (message "Fetching GitHub notifications...") + (doom-modeline--github-fetch-notifications))) + map)) + doom-modeline-spc))) + + +;; +;; Debug states +;; + +;; Highlight the doom-modeline while debugging. +(defvar-local doom-modeline--debug-cookie nil) +(defun doom-modeline--debug-visual (&rest _) + "Update the face of mode-line for debugging." + (mapc (lambda (buffer) + (with-current-buffer buffer + (setq doom-modeline--debug-cookie + (face-remap-add-relative 'doom-modeline 'doom-modeline-debug-visual)) + (force-mode-line-update))) + (buffer-list))) + +(defun doom-modeline--normal-visual (&rest _) + "Restore the face of mode-line." + (mapc (lambda (buffer) + (with-current-buffer buffer + (when doom-modeline--debug-cookie + (face-remap-remove-relative doom-modeline--debug-cookie) + (force-mode-line-update)))) + (buffer-list))) + +(add-hook 'dap-session-created-hook #'doom-modeline--debug-visual) +(add-hook 'dap-terminated-hook #'doom-modeline--normal-visual) + +(defun doom-modeline-debug-icon (face &rest args) + "Display debug icon with FACE and ARGS." + (doom-modeline-icon 'faicon "bug" "🐛" "!" :face face :v-adjust -0.0575 args)) + +(defun doom-modeline--debug-dap () + "The current `dap-mode' state." + (when (and (bound-and-true-p dap-mode) + (bound-and-true-p lsp-mode)) + (when-let ((session (dap--cur-session))) + (when (dap--session-running session) + (propertize (doom-modeline-debug-icon 'doom-modeline-info) + 'help-echo (format "DAP (%s - %s) +mouse-1: Display debug hydra +mouse-2: Display recent configurations +mouse-3: Disconnect session" + (dap--debug-session-name session) + (dap--debug-session-state session)) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'dap-hydra) + (define-key map [mode-line mouse-2] + #'dap-debug-recent) + (define-key map [mode-line mouse-3] + #'dap-disconnect) + map)))))) + +(defvar-local doom-modeline--debug-dap nil) +(defun doom-modeline-update-debug-dap (&rest _) + "Update dap debug state." + (setq doom-modeline--debug-dap (doom-modeline--debug-dap))) + +(add-hook 'dap-session-created-hook #'doom-modeline-update-debug-dap) +(add-hook 'dap-session-changed-hook #'doom-modeline-update-debug-dap) +(add-hook 'dap-terminated-hook #'doom-modeline-update-debug-dap) + +(defsubst doom-modeline--debug-edebug () + "The current `edebug' state." + (when (bound-and-true-p edebug-mode) + (propertize (doom-modeline-debug-icon 'doom-modeline-info) + 'help-echo (format "EDebug (%s) +mouse-1: Show help +mouse-2: Next +mouse-3: Stop debugging" + edebug-execution-mode) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'edebug-help) + (define-key map [mode-line mouse-2] + #'edebug-next-mode) + (define-key map [mode-line mouse-3] + #'edebug-stop) + map)))) + +(defsubst doom-modeline--debug-on-error () + "The current `debug-on-error' state." + (when debug-on-error + (propertize (doom-modeline-debug-icon 'doom-modeline-urgent) + 'help-echo "Debug on Error +mouse-1: Toggle Debug on Error" + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map 'mouse-1 #'toggle-debug-on-error)))) + +(defsubst doom-modeline--debug-on-quit () + "The current `debug-on-quit' state." + (when debug-on-quit + (propertize (doom-modeline-debug-icon 'doom-modeline-warning) + 'help-echo "Debug on Quit +mouse-1: Toggle Debug on Quit" + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map 'mouse-1 #'toggle-debug-on-quit)))) + +(doom-modeline-def-segment debug + "The current debug state." + (when (and (doom-modeline--active) + (not doom-modeline--limited-width-p)) + (let* ((dap doom-modeline--debug-dap) + (edebug (doom-modeline--debug-edebug)) + (on-error (doom-modeline--debug-on-error)) + (on-quit (doom-modeline--debug-on-quit)) + (vsep doom-modeline-vspc) + (sep (and (or dap edebug on-error on-quit) doom-modeline-spc))) + (concat sep + (and dap (concat dap (and (or edebug on-error on-quit) vsep))) + (and edebug (concat edebug (and (or on-error on-quit) vsep))) + (and on-error (concat on-error (and on-quit vsep))) + on-quit + sep)))) + + +;; +;; PDF pages +;; + +(defvar-local doom-modeline--pdf-pages nil) +(defun doom-modeline-update-pdf-pages () + "Update PDF pages." + (setq doom-modeline--pdf-pages + (format " P%d/%d " + (or (eval `(pdf-view-current-page)) 0) + (pdf-cache-number-of-pages)))) +(add-hook 'pdf-view-change-page-hook #'doom-modeline-update-pdf-pages) + +(doom-modeline-def-segment pdf-pages + "Display PDF pages." + doom-modeline--pdf-pages) + + +;; +;; `mu4e-alert' notifications +;; + +(doom-modeline-def-segment mu4e + "Show notifications of any unread emails in `mu4e'." + (when (and doom-modeline-mu4e + (doom-modeline--active) + (not doom-modeline--limited-width-p) + (bound-and-true-p mu4e-alert-mode-line) + (numberp mu4e-alert-mode-line) + ;; don't display if the unread mails count is zero + (> mu4e-alert-mode-line 0)) + (concat + doom-modeline-spc + (propertize + (concat + (doom-modeline-icon 'material "email" "📧" "#" + :face 'doom-modeline-notification + :height 1.1 :v-adjust -0.2) + doom-modeline-vspc + (propertize + (if (> mu4e-alert-mode-line doom-modeline-number-limit) + (format "%d+" doom-modeline-number-limit) + (number-to-string mu4e-alert-mode-line)) + 'face '(:inherit + (doom-modeline-unread-number doom-modeline-notification)))) + 'mouse-face 'doom-modeline-highlight + 'keymap '(mode-line keymap + (mouse-1 . mu4e-alert-view-unread-mails) + (mouse-2 . mu4e-alert-view-unread-mails) + (mouse-3 . mu4e-alert-view-unread-mails)) + 'help-echo (concat (if (= mu4e-alert-mode-line 1) + "You have an unread email" + (format "You have %s unread emails" mu4e-alert-mode-line)) + "\nClick here to view " + (if (= mu4e-alert-mode-line 1) "it" "them"))) + doom-modeline-spc))) + +(defun doom-modeline-override-mu4e-alert-modeline (&rest _) + "Delete `mu4e-alert-mode-line' from global modeline string." + (when (featurep 'mu4e-alert) + (if (and doom-modeline-mu4e + (bound-and-true-p doom-modeline-mode)) + ;; Delete original modeline + (progn + (setq global-mode-string + (delete '(:eval mu4e-alert-mode-line) global-mode-string)) + (setq mu4e-alert-modeline-formatter #'identity)) + ;; Recover default settings + (setq mu4e-alert-modeline-formatter #'mu4e-alert-default-mode-line-formatter)))) +(advice-add #'mu4e-alert-enable-mode-line-display + :after #'doom-modeline-override-mu4e-alert-modeline) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-mu4e-alert-modeline) + + +;; +;; `gnus' notifications +;; + +(defvar doom-modeline--gnus-unread-mail 0) +(defvar doom-modeline--gnus-started nil + "Used to determine if gnus has started.") +(defun doom-modeline-update-gnus-status (&rest _) + "Get the total number of unread news of gnus group." + (setq doom-modeline--gnus-unread-mail + (when (and doom-modeline-gnus + doom-modeline--gnus-started) + (let ((total-unread-news-number 0)) + (mapc (lambda (g) + (let* ((group (car g)) + (unread (eval `(gnus-group-unread ,group)))) + (when (and (not (seq-contains-p doom-modeline-gnus-excluded-groups group)) + (numberp unread) + (> unread 0)) + (setq total-unread-news-number (+ total-unread-news-number unread))))) + gnus-newsrc-alist) + total-unread-news-number)))) + +;; Update the modeline after changes have been made +(add-hook 'gnus-group-update-hook #'doom-modeline-update-gnus-status) +(add-hook 'gnus-summary-update-hook #'doom-modeline-update-gnus-status) +(add-hook 'gnus-group-update-group-hook #'doom-modeline-update-gnus-status) +(add-hook 'gnus-after-getting-new-news-hook #'doom-modeline-update-gnus-status) + +;; Only start to listen to gnus when gnus is actually running +(defun doom-modeline-start-gnus-listener () + "Start GNUS listener." + (when (and doom-modeline-gnus + (not doom-modeline--gnus-started)) + (setq doom-modeline--gnus-started t) + ;; Scan gnus in the background if the timer is higher than 0 + (doom-modeline-update-gnus-status) + (if (> doom-modeline-gnus-timer 0) + (gnus-demon-add-handler 'gnus-demon-scan-news doom-modeline-gnus-timer doom-modeline-gnus-idle)))) +(add-hook 'gnus-started-hook #'doom-modeline-start-gnus-listener) + +;; Stop the listener if gnus isn't running +(defun doom-modeline-stop-gnus-listener () + "Stop GNUS listener." + (setq doom-modeline--gnus-started nil)) +(add-hook 'gnus-exit-gnus-hook #'doom-modeline-stop-gnus-listener) + +(doom-modeline-def-segment gnus + "Show notifications of any unread emails in `gnus'." + (when (and (doom-modeline--active) + (not doom-modeline--limited-width-p) + doom-modeline-gnus + doom-modeline--gnus-started + ;; Don't display if the unread mails count is zero + (numberp doom-modeline--gnus-unread-mail) + (> doom-modeline--gnus-unread-mail 0)) + (concat + doom-modeline-spc + (propertize + (concat + (doom-modeline-icon 'material "email" "📧" "#" + :face 'doom-modeline-notification + :height 1.1 :v-adjust -0.2) + doom-modeline-vspc + (propertize + (if (> doom-modeline--gnus-unread-mail doom-modeline-number-limit) + (format "%d+" doom-modeline-number-limit) + (number-to-string doom-modeline--gnus-unread-mail)) + 'face '(:inherit + (doom-modeline-unread-number doom-modeline-notification)))) + 'mouse-face 'doom-modeline-highlight + 'help-echo (if (= doom-modeline--gnus-unread-mail 1) + "You have an unread email" + (format "You have %s unread emails" doom-modeline--gnus-unread-mail))) + doom-modeline-spc))) + + +;; +;; IRC notifications +;; + +(defun doom-modeline--shorten-irc (name) + "Wrapper for `tracking-shorten' and `erc-track-shorten-function' with NAME. + +One key difference is that when `tracking-shorten' and +`erc-track-shorten-function' returns nil we will instead return the original +value of name. This is necessary in cases where the user has stylized the name +to be an icon and we don't want to remove that so we just return the original." + (or (and (boundp 'tracking-shorten) + (car (tracking-shorten (list name)))) + (and (boundp 'erc-track-shorten-function) + (functionp erc-track-shorten-function) + (car (funcall erc-track-shorten-function (list name)))) + (and (boundp 'rcirc-short-buffer-name) + (rcirc-short-buffer-name name)) + name)) + +(defun doom-modeline--tracking-buffers (buffers) + "Logic to convert some irc BUFFERS to their font-awesome icon." + (mapconcat + (lambda (b) + (propertize + (doom-modeline--shorten-irc (funcall doom-modeline-irc-stylize b)) + 'face '(:inherit (doom-modeline-unread-number doom-modeline-notification)) + 'help-echo (format "IRC Notification: %s\nmouse-1: Switch to buffer" b) + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map 'mouse-1 + (lambda () + (interactive) + (when (buffer-live-p (get-buffer b)) + (switch-to-buffer b)))))) + buffers + doom-modeline-vspc)) + +(defun doom-modeline--circe-p () + "Check if `circe' is in use." + (boundp 'tracking-mode-line-buffers)) + +(defun doom-modeline--erc-p () + "Check if `erc' is in use." + (boundp 'erc-modified-channels-alist)) + +(defun doom-modeline--rcirc-p () + "Check if `rcirc' is in use." + (bound-and-true-p rcirc-track-minor-mode)) + +(defun doom-modeline--get-buffers () + "Gets the buffers that have activity." + (cond + ((doom-modeline--circe-p) + tracking-buffers) + ((doom-modeline--erc-p) + (mapcar (lambda (l) + (buffer-name (car l))) + erc-modified-channels-alist)) + ((doom-modeline--rcirc-p) + (mapcar (lambda (b) + (buffer-name b)) + rcirc-activity)))) + +;; Create a modeline segment that contains all the irc tracked buffers +(doom-modeline-def-segment irc-buffers + "The list of shortened, unread irc buffers." + (when (and doom-modeline-irc + (doom-modeline--active) + (not doom-modeline--limited-width-p)) + (let* ((buffers (doom-modeline--get-buffers)) + (number (length buffers))) + (when (> number 0) + (concat + doom-modeline-spc + (doom-modeline--tracking-buffers buffers) + doom-modeline-spc))))) + +(doom-modeline-def-segment irc + "A notification icon for any unread irc buffer." + (when (and doom-modeline-irc + (doom-modeline--active) + (not doom-modeline--limited-width-p)) + (let* ((buffers (doom-modeline--get-buffers)) + (number (length buffers))) + (when (> number 0) + (concat + doom-modeline-spc + + (propertize (concat + (doom-modeline-icon 'material "message" "🗊" "#" + :face 'doom-modeline-notification + :height 1.0 :v-adjust -0.225) + doom-modeline-vspc + ;; Display the number of unread buffers + (propertize (number-to-string number) + 'face '(:inherit + (doom-modeline-unread-number + doom-modeline-notification)))) + 'help-echo (format "IRC Notifications: %s\n%s" + (mapconcat + (lambda (b) (funcall doom-modeline-irc-stylize b)) + buffers + ", ") + (cond + ((doom-modeline--circe-p) + "mouse-1: Switch to previous unread buffer +mouse-3: Switch to next unread buffer") + ((doom-modeline--erc-p) + "mouse-1: Switch to buffer +mouse-3: Switch to next unread buffer") + ((doom-modeline--rcirc-p) + "mouse-1: Switch to server buffer +mouse-3: Switch to next unread buffer"))) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (cond + ((doom-modeline--circe-p) + (define-key map [mode-line mouse-1] + #'tracking-previous-buffer) + (define-key map [mode-line mouse-3] + #'tracking-next-buffer)) + ((doom-modeline--erc-p) + (define-key map [mode-line mouse-1] + #'erc-switch-to-buffer) + (define-key map [mode-line mouse-3] + #'erc-track-switch-buffer)) + ((doom-modeline--rcirc-p) + (define-key map [mode-line mouse-1] + #'rcirc-switch-to-server-buffer) + (define-key map [mode-line mouse-3] + #'rcirc-next-active-buffer))) + map)) + + ;; Display the unread irc buffers as well + (when doom-modeline-irc-buffers + (concat doom-modeline-spc + (doom-modeline--tracking-buffers buffers))) + + doom-modeline-spc))))) + +(defun doom-modeline-override-rcirc-modeline () + "Override default `rcirc' mode-line." + (if (bound-and-true-p doom-modeline-mode) + (setq global-mode-string + (delq 'rcirc-activity-string global-mode-string)) + (when (and rcirc-track-minor-mode + (not (memq 'rcirc-activity-string global-mode-string))) + (setq global-mode-string + (append global-mode-string '(rcirc-activity-string)))))) +(add-hook 'rcirc-track-minor-mode-hook #'doom-modeline-override-rcirc-modeline) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-rcirc-modeline) + + +;; +;; Battery status +;; + +(defvar doom-modeline--battery-status nil) +(defun doom-modeline-update-battery-status () + "Update battery status." + (setq doom-modeline--battery-status + (when (bound-and-true-p display-battery-mode) + (let* ((data (and battery-status-function + (functionp battery-status-function) + (funcall battery-status-function))) + (charging? (string-equal "AC" (cdr (assoc ?L data)))) + (percentage (car (read-from-string (or (cdr (assq ?p data)) "ERR")))) + (valid-percentage? (and (numberp percentage) + (>= percentage 0) + (<= percentage battery-mode-line-limit))) + (face (if valid-percentage? + (cond (charging? 'doom-modeline-battery-charging) + ((< percentage battery-load-critical) 'doom-modeline-battery-critical) + ((< percentage 25) 'doom-modeline-battery-warning) + ((< percentage 95) 'doom-modeline-battery-normal) + (t 'doom-modeline-battery-full)) + 'doom-modeline-battery-error)) + (icon (if valid-percentage? + (cond (charging? + (doom-modeline-icon 'alltheicon "battery-charging" "🔋" "+" + :face face :height 1.4 :v-adjust -0.1)) + ((> percentage 95) + (doom-modeline-icon 'faicon "battery-full" "🔋" "-" + :face face :v-adjust -0.0575)) + ((> percentage 70) + (doom-modeline-icon 'faicon "battery-three-quarters" "🔋" "-" + :face face :v-adjust -0.0575)) + ((> percentage 40) + (doom-modeline-icon 'faicon "battery-half" "🔋" "-" + :face face :v-adjust -0.0575)) + ((> percentage battery-load-critical) + (doom-modeline-icon 'faicon "battery-quarter" "🔋" "-" + :face face :v-adjust -0.0575)) + (t (doom-modeline-icon 'faicon "battery-empty" "🔋" "!" + :face face :v-adjust -0.0575))) + (doom-modeline-icon 'faicon "battery-empty" "⚠" "N/A" + :face face :v-adjust -0.0575))) + (text (if valid-percentage? (format "%d%%%%" percentage) "")) + (help-echo (if (and battery-echo-area-format data valid-percentage?) + (battery-format battery-echo-area-format data) + "Battery status not available"))) + (cons (propertize icon 'help-echo help-echo) + (propertize text 'face face 'help-echo help-echo)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-battery-status)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-battery-status)))))) + +(doom-modeline-def-segment battery + "Display battery status." + (when (and (doom-modeline--active) + (not doom-modeline--limited-width-p) + (bound-and-true-p display-battery-mode)) + (concat doom-modeline-spc + (concat + (car doom-modeline--battery-status) + doom-modeline-vspc + (cdr doom-modeline--battery-status)) + doom-modeline-spc))) + +(defun doom-modeline-override-battery-modeline () + "Override default battery mode-line." + (if (bound-and-true-p doom-modeline-mode) + (progn + (advice-add #'battery-update :override #'doom-modeline-update-battery-status) + (setq global-mode-string + (delq 'battery-mode-line-string global-mode-string)) + (and (bound-and-true-p display-battery-mode) (battery-update))) + (progn + (advice-remove #'battery-update #'doom-modeline-update-battery-status) + (when (and display-battery-mode battery-status-function battery-mode-line-format + (not (memq 'battery-mode-line-string global-mode-string))) + (setq global-mode-string + (append global-mode-string '(battery-mode-line-string))))))) +(add-hook 'display-battery-mode-hook #'doom-modeline-override-battery-modeline) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-battery-modeline) + + +;; +;; Package information +;; + +(doom-modeline-def-segment package + "Show package information via `paradox'." + (concat + (doom-modeline-display-text + (format-mode-line 'mode-line-front-space)) + + (when (and doom-modeline-icon doom-modeline-major-mode-icon) + (concat + doom-modeline-spc + (doom-modeline-icon 'faicon "archive" nil nil + :face (doom-modeline-face + (if doom-modeline-major-mode-color-icon + 'all-the-icons-silver + 'mode-line)) + :height 1.0 + :v-adjust -0.0575))) + (doom-modeline-display-text + (format-mode-line 'mode-line-buffer-identification)))) + + +;; +;; Helm +;; + +(defvar doom-modeline--helm-buffer-ids + '(("*helm*" . "HELM") + ("*helm M-x*" . "HELM M-x") + ("*swiper*" . "SWIPER") + ("*Projectile Perspectives*" . "HELM Projectile Perspectives") + ("*Projectile Layouts*" . "HELM Projectile Layouts") + ("*helm-ag*" . (lambda () + (format "HELM Ag: Using %s" + (car (split-string helm-ag-base-command)))))) + "Alist of custom helm buffer names to use. +The cdr can also be a function that returns a name to use.") + +(doom-modeline-def-segment helm-buffer-id + "Helm session identifier." + (when (bound-and-true-p helm-alive-p) + (concat + doom-modeline-spc + (when doom-modeline-icon + (concat + (doom-modeline-icon 'fileicon "elisp" nil nil + :face (doom-modeline-face + (and doom-modeline-major-mode-color-icon + 'all-the-icons-blue)) + :height 1.0 + :v-adjust -0.15) + doom-modeline-spc)) + (propertize + (let ((custom (cdr (assoc (buffer-name) doom-modeline--helm-buffer-ids))) + (case-fold-search t) + (name (replace-regexp-in-string "-" " " (buffer-name)))) + (cond ((stringp custom) custom) + ((functionp custom) (funcall custom)) + (t + (string-match "\\*helm:? \\(mode \\)?\\([^\\*]+\\)\\*" name) + (concat "HELM " (capitalize (match-string 2 name)))))) + 'face (doom-modeline-face 'doom-modeline-buffer-file)) + doom-modeline-spc))) + +(doom-modeline-def-segment helm-number + "Number of helm candidates." + (when (bound-and-true-p helm-alive-p) + (concat + (propertize (format " %d/%d" + (helm-candidate-number-at-point) + (helm-get-candidate-number t)) + 'face (doom-modeline-face 'doom-modeline-buffer-path)) + (propertize (format " (%d total) " (helm-get-candidate-number)) + 'face (doom-modeline-face 'doom-modeline-info))))) + +(doom-modeline-def-segment helm-help + "Helm keybindings help." + (when (bound-and-true-p helm-alive-p) + (mapcar + (lambda (s) + (if (string-prefix-p "\\<" s) + (propertize (substitute-command-keys s) + 'face (doom-modeline-face + 'doom-modeline-buffer-file)) + s)) + '("\\\\[helm-help]" "(help) " + "\\\\[helm-select-action]" "(actions) " + "\\\\[helm-maybe-exit-minibuffer]/F1/F2..." "(action) ")))) + +(doom-modeline-def-segment helm-prefix-argument + "Helm prefix argument." + (when (and (bound-and-true-p helm-alive-p) + helm--mode-line-display-prefarg) + (let ((arg (prefix-numeric-value (or prefix-arg current-prefix-arg)))) + (unless (= arg 1) + (propertize (format "C-u %s" arg) + 'face (doom-modeline-face 'doom-modeline-info)))))) + +(defvar doom-modeline--helm-current-source nil + "The currently active helm source.") +(doom-modeline-def-segment helm-follow + "Helm follow indicator." + (and (bound-and-true-p helm-alive-p) + doom-modeline--helm-current-source + (eq 1 (cdr (assq 'follow doom-modeline--helm-current-source))) + "HF")) + +;; +;; Git timemachine +;; + +(doom-modeline-def-segment git-timemachine + (concat + doom-modeline-spc + (doom-modeline--buffer-mode-icon) + (doom-modeline--buffer-state-icon) + (propertize + "*%b*" + 'face (doom-modeline-face 'doom-modeline-buffer-timemachine)))) + +;; +;; Markdown/Org preview +;; + +(doom-modeline-def-segment grip + (when (bound-and-true-p grip-mode) + (concat + doom-modeline-spc + (let ((face (doom-modeline-face + (if grip--process + (pcase (process-status grip--process) + ('run 'doom-modeline-buffer-path) + ('exit 'doom-modeline-warning) + (_ 'doom-modeline-urgent)) + 'doom-modeline-urgent)))) + (propertize (doom-modeline-icon 'material "pageview" "🗐" "@" + :face (if doom-modeline-icon + `(:inherit ,face :weight normal) + face) + :height 1.2 :v-adjust -0.2) + 'help-echo (format "Preview on %s +mouse-1: Preview in browser +mouse-2: Stop preview +mouse-3: Restart preview" + (grip--preview-url)) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'grip-browse-preview) + (define-key map [mode-line mouse-2] + #'grip-stop-preview) + (define-key map [mode-line mouse-3] + #'grip-restart-preview) + map))) + doom-modeline-spc))) + +;; +;; Follow mode +;; + +(doom-modeline-def-segment follow + (when (bound-and-true-p follow-mode) + (let* ((windows (follow-all-followers)) + (nwindows (length windows)) + (nfollowing (- (length (memq (selected-window) windows)) 1))) + (concat + doom-modeline-spc + (propertize (format "Follow %d/%d" (- nwindows nfollowing) nwindows) + 'face 'doom-modeline-buffer-minor-mode))))) + +;; +;; Display time +;; + +(doom-modeline-def-segment time + (when (and doom-modeline-time + (bound-and-true-p display-time-mode) + (not doom-modeline--limited-width-p)) + (concat + doom-modeline-spc + (when doom-modeline-time-icon + (concat + (doom-modeline-icon 'octicon "calendar" "📅" "" + :face 'doom-modeline-time + :v-adjust -0.05) + (and (or doom-modeline-icon doom-modeline-unicode-fallback) + doom-modeline-spc))) + (propertize display-time-string + 'face (doom-modeline-face 'doom-modeline-time))))) + +(defun doom-modeline-override-display-time-modeline () + "Override default display-time mode-line." + (if (bound-and-true-p doom-modeline-mode) + (setq global-mode-string (delq 'display-time-string global-mode-string)) + (setq global-mode-string (append global-mode-string '(display-time-string))))) +(add-hook 'display-time-mode-hook #'doom-modeline-override-display-time-modeline) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-display-time-modeline) + +(provide 'doom-modeline-segments) + +;;; doom-modeline-segments.el ends here diff --git a/code/elpa/doom-modeline-20220816.1627/doom-modeline.el b/code/elpa/doom-modeline-20220816.1627/doom-modeline.el new file mode 100644 index 0000000..faeb439 --- /dev/null +++ b/code/elpa/doom-modeline-20220816.1627/doom-modeline.el @@ -0,0 +1,305 @@ +;;; doom-modeline.el --- A minimal and modern mode-line -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2020 Vincent Zhang + +;; Author: Vincent Zhang +;; Homepage: https://github.com/seagle0128/doom-modeline +;; Version: 3.3.2 +;; Package-Requires: ((emacs "25.1") (compat "28.1.1.1") (shrink-path "0.2.0")) +;; Keywords: faces mode-line + +;; 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 . +;; + +;;; Commentary: +;; +;; This package offers a fancy and fast mode-line inspired by minimalism design. +;; +;; It's integrated into Doom Emacs (https://github.com/hlissner/doom-emacs) and +;; Centaur Emacs (https://github.com/seagle0128/.emacs.d). +;; +;; The doom-modeline offers: +;; - A match count panel (for anzu, iedit, multiple-cursors, symbol-overlay, +;; evil-search and evil-substitute) +;; - An indicator for recording a macro +;; - Current environment version (e.g. python, ruby, go, etc.) in the major-mode +;; - A customizable mode-line height (see doom-modeline-height) +;; - A minor modes segment which is compatible with minions +;; - An error/warning count segment for flymake/flycheck +;; - A workspace number segment for eyebrowse +;; - A perspective name segment for persp-mode +;; - A window number segment for winum and window-numbering +;; - An indicator for modal editing state, including evil, overwrite, god, ryo +;; and xah-fly-keys, etc. +;; - An indicator for battery status +;; - An indicator for current input method +;; - An indicator for debug state +;; - An indicator for remote host +;; - An indicator for LSP state with lsp-mode or eglot +;; - An indicator for github notifications +;; - An indicator for unread emails with mu4e-alert +;; - An indicator for unread emails with gnus (basically builtin) +;; - An indicator for irc notifications with circe, rcirc or erc. +;; - An indicator for buffer position which is compatible with nyan-mode or poke-line +;; - An indicator for party parrot +;; - An indicator for PDF page number with pdf-tools +;; - An indicator for markdown/org previews with grip +;; - Truncated file name, file icon, buffer state and project name in buffer +;; information segment, which is compatible with project, find-file-in-project +;; and projectile +;; - New mode-line for Info-mode buffers +;; - New package mode-line for paradox +;; - New mode-line for helm buffers +;; - New mode-line for git-timemachine buffers +;; +;; Installation: +;; From melpa, `M-x package-install RET doom-modeline RET`. +;; In `init.el`, +;; (require 'doom-modeline) +;; (doom-modeline-mode 1) +;; or +;; (use-package doom-modeline +;; :ensure t +;; :hook (after-init . doom-modeline-mode)) +;; + +;;; Code: + +(require 'doom-modeline-core) +(require 'doom-modeline-segments) + + +;; +;; Mode lines +;; + +(doom-modeline-def-modeline 'main + '(bar workspace-name window-number modals matches follow buffer-info remote-host buffer-position word-count parrot selection-info) + '(objed-state misc-info persp-name battery grip irc mu4e gnus github debug repl lsp minor-modes input-method indent-info buffer-encoding major-mode process vcs checker time)) + +(doom-modeline-def-modeline 'minimal + '(bar matches buffer-info-simple) + '(media-info major-mode time)) + +(doom-modeline-def-modeline 'special + '(bar window-number modals matches buffer-info remote-host buffer-position word-count parrot selection-info) + '(objed-state misc-info battery irc-buffers debug minor-modes input-method indent-info buffer-encoding major-mode process time)) + +(doom-modeline-def-modeline 'project + '(bar window-number modals buffer-default-directory remote-host buffer-position) + '(misc-info battery irc mu4e gnus github debug minor-modes input-method major-mode process time)) + +(doom-modeline-def-modeline 'dashboard + '(bar window-number buffer-default-directory-simple remote-host) + '(misc-info battery irc mu4e gnus github debug minor-modes input-method major-mode process time)) + +(doom-modeline-def-modeline 'vcs + '(bar window-number modals matches buffer-info remote-host buffer-position parrot selection-info) + '(misc-info battery irc mu4e gnus github debug minor-modes buffer-encoding major-mode process time)) + +(doom-modeline-def-modeline 'package + '(bar window-number package) + '(misc-info major-mode process time)) + +(doom-modeline-def-modeline 'info + '(bar window-number buffer-info info-nodes buffer-position parrot selection-info) + '(misc-info buffer-encoding major-mode time)) + +(doom-modeline-def-modeline 'media + '(bar window-number buffer-size buffer-info) + '(misc-info media-info major-mode process vcs time)) + +(doom-modeline-def-modeline 'message + '(bar window-number modals matches buffer-info-simple buffer-position word-count parrot selection-info) + '(objed-state misc-info battery debug minor-modes input-method indent-info buffer-encoding major-mode time)) + +(doom-modeline-def-modeline 'pdf + '(bar window-number matches buffer-info pdf-pages) + '(misc-info major-mode process vcs time)) + +(doom-modeline-def-modeline 'org-src + '(bar window-number modals matches buffer-info-simple buffer-position word-count parrot selection-info) + '(objed-state misc-info debug lsp minor-modes input-method indent-info buffer-encoding major-mode process checker time)) + +(doom-modeline-def-modeline 'helm + '(bar helm-buffer-id helm-number helm-follow helm-prefix-argument) + '(helm-help time)) + +(doom-modeline-def-modeline 'timemachine + '(bar window-number modals matches git-timemachine buffer-position word-count parrot selection-info) + '(misc-info minor-modes indent-info buffer-encoding major-mode time)) + + +;; +;; Interfaces +;; + +;;;###autoload +(defun doom-modeline-set-main-modeline (&optional default) + "Set main mode-line. +If DEFAULT is non-nil, set the default mode-line for all buffers." + (doom-modeline-set-modeline 'main default)) + +;;;###autoload +(defun doom-modeline-set-minimal-modeline () + "Set minimal mode-line." + (doom-modeline-set-modeline 'minimal)) + +;;;###autoload +(defun doom-modeline-set-special-modeline () + "Set special mode-line." + (doom-modeline-set-modeline 'special)) + +;;;###autoload +(defun doom-modeline-set-project-modeline () + "Set project mode-line." + (doom-modeline-set-modeline 'project)) + +;;;###autoload +(defun doom-modeline-set-dashboard-modeline () + "Set dashboard mode-line." + (doom-modeline-set-modeline 'dashboard)) + +;;;###autoload +(defun doom-modeline-set-vcs-modeline () + "Set vcs mode-line." + (doom-modeline-set-modeline 'vcs)) + +;;;###autoload +(defun doom-modeline-set-info-modeline () + "Set Info mode-line." + (doom-modeline-set-modeline 'info)) + +;;;###autoload +(defun doom-modeline-set-package-modeline () + "Set package mode-line." + (doom-modeline-set-modeline 'package)) + +;;;###autoload +(defun doom-modeline-set-media-modeline () + "Set media mode-line." + (doom-modeline-set-modeline 'media)) + +;;;###autoload +(defun doom-modeline-set-message-modeline () + "Set message mode-line." + (doom-modeline-set-modeline 'message)) + +;;;###autoload +(defun doom-modeline-set-pdf-modeline () + "Set pdf mode-line." + (doom-modeline-set-modeline 'pdf)) + +;;;###autoload +(defun doom-modeline-set-org-src-modeline () + "Set org-src mode-line." + (doom-modeline-set-modeline 'org-src)) + +;;;###autoload +(defun doom-modeline-set-helm-modeline (&rest _) ; To advice helm + "Set helm mode-line." + (doom-modeline-set-modeline 'helm)) + +;;;###autoload +(defun doom-modeline-set-timemachine-modeline () + "Set timemachine mode-line." + (doom-modeline-set-modeline 'timemachine)) + + +;; +;; Minor mode +;; + +(defvar doom-modeline-mode-map (make-sparse-keymap)) + +;; Suppress warnings +(defvar 2C-mode-line-format) +(declare-function helm-display-mode-line "ext:helm-core") + +;;;###autoload +(define-minor-mode doom-modeline-mode + "Toggle `doom-modeline' on or off." + :group 'doom-modeline + :global t + :lighter nil + :keymap doom-modeline-mode-map + (if doom-modeline-mode + (progn + (doom-modeline-refresh-bars) ; Create bars + (doom-modeline-set-main-modeline t) ; Set default mode-line + + ;; Apply to all existing buffers. + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-set-main-modeline))) + + ;; For two-column editing + (setq 2C-mode-line-format (doom-modeline 'special)) + + ;; Add hooks + (add-hook 'Info-mode-hook #'doom-modeline-set-info-modeline) + (add-hook 'dired-mode-hook #'doom-modeline-set-project-modeline) + (add-hook 'dashboard-mode-hook #'doom-modeline-set-dashboard-modeline) + (add-hook 'image-mode-hook #'doom-modeline-set-media-modeline) + (add-hook 'message-mode-hook #'doom-modeline-set-message-modeline) + (add-hook 'git-commit-mode-hook #'doom-modeline-set-message-modeline) + (add-hook 'magit-mode-hook #'doom-modeline-set-vcs-modeline) + (add-hook 'circe-mode-hook #'doom-modeline-set-special-modeline) + (add-hook 'erc-mode-hook #'doom-modeline-set-special-modeline) + (add-hook 'rcirc-mode-hook #'doom-modeline-set-special-modeline) + (add-hook 'pdf-view-mode-hook #'doom-modeline-set-pdf-modeline) + (add-hook 'org-src-mode-hook #'doom-modeline-set-org-src-modeline) + (add-hook 'git-timemachine-mode-hook #'doom-modeline-set-timemachine-modeline) + (add-hook 'paradox-menu-mode-hook #'doom-modeline-set-package-modeline) + (add-hook 'xwidget-webkit-mode-hook #'doom-modeline-set-minimal-modeline) + + ;; Add advices + (advice-add #'helm-display-mode-line :after #'doom-modeline-set-helm-modeline)) + (progn + ;; Restore mode-line + (let ((original-format (doom-modeline--original-value 'mode-line-format))) + (setq-default mode-line-format original-format) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (setq mode-line-format original-format)))) + + ;; For two-column editing + (setq 2C-mode-line-format (doom-modeline--original-value '2C-mode-line-format)) + + ;; Remove hooks + (remove-hook 'Info-mode-hook #'doom-modeline-set-info-modeline) + (remove-hook 'dired-mode-hook #'doom-modeline-set-project-modeline) + (remove-hook 'dashboard-mode-hook #'doom-modeline-set-dashboard-modeline) + (remove-hook 'image-mode-hook #'doom-modeline-set-media-modeline) + (remove-hook 'message-mode-hook #'doom-modeline-set-message-modeline) + (remove-hook 'git-commit-mode-hook #'doom-modeline-set-message-modeline) + (remove-hook 'magit-mode-hook #'doom-modeline-set-vcs-modeline) + (remove-hook 'circe-mode-hook #'doom-modeline-set-special-modeline) + (remove-hook 'erc-mode-hook #'doom-modeline-set-special-modeline) + (remove-hook 'rcirc-mode-hook #'doom-modeline-set-special-modeline) + (remove-hook 'pdf-view-mode-hook #'doom-modeline-set-pdf-modeline) + (remove-hook 'org-src-mode-hook #'doom-modeline-set-org-src-modeline) + (remove-hook 'git-timemachine-mode-hook #'doom-modeline-set-timemachine-modeline) + (remove-hook 'paradox-menu-mode-hook #'doom-modeline-set-package-modeline) + (remove-hook 'xwidget-webkit-mode-hook #'doom-modeline-set-minimal-modeline) + + ;; Remove advices + (advice-remove #'helm-display-mode-line #'doom-modeline-set-helm-modeline)))) + +(provide 'doom-modeline) + +;;; doom-modeline.el ends here diff --git a/code/elpa/f-20220814.1054/f-autoloads.el b/code/elpa/f-20220814.1054/f-autoloads.el new file mode 100644 index 0000000..4dd1788 --- /dev/null +++ b/code/elpa/f-20220814.1054/f-autoloads.el @@ -0,0 +1,26 @@ +;;; f-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "f" "f.el" (0 0 0 0)) +;;; Generated autoloads from f.el + +(register-definition-prefixes "f" '("f-")) + +;;;*** + +;;;### (autoloads nil nil ("f-pkg.el" "f-shortdoc.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; f-autoloads.el ends here diff --git a/code/elpa/f-20220814.1054/f-pkg.el b/code/elpa/f-20220814.1054/f-pkg.el new file mode 100644 index 0000000..15648d4 --- /dev/null +++ b/code/elpa/f-20220814.1054/f-pkg.el @@ -0,0 +1,14 @@ +(define-package "f" "20220814.1054" "Modern API for working with files and directories" + '((emacs "24.1") + (s "1.7.0") + (dash "2.2.0")) + :commit "85c91f95f8b98e153fd959ae467b46bf79622c5d" :authors + '(("Johan Andersson" . "johan.rejeep@gmail.com")) + :maintainer + '("Lucien Cartier-Tilet" . "lucien@phundrak.com") + :keywords + '("files" "directories") + :url "http://github.com/rejeep/f.el") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/f-20220814.1054/f-shortdoc.el b/code/elpa/f-20220814.1054/f-shortdoc.el new file mode 100644 index 0000000..9d09f8b --- /dev/null +++ b/code/elpa/f-20220814.1054/f-shortdoc.el @@ -0,0 +1,379 @@ +;; -*- no-byte-compile: t; -*- +;;; f-shortdoc.el --- Shortdoc for f.el -*- lexical-binding: t -*- + +;; Author: Lucien Cartier-Tilet +;; Maintainer: Lucien Cartier-Tilet +;; Version: 0.1.0 +;; Package-Requires: ((emacs "28.1")) +;; Homepage: https://github.com/rejeep/f.el + +;; 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 . + + +;;; Commentary: + +;; Shortdoc implementation for f.el + +;;; Code: + +(when (version<= "28.1" emacs-version) + (require 'shortdoc) + + (define-short-documentation-group f + "Paths" + (f-join + :eval (f-join "path") + :eval (f-join "path" "to") + :eval (f-join "/" "path" "to" "heaven") + :eval (f-join "path" "/to" "file")) + + (f-split + :eval (f-split "path") + :eval (f-split "path/to") + :eval (f-split "/path/to/heaven") + :eval (f-split "~/back/to/earth")) + + (f-expand + :no-eval (f-expand "name") + :result-string "/default/directory/name" + :no-eval (f-expand "name" "other/directory") + :result-string "other/directory/name") + + (f-filename + :eval (f-filename "path/to/file.ext") + :eval (f-filename "path/to/directory")) + + (f-dirname + :eval (f-dirname "path/to/file.ext") + :eval (f-dirname "path/to/directory") + :eval (f-dirname "/")) + + (f-common-parent + :eval (f-common-parent '("foo/bar/baz" "foo/bar/qux" "foo/bar/mux")) + :eval (f-common-parent '("/foo/bar/baz" "/foo/bar/qux" "/foo/bax/mux")) + :eval (f-common-parent '("foo/bar/baz" "quack/bar/qux" "lack/bar/mux"))) + + (f-ext + :eval (f-ext "path/to/file") + :eval (f-ext "path/to/file.txt") + :eval (f-ext "path/to/file.txt.org")) + + (f-no-ext + :eval (f-no-ext "path/to/file") + :eval (f-no-ext "path/to/file.txt") + :eval (f-no-ext "path/to/file.txt.org")) + + (f-swap-ext + :eval (f-swap-ext "path/to/file.ext" "org")) + + (f-base + :eval (f-base "path/to/file.ext") + :eval (f-base "path/to/directory")) + + (f-relative + :eval (f-relative "/some/path/relative/to/my/file.txt" "/some/path/") + :eval (f-relative "/default/directory/my/file.txt")) + + (f-short + :no-eval (f-short "/Users/foo/Code/on/macOS") + :result-string "~/Code/on/macOS" + :no-eval (f-short "/home/foo/Code/on/linux") + :result-string "~/Code/on/linux" + :eval (f-short "/path/to/Code/bar")) + + (f-long + :eval (f-long "~/Code/bar") + :eval (f-long "/path/to/Code/bar")) + + (f-canonical + :eval (f-canonical "/path/to/real/file") + :no-eval (f-canonical "/link/to/file") + :result-string "/path/to/real/file") + + (f-slash + :no-eval (f-slash "/path/to/file") + :result-string "/path/to/file" + :no-eval (f-slash "/path/to/dir") + :result-string "/path/to/dir/" + :no-eval (f-slash "/path/to/dir/") + :result-string "/path/to/dir/") + + (f-full + :eval (f-full "~/path/to/file") + :eval (f-full "~/path/to/dir") + :eval (f-full "~/path/to/dir/")) + + (f-uniquify + :eval (f-uniquify '("/foo/bar" "/foo/baz" "/foo/quux")) + :eval (f-uniquify '("/foo/bar" "/www/bar" "/foo/quux")) + :eval (f-uniquify '("/foo/bar" "/www/bar" "/www/bar/quux")) + :eval (f-uniquify '("/foo/bar" "/foo/baz" "/home/www/bar" "/home/www/baz" "/var/foo" "/opt/foo/www/baz"))) + + (f-uniquify-alist + :eval (f-uniquify-alist '("/foo/bar" "/foo/baz" "/foo/quux")) + :eval (f-uniquify-alist '("/foo/bar" "/www/bar" "/foo/quux")) + :eval (f-uniquify-alist '("/foo/bar" "/www/bar" "/www/bar/quux")) + :eval (f-uniquify-alist '("/foo/bar" "/foo/baz" "/home/www/bar" "/home/www/baz" "/var/foo" "/opt/foo/www/baz"))) + + "I/O" + (f-read-bytes + :no-eval* (f-read-bytes "path/to/binary/data")) + + (f-write-bytes + :no-eval* (f-write-bytes (unibyte-string 72 101 108 108 111 32 119 111 114 108 100) "path/to/binary/data")) + + (f-append-bytes + :no-eval* (f-append-bytes "path/to/file" (unibyte-string 72 101 108 108 111 32 119 111 114 108 100))) + + (f-read-text + :no-eval* (f-read-text "path/to/file.txt" 'utf-8) + :no-eval* (f-read "path/to/file.txt" 'utf-8)) + + (f-write-text + :no-eval* (f-write-text "Hello world" 'utf-8 "path/to/file.txt") + :no-eval* (f-write "Hello world" 'utf-8 "path/to/file.txt")) + + (f-append-text + :no-eval* (f-append-text "Hello world" 'utf-8 "path/to/file.txt") + :no-eval* (f-append "Hello world" 'utf-8 "path/to/file.txt")) + + "Destructive" + (f-mkdir + :no-eval (f-mkdir "dir") + :result-string "creates /default/directory/dir" + :no-eval (f-mkdir "other" "dir") + :result-string "creates /default/directory/other/dir" + :no-eval (f-mkdir "/" "some" "path") + :result-string "creates /some/path" + :no-eval (f-mkdir "~" "yet" "another" "dir") + :result-string "creates ~/yet/another/dir") + + (f-mkdir-full-path + :no-eval (f-mkdir-full-path "dir") + :result-string "creates /default/directory/dir" + :no-eval (f-mkdir-full-path "other/dir") + :result-string "creates /default/directory/other/dir" + :no-eval (f-mkdir-full-path "/some/path") + :result-string "creates /some/path" + :no-eval (f-mkdir-full-path "~/yet/another/dir") + :result-string "creates ~/yet/another/dir") + + (f-delete + :no-eval* (f-delete "dir") + :no-eval* (f-delete "other/dir" t) + :no-eval* (f-delete "path/to/file.txt")) + + (f-symlink + :no-eval* (f-symlink "path/to/source" "path/to/link")) + + (f-move + :no-eval* (f-move "path/to/file.txt" "new-file.txt") + :no-eval* (f-move "path/to/file.txt" "other/path")) + + (f-copy + :no-eval* (f-copy "path/to/file.txt" "new-file.txt") + :no-eval* (f-copy "path/to/dir" "other/dir")) + + (f-copy-contents + :no-eval* (f-copy-contents "path/to/dir" "path/to/other/dir")) + + (f-touch + :no-eval* (f-touch "path/to/existing/file.txt") + :no-eval* (f-touch "path/to/non/existing/file.txt")) + + "Predicates" + (f-exists-p + :no-eval* (f-exists-p "path/to/file.txt") + :no-eval* (f-exists-p "path/to/dir")) + + (f-directory-p + :no-eval* (f-directory-p "path/to/file.txt") + :no-eval* (f-directory-p "path/to/dir")) + + (f-file-p + :no-eval* (f-file-p "path/to/file.txt") + :no-eval* (f-file-p "path/to/dir")) + + (f-symlink-p + :no-eval* (f-symlink-p "path/to/file.txt") + :no-eval* (f-symlink-p "path/to/dir") + :no-eval* (f-symlink-p "path/to/link")) + + (f-readable-p + :no-eval* (f-readable-p "path/to/file.txt") + :no-eval* (f-readable-p "path/to/dir")) + + (f-writable-p + :no-eval* (f-writable-p "path/to/file.txt") + :no-eval* (f-writable-p "path/to/dir")) + + (f-executable-p + :no-eval* (f-executable-p "path/to/file.txt") + :no-eval* (f-executable-p "path/to/dir")) + + (f-absolute-p + :eval (f-absolute-p "path/to/dir") + :eval (f-absolute-p "/full/path/to/dir")) + + (f-relative-p + :eval (f-relative-p "path/to/dir") + :eval (f-relative-p "/full/path/to/dir")) + + (f-root-p + :eval (f-root-p "/") + :eval (f-root-p "/not/root")) + + (f-ext-p + :eval (f-ext-p "path/to/file.el" "el") + :eval (f-ext-p "path/to/file.el" "txt") + :eval (f-ext-p "path/to/file.el") + :eval (f-ext-p "path/to/file")) + + (f-same-p + :eval (f-same-p "foo.txt" "foo.txt") + :eval (f-same-p "foo/bar/../baz" "foo/baz") + :eval (f-same-p "/path/to/foo.txt" "/path/to/bar.txt")) + + (f-parent-of-p + :no-eval (f-parent-of-p "/path/to" "/path/to/dir") + :result t + :no-eval (f-parent-of-p "/path/to/dir" "/path/to") + :result nil + :no-eval (f-parent-of-p "/path/to" "/path/to") + :result nil) + + (f-child-of-p + :no-eval (f-child-of-p "/path/to" "/path/to/dir") + :result nil + :no-eval (f-child-of-p "/path/to/dir" "/path/to") + :result t + :no-eval (f-child-of-p "/path/to" "/path/to") + :result nil) + + (f-ancestor-of-p + :no-eval (f-ancestor-of-p "/path/to" "/path/to/dir") + :result t + :no-eval (f-ancestor-of-p "/path" "/path/to/dir") + :result t + :no-eval (f-ancestor-of-p "/path/to/dir" "/path/to") + :result nil + :no-eval (f-ancestor-of-p "/path/to" "/path/to") + :result nil) + + (f-descendant-of-p + :no-eval (f-descendant-of-p "/path/to/dir" "/path/to") + :result t + :no-eval (f-descendant-of-p "/path/to/dir" "/path") + :result t + :no-eval (f-descendant-of-p "/path/to" "/path/to/dir") + :result nil + :no-eval (f-descendant-of-p "/path/to" "/path/to") + :result nil) + + (f-hidden-p + :no-eval (f-hidden-p "/path/to/foo") + :result nil + :no-eval (f-hidden-p "/path/to/.foo") + :result t) + + (f-empty-p + :no-eval (f-empty-p "/path/to/empty-file") + :result t + :no-eval (f-empty-p "/path/to/file-with-contents") + :result nil + :no-eval (f-empty-p "/path/to/empty-dir/") + :result t + :no-eval (f-empty-p "/path/to/dir-with-contents/") + :result nil) + + "Stats" + (f-size + :no-eval* (f-size "path/to/file.txt") + :no-eval* (f-size "path/to/dir")) + + (f-depth + :eval (f-depth "/") + :eval (f-depth "/var/") + :eval (f-depth "/usr/local/bin")) + + (f-change-time + :no-eval* (f-change-time "path/to/file.txt") + :no-eval* (f-change-time "path/to/dir")) + + (f-modification-time + :no-eval* (f-modification-time "path/to/file.txt") + :no-eval* (f-modification-time "path/to/dir")) + + (f-access-time + :no-eval* (f-access-time "path/to/file.txt") + :no-eval* (f-access-time "path/to/dir")) + + "Misc" + (f-this-file + :no-eval* (f-this-file)) + + (f-path-separator + :eval (f-path-separator)) + + (f-glob + :noeval* (f-glob "path/to/*.el") + :noeval* (f-glob "*.el" "path/to")) + + (f-entries + :no-eval* (f-entries "path/to/dir") + :no-eval* (f-entries "path/to/dir" (lambda (file) (s-matches? "test" file))) + :no-eval* (f-entries "path/to/dir" nil t) + :no-eval* (f--entries "path/to/dir" (s-matches? "test" it))) + + (f-directories + :no-eval* (f-directories "path/to/dir") + :no-eval* (f-directories "path/to/dir" (lambda (dir) (equal (f-filename dir) "test"))) + :no-eval* (f-directories "path/to/dir" nil t) + :no-eval* (f--directories "path/to/dir" (equal (f-filename it) "test"))) + + (f-files + :no-eval* (f-files "path/to/dir") + :no-eval* (f-files "path/to/dir" (lambda (file) (equal (f-ext file) "el"))) + :no-eval* (f-files "path/to/dir" nil t) + :no-eval* (f--files "path/to/dir" (equal (f-ext it) "el"))) + + (f-root + :eval (f-root)) + + (f-traverse-upwards + :no-eval* (f-traverse-upwards + (lambda (path) + (f-exists? (f-expand ".git" path))) + start-path) + + :no-eval* (f--traverse-upwards (f-exists? (f-expand ".git" it)) start-path)) + + (f-with-sandbox + :no-eval (f-with-sandbox foo-path + (f-touch (f-expand "foo" foo-path))) + :no-eval (f-with-sandbox (list foo-path bar-path) + (f-touch (f-expand "foo" foo-path)) + (f-touch (f-expand "bar" bar-path))) + :no-eval (f-with-sandbox foo-path + (f-touch (f-expand "bar" bar-path)))))) ;; "Destructive operation outside sandbox" + +(eval-when-compile + (when (version< emacs-version "28.1") + (warn "Emacs should not be compiling this file"))) + +(provide 'f-shortdoc) + +;;; f-shortdoc.el ends here diff --git a/code/elpa/f-20220814.1054/f.el b/code/elpa/f-20220814.1054/f.el new file mode 100644 index 0000000..2b2a5a3 --- /dev/null +++ b/code/elpa/f-20220814.1054/f.el @@ -0,0 +1,646 @@ +;;; f.el --- Modern API for working with files and directories -*- lexical-binding: t; -*- + +;; Copyright (C) 2013 Johan Andersson + +;; Author: Johan Andersson +;; Maintainer: Lucien Cartier-Tilet +;; Version: 0.20.0 +;; Package-Requires: ((emacs "24.1") (s "1.7.0") (dash "2.2.0")) +;; Keywords: files, directories +;; Homepage: http://github.com/rejeep/f.el + +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Code: + + + +(require 's) +(require 'dash) +(when (version<= "28.1" emacs-version) + (require 'f-shortdoc)) + +(put 'f-guard-error 'error-conditions '(error f-guard-error)) +(put 'f-guard-error 'error-message "Destructive operation outside sandbox") + +(defvar f--guard-paths nil + "List of allowed paths to modify when guarded. + +Do not modify this variable.") + +(defmacro f--destructive (path &rest body) + "If PATH is allowed to be modified, yield BODY. + +If PATH is not allowed to be modified, throw error." + (declare (indent 1)) + `(if f--guard-paths + (if (--any? (or (f-same-p it ,path) + (f-ancestor-of-p it ,path)) f--guard-paths) + (progn ,@body) + (signal 'f-guard-error (list ,path f--guard-paths))) + ,@body)) + + +;;;; Paths + +(defun f-join (&rest args) + "Join ARGS to a single path. + +Be aware if one of the arguments is an absolute path, `f-join' +will discard all the preceeding arguments and make this absolute +path the new root of the generated path." + (let (path + (relative (f-relative-p (car args)))) + (-map + (lambda (arg) + (setq path (cond ((not path) arg) + ((f-absolute-p arg) + (progn + (setq relative nil) + arg)) + (t (f-expand arg path))))) + args) + (if relative (f-relative path) path))) + +(defun f-split (path) + "Split PATH and return list containing parts." + (let ((parts (split-string path (f-path-separator) 'omit-nulls))) + (if (string= (s-left 1 path) (f-path-separator)) + (push (f-path-separator) parts) + parts))) + +(defun f-expand (path &optional dir) + "Expand PATH relative to DIR (or `default-directory'). +PATH and DIR can be either a directory names or directory file +names. Return a directory name if PATH is a directory name, and +a directory file name otherwise. File name handlers are +ignored." + (let (file-name-handler-alist) + (expand-file-name path dir))) + +(defun f-filename (path) + "Return the name of PATH." + (file-name-nondirectory (directory-file-name path))) + +(defalias 'f-parent 'f-dirname) + +(defun f-dirname (path) + "Return the parent directory to PATH." + (let ((parent (file-name-directory + (directory-file-name (f-expand path default-directory))))) + (unless (f-same-p path parent) + (if (f-relative-p path) + (f-relative parent) + (directory-file-name parent))))) + +(defun f-common-parent (paths) + "Return the deepest common parent directory of PATHS." + (cond + ((not paths) nil) + ((not (cdr paths)) (f-parent (car paths))) + (:otherwise + (let* ((paths (-map 'f-split paths)) + (common (caar paths)) + (re nil)) + (while (and (not (null (car paths))) (--all? (equal (car it) common) paths)) + (setq paths (-map 'cdr paths)) + (push common re) + (setq common (caar paths))) + (cond + ((null re) "") + ((and (= (length re) 1) (f-root-p (car re))) + (f-root)) + (:otherwise + (concat (apply 'f-join (nreverse re)) "/"))))))) + +(defalias 'f-ext 'file-name-extension) + +(defalias 'f-no-ext 'file-name-sans-extension) + +(defun f-swap-ext (path ext) + "Return PATH but with EXT as the new extension. +EXT must not be nil or empty." + (if (s-blank-p ext) + (error "Extension cannot be empty or nil") + (concat (f-no-ext path) "." ext))) + +(defun f-base (path) + "Return the name of PATH, excluding the extension of file." + (f-no-ext (f-filename path))) + +(defalias 'f-relative 'file-relative-name) + +(defalias 'f-short 'abbreviate-file-name) +(defalias 'f-abbrev 'abbreviate-file-name) + +(defun f-long (path) + "Return long version of PATH." + (f-expand path)) + +(defalias 'f-canonical 'file-truename) + +(defun f-slash (path) + "Append slash to PATH unless one already. + +Some functions, such as `call-process' requires there to be an +ending slash." + (if (f-dir-p path) + (file-name-as-directory path) + path)) + +(defun f-full (path) + "Return absolute path to PATH, with ending slash." + (f-slash (f-long path))) + +(defun f--uniquify (paths) + "Helper for `f-uniquify' and `f-uniquify-alist'." + (let* ((files-length (length paths)) + (uniq-filenames (--map (cons it (f-filename it)) paths)) + (uniq-filenames-next (-group-by 'cdr uniq-filenames))) + (while (/= files-length (length uniq-filenames-next)) + (setq uniq-filenames-next + (-group-by 'cdr + (--mapcat + (let ((conf-files (cdr it))) + (if (> (length conf-files) 1) + (--map (cons + (car it) + (concat + (f-filename (s-chop-suffix (cdr it) + (car it))) + (f-path-separator) (cdr it))) + conf-files) + conf-files)) + uniq-filenames-next)))) + uniq-filenames-next)) + +(defun f-uniquify (files) + "Return unique suffixes of FILES. + +This function expects no duplicate paths." + (-map 'car (f--uniquify files))) + +(defun f-uniquify-alist (files) + "Return alist mapping FILES to unique suffixes of FILES. + +This function expects no duplicate paths." + (-map 'cadr (f--uniquify files))) + + +;;;; I/O + +(defun f-read-bytes (path &optional beg end) + "Read binary data from PATH. + +Return the binary data as unibyte string. The optional second and +third arguments BEG and END specify what portion of the file to +read." + (with-temp-buffer + (set-buffer-multibyte nil) + (setq buffer-file-coding-system 'binary) + (insert-file-contents-literally path nil beg end) + (buffer-substring-no-properties (point-min) (point-max)))) + +(defalias 'f-read 'f-read-text) +(defun f-read-text (path &optional coding) + "Read text with PATH, using CODING. + +CODING defaults to `utf-8'. + +Return the decoded text as multibyte string." + (decode-coding-string (f-read-bytes path) (or coding 'utf-8))) + +(defalias 'f-write 'f-write-text) +(defun f-write-text (text coding path) + "Write TEXT with CODING to PATH. + +TEXT is a multibyte string. CODING is a coding system to encode +TEXT with. PATH is a file name to write to." + (f-write-bytes (encode-coding-string text coding) path)) + +(defun f-unibyte-string-p (s) + "Determine whether S is a unibyte string." + (not (multibyte-string-p s))) + +(defun f-write-bytes (data path) + "Write binary DATA to PATH. + +DATA is a unibyte string. PATH is a file name to write to." + (f--write-bytes data path nil)) + +(defalias 'f-append 'f-append-text) +(defun f-append-text (text coding path) + "Append TEXT with CODING to PATH. + +If PATH does not exist, it is created." + (f-append-bytes (encode-coding-string text coding) path)) + +(defun f-append-bytes (data path) + "Append binary DATA to PATH. + +If PATH does not exist, it is created." + (f--write-bytes data path :append)) + +(defun f--write-bytes (data filename append) + "Write binary DATA to FILENAME. +If APPEND is non-nil, append the DATA to the existing contents." + (f--destructive filename + (unless (f-unibyte-string-p data) + (signal 'wrong-type-argument (list 'f-unibyte-string-p data))) + (let ((coding-system-for-write 'binary) + (write-region-annotate-functions nil) + (write-region-post-annotation-function nil)) + (write-region data nil filename append :silent) + nil))) + + +;;;; Destructive + +(defun f-mkdir (&rest dirs) + "Create directories DIRS. + +DIRS should be a successive list of directories forming together +a full path. The easiest way to call this function with a fully +formed path is using `f-split' alongside it: + + (apply #\\='f-mkdir (f-split \"path/to/file\")) + +Although it works sometimes, it is not recommended to use fully +formed paths in the function. In this case, it is recommended to +use `f-mkdir-full-path' instead." + (let (path) + (-each + dirs + (lambda (dir) + (setq path (f-expand dir path)) + (unless (f-directory-p path) + (f--destructive path (make-directory path))))))) + +(defun f-mkdir-full-path (dir) + "Create DIR from a full path. + +This function is similar to `f-mkdir' except it can accept a full +path instead of requiring several successive directory names." + (apply #'f-mkdir (f-split dir))) + +(defun f-delete (path &optional force) + "Delete PATH, which can be file or directory. + +If FORCE is t, a directory will be deleted recursively." + (f--destructive path + (if (or (f-file-p path) (f-symlink-p path)) + (delete-file path) + (delete-directory path force)))) + +(defun f-symlink (source path) + "Create a symlink to SOURCE from PATH." + (f--destructive path (make-symbolic-link source path))) + +(defun f-move (from to) + "Move or rename FROM to TO. +If TO is a directory name, move FROM into TO." + (f--destructive to (rename-file from to t))) + +(defun f-copy (from to) + "Copy file or directory FROM to TO. +If FROM names a directory and TO is a directory name, copy FROM +into TO as a subdirectory." + (f--destructive to + (if (f-file-p from) + (copy-file from to) + ;; The behavior of `copy-directory' differs between Emacs 23 and + ;; 24 in that in Emacs 23, the contents of `from' is copied to + ;; `to', while in Emacs 24 the directory `from' is copied to + ;; `to'. We want the Emacs 24 behavior. + (if (> emacs-major-version 23) + (copy-directory from to) + (if (f-dir-p to) + (progn + (apply 'f-mkdir (f-split to)) + (let ((new-to (f-expand (f-filename from) to))) + (copy-directory from new-to))) + (copy-directory from to)))))) + +(defun f-copy-contents (from to) + "Copy contents in directory FROM, to directory TO." + (unless (f-exists-p to) + (error "Cannot copy contents to non existing directory %s" to)) + (unless (f-dir-p from) + (error "Cannot copy contents as %s is a file" from)) + (--each (f-entries from) + (f-copy it (file-name-as-directory to)))) + +(defun f-touch (path) + "Update PATH last modification date or create if it does not exist." + (f--destructive path + (if (f-file-p path) + (set-file-times path) + (f-write-bytes "" path)))) + + +;;;; Predicates + +(defalias 'f-exists-p 'file-exists-p) +(defalias 'f-exists? 'file-exists-p) + +(defalias 'f-directory-p 'file-directory-p) +(defalias 'f-directory? 'file-directory-p) +(defalias 'f-dir-p 'file-directory-p) +(defalias 'f-dir? 'file-directory-p) + + +(defalias 'f-file-p 'file-regular-p) +(defalias 'f-file? 'file-regular-p) + +(defun f-symlink-p (path) + "Return t if PATH is symlink, false otherwise." + (not (not (file-symlink-p path)))) + +(defalias 'f-symlink? 'f-symlink-p) + +(defalias 'f-readable-p 'file-readable-p) +(defalias 'f-readable? 'file-readable-p) + +(defalias 'f-writable-p 'file-writable-p) +(defalias 'f-writable? 'file-writable-p) + +(defalias 'f-executable-p 'file-executable-p) +(defalias 'f-executable? 'file-executable-p) + +(defalias 'f-absolute-p 'file-name-absolute-p) +(defalias 'f-absolute? 'file-name-absolute-p) + +(defun f-relative-p (path) + "Return t if PATH is relative, false otherwise." + (not (f-absolute-p path))) + +(defalias 'f-relative? 'f-relative-p) + +(defun f-root-p (path) + "Return t if PATH is root directory, false otherwise." + (not (f-parent path))) + +(defalias 'f-root? 'f-root-p) + +(defun f-ext-p (path &optional ext) + "Return t if extension of PATH is EXT, false otherwise. + +If EXT is nil or omitted, return t if PATH has any extension, +false otherwise. + +The extension, in a file name, is the part that follows the last +'.', excluding version numbers and backup suffixes." + (if ext + (string= (f-ext path) ext) + (not (eq (f-ext path) nil)))) + +(defalias 'f-ext? 'f-ext-p) + +(defalias 'f-equal-p 'f-same-p) +(defalias 'f-equal? 'f-same-p) + +(defun f-same-p (path-a path-b) + "Return t if PATH-A and PATH-B are references to same file." + (equal + (f-canonical (directory-file-name (f-expand path-a))) + (f-canonical (directory-file-name (f-expand path-b))))) + +(defalias 'f-same? 'f-same-p) + +(defun f-parent-of-p (path-a path-b) + "Return t if PATH-A is parent of PATH-B." + (--when-let (f-parent path-b) + (f-same-p path-a it))) + +(defalias 'f-parent-of? 'f-parent-of-p) + +(defun f-child-of-p (path-a path-b) + "Return t if PATH-A is child of PATH-B." + (--when-let (f-parent path-a) + (f-same-p it path-b))) + +(defalias 'f-child-of? 'f-child-of-p) + +(defun f-ancestor-of-p (path-a path-b) + "Return t if PATH-A is ancestor of PATH-B." + (unless (f-same-p path-a path-b) + (string-prefix-p (f-full path-a) + (f-full path-b)))) + +(defalias 'f-ancestor-of? 'f-ancestor-of-p) + +(defun f-descendant-of-p (path-a path-b) + "Return t if PATH-A is desendant of PATH-B." + (unless (f-same-p path-a path-b) + (string-prefix-p (f-full path-b) + (f-full path-a)))) + +(defalias 'f-descendant-of? 'f-descendant-of-p) + +(defun f-hidden-p (path) + "Return t if PATH is hidden, nil otherwise." + (unless (f-exists-p path) + (error "Path does not exist: %s" path)) + (string= (substring path 0 1) ".")) + +(defalias 'f-hidden? 'f-hidden-p) + +(defun f-empty-p (path) + "If PATH is a file, return t if the file in PATH is empty, nil otherwise. +If PATH is directory, return t if directory has no files, nil otherwise." + (if (f-directory-p path) + (equal (f-files path nil t) nil) + (= (f-size path) 0))) + +(defalias 'f-empty? 'f-empty-p) + + +;;;; Stats + +(defun f-size (path) + "Return size of PATH. + +If PATH is a file, return size of that file. If PATH is +directory, return sum of all files in PATH." + (if (f-directory-p path) + (-sum (-map 'f-size (f-files path nil t))) + (nth 7 (file-attributes path)))) + +(defun f-depth (path) + "Return the depth of PATH. + +At first, PATH is expanded with `f-expand'. Then the full path is used to +detect the depth. +'/' will be zero depth, '/usr' will be one depth. And so on." + (- (length (f-split (f-expand path))) 1)) + +(defun f-change-time (path) + "Return the last status change time of PATH. + +The status change time (ctime) of PATH in the same format as +`current-time'. See `file-attributes' for technical details." + (nth 6 (file-attributes path))) + +(defun f-modification-time (path) + "Return the last modification time of PATH. + +The modification time (mtime) of PATH in the same format as +`current-time'. See `file-attributes' for technical details." + (nth 5 (file-attributes path))) + +(defun f-access-time (path) + "Return the last access time of PATH. + +The access time (atime) of PATH is in the same format as +`current-time'. See `file-attributes' for technical details." + (nth 4 (file-attributes path))) + + +;;;; Misc + +(defun f-this-file () + "Return path to this file." + (cond + (load-in-progress load-file-name) + ((and (boundp 'byte-compile-current-file) byte-compile-current-file) + byte-compile-current-file) + (:else (buffer-file-name)))) + +(defvar f--path-separator nil + "A variable to cache result of `f-path-separator'.") + +(defun f-path-separator () + "Return path separator." + (or f--path-separator + (setq f--path-separator (substring (f-join "x" "y") 1 2)))) + +(defun f-glob (pattern &optional path) + "Find PATTERN in PATH." + (file-expand-wildcards + (f-join (or path default-directory) pattern))) + +(defun f--collect-entries (path recursive) + (let (result + (entries + (-reject + (lambda (file) + (member (f-filename file) '("." ".."))) + (directory-files path t)))) + (cond (recursive + (-map + (lambda (entry) + (if (f-file-p entry) + (setq result (cons entry result)) + (when (f-directory-p entry) + (setq result (cons entry result)) + (if (f-readable-p entry) + (setq result (append result (f--collect-entries entry recursive))) + result)))) + entries)) + (t (setq result entries))) + result)) + +(defmacro f--entries (path body &optional recursive) + "Anaphoric version of `f-entries'." + `(f-entries + ,path + (lambda (path) + (let ((it path)) + ,body)) + ,recursive)) + +(defun f-entries (path &optional fn recursive) + "Find all files and directories in PATH. + +FN - called for each found file and directory. If FN returns a thruthy +value, file or directory will be included. +RECURSIVE - Search for files and directories recursive." + (let ((entries (f--collect-entries path recursive))) + (if fn (-select fn entries) entries))) + +(defmacro f--directories (path body &optional recursive) + "Anaphoric version of `f-directories'." + `(f-directories + ,path + (lambda (path) + (let ((it path)) + ,body)) + ,recursive)) + +(defun f-directories (path &optional fn recursive) + "Find all directories in PATH. See `f-entries'." + (let ((directories (-select 'f-directory-p (f--collect-entries path recursive)))) + (if fn (-select fn directories) directories))) + +(defmacro f--files (path body &optional recursive) + "Anaphoric version of `f-files'." + `(f-files + ,path + (lambda (path) + (let ((it path)) + ,body)) + ,recursive)) + +(defun f-files (path &optional fn recursive) + "Find all files in PATH. See `f-entries'." + (let ((files (-select 'f-file-p (f--collect-entries path recursive)))) + (if fn (-select fn files) files))) + +(defmacro f--traverse-upwards (body &optional path) + "Anaphoric version of `f-traverse-upwards'." + `(f-traverse-upwards + (lambda (dir) + (let ((it dir)) + ,body)) + ,path)) + +(defun f-traverse-upwards (fn &optional path) + "Traverse up as long as FN return nil, starting at PATH. + +If FN returns a non-nil value, the path sent as argument to FN is +returned. If no function callback return a non-nil value, nil is +returned." + (unless path + (setq path default-directory)) + (when (f-relative-p path) + (setq path (f-expand path))) + (if (funcall fn path) + path + (unless (f-root-p path) + (f-traverse-upwards fn (f-parent path))))) + +(defun f-root () + "Return absolute root." + (f-traverse-upwards 'f-root-p)) + +(defmacro f-with-sandbox (path-or-paths &rest body) + "Only allow PATH-OR-PATHS and descendants to be modified in BODY." + (declare (indent 1)) + `(let ((paths (if (listp ,path-or-paths) + ,path-or-paths + (list ,path-or-paths)))) + (unwind-protect + (let ((f--guard-paths paths)) + ,@body) + (setq f--guard-paths nil)))) + +(provide 'f) + +;;; f.el ends here diff --git a/code/elpa/helm-20220822.659/.dir-locals.el b/code/elpa/helm-20220822.659/.dir-locals.el new file mode 100644 index 0000000..8cbef6a --- /dev/null +++ b/code/elpa/helm-20220822.659/.dir-locals.el @@ -0,0 +1,8 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((nil . ((bug-reference-bug-regexp . "\\(\\b\\(?:[Ii]ssue ?#?\\|[Bb]ug ?#?\\|[Pp]atch ?#\\|RFE ?#\\|PR [a-z+-]+/\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)\\)") + (bug-reference-url-format . "https://github.com/emacs-helm/helm/issues/%s") + (byte-compile-warnings . (not obsolete docstrings docstrings-non-ascii-quotes)))) + (emacs-lisp-mode . ((mode . bug-reference-prog) + (indent-tabs-mode . nil)))) diff --git a/code/elpa/helm-20220822.659/emacs-helm.sh b/code/elpa/helm-20220822.659/emacs-helm.sh new file mode 100644 index 0000000..cb7ecfd --- /dev/null +++ b/code/elpa/helm-20220822.659/emacs-helm.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env sh + + +## Copyright (C) 2012 ~ 2021 Thierry Volpiatto +## +## 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 . + +## Commentary: +# Preconfigured `emacs -Q' with a basic Helm configuration. + + +# If TEMP env var exists, use it, otherwise declare it. +test -z "$TEMP" && TEMP="/tmp" + +CONF_FILE="$TEMP/helm-cfg.el" +EMACS=emacs +TOOLBARS=-1 +LOAD_PACKAGES= + +usage () { + cat >&1 < $CONF_FILE <\`helm-find-files'\\n\ +;; - \`occur'(M-s o) =>\`helm-occur'\\n\ +;; - \`list-buffers'(C-x C-b) =>\`helm-buffers-list'\\n\ +;; - \`completion-at-point'(M-tab) =>\`helm-lisp-completion-at-point'[1]\\n\ +;; - \`apropos-command'(C-h a) =>\`helm-apropos'\\n\ +;; - \`dabbrev-expand'(M-/) =>\`helm-dabbrev'\\n\ +;; - \`execute-extended-command'(M-x) =>\`helm-M-x'\\n\\n +;; Some other Emacs commands are \"helmized\" by \`helm-mode'.\\n\ +;; [1] Coming with emacs-24.4, \`completion-at-point' is \"helmized\" by \`helm-mode'\\n\ + +;; which provides Helm completion in many places like \`shell-mode'.\\n\ +;; Find context help for most Helm commands with \`C-h m'.\\n\ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\\n\\n")) + +(setq load-path (quote $LOAD_PATH)) + +(defvar default-package-manager nil) +;; /home/you/.emacs.d/.local/straight/build-27.1/helm +(defvar initial-package-directory (file-name-directory (file-truename "$0"))) + +(defvar bootstrap-version) +(let* ((packages "$LOAD_PACKAGES") + (pkg-list (and packages + (not (equal packages "")) + (split-string packages ","))) + ;; /home/you/.emacs.d/.local/straight/build-27.1 + (straight-path (file-name-directory (directory-file-name initial-package-directory))) + ;; /home/you/.emacs.d/.local/straight/build-27.1/async + (async-path (expand-file-name "async" straight-path)) + ;; /home/you/.emacs.d/.local/straight/repos/straight.el/bootstrap.el + (bootstrap-file + (expand-file-name "repos/straight.el/bootstrap.el" + (file-name-directory (directory-file-name straight-path)))) + (bootstrap-version 5)) + (when (file-exists-p bootstrap-file) + (setq default-package-manager 'straight) + (load bootstrap-file nil 'nomessage) + (add-to-list 'load-path async-path) + (when pkg-list + (dolist (pkg pkg-list) + (let* ((pkg-path (expand-file-name pkg straight-path)) + (autoload-file (expand-file-name + (format "%s-autoloads.el" pkg) + pkg-path))) + (add-to-list 'load-path pkg-path) + (if (file-exists-p autoload-file) + (load autoload-file nil 'nomessage) + (straight-use-package (intern pkg)))))))) + +(unless (eq default-package-manager 'straight) + (require 'package) + ;; User may be using a non standard \`package-user-dir'. + ;; Modify \`package-directory-list' instead of \`package-user-dir' + ;; in case the user starts Helm from a non-ELPA installation. + (unless (file-equal-p package-user-dir (locate-user-emacs-file "elpa")) + ;; Something like /home/you/.emacs.d/somedir/else/elpa/ + ;; starting from default-directory is wrong in case helm.sh is a symlink + ;; or e.g. helm --chdir foo have been used. + (add-to-list 'package-directory-list (directory-file-name + (file-name-directory + (directory-file-name initial-package-directory))))) + + (let* ((str-lst "$LOAD_PACKAGES") + (load-packages (and str-lst + (not (string= str-lst "")) + (split-string str-lst ",")))) + (setq package-load-list + (if (equal load-packages '("all")) + '(all) + (append '((helm-core t) (helm t) (async t) (popup t)) + (mapcar (lambda (p) (list (intern p) t)) load-packages))))) + + (package-initialize)) + +(add-to-list 'load-path initial-package-directory) + +(unless (> $TOOLBARS 0) + (setq default-frame-alist '((vertical-scroll-bars . nil) + (tool-bar-lines . 0) + (menu-bar-lines . 0) + (fullscreen . nil)))) +(blink-cursor-mode -1) +(require 'helm-config) +(helm-mode 1) +(with-eval-after-load 'tramp-cache (setq tramp-cache-read-persistent-data t)) +(with-eval-after-load 'auth-source (setq auth-source-save-behavior nil)) +(define-key global-map [remap find-file] 'helm-find-files) +(define-key global-map [remap occur] 'helm-occur) +(define-key global-map [remap list-buffers] 'helm-buffers-list) +(define-key global-map [remap dabbrev-expand] 'helm-dabbrev) +(define-key global-map [remap execute-extended-command] 'helm-M-x) +(define-key global-map [remap apropos-command] 'helm-apropos) +(unless (boundp 'completion-in-region-function) + (define-key lisp-interaction-mode-map [remap completion-at-point] 'helm-lisp-completion-at-point) + (define-key emacs-lisp-mode-map [remap completion-at-point] 'helm-lisp-completion-at-point)) +(add-hook 'kill-emacs-hook #'(lambda () (and (file-exists-p "$CONF_FILE") (delete-file "$CONF_FILE")))) +EOF + +$EMACS -Q -l "$CONF_FILE" "$@" diff --git a/code/elpa/helm-20220822.659/helm-adaptive.el b/code/elpa/helm-20220822.659/helm-adaptive.el new file mode 100644 index 0000000..d535cfb --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-adaptive.el @@ -0,0 +1,284 @@ +;;; helm-adaptive.el --- Adaptive Sorting of Candidates. -*- lexical-binding: t -*- + +;; Original Author: Tamas Patrovics + +;; Copyright (C) 2007 Tamas Patrovics +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) + + +(defgroup helm-adapt nil + "Adaptative sorting of candidates for Helm." + :group 'helm) + +(defcustom helm-adaptive-history-file + (locate-user-emacs-file "helm-adaptive-history") + "Path of file where history information is stored. +When nil history is not saved nor restored after Emacs restart +unless you save/restore `helm-adaptive-history' with something +else like psession or desktop." + :type 'string) + +(defcustom helm-adaptive-history-length 50 + "Maximum number of candidates stored for a source." + :type 'number) + +(defcustom helm-adaptive-sort-by-frequent-recent-usage t + "Try to sort on an average of frequent and recent usage when non-nil. + +When nil sort on frequency usage only. + +Only frequency: +When candidate have low frequency, you have to hit on it many +times to make it going up on top. + +Frequency+recent: +Even with a low frequency, candidate go up on top. If a candidate +have a high frequency but it is not used since some time, it goes +down slowly, but as soon you reuse it it go up on top quickly." + :type 'boolean) + +;; Internal +(defvar helm-adaptive-done nil + "nil if history information is not yet stored for the current +selection.") + +(defvar helm-adaptive-history nil + "Contains the stored history information. +Format: ((SOURCE-NAME + (SELECTED-CANDIDATE (PATTERN . NUMBER-OF-USE) ...) ...) ...)") + +(defconst helm-adaptive-freq-coefficient 5) +(defconst helm-adaptive-recent-coefficient 2) + +(defun helm-adaptive-done-reset () + (setq helm-adaptive-done nil)) + +;;;###autoload +(define-minor-mode helm-adaptive-mode + "Toggle adaptive sorting in all sources." + :global t + (if helm-adaptive-mode + (progn + (unless helm-adaptive-history + (helm-adaptive-maybe-load-history)) + (add-hook 'kill-emacs-hook #'helm-adaptive-save-history) + ;; Should run at beginning of `helm-initial-setup'. + (add-hook 'helm-before-initialize-hook #'helm-adaptive-done-reset) + ;; Should run at beginning of `helm-exit-minibuffer'. + (add-hook 'helm-before-action-hook #'helm-adaptive-store-selection) + ;; Should run at beginning of `helm-select-action'. + (add-hook 'helm-select-action-hook #'helm-adaptive-store-selection)) + (helm-adaptive-save-history) + (setq helm-adaptive-history nil) + (remove-hook 'kill-emacs-hook #'helm-adaptive-save-history) + (remove-hook 'helm-before-initialize-hook #'helm-adaptive-done-reset) + (remove-hook 'helm-before-action-hook #'helm-adaptive-store-selection) + (remove-hook 'helm-select-action-hook #'helm-adaptive-store-selection))) + +(defun helm-adapt-use-adaptive-p (&optional source-name) + "Return current source only if it use adaptive history, nil otherwise." + (when helm-adaptive-mode + (let* ((source (or source-name (helm-get-current-source))) + (adapt-source (or (assoc-default 'filtered-candidate-transformer source) + (assoc-default 'candidate-transformer source)))) + (if (listp adapt-source) + (and (memq 'helm-adaptive-sort adapt-source) source) + (and (eq adapt-source 'helm-adaptive-sort) source))))) + +(defun helm-adaptive-store-selection () + "Store history information for the selected candidate." + (unless helm-adaptive-done + (setq helm-adaptive-done t) + (let ((source (helm-adapt-use-adaptive-p))) + (when source + (let* ((source-name (assoc-default 'name source)) + (source-info (or (assoc source-name helm-adaptive-history) + (progn + (push (list source-name) helm-adaptive-history) + (car helm-adaptive-history)))) + (selection (helm-get-selection nil t)) + (selection-info (progn + (setcdr source-info + (cons + (let ((found (assoc selection (cdr source-info)))) + (if (not found) + ;; new entry + (list selection) + ;; move entry to the beginning of the + ;; list, so that it doesn't get + ;; trimmed when the history is + ;; truncated + (setcdr source-info + (delete found (cdr source-info))) + found)) + (cdr source-info))) + (cadr source-info))) + (pattern-info (progn + (setcdr selection-info + (cons + (let ((found (assoc helm-pattern (cdr selection-info)))) + (if (not found) + ;; new entry + (cons helm-pattern 0) + ;; move entry to the beginning of the + ;; list, so if two patterns used the + ;; same number of times then the one + ;; used last appears first in the list + (setcdr selection-info + (delete found (cdr selection-info))) + found)) + (cdr selection-info))) + (cadr selection-info))) + (timestamp-info (helm-aif (assq 'timestamp (cdr selection-info)) + it + (setcdr selection-info (cons (cons 'timestamp 0) (cdr selection-info))) + (cadr selection-info)))) + ;; Increase usage count. + (setcdr pattern-info (1+ (cdr pattern-info))) + ;; Update timestamp. + (setcdr timestamp-info (float-time)) + ;; Truncate history if needed. + (if (> (length (cdr selection-info)) helm-adaptive-history-length) + (setcdr selection-info + (cl-subseq (cdr selection-info) 0 helm-adaptive-history-length)))))))) + +(defun helm-adaptive-maybe-load-history () + "Load `helm-adaptive-history-file' which contain `helm-adaptive-history'. +Returns nil if `helm-adaptive-history-file' doesn't exist." + (when (and helm-adaptive-history-file + (file-readable-p helm-adaptive-history-file)) + (load-file helm-adaptive-history-file))) + +(defun helm-adaptive-save-history (&optional arg) + "Save history information to the file given by `helm-adaptive-history-file'." + (interactive "p") + (when helm-adaptive-history-file + (with-temp-buffer + (insert + ";; -*- mode: emacs-lisp -*-\n" + ";; History entries used for helm adaptive display.\n") + (let (print-length print-level) + (prin1 `(setq helm-adaptive-history ',helm-adaptive-history) + (current-buffer))) + (insert ?\n) + (write-region (point-min) (point-max) helm-adaptive-history-file nil + (unless arg 'quiet))))) + +(defun helm-adaptive-sort (candidates source) + "Sort the CANDIDATES for SOURCE by usage frequency. +This is a filtered candidate transformer you can use with the +`filtered-candidate-transformer' attribute." + (let* ((source-name (assoc-default 'name source)) + (source-info (assoc source-name helm-adaptive-history))) + (if source-info + (let ((usage + ;; Loop in the SOURCE entry of `helm-adaptive-history' + ;; and assemble a list containing the (CANDIDATE + ;; . USAGE-COUNT) pairs. + (cl-loop with cf = (if helm-adaptive-sort-by-frequent-recent-usage + helm-adaptive-freq-coefficient 1) + with cr = helm-adaptive-recent-coefficient + for (src-cand . infos) in (cdr source-info) + for count-freq = 0 + for count-rec = + (helm-aif (and helm-adaptive-sort-by-frequent-recent-usage + (assq 'timestamp infos)) + (* cr (+ (float-time) (cdr it))) + 0) + do (cl-loop for (pattern . score) in + (remove (assq 'timestamp infos) infos) + ;; If current pattern is equal to + ;; the previously used one then + ;; this candidate has priority + ;; (that's why its count-freq is + ;; boosted by 10000) and it only + ;; has to compete with other + ;; candidates which were also + ;; selected with the same pattern. + if (equal pattern helm-pattern) + return (setq count-freq (+ 10000 score)) + else do (cl-incf count-freq score)) + and collect (cons src-cand (+ (* count-freq cf) count-rec)) + into results + ;; Sort the list in descending order, so + ;; candidates with highest priority come + ;; first. + finally return + (sort results (lambda (first second) + (> (cdr first) (cdr second))))))) + (if (consp usage) + ;; Put those candidates first which have the highest usage count. + (cl-loop for (cand . _freq) in usage + for info = (or (and (assq 'multiline source) + (replace-regexp-in-string + "\n\\'" "" cand)) + ;; Some transformers like in + ;; bookmarks may add a leading + ;; space to provide additional + ;; infos like an icon as a + ;; display prop, strip out this + ;; leading space for + ;; comparison. Same for a + ;; trailing space (helm + ;; boookmark add bmk location as + ;; a display prop when + ;; displaying it). + (helm-aand (replace-regexp-in-string "\\` " "" cand) + (replace-regexp-in-string " \\'" "" it))) + when (cl-member info candidates + :test 'helm-adaptive-compare) + collect (car it) into sorted + and do (setq candidates + (cl-remove info candidates + :test 'helm-adaptive-compare)) + finally return (append sorted candidates)) + (message "Your `%s' is maybe corrupted or too old, \ +you should reinitialize it with `helm-reset-adaptive-history'" + helm-adaptive-history-file) + (sit-for 1) + candidates)) + ;; if there is no information stored for this source then do nothing + candidates))) + +;;;###autoload +(defun helm-reset-adaptive-history () + "Delete all `helm-adaptive-history' and his file. +Useful when you have a old or corrupted +`helm-adaptive-history-file'." + (interactive) + (when (y-or-n-p "Really delete all your `helm-adaptive-history'? ") + (setq helm-adaptive-history nil) + (when (and helm-adaptive-history-file + (file-exists-p helm-adaptive-history-file)) + (delete-file helm-adaptive-history-file)))) + +(defun helm-adaptive-compare (x y) + "Compare display parts if some of candidates X and Y. + +Arguments X and Y are cons cell in (DISPLAY . REAL) format or +atoms." + (equal (if (listp x) (car x) x) + (if (listp y) (car y) y))) + + +(provide 'helm-adaptive) + +;;; helm-adaptive.el ends here diff --git a/code/elpa/helm-20220822.659/helm-autoloads.el b/code/elpa/helm-20220822.659/helm-autoloads.el new file mode 100644 index 0000000..6e3b88a --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-autoloads.el @@ -0,0 +1,1173 @@ +;;; helm-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "helm-adaptive" "helm-adaptive.el" (0 0 0 0)) +;;; Generated autoloads from helm-adaptive.el + +(defvar helm-adaptive-mode nil "\ +Non-nil if Helm-Adaptive mode is enabled. +See the `helm-adaptive-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `helm-adaptive-mode'.") + +(custom-autoload 'helm-adaptive-mode "helm-adaptive" nil) + +(autoload 'helm-adaptive-mode "helm-adaptive" "\ +Toggle adaptive sorting in all sources. + +This is a minor mode. If called interactively, toggle the +`Helm-Adaptive mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='helm-adaptive-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(autoload 'helm-reset-adaptive-history "helm-adaptive" "\ +Delete all `helm-adaptive-history' and his file. +Useful when you have a old or corrupted +`helm-adaptive-history-file'." t nil) + +(register-definition-prefixes "helm-adaptive" '("helm-adapt")) + +;;;*** + +;;;### (autoloads nil "helm-bookmark" "helm-bookmark.el" (0 0 0 0)) +;;; Generated autoloads from helm-bookmark.el + +(autoload 'helm-bookmarks "helm-bookmark" "\ +Preconfigured `helm' for bookmarks." t nil) + +(autoload 'helm-filtered-bookmarks "helm-bookmark" "\ +Preconfigured `helm' for bookmarks (filtered by category). +Optional source `helm-source-bookmark-addressbook' is loaded only +if external addressbook-bookmark package is installed." t nil) + +(register-definition-prefixes "helm-bookmark" '("bmkext-jump-" "bookmark" "helm-")) + +;;;*** + +;;;### (autoloads nil "helm-buffers" "helm-buffers.el" (0 0 0 0)) +;;; Generated autoloads from helm-buffers.el + +(autoload 'helm-buffers-list "helm-buffers" "\ +Preconfigured `helm' to list buffers." t nil) + +(autoload 'helm-mini "helm-buffers" "\ +Preconfigured `helm' displaying `helm-mini-default-sources'." t nil) + +(register-definition-prefixes "helm-buffers" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-color" "helm-color.el" (0 0 0 0)) +;;; Generated autoloads from helm-color.el + +(autoload 'helm-colors "helm-color" "\ +Preconfigured `helm' for color." t nil) + +(register-definition-prefixes "helm-color" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-comint" "helm-comint.el" (0 0 0 0)) +;;; Generated autoloads from helm-comint.el + +(autoload 'helm-comint-prompts "helm-comint" "\ +Pre-configured `helm' to browse the prompts of the current comint buffer." t nil) + +(autoload 'helm-comint-prompts-all "helm-comint" "\ +Pre-configured `helm' to browse the prompts of all comint sessions." t nil) + +(autoload 'helm-comint-input-ring "helm-comint" "\ +Preconfigured `helm' that provide completion of `comint' history." t nil) + +(register-definition-prefixes "helm-comint" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-command" "helm-command.el" (0 0 0 0)) +;;; Generated autoloads from helm-command.el + +(autoload 'helm-M-x "helm-command" "\ +Preconfigured `helm' for Emacs commands. +It is `helm' replacement of regular `M-x' +`execute-extended-command'. + +Unlike regular `M-x' Emacs vanilla `execute-extended-command' +command, the prefix args if needed, can be passed AFTER starting +`helm-M-x'. When a prefix arg is passed BEFORE starting +`helm-M-x', the first `C-u' while in `helm-M-x' session will +disable it. + +You can get help on each command by persistent action. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-command" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-dabbrev" "helm-dabbrev.el" (0 0 0 0)) +;;; Generated autoloads from helm-dabbrev.el + +(autoload 'helm-dabbrev "helm-dabbrev" "\ +Preconfigured helm for dynamic abbreviations." t nil) + +(register-definition-prefixes "helm-dabbrev" '("helm-dabbrev-")) + +;;;*** + +;;;### (autoloads nil "helm-elisp" "helm-elisp.el" (0 0 0 0)) +;;; Generated autoloads from helm-elisp.el + +(autoload 'helm-lisp-completion-at-point "helm-elisp" "\ +Preconfigured Helm for Lisp symbol completion at point." t nil) + +(autoload 'helm-complete-file-name-at-point "helm-elisp" "\ +Preconfigured Helm to complete file name at point. + +\(fn &optional FORCE)" t nil) + +(autoload 'helm-lisp-indent "helm-elisp" nil t nil) + +(autoload 'helm-lisp-completion-or-file-name-at-point "helm-elisp" "\ +Preconfigured Helm to complete Lisp symbol or filename at point. +Filename completion happens if string start after or between a +double quote." t nil) + +(autoload 'helm-apropos "helm-elisp" "\ +Preconfigured Helm to describe commands, functions, variables and faces. +In non interactives calls DEFAULT argument should be provided as +a string, i.e. the `symbol-name' of any existing symbol. + +\(fn DEFAULT)" t nil) + +(autoload 'helm-manage-advice "helm-elisp" "\ +Preconfigured `helm' to disable/enable function advices." t nil) + +(autoload 'helm-locate-library "helm-elisp" "\ +Preconfigured helm to locate elisp libraries." t nil) + +(autoload 'helm-timers "helm-elisp" "\ +Preconfigured `helm' for timers." t nil) + +(autoload 'helm-complex-command-history "helm-elisp" "\ +Preconfigured `helm' for complex command history." t nil) + +(register-definition-prefixes "helm-elisp" '("helm-" "with-helm-show-completion")) + +;;;*** + +;;;### (autoloads nil "helm-elisp-package" "helm-elisp-package.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from helm-elisp-package.el + +(autoload 'helm-list-elisp-packages "helm-elisp-package" "\ +Preconfigured `helm' for listing and handling Emacs packages. + +\(fn ARG)" t nil) + +(autoload 'helm-list-elisp-packages-no-fetch "helm-elisp-package" "\ +Preconfigured Helm for Emacs packages. + +Same as `helm-list-elisp-packages' but don't fetch packages on +remote. Called with a prefix ARG always fetch packages on +remote. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-elisp-package" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-epa" "helm-epa.el" (0 0 0 0)) +;;; Generated autoloads from helm-epa.el + +(defvar helm-epa-mode nil "\ +Non-nil if Helm-Epa mode is enabled. +See the `helm-epa-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `helm-epa-mode'.") + +(custom-autoload 'helm-epa-mode "helm-epa" nil) + +(autoload 'helm-epa-mode "helm-epa" "\ +Enable helm completion on gpg keys in epa functions. + +This is a minor mode. If called interactively, toggle the +`Helm-Epa mode' mode. If the prefix argument is positive, enable +the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='helm-epa-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(autoload 'helm-epa-list-keys "helm-epa" "\ +List all gpg keys. +This is the helm interface for `epa-list-keys'." t nil) + +(register-definition-prefixes "helm-epa" '("helm-epa")) + +;;;*** + +;;;### (autoloads nil "helm-eshell" "helm-eshell.el" (0 0 0 0)) +;;; Generated autoloads from helm-eshell.el + +(autoload 'helm-esh-pcomplete "helm-eshell" "\ +Preconfigured `helm' to provide Helm completion in Eshell." t nil) + +(autoload 'helm-eshell-history "helm-eshell" "\ +Preconfigured Helm for Eshell history." t nil) + +(autoload 'helm-eshell-prompts "helm-eshell" "\ +Pre-configured `helm' to browse the prompts of the current Eshell." t nil) + +(autoload 'helm-eshell-prompts-all "helm-eshell" "\ +Pre-configured `helm' to browse the prompts of all Eshell sessions." t nil) + +(register-definition-prefixes "helm-eshell" '("helm-e")) + +;;;*** + +;;;### (autoloads nil "helm-eval" "helm-eval.el" (0 0 0 0)) +;;; Generated autoloads from helm-eval.el + +(autoload 'helm-eval-expression "helm-eval" "\ +Preconfigured `helm' for `helm-source-evaluation-result'. + +\(fn ARG)" t nil) + +(autoload 'helm-eval-expression-with-eldoc "helm-eval" "\ +Preconfigured `helm' for `helm-source-evaluation-result' with `eldoc' support." t nil) + +(autoload 'helm-calcul-expression "helm-eval" "\ +Preconfigured `helm' for `helm-source-calculation-result'." t nil) + +(register-definition-prefixes "helm-eval" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-external" "helm-external.el" (0 0 0 0)) +;;; Generated autoloads from helm-external.el + +(autoload 'helm-run-external-command "helm-external" "\ +Preconfigured `helm' to run External PROGRAM asyncronously from Emacs. +If program is already running try to run `helm-raise-command' if +defined otherwise exit with error. You can set your own list of +commands with `helm-external-commands-list'." t nil) + +(register-definition-prefixes "helm-external" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-fd" "helm-fd.el" (0 0 0 0)) +;;; Generated autoloads from helm-fd.el + +(register-definition-prefixes "helm-fd" '("helm-fd-")) + +;;;*** + +;;;### (autoloads nil "helm-files" "helm-files.el" (0 0 0 0)) +;;; Generated autoloads from helm-files.el + +(defvar helm-ff-icon-mode nil "\ +Non-nil if Helm-Ff-Icon mode is enabled. +See the `helm-ff-icon-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `helm-ff-icon-mode'.") + +(custom-autoload 'helm-ff-icon-mode "helm-files" nil) + +(autoload 'helm-ff-icon-mode "helm-files" "\ +Display icons from `all-the-icons' package in HFF when enabled. + +This is a minor mode. If called interactively, toggle the +`Helm-Ff-Icon mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='helm-ff-icon-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +NOTE: This mode is building `helm-source-find-files', so if you enable +it from your init file, ensure to call it _after_ your defmethod's +`helm-setup-user-source' definitions (if some) to ensure they are called. + +\(fn &optional ARG)" t nil) + +(autoload 'helm-ff-cleanup-image-dired-dir-and-cache "helm-files" "\ +Cleanup `image-dired-dir' directory. +Delete all thumb files that are no more associated with an existing +image file in `helm-ff-image-dired-thumbnails-cache'." t nil) + +(autoload 'helm-projects-history "helm-files" "\ + + +\(fn ARG)" t nil) + +(autoload 'helm-browse-project "helm-files" "\ +Preconfigured helm to browse projects. +Browse files and see status of project with its VCS. +Only HG and GIT are supported for now. +Fall back to `helm-browse-project-find-files' if current +directory is not under control of one of those VCS. +With a prefix ARG browse files recursively, with two prefix ARG +rebuild the cache. +If the current directory is found in the cache, start +`helm-browse-project-find-files' even with no prefix ARG. +NOTE: The prefix ARG have no effect on the VCS controlled +directories. + +Needed dependencies for VCS: + +and +. + +\(fn ARG)" t nil) + +(autoload 'helm-find-files "helm-files" "\ +Preconfigured `helm' for helm implementation of `find-file'. +Called with a prefix arg show history if some. +Don't call it from programs, use `helm-find-files-1' instead. +This is the starting point for nearly all actions you can do on +files. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-files" '("eshell-command-aliases-list" "helm-")) + +;;;*** + +;;;### (autoloads nil "helm-find" "helm-find.el" (0 0 0 0)) +;;; Generated autoloads from helm-find.el + +(autoload 'helm-find "helm-find" "\ +Preconfigured `helm' for the find shell command. + +Recursively find files whose names are matched by all specified +globbing PATTERNs under the current directory using the external +program specified in `find-program' (usually \"find\"). Every +input PATTERN is silently wrapped into two stars: *PATTERN*. + +With prefix argument, prompt for a directory to search. + +When user option `helm-findutils-search-full-path' is non-nil, +match against complete paths, otherwise, against file names +without directory part. + +The (possibly empty) list of globbing PATTERNs can be followed by +the separator \"*\" plus any number of additional arguments that +are passed to \"find\" literally. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-find" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-font" "helm-font.el" (0 0 0 0)) +;;; Generated autoloads from helm-font.el + +(autoload 'helm-select-xfont "helm-font" "\ +Preconfigured `helm' to select Xfont." t nil) + +(autoload 'helm-ucs "helm-font" "\ +Preconfigured `helm' for `ucs-names'. + +Called with a prefix arg force reloading cache. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-font" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-for-files" "helm-for-files.el" (0 0 0 +;;;;;; 0)) +;;; Generated autoloads from helm-for-files.el + +(autoload 'helm-for-files "helm-for-files" "\ +Preconfigured `helm' for opening files. +Run all sources defined in `helm-for-files-preferred-list'." t nil) + +(autoload 'helm-multi-files "helm-for-files" "\ +Preconfigured helm like `helm-for-files' but running locate only on demand. + +Allow toggling back and forth from locate to others sources with +`helm-multi-files-toggle-locate-binding' key. +This avoids launching locate needlessly when what you are +searching for is already found." t nil) + +(autoload 'helm-recentf "helm-for-files" "\ +Preconfigured `helm' for `recentf'." t nil) + +(register-definition-prefixes "helm-for-files" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-global-bindings" "helm-global-bindings.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from helm-global-bindings.el + +(register-definition-prefixes "helm-global-bindings" '("helm-command-")) + +;;;*** + +;;;### (autoloads nil "helm-grep" "helm-grep.el" (0 0 0 0)) +;;; Generated autoloads from helm-grep.el + +(autoload 'helm-goto-precedent-file "helm-grep" "\ +Go to previous file in Helm grep/etags buffers." t nil) + +(autoload 'helm-goto-next-file "helm-grep" "\ +Go to previous file in Helm grep/etags buffers." t nil) + +(autoload 'helm-revert-next-error-last-buffer "helm-grep" "\ +Revert last `next-error' buffer from `current-buffer'. + +Accept to revert only `helm-grep-mode' or `helm-occur-mode' buffers. +Use this when you want to revert the `next-error' buffer after +modifications in `current-buffer'." t nil) + +(autoload 'helm-do-grep-ag "helm-grep" "\ +Preconfigured `helm' for grepping with AG in `default-directory'. +With prefix arg prompt for type if available with your AG +version. + +\(fn ARG)" t nil) + +(autoload 'helm-grep-do-git-grep "helm-grep" "\ +Preconfigured `helm' for git-grepping `default-directory'. +With a prefix arg ARG git-grep the whole repository. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-grep" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-help" "helm-help.el" (0 0 0 0)) +;;; Generated autoloads from helm-help.el + +(autoload 'helm-documentation "helm-help" "\ +Preconfigured `helm' for Helm documentation. +With a prefix arg refresh the documentation. + +Find here the documentation of all documented sources." t nil) + +(defvar helm-comp-read-mode-line "\\C/\\[helm-cr-empty-string]:Empty \\\\[helm-help]:Help \\[helm-select-action]:Act \\[helm-maybe-exit-minibuffer]/f1/f2/f-n:NthAct \\[helm-toggle-suspend-update]:Tog.suspend \\[helm-customize-group]:Conf") + +(defvar helm-read-file-name-mode-line-string "\\\\[helm-help]:Help C/\\[helm-cr-empty-string]:Empty \\\\[helm-select-action]:Act \\[helm-maybe-exit-minibuffer]/f1/f2/f-n:NthAct \\[helm-toggle-suspend-update]:Tog.suspend \\[helm-customize-group]:Conf" "\ +String displayed in mode-line in `helm-source-find-files'.") + +(defvar helm-top-mode-line "\\\\[helm-help]:Help \\\\[helm-select-action]:Act \\[helm-maybe-exit-minibuffer]/f1/f2/f-n:NthAct \\[helm-toggle-suspend-update]:Tog.suspend \\[helm-customize-group]:Conf") + +(register-definition-prefixes "helm-help" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-id-utils" "helm-id-utils.el" (0 0 0 0)) +;;; Generated autoloads from helm-id-utils.el + +(autoload 'helm-gid "helm-id-utils" "\ +Preconfigured `helm' for `gid' command line of `ID-Utils'. +Need A database created with the command `mkid' above +`default-directory'. +Need id-utils as dependency which provide `mkid', `gid' etc.. +See ." t nil) + +(register-definition-prefixes "helm-id-utils" '("helm-gid-")) + +;;;*** + +;;;### (autoloads nil "helm-imenu" "helm-imenu.el" (0 0 0 0)) +;;; Generated autoloads from helm-imenu.el + +(autoload 'helm-imenu "helm-imenu" "\ +Preconfigured `helm' for `imenu'." t nil) + +(autoload 'helm-imenu-in-all-buffers "helm-imenu" "\ +Fetch Imenu entries in all buffers with similar mode as current. +A mode is similar as current if it is the same, it is derived +i.e. `derived-mode-p' or it have an association in +`helm-imenu-all-buffer-assoc'." t nil) + +(register-definition-prefixes "helm-imenu" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-info" "helm-info.el" (0 0 0 0)) +;;; Generated autoloads from helm-info.el + +(autoload 'helm-info "helm-info" "\ +Preconfigured `helm' for searching Info files' indices. + +With a prefix argument \\[universal-argument], set REFRESH to +non-nil. + +Optional parameter REFRESH, when non-nil, re-evaluates +`helm-default-info-index-list'. If the variable has been +customized, set it to its saved value. If not, set it to its +standard value. See `custom-reevaluate-setting' for more. + +REFRESH is useful when new Info files are installed. If +`helm-default-info-index-list' has not been customized, the new +Info files are made available. + +\(fn &optional REFRESH)" t nil) + +(autoload 'helm-info-at-point "helm-info" "\ +Preconfigured `helm' for searching info at point." t nil) + +(register-definition-prefixes "helm-info" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-locate" "helm-locate.el" (0 0 0 0)) +;;; Generated autoloads from helm-locate.el + +(autoload 'helm-projects-find-files "helm-locate" "\ +Find files with locate in `helm-locate-project-list'. +With a prefix arg refresh the database in each project. + +\(fn UPDATE)" t nil) + +(autoload 'helm-locate "helm-locate" "\ +Preconfigured `helm' for Locate. +Note: you can add locate options after entering pattern. +See \\='man locate' for valid options and also `helm-locate-command'. + +You can specify a local database with prefix argument ARG. +With two prefix arg, refresh the current local db or create it if +it doesn't exists. + +To create a user specific db, use +\"updatedb -l 0 -o db_path -U directory\". +Where db_path is a filename matched by +`helm-locate-db-file-regexp'. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-locate" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-man" "helm-man.el" (0 0 0 0)) +;;; Generated autoloads from helm-man.el + +(autoload 'helm-man-woman "helm-man" "\ +Preconfigured `helm' for Man and Woman pages. +With a prefix arg reinitialize the cache. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-man" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-misc" "helm-misc.el" (0 0 0 0)) +;;; Generated autoloads from helm-misc.el + +(defvar helm-minibuffer-history-mode nil "\ +Non-nil if Helm-Minibuffer-History mode is enabled. +See the `helm-minibuffer-history-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `helm-minibuffer-history-mode'.") + +(custom-autoload 'helm-minibuffer-history-mode "helm-misc" nil) + +(autoload 'helm-minibuffer-history-mode "helm-misc" "\ +Bind `helm-minibuffer-history-key' in al minibuffer maps. +This mode is enabled by `helm-mode', so there is no need to enable it directly. + +This is a minor mode. If called interactively, toggle the +`Helm-Minibuffer-History mode' mode. If the prefix argument is +positive, enable the mode, and if it is zero or negative, disable +the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='helm-minibuffer-history-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(autoload 'helm-world-time "helm-misc" "\ +Preconfigured `helm' to show world time. +Default action change TZ environment variable locally to emacs." t nil) + +(autoload 'helm-insert-latex-math "helm-misc" "\ +Preconfigured helm for latex math symbols completion." t nil) + +(autoload 'helm-ratpoison-commands "helm-misc" "\ +Preconfigured `helm' to execute ratpoison commands." t nil) + +(autoload 'helm-stumpwm-commands "helm-misc" "\ +Preconfigured helm for stumpwm commands." t nil) + +(autoload 'helm-minibuffer-history "helm-misc" "\ +Preconfigured `helm' for `minibuffer-history'." t nil) + +(register-definition-prefixes "helm-misc" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-mode" "helm-mode.el" (0 0 0 0)) +;;; Generated autoloads from helm-mode.el + +(autoload 'helm-comp-read "helm-mode" "\ +Read a string in the minibuffer, with helm completion. + +It is helm `completing-read' equivalent. + +- PROMPT is the prompt name to use. + +- COLLECTION can be a list, alist, vector, obarray or hash-table. + For alists and hash-tables their car are use as real value of + candidate unless ALISTP is non-nil. + It can be also a function that receives three arguments: + the values string, predicate and t. See `all-completions' for more details. + +Keys description: + +- TEST: A predicate called with one arg i.e candidate. + +- INITIAL-INPUT: Same as input arg in `helm'. + +- PRESELECT: See preselect arg of `helm'. + +- DEFAULT: This option is used only for compatibility with regular + Emacs `completing-read' (Same as DEFAULT arg of `completing-read'). + +- BUFFER: Name of helm-buffer. + +- MUST-MATCH: Candidate selected must be one of COLLECTION. + +- FUZZY: Enable fuzzy matching. + +- REVERSE-HISTORY: When non--nil display history source after current + source completion. + +- REQUIRES-PATTERN: Same as helm attribute, default is 0. + +- HISTORY: A symbol where each result will be saved. + If not specified as a symbol an error will popup. + When specified, all elements of HISTORY are displayed in + a special source before or after COLLECTION according to REVERSE-HISTORY. + The main difference with INPUT-HISTORY is that the result of the + completion is saved whereas in INPUT-HISTORY it is the minibuffer + contents which is saved when you exit. + Don't use the same symbol for INPUT-HISTORY and HISTORY. + NOTE: As mentionned above this has nothing to do with + `minibuffer-history-variable', therefore if you want to save this + history persistently, you will have to add this variable to the + relevant variable of your favorite tool for persistent emacs session + i.e. psession, desktop etc... + +- RAW-HISTORY: When non-nil do not remove backslashs if some in + HISTORY candidates. + +- INPUT-HISTORY: A symbol. The minibuffer input history will be + stored there, if nil or not provided, `minibuffer-history' + will be used instead. You can navigate in this history with + `M-p' and `M-n'. + Don't use the same symbol for INPUT-HISTORY and HISTORY. + +- CASE-FOLD: Same as `helm-case-fold-search'. + +- PERSISTENT-ACTION: A function called with one arg i.e candidate. + +- PERSISTENT-HELP: A string to document PERSISTENT-ACTION. + +- MODE-LINE: A string or list to display in mode line. + Default is `helm-comp-read-mode-line'. + +- KEYMAP: A keymap to use in this `helm-comp-read'. + (the keymap will be shared with history source) + +- NAME: The name related to this local source. + +- HEADER-NAME: A function to alter NAME, see `helm'. + +- EXEC-WHEN-ONLY-ONE: Bound `helm-execute-action-at-once-if-one' + to non--nil. (possibles values are t or nil). + +- VOLATILE: Use volatile attribute. + +- SORT: A predicate to give to `sort' e.g `string-lessp' + Use this only on small data as it is inefficient. + If you want to sort faster add a sort function to + FC-TRANSFORMER. + Note that FUZZY when enabled is already providing a sort function. + +- FC-TRANSFORMER: A `filtered-candidate-transformer' function + or a list of functions. + +- HIST-FC-TRANSFORMER: A `filtered-candidate-transformer' + function for the history source. + +- MARKED-CANDIDATES: If non-nil return candidate or marked candidates as a list. + +- NOMARK: When non--nil don't allow marking candidates. + +- ALISTP: + When non-nil (default) pass the value of (DISPLAY . REAL) + candidate in COLLECTION to action when COLLECTION is an alist or a + hash-table, otherwise DISPLAY is always returned as result on exit, + which is the default when using `completing-read'. + See `helm-comp-read-get-candidates'. + +- CANDIDATES-IN-BUFFER: when non--nil use a source build with + `helm-source-in-buffer' which is much faster. + Argument VOLATILE have no effect when CANDIDATES-IN-BUFFER is non--nil. + +- MATCH-PART: Allow matching only one part of candidate. + See match-part documentation in `helm-source'. + +- MATCH-DYNAMIC: See match-dynamic in `helm-source-sync' + It has no effect when used with CANDIDATES-IN-BUFFER. + +- ALLOW-NEST: Allow nesting this `helm-comp-read' in a helm session. + See `helm'. + +- MULTILINE: See multiline in `helm-source'. + +- COERCE: See coerce in `helm-source'. + +- GROUP: See group in `helm-source'. + +Any prefix args passed during `helm-comp-read' invocation will be recorded +in `helm-current-prefix-arg', otherwise if prefix args were given before +`helm-comp-read' invocation, the value of `current-prefix-arg' will be used. +That means you can pass prefix args before or after calling a command +that use `helm-comp-read'. See `helm-M-x' for example. + +\(fn PROMPT COLLECTION &key TEST INITIAL-INPUT DEFAULT PRESELECT (BUFFER \"*Helm Completions*\") MUST-MATCH FUZZY REVERSE-HISTORY (REQUIRES-PATTERN 0) (HISTORY nil SHISTORY) RAW-HISTORY INPUT-HISTORY (CASE-FOLD helm-comp-read-case-fold-search) (PERSISTENT-ACTION nil) (PERSISTENT-HELP \"DoNothing\") (MODE-LINE helm-comp-read-mode-line) HELP-MESSAGE (KEYMAP helm-comp-read-map) (NAME \"Helm Completions\") HEADER-NAME CANDIDATES-IN-BUFFER DIACRITICS MATCH-PART MATCH-DYNAMIC EXEC-WHEN-ONLY-ONE QUIT-WHEN-NO-CAND (VOLATILE t) SORT FC-TRANSFORMER HIST-FC-TRANSFORMER (MARKED-CANDIDATES helm-comp-read-use-marked) NOMARK (ALISTP t) (CANDIDATE-NUMBER-LIMIT helm-candidate-number-limit) MULTILINE ALLOW-NEST COERCE (GROUP \\='helm))" nil nil) + +(autoload 'helm-read-file-name "helm-mode" "\ +Read a file name with helm completion. + +It is helm `read-file-name' emulation. + +Argument PROMPT is the default prompt to use. + +Keys description: + +- NAME: Source name, default to \"Read File Name\". + +- INITIAL-INPUT: Where to start reading file name, + default to `default-directory' or $HOME. + +- BUFFER: `helm-buffer' name, defaults to \"*Helm Completions*\". + +- TEST: A predicate called with one arg \\='candidate'. + +- NORET: Allow disabling helm-ff-RET (have no effect if helm-ff-RET + isn't bound to RET). + +- CASE-FOLD: Same as `helm-case-fold-search'. + +- PRESELECT: helm preselection. + +- HISTORY: Display HISTORY in a special source. + +- MUST-MATCH: Can be \\='confirm, nil, or t. + +- FUZZY: Enable fuzzy matching when non-nil (Enabled by default). + +- MARKED-CANDIDATES: When non--nil return a list of marked candidates. + +- NOMARK: When non--nil don't allow marking candidates. + +- ALISTP: Don't use `all-completions' in history + (take effect only on history). + +- PERSISTENT-ACTION-IF: a persistent if action function. + +- PERSISTENT-HELP: persistent help message. + +- MODE-LINE: A mode line message, default is + `helm-read-file-name-mode-line-string'. + +\(fn PROMPT &key (NAME \"Read File Name\") INITIAL-INPUT (BUFFER \"*Helm file completions*\") TEST NORET (CASE-FOLD helm-file-name-case-fold-search) PRESELECT HISTORY MUST-MATCH (FUZZY t) DEFAULT MARKED-CANDIDATES (CANDIDATE-NUMBER-LIMIT helm-ff-candidate-number-limit) NOMARK (ALISTP t) (PERSISTENT-ACTION-IF \\='helm-find-files-persistent-action-if) (PERSISTENT-HELP \"Hit1 Expand Candidate, Hit2 or (C-u) Find file\") (MODE-LINE helm-read-file-name-mode-line-string))" nil nil) + +(defvar helm-mode nil "\ +Non-nil if Helm mode is enabled. +See the `helm-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `helm-mode'.") + +(custom-autoload 'helm-mode "helm-mode" nil) + +(autoload 'helm-mode "helm-mode" "\ +Toggle generic helm completion. + +All functions in Emacs that use `completing-read', +`read-file-name', `completion-in-region' and friends will use helm +interface when this mode is turned on. + +However you can modify this behavior for functions of your choice +with `helm-completing-read-handlers-alist'. + +Called with a positive arg, turn on unconditionally, with a +negative arg turn off. +You can toggle it with M-x `helm-mode'. + +About `ido-mode': +DO NOT enable `ido-everywhere' when using `helm-mode'. Instead of +using `ido-mode', add the commands where you want to use ido to +`helm-completing-read-handlers-alist' with `ido' as value. + +Note: This mode is incompatible with Emacs23. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "helm-mode" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-net" "helm-net.el" (0 0 0 0)) +;;; Generated autoloads from helm-net.el + +(autoload 'helm-browse-url-firefox "helm-net" "\ +Same as `browse-url-firefox' but detach from Emacs. + +So when you quit Emacs you can keep your Firefox session open and +not be prompted to kill the Firefox process. + +NOTE: Probably not supported on some systems (e.g., Windows). + +\(fn URL &optional IGNORE)" t nil) + +(autoload 'helm-browse-url-opera "helm-net" "\ +Browse URL with Opera browser and detach from Emacs. + +So when you quit Emacs you can keep your Opera session open and +not be prompted to kill the Opera process. + +NOTE: Probably not supported on some systems (e.g., Windows). + +\(fn URL &optional IGNORE)" t nil) + +(autoload 'helm-browse-url-chromium "helm-net" "\ +Browse URL with Google Chrome browser. + +\(fn URL &optional IGNORE)" t nil) + +(autoload 'helm-browse-url-uzbl "helm-net" "\ +Browse URL with uzbl browser. + +\(fn URL &optional IGNORE)" t nil) + +(autoload 'helm-browse-url-conkeror "helm-net" "\ +Browse URL with conkeror browser. + +\(fn URL &optional IGNORE)" t nil) + +(autoload 'helm-browse-url-nyxt "helm-net" "\ +Browse URL with nyxt browser. + +\(fn URL &optional IGNORE)" t nil) + +(autoload 'helm-surfraw "helm-net" "\ +Preconfigured `helm' to search PATTERN with search ENGINE. + +\(fn PATTERN ENGINE)" t nil) + +(autoload 'helm-google-suggest "helm-net" "\ +Preconfigured `helm' for Google search with Google suggest." t nil) + +(register-definition-prefixes "helm-net" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-occur" "helm-occur.el" (0 0 0 0)) +;;; Generated autoloads from helm-occur.el + +(autoload 'helm-occur "helm-occur" "\ +Preconfigured helm for searching lines matching pattern in `current-buffer'. + +When `helm-source-occur' is member of +`helm-sources-using-default-as-input' which is the default, +symbol at point is searched at startup. + +When a region is marked search only in this region by narrowing. + +To search in multiples buffers start from one of the commands listing +buffers (i.e. a helm command using `helm-source-buffers-list' like +`helm-mini') and use the multi occur buffers action. + +This is the helm implementation that collect lines matching pattern +like vanilla Emacs `occur' but have nothing to do with it, the search +engine beeing completely different and also much faster." t nil) + +(autoload 'helm-occur-visible-buffers "helm-occur" "\ +Run helm-occur on all visible buffers in frame." t nil) + +(autoload 'helm-occur-from-isearch "helm-occur" "\ +Invoke `helm-occur' from isearch. + +To use this bind it to a key in `isearch-mode-map'." t nil) + +(autoload 'helm-multi-occur-from-isearch "helm-occur" "\ +Invoke `helm-multi-occur' from isearch. + +With a prefix arg, reverse the behavior of +`helm-moccur-always-search-in-current'. +The prefix arg can be set before calling +`helm-multi-occur-from-isearch' or during the buffer selection. + +To use this bind it to a key in `isearch-mode-map'." t nil) + +(register-definition-prefixes "helm-occur" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-regexp" "helm-regexp.el" (0 0 0 0)) +;;; Generated autoloads from helm-regexp.el + +(autoload 'helm-regexp "helm-regexp" "\ +Preconfigured helm to build regexps. +`query-replace-regexp' can be run from there against found regexp." t nil) + +(register-definition-prefixes "helm-regexp" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-ring" "helm-ring.el" (0 0 0 0)) +;;; Generated autoloads from helm-ring.el + +(autoload 'helm-mark-ring "helm-ring" "\ +Preconfigured `helm' for `helm-source-mark-ring'." t nil) + +(autoload 'helm-global-mark-ring "helm-ring" "\ +Preconfigured `helm' for `helm-source-global-mark-ring'." t nil) + +(autoload 'helm-all-mark-rings "helm-ring" "\ +Preconfigured `helm' for mark rings. +Source used are `helm-source-global-mark-ring' and +`helm-source-mark-ring'." t nil) + +(autoload 'helm-register "helm-ring" "\ +Preconfigured `helm' for Emacs registers." t nil) + +(autoload 'helm-show-kill-ring "helm-ring" "\ +Preconfigured `helm' for `kill-ring'. +It is drop-in replacement of `yank-pop'. + +First call open the kill-ring browser, next calls move to next line." t nil) + +(autoload 'helm-execute-kmacro "helm-ring" "\ +Preconfigured helm for keyboard macros. +Define your macros with `f3' and `f4'. +See (info \"(emacs) Keyboard Macros\") for detailed infos. +This command is useful when used with persistent action." t nil) + +(register-definition-prefixes "helm-ring" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-semantic" "helm-semantic.el" (0 0 0 0)) +;;; Generated autoloads from helm-semantic.el + +(autoload 'helm-semantic "helm-semantic" "\ +Preconfigured `helm' for `semantic'. +If ARG is supplied, pre-select symbol at point instead of current. + +\(fn ARG)" t nil) + +(autoload 'helm-semantic-or-imenu "helm-semantic" "\ +Preconfigured helm for `semantic' or `imenu'. +If ARG is supplied, pre-select symbol at point instead of current +semantic tag in scope. + +If `semantic-mode' is active in the current buffer, then use +semantic for generating tags, otherwise fall back to `imenu'. +Fill in the symbol at point by default. + +\(fn ARG)" t nil) + +(register-definition-prefixes "helm-semantic" '("helm-s")) + +;;;*** + +;;;### (autoloads nil "helm-shell" "helm-shell.el" (0 0 0 0)) +;;; Generated autoloads from helm-shell.el + +(defalias 'helm-shell-prompts 'helm-comint-prompts) + +(defalias 'helm-shell-prompts-all 'helm-comint-prompts-all) + +;;;*** + +;;;### (autoloads nil "helm-sys" "helm-sys.el" (0 0 0 0)) +;;; Generated autoloads from helm-sys.el + +(defvar helm-top-poll-mode nil "\ +Non-nil if Helm-Top-Poll mode is enabled. +See the `helm-top-poll-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `helm-top-poll-mode'.") + +(custom-autoload 'helm-top-poll-mode "helm-sys" nil) + +(autoload 'helm-top-poll-mode "helm-sys" "\ +Refresh automatically helm top buffer once enabled. + +This is a minor mode. If called interactively, toggle the +`Helm-Top-Poll mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='helm-top-poll-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(autoload 'helm-top "helm-sys" "\ +Preconfigured `helm' for top command." t nil) + +(autoload 'helm-list-emacs-process "helm-sys" "\ +Preconfigured `helm' for Emacs process." t nil) + +(autoload 'helm-xrandr-set "helm-sys" "\ +Preconfigured helm for xrandr." t nil) + +(register-definition-prefixes "helm-sys" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-tags" "helm-tags.el" (0 0 0 0)) +;;; Generated autoloads from helm-tags.el + +(autoload 'helm-etags-select "helm-tags" "\ +Preconfigured helm for etags. +If called with a prefix argument REINIT +or if any of the tag files have been modified, reinitialize cache. + +This function aggregates three sources of tag files: + + 1) An automatically located file in the parent directories, + by `helm-etags-get-tag-file'. + 2) `tags-file-name', which is commonly set by `find-tag' command. + 3) `tags-table-list' which is commonly set by `visit-tags-table' command. + +\(fn REINIT)" t nil) + +(register-definition-prefixes "helm-tags" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-types" "helm-types.el" (0 0 0 0)) +;;; Generated autoloads from helm-types.el + +(register-definition-prefixes "helm-types" '("helm-")) + +;;;*** + +;;;### (autoloads nil "helm-utils" "helm-utils.el" (0 0 0 0)) +;;; Generated autoloads from helm-utils.el + +(defvar helm-popup-tip-mode nil "\ +Non-nil if Helm-Popup-Tip mode is enabled. +See the `helm-popup-tip-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `helm-popup-tip-mode'.") + +(custom-autoload 'helm-popup-tip-mode "helm-utils" nil) + +(autoload 'helm-popup-tip-mode "helm-utils" "\ +Show help-echo informations in a popup tip at end of line. + +This is a minor mode. If called interactively, toggle the +`Helm-Popup-Tip mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='helm-popup-tip-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "helm-utils" '("helm-" "with-helm-display-marked-candidates")) + +;;;*** + +;;;### (autoloads nil "helm-x-files" "helm-x-files.el" (0 0 0 0)) +;;; Generated autoloads from helm-x-files.el + +(register-definition-prefixes "helm-x-files" '("helm-")) + +;;;*** + +;;;### (autoloads nil nil ("helm-config.el" "helm-easymenu.el" "helm-pkg.el" +;;;;;; "helm.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; helm-autoloads.el ends here diff --git a/code/elpa/helm-20220822.659/helm-bookmark.el b/code/elpa/helm-20220822.659/helm-bookmark.el new file mode 100644 index 0000000..29731d7 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-bookmark.el @@ -0,0 +1,830 @@ +;;; helm-bookmark.el --- Helm for Emacs regular Bookmarks. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'cl-lib) +(require 'bookmark) +(require 'helm) +(require 'helm-lib) +(require 'helm-help) +(require 'helm-types) +(require 'helm-utils) +(require 'helm-info) +(require 'helm-adaptive) +(require 'helm-net) + +(declare-function helm-browse-project "helm-files" (arg)) +(declare-function addressbook-bookmark-edit "ext:addressbook-bookmark.el" (bookmark)) +(declare-function all-the-icons-fileicon "ext:all-the-icons.el") +(declare-function all-the-icons-icon-for-file"ext:all-the-icons.el") +(declare-function all-the-icons-octicon "ext:all-the-icons.el") + + +(defgroup helm-bookmark nil + "Predefined configurations for `helm.el'." + :group 'helm) + +(defcustom helm-bookmark-show-location nil + "Show location of bookmark on display." + :type 'boolean) + +(defcustom helm-bookmark-default-filtered-sources + (append '(helm-source-bookmark-org + helm-source-bookmark-files&dirs + helm-source-bookmark-helm-find-files + helm-source-bookmark-info + helm-source-bookmark-gnus + helm-source-bookmark-mu4e + helm-source-bookmark-man + helm-source-bookmark-images + helm-source-bookmark-w3m) + (list 'helm-source-bookmark-uncategorized + 'helm-source-bookmark-set)) + "List of sources to use in `helm-filtered-bookmarks'." + :type '(repeat (choice symbol))) + +(defcustom helm-bookmark-use-icon nil + "Display candidates with an icon with `all-the-icons' when non nil." + :type 'boolean) + +(defcustom helm-bookmark-default-sort-method 'adaptive + "Sort method for `helm-filtered-bookmarks'. + +Value can be either \\='native' or \\='adaptive'. + +Once you use \\='native' the bookmark variable `bookmark-sort-flag' +will be honored." + :type '(choice + (symbol :tag "Helm adaptive sort method" adaptive) + (symbol :tag "Native bookmark sort method" native)) + ;; Don't use the :set function until functions and variables below + ;; are not loaded i.e. use set-default only for now. + :initialize 'custom-initialize-changed + :set (lambda (var val) + (set var val) + (cl-loop for s in (remove 'helm-source-bookmark-set + helm-bookmark-default-filtered-sources) + for fn = (intern (format "%s-builder" s)) + do (set s (funcall fn))))) + +(defgroup helm-bookmark-faces nil + "Customize the appearance of helm-bookmark." + :prefix "helm-" + :group 'helm-bookmark + :group 'helm-faces) + +(defface helm-bookmark-info + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "green")) + "Face used for W3m Emacs bookmarks (not w3m bookmarks)." + :group 'helm-bookmark-faces) + +(defface helm-bookmark-w3m + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "yellow")) + "Face used for W3m Emacs bookmarks (not w3m bookmarks)." + :group 'helm-bookmark-faces) + +(defface helm-bookmark-gnus + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "magenta")) + "Face used for Gnus bookmarks." + :group 'helm-bookmark-faces) + +(defface helm-bookmark-man + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Orange4")) + "Face used for Woman/man bookmarks." + :group 'helm-bookmark-faces) + +(defface helm-bookmark-file + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Deepskyblue2")) + "Face used for file bookmarks." + :group 'helm-bookmark-faces) + +(defface helm-bookmark-file-not-found + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Slategray4")) + "Face used for file bookmarks." + :group 'helm-bookmark-faces) + +(defface helm-bookmark-directory + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit helm-ff-directory)) + "Face used for file bookmarks." + :group 'helm-bookmark-faces) + +(defface helm-bookmark-addressbook + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "tomato")) + "Face used for addressbook bookmarks." + :group 'helm-bookmark-faces) + + +(defvar helm-bookmark-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c o") #'helm-bookmark-run-jump-other-window) + (define-key map (kbd "C-c C-o") #'helm-bookmark-run-jump-other-frame) + (define-key map (kbd "C-d") #'helm-bookmark-run-delete) + (define-key map (kbd "C-]") #'helm-bookmark-toggle-filename) + (define-key map (kbd "M-e") #'helm-bookmark-run-edit) + map) + "Generic Keymap for Emacs bookmark sources.") + +(defclass helm-source-basic-bookmarks (helm-source-in-buffer helm-type-bookmark) + ((init :initform (lambda () + (bookmark-maybe-load-default-file) + (helm-init-candidates-in-buffer + 'global + (if (and (fboundp 'bookmark-maybe-sort-alist) + (fboundp 'bookmark-name-from-full-record)) + (mapcar 'bookmark-name-from-full-record + (bookmark-maybe-sort-alist)) + (bookmark-all-names))))) + (filtered-candidate-transformer :initform 'helm-bookmark-transformer) + (find-file-target :initform #'helm-bookmarks-quit-an-find-file-fn))) + +(defvar helm-source-bookmarks + (helm-make-source "Bookmarks" 'helm-source-basic-bookmarks) + "See (info \"(emacs)Bookmarks\").") + +(defun helm-bookmark-transformer (candidates _source) + (cl-loop for i in candidates + for loc = (bookmark-location i) + for len = (string-width i) + for trunc = (if (> len bookmark-bmenu-file-column) + (helm-substring i bookmark-bmenu-file-column) + i) + for sep = (make-string (- (+ bookmark-bmenu-file-column 2) + (length trunc)) + ? ) + if helm-bookmark-show-location + collect (cons (concat trunc sep (if (listp loc) (car loc) loc)) i) + else collect i)) + +(defun helm-bookmark-toggle-filename-1 (_candidate) + (let* ((real (helm-get-selection helm-buffer)) + (trunc (if (> (string-width real) bookmark-bmenu-file-column) + (helm-substring real bookmark-bmenu-file-column) + real))) + (setq helm-bookmark-show-location (not helm-bookmark-show-location)) + (helm-update (if helm-bookmark-show-location + (regexp-quote trunc) + (regexp-quote real))))) + +(defun helm-bookmark-toggle-filename () + "Toggle bookmark location visibility." + (interactive) + (with-helm-alive-p + (helm-set-attr 'toggle-filename + '(helm-bookmark-toggle-filename-1 . never-split)) + (helm-execute-persistent-action 'toggle-filename))) +(put 'helm-bookmark-toggle-filename 'helm-only t) + +(defun helm-bookmark-jump (candidate) + "Jump to bookmark action." + (let ((current-prefix-arg helm-current-prefix-arg) + non-essential) + (bookmark-jump candidate))) + +(defun helm-bookmark-jump-other-frame (candidate) + "Jump to bookmark in other frame action." + (let ((current-prefix-arg helm-current-prefix-arg) + non-essential) + (bookmark-jump candidate 'switch-to-buffer-other-frame))) + +(defun helm-bookmark-jump-other-window (candidate) + "Jump to bookmark in other window action." + (let (non-essential) + (bookmark-jump-other-window candidate))) + + +;;; bookmark-set +;; +(defvar helm-source-bookmark-set + (helm-build-dummy-source "Set Bookmark" + :filtered-candidate-transformer + (lambda (_candidates _source) + (list (or (and (not (string= helm-pattern "")) + helm-pattern) + "Enter a bookmark name to record"))) + :action '(("Set bookmark" . (lambda (candidate) + (if (string= helm-pattern "") + (message "No bookmark name given for record") + (bookmark-set candidate)))))) + "See (info \"(emacs)Bookmarks\").") + + +;;; Predicates +;; +(defconst helm-bookmark--non-file-filename " - no file -" + "Name to use for `filename' entry, for non-file bookmarks.") + +(defun helm-bookmark-gnus-bookmark-p (bookmark) + "Return non-nil if BOOKMARK is a Gnus bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (or (eq (bookmark-get-handler bookmark) 'bmkext-jump-gnus) + (eq (bookmark-get-handler bookmark) 'gnus-summary-bookmark-jump) + (eq (bookmark-get-handler bookmark) 'bookmarkp-jump-gnus))) + +(defun helm-bookmark-mu4e-bookmark-p (bookmark) + "Return non nil if BOOKMARK is a mu4e bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (memq (bookmark-get-handler bookmark) + '(mu4e-bookmark-jump mu4e--jump-to-bookmark))) + +(defun helm-bookmark-w3m-bookmark-p (bookmark) + "Return non-nil if BOOKMARK is a W3m bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (or (eq (bookmark-get-handler bookmark) 'bmkext-jump-w3m) + (eq (bookmark-get-handler bookmark) 'bookmark-w3m-bookmark-jump) + (eq (bookmark-get-handler bookmark) 'bookmarkp-jump-w3m))) + +(defun helm-bookmark-woman-bookmark-p (bookmark) + "Return non-nil if BOOKMARK is a Woman bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (or (eq (bookmark-get-handler bookmark) 'bmkext-jump-woman) + (eq (bookmark-get-handler bookmark) 'woman-bookmark-jump) + (eq (bookmark-get-handler bookmark) 'bookmarkp-jump-woman))) + +(defun helm-bookmark-man-bookmark-p (bookmark) + "Return non-nil if BOOKMARK is a Man bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (or (eq (bookmark-get-handler bookmark) 'bmkext-jump-man) + (eq (bookmark-get-handler bookmark) 'Man-bookmark-jump) + (eq (bookmark-get-handler bookmark) 'bookmarkp-jump-man))) + +(defun helm-bookmark-woman-man-bookmark-p (bookmark) + "Return non-nil if BOOKMARK is a Man or Woman bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (or (helm-bookmark-man-bookmark-p bookmark) + (helm-bookmark-woman-bookmark-p bookmark))) + +(defun helm-bookmark-info-bookmark-p (bookmark) + "Return non-nil if BOOKMARK is an Info bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (eq (bookmark-get-handler bookmark) 'Info-bookmark-jump)) + +(defun helm-bookmark-image-bookmark-p (bookmark) + "Return non-nil if BOOKMARK bookmarks an image file." + (if (stringp bookmark) + (assq 'image-type (assq bookmark bookmark-alist)) + (assq 'image-type bookmark))) + +(defun helm-bookmark-file-p (bookmark) + "Return non-nil if BOOKMARK bookmarks a file or directory. +BOOKMARK is a bookmark name or a bookmark record. +This excludes bookmarks of a more specific kind (Info, Gnus, and W3m)." + (let* ((filename (bookmark-get-filename bookmark)) + (isnonfile (equal filename helm-bookmark--non-file-filename))) + (and filename (not isnonfile) (not (bookmark-get-handler bookmark))))) + +(defun helm-bookmark-org-file-p (bookmark) + (let* ((filename (bookmark-get-filename bookmark))) + (or (string-suffix-p ".org" filename t) + (string-suffix-p ".org_archive" filename t)))) + +(defun helm-bookmark-helm-find-files-p (bookmark) + "Return non-nil if BOOKMARK bookmarks a `helm-find-files' session. +BOOKMARK is a bookmark name or a bookmark record." + (eq (bookmark-get-handler bookmark) 'helm-ff-bookmark-jump)) + +(defun helm-bookmark-addressbook-p (bookmark) + "Return non--nil if BOOKMARK is a contact recorded with addressbook-bookmark. +BOOKMARK is a bookmark name or a bookmark record." + (if (listp bookmark) + (string= (assoc-default 'type bookmark) "addressbook") + (string= (assoc-default + 'type (assoc bookmark bookmark-alist)) "addressbook"))) + +(defun helm-bookmark-uncategorized-bookmark-p (bookmark) + "Return non--nil if BOOKMARK match no known category." + (cl-loop for pred in '(helm-bookmark-org-file-p + helm-bookmark-addressbook-p + helm-bookmark-gnus-bookmark-p + helm-bookmark-mu4e-bookmark-p + helm-bookmark-w3m-bookmark-p + helm-bookmark-woman-man-bookmark-p + helm-bookmark-info-bookmark-p + helm-bookmark-image-bookmark-p + helm-bookmark-file-p + helm-bookmark-helm-find-files-p + helm-bookmark-addressbook-p) + never (funcall pred bookmark))) + +(defun helm-bookmark-filter-setup-alist (fn) + "Return a filtered `bookmark-alist' sorted alphabetically." + (cl-loop for b in (if (and (fboundp 'bookmark-maybe-sort-alist) + (eq helm-bookmark-default-sort-method 'native)) + (bookmark-maybe-sort-alist) + bookmark-alist) + for name = (car b) + when (funcall fn b) collect + (propertize name 'location (bookmark-location name)))) + +;;; Bookmark handlers +;; +(defvar w3m-async-exec) +(defun helm-bookmark-jump-w3m (bookmark) + "Jump to W3m bookmark BOOKMARK, setting a new tab. +If `browse-url-browser-function' is set to something else than +`w3m-browse-url' use it." + (require 'helm-net) + (let* ((file (or (bookmark-prop-get bookmark 'filename) + (bookmark-prop-get bookmark 'url))) + (buf (generate-new-buffer-name "*w3m*")) + (w3m-async-exec nil) + ;; If user don't have anymore w3m installed let it browse its + ;; bookmarks with default browser otherwise assume bookmark + ;; have been bookmarked from w3m and use w3m. + (browse-url-browser-function (or (and (fboundp 'w3m-browse-url) + (executable-find "w3m") + 'w3m-browse-url) + browse-url-browser-function)) + (really-use-w3m (equal browse-url-browser-function 'w3m-browse-url))) + (helm-browse-url file really-use-w3m) + (when really-use-w3m + (bookmark-default-handler + `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark)))))) + +;; All bookmarks recorded with the handler provided with w3m +;; (`bookmark-w3m-bookmark-jump') will use our handler which open +;; the bookmark in a new tab or in an external browser depending +;; on `browse-url-browser-function'. +(defalias 'bookmark-w3m-bookmark-jump #'helm-bookmark-jump-w3m) + +;; Provide compatibility with old handlers provided in external +;; packages bookmark-extensions.el and bookmark+. +(defalias 'bmkext-jump-woman #'woman-bookmark-jump) +(defalias 'bmkext-jump-man #'Man-bookmark-jump) +(defalias 'bmkext-jump-w3m #'helm-bookmark-jump-w3m) +(defalias 'bmkext-jump-gnus #'gnus-summary-bookmark-jump) +(defalias 'bookmarkp-jump-gnus #'gnus-summary-bookmark-jump) +(defalias 'bookmarkp-jump-w3m #'helm-bookmark-jump-w3m) +(defalias 'bookmarkp-jump-woman #'woman-bookmark-jump) +(defalias 'bookmarkp-jump-man #'Man-bookmark-jump) + + +;;;; Filtered bookmark sources +;; +;; +(defclass helm-source-filtered-bookmarks (helm-source-in-buffer helm-type-bookmark) + ((filtered-candidate-transformer + :initform (delq nil + `(,(and (eq helm-bookmark-default-sort-method 'adaptive) + 'helm-adaptive-sort) + helm-highlight-bookmark))) + (find-file-target :initform #'helm-bookmarks-quit-an-find-file-fn))) + +(defun helm-bookmarks-quit-an-find-file-fn (source) + (let* ((sel (helm-get-selection nil nil source)) + (bmk (assoc (replace-regexp-in-string "\\`\\*" "" sel) + bookmark-alist))) + (helm-aif (bookmark-get-filename bmk) + (if (and helm--url-regexp + (string-match helm--url-regexp it)) + it (expand-file-name it)) + (expand-file-name default-directory)))) + +(defun helm-bookmark-build-source (name buildfn &optional class &rest args) + (apply #'helm-make-source name + (or class 'helm-source-filtered-bookmarks) + :init (lambda () + (bookmark-maybe-load-default-file) + (helm-init-candidates-in-buffer + 'global (funcall buildfn))) + args)) + +;;; W3m bookmarks. +;; +(defun helm-bookmark-w3m-setup-alist () + "Specialized filter function for bookmarks w3m." + (helm-bookmark-filter-setup-alist 'helm-bookmark-w3m-bookmark-p)) + +(defun helm-source-bookmark-w3m-builder () + (helm-bookmark-build-source "Bookmark W3m" #'helm-bookmark-w3m-setup-alist)) + +(defvar helm-source-bookmark-w3m (helm-source-bookmark-w3m-builder)) + +;;; Images +;; +(defun helm-bookmark-images-setup-alist () + "Specialized filter function for images bookmarks." + (helm-bookmark-filter-setup-alist 'helm-bookmark-image-bookmark-p)) + +(defun helm-source-bookmark-images-builder () + (helm-bookmark-build-source "Bookmark Images" #'helm-bookmark-images-setup-alist)) + +(defvar helm-source-bookmark-images (helm-source-bookmark-images-builder)) + +;;; Woman Man +;; +(defun helm-bookmark-man-setup-alist () + "Specialized filter function for bookmarks w3m." + (helm-bookmark-filter-setup-alist 'helm-bookmark-woman-man-bookmark-p)) + +(defun helm-source-bookmark-man-builder () + (helm-bookmark-build-source "Bookmark Woman&Man" #'helm-bookmark-man-setup-alist)) + +(defvar helm-source-bookmark-man (helm-source-bookmark-man-builder)) + +;;; Org files +;; +(defun helm-bookmark-org-setup-alist () + "Specialized filter function for Org file bookmarks." + (helm-bookmark-filter-setup-alist 'helm-bookmark-org-file-p)) + +(defun helm-source-bookmark-org-builder () + (helm-bookmark-build-source "Bookmark Org files" #'helm-bookmark-org-setup-alist)) + +(defvar helm-source-bookmark-org (helm-source-bookmark-org-builder)) + +;;; Gnus +;; +(defun helm-bookmark-gnus-setup-alist () + "Specialized filter function for bookmarks gnus." + (helm-bookmark-filter-setup-alist 'helm-bookmark-gnus-bookmark-p)) + +(defun helm-source-bookmark-gnus-builder () + (helm-bookmark-build-source "Bookmark Gnus" #'helm-bookmark-gnus-setup-alist)) + +(defvar helm-source-bookmark-gnus (helm-source-bookmark-gnus-builder)) + +;;; Mu4e +;; +(defun helm-bookmark-mu4e-setup-alist () + (helm-bookmark-filter-setup-alist 'helm-bookmark-mu4e-bookmark-p)) + +(defun helm-source-bookmark-mu4e-builder () + (helm-bookmark-build-source "Bookmark Mu4e" #'helm-bookmark-mu4e-setup-alist)) + +(defvar helm-source-bookmark-mu4e (helm-source-bookmark-mu4e-builder)) + +;;; Info +;; +(defun helm-bookmark-info-setup-alist () + "Specialized filter function for bookmarks info." + (helm-bookmark-filter-setup-alist 'helm-bookmark-info-bookmark-p)) + +(defun helm-source-bookmark-info-builder () + (helm-bookmark-build-source "Bookmark Info" #'helm-bookmark-info-setup-alist)) + +(defvar helm-source-bookmark-info (helm-source-bookmark-info-builder)) + +;;; Files and directories +;; +(defun helm-bookmark-local-files-setup-alist () + "Specialized filter function for bookmarks locals files." + (helm-bookmark-filter-setup-alist 'helm-bookmark-file-p)) + +(defun helm-source-bookmark-files&dirs-builder () + (helm-bookmark-build-source + "Bookmark Files&Directories" #'helm-bookmark-local-files-setup-alist)) + +(defvar helm-source-bookmark-files&dirs + (helm-source-bookmark-files&dirs-builder)) + +;;; Helm find files sessions. +;; +(defun helm-bookmark-helm-find-files-setup-alist () + "Specialized filter function for `helm-find-files' bookmarks." + (helm-bookmark-filter-setup-alist 'helm-bookmark-helm-find-files-p)) + +(defun helm-bookmark-browse-project (candidate) + "Run `helm-browse-project' from action." + (with-helm-default-directory + (bookmark-get-filename candidate) + (helm-browse-project nil))) + +(defun helm-bookmark-run-browse-project () + "Run `helm-bookmark-browse-project' from keyboard." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-bookmark-browse-project))) +(put 'helm-bookmark-run-browse-project 'helm-only t) + +(defvar helm-bookmark-find-files-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-bookmark-map) + (define-key map (kbd "C-x C-d") #'helm-bookmark-run-browse-project) + map)) + +(defclass helm-bookmark-override-inheritor (helm-source) ()) + +(cl-defmethod helm--setup-source ((source helm-bookmark-override-inheritor)) + ;; Ensure `helm-source-in-buffer' method is called. + (cl-call-next-method) + (setf (slot-value source 'action) + (helm-append-at-nth + (cl-loop for (name . action) in helm-type-bookmark-actions + unless (memq action '(helm-bookmark-jump-other-frame + helm-bookmark-jump-other-window)) + collect (cons name action)) + '(("Browse project" . helm-bookmark-browse-project)) 1)) + (setf (slot-value source 'keymap) helm-bookmark-find-files-map)) + +(defclass helm-bookmark-find-files-class (helm-source-filtered-bookmarks + helm-bookmark-override-inheritor) + ()) + +(defun helm-source-bookmark-helm-find-files-builder () + (helm-bookmark-build-source + "Bookmark helm-find-files sessions" + #'helm-bookmark-helm-find-files-setup-alist + 'helm-bookmark-find-files-class + :persistent-action (lambda (_candidate) (ignore)) + :persistent-help "Do nothing")) + +(defvar helm-source-bookmark-helm-find-files + (helm-source-bookmark-helm-find-files-builder)) + +;;; Uncategorized bookmarks +;; +(defun helm-bookmark-uncategorized-setup-alist () + "Specialized filter function for uncategorized bookmarks." + (helm-bookmark-filter-setup-alist 'helm-bookmark-uncategorized-bookmark-p)) + +(defun helm-source-bookmark-uncategorized-builder () + (helm-bookmark-build-source + "Bookmark uncategorized" #'helm-bookmark-uncategorized-setup-alist)) + +(defvar helm-source-bookmark-uncategorized + (helm-source-bookmark-uncategorized-builder)) + + +;;; Transformer +;; +(defun helm-highlight-bookmark (bookmarks _source) + "Used as `filtered-candidate-transformer' to colorize bookmarks." + (let ((non-essential t)) + (cl-loop for i in bookmarks + for isfile = (bookmark-get-filename i) + for hff = (helm-bookmark-helm-find-files-p i) + for handlerp = (and (fboundp 'bookmark-get-handler) + (bookmark-get-handler i)) + for isw3m = (and (fboundp 'helm-bookmark-w3m-bookmark-p) + (helm-bookmark-w3m-bookmark-p i)) + for isgnus = (and (fboundp 'helm-bookmark-gnus-bookmark-p) + (helm-bookmark-gnus-bookmark-p i)) + for ismu4e = (and (fboundp 'helm-bookmark-mu4e-bookmark-p) + (helm-bookmark-mu4e-bookmark-p i)) + for isman = (and (fboundp 'helm-bookmark-man-bookmark-p) ; Man + (helm-bookmark-man-bookmark-p i)) + for iswoman = (and (fboundp 'helm-bookmark-woman-bookmark-p) ; Woman + (helm-bookmark-woman-bookmark-p i)) + for isannotation = (bookmark-get-annotation i) + for isabook = (string= (bookmark-prop-get i 'type) + "addressbook") + for isinfo = (eq handlerp 'Info-bookmark-jump) + for loc = (bookmark-location i) + for len = (string-width i) + for trunc = (if (and helm-bookmark-show-location + (> len bookmark-bmenu-file-column)) + (helm-substring + i bookmark-bmenu-file-column) + i) + for icon = (when helm-bookmark-use-icon + (cond ((and isfile hff) + (all-the-icons-octicon "file-directory")) + ((and isfile isinfo) (all-the-icons-octicon "info")) + (isfile (all-the-icons-icon-for-file isfile)) + ((or iswoman isman) + (all-the-icons-fileicon "man-page")) + ((or isgnus ismu4e) + (all-the-icons-octicon "mail-read")))) + ;; Add a * if bookmark have annotation + if (and isannotation (not (string-equal isannotation ""))) + do (setq trunc (concat "*" (if helm-bookmark-show-location trunc i))) + for sep = (and helm-bookmark-show-location + (make-string (- (+ bookmark-bmenu-file-column 2) + (string-width trunc)) + ? )) + for bmk = (cond ( ;; info buffers + isinfo + (propertize trunc 'face 'helm-bookmark-info + 'help-echo isfile)) + ( ;; w3m buffers + isw3m + (propertize trunc 'face 'helm-bookmark-w3m + 'help-echo isfile)) + ( ;; gnus buffers + isgnus + (propertize trunc 'face 'helm-bookmark-gnus + 'help-echo isfile)) + ( ;; Man Woman + (or iswoman isman) + (propertize trunc 'face 'helm-bookmark-man + 'help-echo isfile)) + ( ;; Addressbook + isabook + (propertize trunc 'face 'helm-bookmark-addressbook)) + (;; Directories (helm-find-files) + hff + (if (and (file-remote-p isfile) + (not (file-remote-p isfile nil t))) + (propertize trunc 'face 'helm-bookmark-file-not-found + 'help-echo isfile) + (propertize trunc 'face 'helm-bookmark-directory + 'help-echo isfile))) + ( ;; Directories (dired) + (and isfile + ;; This is needed because `non-essential' + ;; is not working on Emacs-24.2 and the behavior + ;; of tramp seems to have changed since previous + ;; versions (Need to reenter password even if a + ;; first connection have been established, + ;; probably when host is named differently + ;; i.e machine/localhost) + (and (not (file-remote-p isfile)) + (file-directory-p isfile))) + (propertize trunc 'face 'helm-bookmark-directory + 'help-echo isfile)) + ( ;; Non existing files. + (and isfile + ;; Be safe and call `file-exists-p' + ;; only if file is not remote or + ;; remote but connected. + (or (and (file-remote-p isfile) + (not (file-remote-p isfile nil t))) + (not (file-exists-p isfile)))) + (propertize trunc 'face 'helm-bookmark-file-not-found + 'help-echo isfile)) + ( ;; regular files + t + (propertize trunc 'face 'helm-bookmark-file + 'help-echo isfile))) + collect (if helm-bookmark-show-location + (cons (concat (and icon (propertize " " 'display (concat icon " "))) + bmk + (propertize + " " 'display + (concat sep (if (listp loc) (car loc) loc)))) + i) + (cons (concat (and icon (propertize " " 'display (concat icon " "))) + bmk) + i))))) + + +;;; Edit/rename/save bookmarks. +;; +;; +(defun helm-bookmark-edit-bookmark (bookmark-name) + "Edit bookmark's name and file name, and maybe save them. +BOOKMARK-NAME is the current (old) name of the bookmark to be +renamed." + (let ((bmk (helm-bookmark-get-bookmark-from-name bookmark-name)) + (handler (bookmark-prop-get bookmark-name 'handler))) + (if (eq handler 'addressbook-bookmark-jump) + (addressbook-bookmark-edit + (assoc bmk bookmark-alist)) + (helm-bookmark-edit-bookmark-1 bookmark-name handler)))) + +(defun helm-bookmark-edit-bookmark-1 (bookmark-name handler) + (let* ((helm--reading-passwd-or-string t) + (bookmark-fname (bookmark-get-filename bookmark-name)) + (bookmark-loc (bookmark-prop-get bookmark-name 'location)) + (message-id (bookmark-prop-get bookmark-name 'message-id)) + (new-name (read-from-minibuffer "Name: " bookmark-name)) + (new-loc (and (or bookmark-fname bookmark-loc) + (read-from-minibuffer "FileName or Location: " + (or bookmark-fname + (if (consp bookmark-loc) + (car bookmark-loc) + bookmark-loc))))) + (new-message-id (and (memq handler '(mu4e--jump-to-bookmark + mu4e-bookmark-jump)) + (read-string "Message-id: " message-id)))) + (when (and (not (equal new-name "")) + (or (not (equal new-loc "")) + (not (equal new-message-id ""))) + (y-or-n-p "Save changes? ")) + (if bookmark-fname + (progn + (helm-bookmark-rename bookmark-name new-name 'batch) + (bookmark-set-filename new-name new-loc)) + (bookmark-prop-set + (bookmark-get-bookmark bookmark-name) + (cond (new-loc 'location) + (new-message-id 'message-id)) + (or new-loc new-message-id)) + (helm-bookmark-rename bookmark-name new-name 'batch)) + (helm-bookmark-maybe-save-bookmark) + (list new-name new-loc)))) + +(defun helm-bookmark-maybe-save-bookmark () + "Increment save counter and maybe save `bookmark-alist'." + (setq bookmark-alist-modification-count (1+ bookmark-alist-modification-count)) + (when (bookmark-time-to-save-p) (bookmark-save))) + +(defun helm-bookmark-rename (old &optional new batch) + "Change bookmark's name from OLD to NEW. +Interactively: + If called from the keyboard, then prompt for OLD. + If called from the menubar, select OLD from a menu. +If NEW is nil, then prompt for its string value. + +If BATCH is non-nil, then do not rebuild the menu list. + +While the user enters the new name, repeated `C-w' inserts +consecutive words from the buffer into the new bookmark name." + (interactive (list (bookmark-completing-read "Old bookmark name"))) + (bookmark-maybe-historicize-string old) + (bookmark-maybe-load-default-file) + (save-excursion (skip-chars-forward " ") (setq bookmark-yank-point (point))) + (setq bookmark-current-buffer (current-buffer)) + (let ((newname (or new (read-from-minibuffer + "New name: " nil + (let ((now-map (copy-keymap minibuffer-local-map))) + (define-key now-map "\C-w" #'bookmark-yank-word) + now-map) + nil 'bookmark-history)))) + (bookmark-set-name old newname) + (setq bookmark-current-bookmark newname) + (unless batch (bookmark-bmenu-surreptitiously-rebuild-list)) + (helm-bookmark-maybe-save-bookmark) newname)) + +(defun helm-bookmark-run-edit () + "Run `helm-bookmark-edit-bookmark' from keyboard." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-bookmark-edit-bookmark))) +(put 'helm-bookmark-run-edit 'helm-only t) + + +(defun helm-bookmark-run-jump-other-frame () + "Jump to bookmark other frame from keyboard." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-bookmark-jump-other-frame))) +(put 'helm-bookmark-run-jump-other-frame 'helm-only t) + +(defun helm-bookmark-run-jump-other-window () + "Jump to bookmark from keyboard." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-bookmark-jump-other-window))) +(put 'helm-bookmark-run-jump-other-window 'helm-only t) + +(defun helm-bookmark-run-delete () + "Delete bookmark from keyboard." + (interactive) + (with-helm-alive-p + (when (y-or-n-p "Delete bookmark(s)?") + (helm-exit-and-execute-action 'helm-delete-marked-bookmarks)))) +(put 'helm-bookmark-run-delete 'helm-only t) + +(defun helm-bookmark-get-bookmark-from-name (bmk) + "Return bookmark name even if it is a bookmark with annotation. +E.g. prepended with *." + (let ((bookmark (replace-regexp-in-string "\\`\\*" "" bmk))) + (if (assoc bookmark bookmark-alist) bookmark bmk))) + +(defun helm-delete-marked-bookmarks (_ignore) + "Delete this bookmark or all marked bookmarks." + (cl-dolist (i (helm-marked-candidates)) + (bookmark-delete (helm-bookmark-get-bookmark-from-name i) + 'batch))) + + +;;;###autoload +(defun helm-bookmarks () + "Preconfigured `helm' for bookmarks." + (interactive) + (helm :sources '(helm-source-bookmarks + helm-source-bookmark-set) + :buffer "*helm bookmarks*" + :default (buffer-name helm-current-buffer))) + +;;;###autoload +(defun helm-filtered-bookmarks () + "Preconfigured `helm' for bookmarks (filtered by category). +Optional source `helm-source-bookmark-addressbook' is loaded only +if external addressbook-bookmark package is installed." + (interactive) + (when helm-bookmark-use-icon + (require 'all-the-icons)) + (helm :sources helm-bookmark-default-filtered-sources + :prompt "Search Bookmark: " + :buffer "*helm filtered bookmarks*" + :default (list (thing-at-point 'symbol) + (buffer-name helm-current-buffer)))) + +(provide 'helm-bookmark) + +;;; helm-bookmark.el ends here diff --git a/code/elpa/helm-20220822.659/helm-buffers.el b/code/elpa/helm-20220822.659/helm-buffers.el new file mode 100644 index 0000000..b97b91a --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-buffers.el @@ -0,0 +1,1238 @@ +;;; helm-buffers.el --- helm support for buffers. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-types) +(require 'helm-utils) +(require 'helm-grep) +(require 'helm-regexp) +(require 'helm-help) +(require 'helm-occur) + +(declare-function helm-comp-read "helm-mode") +(declare-function helm-browse-project "helm-files") +(declare-function helm-ff-switch-to-shell "helm-files") + +(defvar dired-buffers) +(defvar org-directory) +(defvar helm-ff-default-directory) + + +(defgroup helm-buffers nil + "Buffers related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-boring-buffer-regexp-list + '("\\` " "\\`\\*helm" "\\`\\*Echo Area" "\\`\\*Minibuf") + "The regexp list that match boring buffers. +Buffer candidates matching these regular expression will be +filtered from the list of candidates if the +`helm-skip-boring-buffers' candidate transformer is used." + :type '(repeat (choice regexp))) + +(defcustom helm-white-buffer-regexp-list nil + "The regexp list of not boring buffers. +These buffers will be displayed even if they match one of +`helm-boring-buffer-regexp-list'." + :type '(repeat (choice regexp))) + +(defcustom helm-buffers-favorite-modes '(lisp-interaction-mode + emacs-lisp-mode + text-mode + org-mode) + "List of preferred mode to open new buffers with." + :type '(repeat (choice function))) + +(defcustom helm-buffer-max-length 20 + "Max length of buffer names before truncate. +When disabled (nil) use the longest `buffer-name' length found." + :type '(choice (const :tag "Disabled" nil) + (integer :tag "Length before truncate"))) + +(defcustom helm-buffer-details-flag t + "Always show details in buffer list when non-nil." + :type 'boolean) + +(defcustom helm-buffers-fuzzy-matching nil + "Fuzzy matching buffer names when non-nil. +Only buffer names are fuzzy matched when this is enabled, +`major-mode' matching is not affected by this." + :type 'boolean) + +(defcustom helm-buffer-skip-remote-checking nil + "Ignore checking for `file-exists-p' on remote files." + :type 'boolean) + +(defcustom helm-buffers-truncate-lines t + "Truncate lines in `helm-buffers-list' when non-nil." + :type 'boolean) + +(defcustom helm-buffers-left-margin-width helm-left-margin-width + "`left-margin-width' value for `helm-mini' and `helm-buffers-list'." + :type 'integer) + +(defcustom helm-mini-default-sources '(helm-source-buffers-list + helm-source-recentf + helm-source-buffer-not-found) + "Default sources list used in `helm-mini'. + +When adding a source here it is up to you to ensure the library +of this source is accessible and properly loaded." + :type '(repeat (choice symbol))) + +(defcustom helm-buffers-end-truncated-string "..." + "The string to display at end of truncated buffer names." + :type 'string) + +(defcustom helm-buffers-column-separator " " + "Separator for columns in buffer listing." + :type 'string) + +(defcustom helm-buffer--pretty-names '((dired-mode . "Dired") + (lisp-interaction-mode . "Lisp Inter")) + "An alist specifying pretty names for modes. +Most of the time buffer's `mode-name' is a string so no need to +add it here as there is no need to compute it, but sometimes it +may be a mode-line specification which may be costly to compute, +in this case add here the pretty name as a string to avoid this +costly computation. Also if some pretty names are too long you +can add your own abbreviation here." + :type '(alist :key-type symbol :value-type string)) + +(defcustom helm-buffers-maybe-switch-to-tab nil + "Switch to buffer in its tab when non nil. +This has no effect when `tab-bar-mode' is not available." + :type 'boolean) + +(defcustom helm-buffer-list-reorder-fn #'helm-buffers-reorder-buffer-list + "A function in charge of ordering the initial buffer list. +It takes two arguments VISIBLES buffers and OTHERS buffers. +Arg VISIBLES handles the buffers visibles in this frame. +Arg OTHERS handles all the other buffers. +You can write a function that reorder VISIBLES and OTHERS as you +want. +Default function returns OTHERS buffers on top and VISIBLES +buffer at the end. See `helm-buffers-reorder-buffer-list'." + :type 'function) + +(defcustom helm-buffers-sort-fn helm-fuzzy-sort-fn + "The sort function to use in `helm-buffers-list'. + +Default to `helm-fuzzy-sort-fn' you can use +`helm-fuzzy-matching-sort-fn-preserve-ties-order' as alternative if +you want to keep the recentest order when narrowing candidates." + :type 'function) + + +;;; Faces +;; +;; +(defgroup helm-buffers-faces nil + "Customize the appearance of helm-buffers." + :prefix "helm-" + :group 'helm-buffers + :group 'helm-faces) + +(defface helm-buffer-saved-out + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "red" :background "black")) + "Face used for buffer files modified outside of emacs." + :group 'helm-buffers-faces) + +(defface helm-buffer-not-saved + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Indianred2")) + "Face used for buffer files not already saved on disk." + :group 'helm-buffers-faces) + +(defface helm-buffer-modified + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-comment-face)) + "Face used for modified buffers." + :group 'helm-buffers-faces) + +(defface helm-no-file-buffer-modified + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "orange" :background "black")) + "Face used for modified buffers." + :group 'helm-buffers-faces) + +(defface helm-buffer-size + `((((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "RosyBrown") + (((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "SlateGray")) + "Face used for buffer size." + :group 'helm-buffers-faces) + +(defface helm-buffer-process + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Sienna3")) + "Face used for process status in buffer." + :group 'helm-buffers-faces) + +(defface helm-buffer-directory + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "DarkRed" :background "LightGray")) + "Face used for directories in `helm-buffers-list'." + :group 'helm-buffers-faces) + +(defface helm-buffer-file + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-builtin-face)) + "Face for buffer file names in `helm-buffers-list'." + :group 'helm-buffers-faces) + +(defface helm-buffer-archive + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Gold")) + "Face for archive file names in `helm-buffers-list'." + :group 'helm-buffers-faces) + +(defface helm-non-file-buffer + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit italic)) + "Face used for non-file buffers in `helm-buffers-list'." + :group 'helm-buffers-faces) + +(defvar helm-buffers-tick-counter nil + "Allows recording local changes to a non-file buffer. +Typical usage of this var is for modes that want to see if their +buffers have changed since last visit. +Such programs may want to record tick counter after visiting +their buffers like this: + + (setq helm-buffers-tick-counter (buffer-modified-tick)) + +See bug#1917. + +Note that this variable is buffer-local.") +(make-variable-buffer-local 'helm-buffers-tick-counter) + + +;;; Buffers keymap +;; +(defvar helm-buffer-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + ;; No need to have separate command for grep and zgrep + ;; as we don't use recursivity for buffers. + ;; So use zgrep for both as it is capable to handle non--compressed files. + (define-key map (kbd "M-g s") #'helm-buffer-run-zgrep) + (define-key map (kbd "C-s") #'helm-buffers-run-occur) + (define-key map (kbd "C-x C-d") #'helm-buffers-run-browse-project) + (define-key map (kbd "C-c o") #'helm-buffer-switch-other-window) + (define-key map (kbd "C-c C-o") #'helm-buffer-switch-other-frame) + (define-key map (kbd "M-g M-g") #'helm-buffer-run-goto-line) + (define-key map (kbd "C-c =") #'helm-buffer-run-ediff) + (define-key map (kbd "M-=") #'helm-buffer-run-ediff-merge) + (define-key map (kbd "C-=") #'helm-buffer-diff-persistent) + (define-key map (kbd "M-G") #'helm-buffer-revert-persistent) + (define-key map (kbd "C-c d") #'helm-buffer-run-kill-persistent) + (define-key map (kbd "M-D") #'helm-buffer-run-kill-buffers) + (define-key map (kbd "C-x C-s") #'helm-buffer-save-persistent) + (define-key map (kbd "C-x s") #'helm-buffer-run-save-some-buffers) + (define-key map (kbd "C-M-%") #'helm-buffer-run-query-replace-regexp) + (define-key map (kbd "M-%") #'helm-buffer-run-query-replace) + (define-key map (kbd "M-R") #'helm-buffer-run-rename-buffer) + (define-key map (kbd "M-m") #'helm-toggle-all-marks) + (define-key map (kbd "M-a") #'helm-mark-all) + (define-key map (kbd "M-e") #'helm-buffer-run-switch-to-shell) + (define-key map (kbd "C-]") #'helm-toggle-buffers-details) + (define-key map (kbd "C-c a") #'helm-buffers-toggle-show-hidden-buffers) + (define-key map (kbd "C-M-SPC") #'helm-buffers-mark-similar-buffers) + (when (fboundp 'tab-bar-mode) + (define-key map (kbd "C-c C-t") #'helm-buffers-switch-to-buffer-new-tab)) + map) + "Keymap for buffer sources in helm.") + + +(defvar helm-buffer-max-len-mode nil) +(defvar helm-buffers-in-project-p nil) +(defvar helm-source-buffers-list nil) + +(defun helm-buffers-list--init () + (require 'dired) + ;; Bug#51 Create the list before `helm-buffer' creation. + ;; We were using a global cache in the past and 'candidates was + ;; bound to this cache, this was a problem when using more than one + ;; source with a different 'buffer-list fn as the same cache was + ;; reused in each source (Bug#1907), now 'candidates attr is set + ;; directly so that each list of candidates is local to source. + (helm-set-attr 'candidates (funcall (helm-get-attr 'buffer-list))) + (let ((result (cl-loop with allbufs = (memq 'helm-shadow-boring-buffers + (helm-get-attr + 'filtered-candidate-transformer + helm-source-buffers-list)) + for b in (if allbufs + (helm-get-attr 'candidates) + (helm-skip-boring-buffers + (helm-get-attr 'candidates) + helm-source-buffers-list)) + maximize (length b) into len-buf + maximize (length (helm-buffer--format-mode-name b)) + into len-mode + finally return (cons len-buf len-mode)))) + (unless (default-value 'helm-buffer-max-length) + (helm-set-local-variable 'helm-buffer-max-length (car result))) + (unless (default-value 'helm-buffer-max-len-mode) + (helm-set-local-variable 'helm-buffer-max-len-mode (cdr result))))) + +(defclass helm-source-buffers (helm-source-sync helm-type-buffer) + ((buffer-list + :initarg :buffer-list + :initform #'helm-buffer-list + :custom function + :documentation + " A function with no arguments to create buffer list.") + (init :initform 'helm-buffers-list--init) + (multimatch :initform nil) + (match :initform 'helm-buffers-match-function) + (persistent-action :initform 'helm-buffers-list-persistent-action) + (keymap :initform 'helm-buffer-map) + (find-file-target :initform #'helm-buffers-quit-and-find-file-fn) + (migemo :initform 'nomultimatch) + (volatile :initform t) + (nohighlight :initform t) + (resume :initform (lambda () (setq helm-buffers-in-project-p nil))) + (help-message :initform 'helm-buffer-help-message))) + +(cl-defun helm-buffers-create-new-buffer-1 (candidate &optional (display-func 'switch-to-buffer)) + (let ((mjm (or (and helm-current-prefix-arg + (intern-soft (helm-comp-read + "Major-mode: " + helm-buffers-favorite-modes))) + (cl-loop for (r . m) in auto-mode-alist + when (string-match r candidate) + return m))) + (buffer (get-buffer-create candidate))) + (if mjm + (with-current-buffer buffer (funcall mjm)) + (set-buffer-major-mode buffer)) + (funcall display-func buffer))) + +(defun helm-buffers-create-new-buffer (candidate) + (helm-buffers-create-new-buffer-1 candidate)) + +(defun helm-buffers-create-new-buffer-ow (candidate) + (helm-buffers-create-new-buffer-1 candidate 'switch-to-buffer-other-window)) + +(defun helm-buffers-not-found-run-switch-ow () + "Run create new buffer other window action from keymap." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffers-create-new-buffer-ow))) +(put 'helm-buffers-not-found-run-switch-ow 'helm-only t) + +(defun helm-buffers-create-new-buffer-of (candidate) + (helm-buffers-create-new-buffer-1 candidate 'switch-to-buffer-other-frame)) + +(defun helm-buffers-not-found-run-switch-of () + "Run create new buffer other frame action from keymap." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffers-create-new-buffer-of))) +(put 'helm-buffers-not-found-run-switch-of 'helm-only t) + +(defvar helm-buffer-not-found-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c o") #'helm-buffers-not-found-run-switch-ow) + (define-key map (kbd "C-c C-o") #'helm-buffers-not-found-run-switch-of) + map) + "Keymap for `helm-source-buffer-not-found' source.") + +(defvar helm-source-buffer-not-found + (helm-build-dummy-source + "Create buffer" + :action (helm-make-actions + "Create buffer (C-u choose mode)" + #'helm-buffers-create-new-buffer + "Create buffer other window (C-u choose mode)" + #'helm-buffers-create-new-buffer-ow + "Create buffer other frame (C-u choose mode)" + #'helm-buffers-create-new-buffer-of) + :keymap helm-buffer-not-found-map)) + + +(defun helm-buffers-get-visible-buffers () + "Returns buffers visibles on current frame." + (let (result) + (walk-windows + (lambda (x) + (push (buffer-name (window-buffer x)) result)) + nil 'visible) + result)) + +(defun helm-buffer-list-1 (&optional visibles) + (cl-loop for b in (buffer-list) + for bn = (buffer-name b) + unless (member bn visibles) + collect bn)) + +(defun helm-buffers-reorder-buffer-list (visibles others) + "Default function to reorder buffer-list. +Arg VISIBLES handles the buffers visibles in this frame. +Arg OTHERS handles all the other buffers. +This function returns OTHERS buffers on top and VISIBLES buffer +at the end." + (nconc others visibles)) + +(defun helm-buffer-list () + "Return the current list of buffers. +The list is reordered with `helm-buffer-list-reorder-fn'." + (let* ((visibles (helm-buffers-get-visible-buffers)) + (others (helm-buffer-list-1 visibles))) + (funcall helm-buffer-list-reorder-fn visibles others))) + +(defun helm-buffer-size (buffer) + "Return size of BUFFER." + (with-current-buffer buffer + (save-restriction + (widen) + (helm-file-human-size + (- (position-bytes (point-max)) + (position-bytes (point-min))))))) + +(defun helm-buffer--show-details (buf-name prefix help-echo + size mode dir face1 face2 + proc details type) + (append + (list + (concat prefix + (let* ((buf-fname (buffer-file-name (get-buffer buf-name))) + (ext (if buf-fname (helm-file-name-extension buf-fname) "")) + (buf-name (propertize buf-name 'face face1 + 'help-echo help-echo + 'type type))) + (when (condition-case _err + (string-match (format "\\.\\(%s\\)" ext) buf-name) + (invalid-regexp nil)) + (add-face-text-property + (match-beginning 1) (match-end 1) + 'helm-ff-file-extension nil buf-name)) + buf-name))) + (and details + (list size mode + (propertize + (if proc + (format "(%s %s in `%s')" + (process-name proc) + (process-status proc) dir) + (format "(in `%s')" dir)) + 'face face2))))) + +(defun helm-buffer--format-mode-name (buf) + "Prevent using `format-mode-line' as much as possible." + (with-current-buffer buf + (helm-acond ((assq major-mode helm-buffer--pretty-names) + (cdr it)) + ((stringp mode-name) mode-name) + (t (format-mode-line mode-name nil nil (get-buffer buf)))))) + +(defun helm-buffer--details (buffer &optional details) + (require 'dired) + (let* ((mode (helm-buffer--format-mode-name buffer)) + (buf (get-buffer buffer)) + (size (propertize (helm-buffer-size buf) + 'face 'helm-buffer-size)) + (proc (get-buffer-process buf)) + (dir (with-current-buffer buffer + (helm-aif default-directory (abbreviate-file-name it)))) + (file-name (helm-aif (buffer-file-name buf) (abbreviate-file-name it))) + (name (buffer-name buf)) + (name-prefix (when (and dir (file-remote-p dir)) + (propertize "@ " 'face 'helm-ff-prefix))) + (archive-p (and (fboundp 'tramp-archive-file-name-p) + (tramp-archive-file-name-p dir)))) + (when name-prefix + ;; Remote tramp buffer names may be hexified, make them more readable. + (setq dir (helm-url-unhex-string dir) + name (helm-url-unhex-string name))) + ;; Handle tramp archive buffers specially. + (if archive-p + (helm-buffer--show-details + name name-prefix file-name size mode dir + 'helm-buffer-archive 'helm-buffer-process nil details 'filebuf) + ;; No fancy things on remote buffers. + (if (and name-prefix helm-buffer-skip-remote-checking) + (helm-buffer--show-details + name name-prefix file-name size mode dir + 'helm-buffer-file 'helm-buffer-process nil details 'filebuf) + (cond + (;; A dired buffer. + (rassoc buf dired-buffers) + (helm-buffer--show-details + name name-prefix dir size mode dir + 'helm-buffer-directory 'helm-buffer-process nil details 'dired)) + ;; A buffer file modified somewhere outside of emacs.=>red + ((and file-name + (file-exists-p file-name) + (not (verify-visited-file-modtime buf))) + (helm-buffer--show-details + name name-prefix file-name size mode dir + 'helm-buffer-saved-out 'helm-buffer-process nil details 'modout)) + ;; A new buffer file not already saved on disk (or a deleted file) .=>indianred2 + ((and file-name (not (file-exists-p file-name))) + (helm-buffer--show-details + name name-prefix file-name size mode dir + 'helm-buffer-not-saved 'helm-buffer-process nil details 'notsaved)) + ;; A buffer file modified and not saved on disk.=>orange + ((and file-name (buffer-modified-p buf)) + (helm-buffer--show-details + name name-prefix file-name size mode dir + 'helm-buffer-modified 'helm-buffer-process nil details 'mod)) + ;; A buffer file not modified and saved on disk.=>green + (file-name + (helm-buffer--show-details + name name-prefix file-name size mode dir + 'helm-buffer-file 'helm-buffer-process nil details 'filebuf)) + ;; A non-file, modified buffer See bug#1917 + ((with-current-buffer name + (and helm-buffers-tick-counter + (/= helm-buffers-tick-counter (buffer-modified-tick)))) + (helm-buffer--show-details + name (and proc name-prefix) dir size mode dir + 'helm-no-file-buffer-modified 'helm-buffer-process proc details 'nofile-mod)) + ;; Any non--file buffer.=>italic + (t + (helm-buffer--show-details + name (and proc name-prefix) dir size mode dir + 'helm-non-file-buffer 'helm-buffer-process proc details 'nofile))))))) + +(defun helm-highlight-buffers (buffers _source) + "Transformer function to highlight BUFFERS list. +Should be called after others transformers i.e. (boring +buffers)." + (cl-assert helm-fuzzy-matching-highlight-fn nil "Wrong type argument functionp: nil") + (cl-loop for i in buffers + for (name size mode meta) = (if helm-buffer-details-flag + (helm-buffer--details i 'details) + (helm-buffer--details i)) + for truncbuf = (if (> (string-width name) helm-buffer-max-length) + (helm-substring-by-width + name helm-buffer-max-length + helm-buffers-end-truncated-string) + (concat name + (make-string + (- (+ helm-buffer-max-length + (length + helm-buffers-end-truncated-string)) + (string-width name)) + ? ))) + for len = (length mode) + when (> len helm-buffer-max-len-mode) + do (setq helm-buffer-max-len-mode len) + for fmode = (concat (make-string + (- (max helm-buffer-max-len-mode len) len) ? ) + mode) + ;; The max length of a number should be 1023.9X where X is the + ;; units, this is 7 characters. + for formatted-size = (and size (format "%7s" size)) + collect (let ((helm-pattern (helm-buffers--pattern-sans-filters + (and helm-buffers-fuzzy-matching "")))) + (cons (if helm-buffer-details-flag + (concat + (funcall helm-fuzzy-matching-highlight-fn + truncbuf) + helm-buffers-column-separator + formatted-size + helm-buffers-column-separator + fmode + helm-buffers-column-separator + meta) + (funcall helm-fuzzy-matching-highlight-fn name)) + (get-buffer i))))) + +(defun helm-buffer--get-preselection (buffer) + (let ((bufname (buffer-name buffer))) + (when (and bufname + (file-remote-p (with-current-buffer bufname + default-directory))) + (setq bufname (concat "@ " (helm-url-unhex-string bufname)))) + (concat "^" + (if (and (null helm-buffer-details-flag) + (numberp helm-buffer-max-length) + (> (string-width bufname) + helm-buffer-max-length)) + (regexp-quote + (helm-substring-by-width + bufname helm-buffer-max-length + helm-buffers-end-truncated-string)) + (concat (regexp-quote bufname) + (if helm-buffer-details-flag + "$" "[[:blank:]]+")))))) + +(defun helm-toggle-buffers-details () + (interactive) + (with-helm-alive-p + (let* ((buf (helm-get-selection)) + (preselect (helm-buffer--get-preselection buf))) + (setq helm-buffer-details-flag (not helm-buffer-details-flag)) + (helm-force-update (lambda () + (helm-awhile (re-search-forward preselect nil t) + (helm-mark-current-line) + (when (equal buf (helm-get-selection)) + (cl-return t)))))))) +(put 'helm-toggle-buffers-details 'helm-only t) + +(defun helm-buffers--pattern-sans-filters (&optional separator) + (cl-loop for p in (helm-mm-split-pattern helm-pattern) + unless (member (substring p 0 1) '("*" "/" "@" "!")) + collect p into lst + finally return (mapconcat #'identity lst (or separator " ")))) + +(defun helm-buffers-sort-transformer (candidates source) + (cl-assert helm-buffers-sort-fn nil "Wrong type argument functionp: nil") + (if (string= helm-pattern "") + candidates + (let ((helm-pattern (helm-buffers--pattern-sans-filters))) + (funcall helm-buffers-sort-fn candidates source)))) + +(defun helm-buffers-mark-similar-buffers-1 (&optional type) + (with-helm-window + (let* ((src (helm-get-current-source)) + (type (or type + (get-text-property + 0 'type (helm-get-selection nil 'withprop src))))) + (save-excursion + (goto-char (helm-get-previous-header-pos)) + (helm-next-line) + (let* ((next-head (helm-get-next-header-pos)) + (end (and next-head + (save-excursion + (goto-char next-head) + (forward-line -1) + (point)))) + (maxpoint (or end (point-max)))) + (while (< (point) maxpoint) + (helm-mark-current-line) + (let ((cand (helm-get-selection nil 'withprop src))) + (when (and (not (helm-this-visible-mark)) + (eq (get-text-property 0 'type cand) type)) + (helm-make-visible-mark))) + (forward-line 1) (end-of-line)))) + (helm-mark-current-line) + (helm-display-mode-line src t) + (when helm-marked-candidates + (message "%s candidates marked" (length helm-marked-candidates)) + (set-window-margins (selected-window) 1))))) + +(defun helm-buffers-mark-similar-buffers () + "Mark All buffers that have same property `type' than current. +I.e. same color." + (interactive) + (with-helm-alive-p + (let ((marked (helm-marked-candidates))) + (if (and (>= (length marked) 1) + (with-helm-window helm-visible-mark-overlays)) + (helm-unmark-all) + (helm-buffers-mark-similar-buffers-1))))) +(put 'helm-buffers-mark-similar-buffers 'helm-only t) + + +;;; match functions +;; +(defun helm-buffer--match-mjm (pattern mjm) + (when (string-match "\\`\\*" pattern) + (cl-loop with patterns = (split-string (substring pattern 1) ",") + for pat in patterns + if (string-match "\\`!" pat) + collect (string-match (substring pat 1) mjm) into neg + else collect (string-match pat mjm) into pos + finally return + (let ((neg-test (cl-loop for i in neg thereis (numberp i))) + (pos-test (cl-loop for i in pos thereis (numberp i)))) + (or + (and neg (not pos) (not neg-test)) + (and pos pos-test) + (and neg neg-test (not neg-test))))))) + +(defvar helm-buffer--memo-hash (make-hash-table :test 'equal)) +(defun helm-buffer--memo-pattern (pattern) + (or (gethash pattern helm-buffer--memo-hash) + (puthash pattern (helm--mapconcat-pattern pattern) + helm-buffer--memo-hash))) + +(defun helm-buffer--match-pattern (pattern candidate &optional nofuzzy) + (let ((bfn (if (and helm-buffers-fuzzy-matching + (not nofuzzy) + (not helm-migemo-mode) + (not (string-match "\\`\\^" pattern))) + #'helm-buffer--memo-pattern + #'identity)) + (mfn (if helm-migemo-mode + #'helm-mm-migemo-string-match #'string-match))) + (if (string-match "\\`!" pattern) + (not (funcall mfn (funcall bfn (substring pattern 1)) + candidate)) + (funcall mfn (funcall bfn pattern) candidate)))) + +(defun helm-buffers--match-from-mjm (candidate) + (let* ((cand (replace-regexp-in-string "^\\s-\\{1\\}" "" candidate)) + (buf (get-buffer cand)) + (regexp (cl-loop with pattern = helm-pattern + for p in (helm-mm-split-pattern pattern) + when (string-match "\\`\\*" p) + return p))) + (if regexp + (when buf + (with-current-buffer buf + (let ((mjm (symbol-name major-mode))) + (helm-buffer--match-mjm regexp mjm)))) + t))) + +(defun helm-buffers--match-from-pat (candidate) + (let* ((regexp-list (cl-loop with pattern = helm-pattern + for p in (helm-mm-split-pattern pattern) + unless (string-match + "\\`\\(\\*\\|/\\|@\\)" p) + collect p)) + (nofuzzy (cdr regexp-list))) + (if regexp-list + (cl-loop for re in regexp-list + always (helm-buffer--match-pattern re candidate nofuzzy)) + t))) + +(defun helm-buffers--match-from-inside (candidate) + (let* ((cand (replace-regexp-in-string "^\\s-\\{1\\}" "" candidate)) + (buf (get-buffer cand)) + (pattern (cl-loop with pat = helm-pattern + for p in (helm-mm-split-pattern pat) + when (string-match "\\`@\\(.*\\)" p) + collect (match-string 1 p) into lst + finally return (mapconcat #'identity lst " "))) + (patterns (helm-mm-3-get-patterns pattern))) + (if (and buf patterns) + (with-current-buffer buf + (save-excursion + (goto-char (point-min)) + (cl-loop for (pred . regexp) in patterns + always + (save-excursion + (funcall + pred + (if helm-migemo-mode + (helm-mm-migemo-forward regexp nil t) + (re-search-forward regexp nil t))))))) + t))) + +(defun helm-buffers--match-from-directory (candidate) + (let* ((cand (replace-regexp-in-string "^\\s-\\{1\\}" "" candidate)) + (buf (get-buffer cand)) + (buf-fname (or (buffer-file-name buf) + (car-safe (rassoc buf dired-buffers)))) + (regexps (cl-loop with pattern = helm-pattern + for p in (helm-mm-split-pattern pattern) + when (string-match "\\`/" p) + collect p))) + (if regexps + (cl-loop for re in regexps + thereis + (and buf-fname + (string-match + (substring re 1) (helm-basedir buf-fname)))) + t))) + +(defun helm-buffers-match-function (candidate) + "Default function to match buffers." + (and (helm-buffers--match-from-pat candidate) + (helm-buffers--match-from-mjm candidate) + (helm-buffers--match-from-inside candidate) + (helm-buffers--match-from-directory candidate))) + + +(defun helm-buffer-query-replace-1 (&optional regexp-flag buffers) + "Query replace in marked buffers. +If REGEXP-FLAG is given use `query-replace-regexp'." + (let ((prompt (if regexp-flag "Query replace regexp" "Query replace")) + (bufs (or buffers (helm-marked-candidates))) + (helm--reading-passwd-or-string t)) + (cl-loop with args = (query-replace-read-args prompt regexp-flag t) + for buf in bufs + do + (save-window-excursion + (switch-to-buffer buf) + (save-excursion + (let ((case-fold-search t)) + (goto-char (point-min)) + (apply #'perform-replace + (list (nth 0 args) (nth 1 args) + t regexp-flag (nth 2 args) nil + multi-query-replace-map)))))))) + +(defun helm-buffer-query-replace-regexp (_candidate) + (helm-buffer-query-replace-1 'regexp)) + +(defun helm-buffer-query-replace (_candidate) + (helm-buffer-query-replace-1)) + +(defun helm-buffer-toggle-diff (candidate) + "Toggle diff buffer CANDIDATE with it's file." + (helm-aif (get-buffer-window "*Diff*" 'visible) + (progn (kill-buffer "*Diff*") + (set-window-buffer it helm-current-buffer)) + (let ((buf (get-buffer candidate))) + (if (buffer-file-name buf) + (diff-buffer-with-file buf) + (user-error "Buffer `%s' is not associated to a file" + (buffer-name buf)))))) + +(defun helm-buffer-diff-persistent () + "Toggle diff buffer without quitting helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'diff-action 'helm-buffer-toggle-diff) + (helm-execute-persistent-action 'diff-action))) +(put 'helm-buffer-diff-persistent 'helm-only t) + +(defun helm-revert-buffer (candidate) + (with-current-buffer candidate + (helm-aif (buffer-file-name) + (and (file-exists-p it) (revert-buffer t t))))) + +(defun helm-revert-marked-buffers (_ignore) + (mapc #'helm-revert-buffer (helm-marked-candidates))) + +(defun helm-buffer-revert-and-update (_candidate) + (with-helm-buffer + (let ((marked (helm-marked-candidates)) + (preselect (helm-buffers--quote-truncated-buffer + (helm-get-selection)))) + (cl-loop for buf in marked do (helm-revert-buffer buf)) + (when helm-marked-candidates (helm-unmark-all)) + (helm-force-update preselect)))) + +(defun helm-buffer-revert-persistent () + "Revert buffer without quitting helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'revert-action '(helm-buffer-revert-and-update . never-split)) + (helm-execute-persistent-action 'revert-action))) +(put 'helm-buffer-revert-persistent 'helm-only t) + +(defun helm-buffer-save-and-update (_candidate) + (with-helm-buffer + (let ((marked (helm-marked-candidates)) + (preselect (helm-get-selection nil t)) + (enable-recursive-minibuffers t)) + (cl-assert marked nil "No buffers need to be saved") + (cl-loop for buf in marked do + (with-current-buffer (get-buffer buf) + (when (buffer-file-name) (save-buffer)))) + (when helm-marked-candidates (helm-unmark-all)) + (helm-force-update (regexp-quote preselect))))) + +(defun helm-buffer-save-some-buffers (_candidate) + (helm-buffers-mark-similar-buffers-1 'mod) + (helm-buffer-save-and-update nil)) + +(defun helm-buffer-run-save-some-buffers () + "Save unsaved file buffers without quitting Helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'save-some-action '(helm-buffer-save-some-buffers . never-split)) + (helm-execute-persistent-action 'save-some-action))) +(put 'helm-buffer-run-save-some-buffers 'helm-only t) + +(defun helm-buffer-save-persistent () + "Save buffer without quitting Helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'save-action '(helm-buffer-save-and-update . never-split)) + (helm-execute-persistent-action 'save-action))) +(put 'helm-buffer-save-persistent 'helm-only t) + +(defun helm-buffers-rename-buffer (candidate) + (with-current-buffer candidate + (rename-buffer (helm-read-string "New name: " (buffer-name)) t))) + +(defun helm-buffer-run-rename-buffer () + "Run rename buffer action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffers-rename-buffer))) +(put 'helm-buffer-run-rename-buffer 'helm-only t) + +(defun helm-switch-to-buffer-at-linum (candidate) + (let ((linum (read-number + "Line number: " + (with-current-buffer candidate + (line-number-at-pos))))) + (switch-to-buffer candidate) + (goto-char (point-min)) + (forward-line (1- linum)))) + +(defun helm-buffer-run-goto-line () + "Switch to buffer at line number." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-switch-to-buffer-at-linum))) +(put 'helm-buffer-run-goto-line 'helm-only t) + +(defun helm-buffer-run-kill-persistent () + "Kill buffer without quitting Helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'kill-action '(helm-buffers-persistent-kill . never-split)) + (helm-execute-persistent-action 'kill-action))) +(put 'helm-buffer-run-kill-persistent 'helm-only t) + +(defun helm-kill-marked-buffers (_ignore) + (let* ((bufs (helm-marked-candidates)) + (killed-bufs (cl-count-if 'kill-buffer bufs))) + (when (buffer-live-p helm-buffer) + (with-helm-buffer + (setq helm-marked-candidates nil + helm-visible-mark-overlays nil))) + (message "Killed %s buffer(s)" killed-bufs))) + +(defun helm-buffer-run-kill-buffers () + "Run kill buffer action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-kill-marked-buffers))) +(put 'helm-buffer-run-kill-buffers 'helm-only t) + +(defun helm-buffer-switch-to-shell (candidate) + (require 'helm-files) + (let ((helm-ff-default-directory + (with-current-buffer candidate + default-directory))) + (helm-ff-switch-to-shell nil))) + +(defun helm-buffer-run-switch-to-shell () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffer-switch-to-shell))) +(put 'helm-buffer-run-switch-to-shell 'no-helm-mx t) + +(defun helm-buffer-run-grep () + "Run Grep action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-grep-buffers))) +(put 'helm-buffer-run-grep 'helm-only t) + +(defun helm-buffer-run-zgrep () + "Run Grep action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-zgrep-buffers))) +(put 'helm-buffer-run-zgrep 'helm-only t) + +(defun helm-buffer-run-query-replace-regexp () + "Run Query replace regexp action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffer-query-replace-regexp))) +(put 'helm-buffer-run-query-replace-regexp 'helm-only t) + +(defun helm-buffer-run-query-replace () + "Run Query replace action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffer-query-replace))) +(put 'helm-buffer-run-query-replace 'helm-only t) + +(defun helm-buffer-switch-other-window () + "Run switch to other window action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffer-switch-buffers-other-window))) +(put 'helm-buffer-switch-other-window 'helm-only t) + +(defun helm-buffer-switch-to-buffer-other-frame (_candidate) + "Display marked buffers in other frame." + (let ((bufs (helm-marked-candidates))) + (select-frame (make-frame)) + (helm-window-show-buffers bufs))) + +(defun helm-buffers-maybe-raise-buffer-frame (candidate) + "Raise buffer frame handling buffer CANDIDATE and switch to it." + (let ((oframe (window-frame (get-buffer-window candidate 0)))) + (unless (eql oframe (selected-frame)) + (raise-frame oframe)) + (with-selected-frame oframe + (switch-to-buffer candidate)))) + +(defun helm-buffer-switch-other-frame () + "Run switch to other frame action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffer-switch-to-buffer-other-frame))) +(put 'helm-buffer-switch-other-frame 'helm-only t) + +(defun helm-buffers-switch-to-buffer-other-tab (_candidate) + (when (fboundp 'switch-to-buffer-other-tab) + (let ((bufs (helm-marked-candidates))) + (cl-loop for buf in bufs + do (switch-to-buffer-other-tab buf))))) + +(defun helm-buffers-switch-to-buffer-new-tab () + "Run switch to buffer in other tab action from `helm-source-buffers-list'." + (interactive) + (cl-assert (fboundp 'tab-bar-mode) nil "Tab-bar-mode not available") + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-buffers-switch-to-buffer-other-tab))) +(put 'helm-buffers-switch-to-buffer-new-tab 'helm-only t) + +(defun helm-buffer-switch-buffers (_candidate) + "Switch to buffer candidates and replace current buffer. + +If more than one buffer marked switch to these buffers in +separate windows. If a prefix arg is given split windows +vertically." + (let ((buffers (helm-marked-candidates))) + (helm-window-show-buffers buffers))) + +(defun helm-buffer-switch-buffers-other-window (_candidate) + "Switch to marked buffers in other windows." + (let ((buffers (helm-marked-candidates))) + (helm-window-show-buffers buffers t))) + +(defun helm-buffer-run-ediff () + "Run ediff action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ediff-marked-buffers))) +(put 'helm-buffer-run-ediff 'helm-only t) + +(defun helm-buffer-run-ediff-merge () + "Run ediff action from `helm-source-buffers-list'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ediff-marked-buffers-merge))) +(put 'helm-buffer-run-ediff-merge 'helm-only t) + +(defun helm-buffers-persistent-kill-1 (buffer-or-name) + "Persistent action to kill buffer." + (let ((buf (get-buffer buffer-or-name)) helm-buf-or-cur) + (if (or (and (eql buf (get-buffer helm-current-buffer)) + (setq helm-buf-or-cur "helm-current-buffer")) + (and (eql buf (get-buffer helm-buffer)) + (setq helm-buf-or-cur "helm-buffer"))) + (progn + (message "Can't kill `%s' without quitting session" helm-buf-or-cur) + (sit-for 1)) + (kill-buffer buf) + (helm-delete-current-selection)))) + +(defun helm-buffers--quote-truncated-buffer (buffer) + (let ((bufname (and (bufferp buffer) + (buffer-name buffer)))) + (when (and bufname + (file-remote-p (with-current-buffer bufname + default-directory))) + (setq bufname (concat "@ " (helm-url-unhex-string bufname)))) + (when bufname + (regexp-quote + (if (and helm-buffer-max-length + helm-buffer-details-flag) + (helm-substring-by-width + bufname helm-buffer-max-length + "") + bufname))))) + +(defun helm-buffers-persistent-kill (_buffer) + (let ((marked (helm-marked-candidates)) + (sel (helm-get-selection))) + (unwind-protect + (cl-loop for b in marked + do (progn + ;; We need to preselect each marked because + ;; helm-buffers-persistent-kill is deleting + ;; current selection. + (helm-preselect + (format "^%s" + (helm-buffers--quote-truncated-buffer b))) + (helm-buffers-persistent-kill-1 b) + (message nil) + (helm--remove-marked-and-update-mode-line b))) + (with-helm-buffer + (setq helm-marked-candidates nil + helm-visible-mark-overlays nil)) + (helm-force-update (helm-buffers--quote-truncated-buffer sel))))) + +(defun helm-buffers-list-persistent-action (candidate) + (let ((current (window-buffer helm-persistent-action-display-window))) + (if (or (helm-follow-mode-p) + (eql current (get-buffer helm-current-buffer)) + (not (eql current (get-buffer candidate)))) + (switch-to-buffer candidate) + (if (and helm-persistent-action-display-window + (window-dedicated-p + (next-window helm-persistent-action-display-window 1))) + (delete-window helm-persistent-action-display-window) + (switch-to-buffer helm-current-buffer))))) + +(defun helm-ediff-marked-buffers (_candidate &optional merge) + "Ediff 2 marked buffers or CANDIDATE and `helm-current-buffer'. +With optional arg MERGE call `ediff-merge-buffers'." + (let* ((mkd (helm-marked-candidates)) + (lg-lst (length mkd)) + buf1 buf2) + (cl-case lg-lst + (0 + (error "Error:You have to mark at least 1 buffer")) + (1 + (setq buf1 helm-current-buffer + buf2 (cl-first mkd))) + (2 + (setq buf1 (cl-first mkd) + buf2 (cl-second mkd))) + (t + (error "Error:Too many buffers marked!"))) + (if merge + (ediff-merge-buffers buf1 buf2) + (ediff-buffers buf1 buf2)))) + +(defun helm-ediff-marked-buffers-merge (candidate) + "Ediff merge `helm-current-buffer' with CANDIDATE. +See `helm-ediff-marked-buffers'." + (helm-ediff-marked-buffers candidate t)) + +(defun helm-multi-occur-as-action (_candidate) + "Multi occur action for `helm-source-buffers-list'. +Can be used by any source that list buffers." + (let ((helm-occur-always-search-in-current + (if helm-current-prefix-arg + (not helm-occur-always-search-in-current) + helm-occur-always-search-in-current)) + (buffers (helm-marked-candidates)) + (input (cl-loop for i in (split-string (or (buffer-local-value + 'helm-input-local + (get-buffer helm-buffer)) + helm-pattern) + " " t) + thereis (and (string-match "\\`@\\([^!]*\\)" i) + (match-string 1 i))))) + (helm-multi-occur-1 buffers input))) + +(defun helm-buffers-run-occur () + "Run `helm-multi-occur-as-action' by key." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-multi-occur-as-action))) +(put 'helm-buffers-run-occur 'helm-only t) + +(defun helm-buffers-toggle-show-hidden-buffers () + (interactive) + (with-helm-alive-p + (let ((filter-attrs (helm-get-attr 'filtered-candidate-transformer + helm-source-buffers-list)) + (sel (helm-get-selection))) + (if (memq 'helm-shadow-boring-buffers filter-attrs) + (helm-set-attr 'filtered-candidate-transformer + (cons 'helm-skip-boring-buffers + (remove 'helm-shadow-boring-buffers + filter-attrs)) + helm-source-buffers-list) + (helm-set-attr 'filtered-candidate-transformer + (cons 'helm-shadow-boring-buffers + (remove 'helm-skip-boring-buffers + filter-attrs)) + helm-source-buffers-list)) + (helm-force-update (helm-buffers--quote-truncated-buffer sel))))) +(put 'helm-buffers-toggle-show-hidden-buffers 'helm-only t) + +(defun helm-buffers-browse-project (buf) + "Browse project from buffer BUF." + (with-current-buffer buf + (helm-browse-project helm-current-prefix-arg))) + +(defun helm-buffers-run-browse-project () + "Run `helm-buffers-browse-project' from key." + (interactive) + (with-helm-alive-p + (if helm-buffers-in-project-p + (user-error "You are already browsing this project") + (helm-exit-and-execute-action 'helm-buffers-browse-project)))) + +(defun helm-buffers-quit-and-find-file-fn (source) + (let* ((sel (helm-get-selection nil nil source)) + (buf (helm-aand (bufferp sel) + (get-buffer sel) + (buffer-name it)))) + (when buf + (or (buffer-file-name sel) + (car (rassoc buf dired-buffers)) + (and (with-current-buffer buf + (eq major-mode 'org-agenda-mode)) + org-directory + (expand-file-name org-directory)) + (with-current-buffer buf + (expand-file-name default-directory)))))) + +;;; Candidate Transformers +;; +;; +(defun helm-skip-boring-buffers (buffers _source) + "Remove buffers matching `helm-boring-buffer-regexp-list' in BUFFERS. +Where BUFFERS is a list of buffer names." + (helm-skip-entries buffers + helm-boring-buffer-regexp-list + helm-white-buffer-regexp-list)) + +(defun helm-shadow-boring-buffers (buffers _source) + "Buffers matching `helm-boring-buffer-regexp' will be +displayed with the `file-name-shadow' face if available." + (helm-shadow-entries buffers helm-boring-buffer-regexp-list)) + + +;;;###autoload +(defun helm-buffers-list () + "Preconfigured `helm' to list buffers." + (interactive) + (unless helm-source-buffers-list + (setq helm-source-buffers-list + (helm-make-source "Buffers" 'helm-source-buffers))) + (helm :sources '(helm-source-buffers-list + helm-source-buffer-not-found) + :buffer "*helm buffers*" + :keymap helm-buffer-map + :truncate-lines helm-buffers-truncate-lines + :left-margin-width helm-buffers-left-margin-width)) + +;;;###autoload +(defun helm-mini () + "Preconfigured `helm' displaying `helm-mini-default-sources'." + (interactive) + (require 'helm-x-files) + (unless helm-source-buffers-list + (setq helm-source-buffers-list + (helm-make-source "Buffers" 'helm-source-buffers))) + (helm :sources helm-mini-default-sources + :buffer "*helm mini*" + :ff-transformer-show-only-basename nil + :truncate-lines helm-buffers-truncate-lines + :left-margin-width helm-buffers-left-margin-width)) + +(defun helm-quit-and-helm-mini () + "Drop into `helm-mini' from `helm'." + (interactive) + (with-helm-alive-p + (helm-run-after-exit 'helm-mini))) + +(provide 'helm-buffers) + +;;; helm-buffers.el ends here diff --git a/code/elpa/helm-20220822.659/helm-color.el b/code/elpa/helm-20220822.659/helm-color.el new file mode 100644 index 0000000..cc9a4c3 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-color.el @@ -0,0 +1,167 @@ +;;; helm-color.el --- colors and faces -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-elisp) + +(declare-function list-colors-display "facemenu") + +;;; Customize Face +;; +;; +(defun helm-custom-faces-init () + "Initialize buffer for `helm-source-customize-face'." + (unless (helm-candidate-buffer) + (save-selected-window + (list-faces-display) + (message nil)) + (helm-init-candidates-in-buffer + 'global + (with-current-buffer (get-buffer "*Faces*") + (buffer-substring + (next-single-char-property-change (point-min) 'face) + (point-max)))) + (kill-buffer "*Faces*"))) + +(defvar helm-source-customize-face + (helm-build-in-buffer-source "Customize Face" + :init 'helm-custom-faces-init + :get-line 'buffer-substring + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + (intern (car (split-string candidate))) + 'helm-describe-face)) + :persistent-help "Describe face" + :action '(("Customize" + . (lambda (line) + (customize-face (intern (car (split-string line)))))) + ("Copy name" + . (lambda (line) + (kill-new (car (split-string line " " t))))))) + "See (info \"(emacs)Faces\")") + +;;; Colors browser +;; +;; +(defun helm-colors-init () + (require 'facemenu) + (unless (helm-candidate-buffer) + (save-selected-window + (list-colors-display) + (message nil)) + (helm-init-candidates-in-buffer + 'global + (with-current-buffer (get-buffer "*Colors*") + (buffer-string))) + (kill-buffer "*Colors*"))) + +(defun helm-color-insert-name (candidate) + (with-helm-current-buffer + (insert (helm-colors-get-name candidate)))) + +(defun helm-color-kill-name (candidate) + (kill-new (helm-colors-get-name candidate))) + +(defun helm-color-insert-rgb (candidate) + (with-helm-current-buffer + (insert (helm-colors-get-rgb candidate)))) + +(defun helm-color-kill-rgb (candidate) + (kill-new (helm-colors-get-rgb candidate))) + +(defun helm-color-run-insert-name () + "Insert name of color from `helm-source-colors'." + (interactive) + (with-helm-alive-p (helm-exit-and-execute-action 'helm-color-insert-name))) +(put 'helm-color-run-insert-name 'helm-only t) + +(defun helm-color-run-kill-name () + "Kill name of color from `helm-source-colors'." + (interactive) + (with-helm-alive-p (helm-exit-and-execute-action 'helm-color-kill-name))) +(put 'helm-color-run-kill-name 'helm-only t) + +(defun helm-color-run-insert-rgb () + "Insert RGB of color from `helm-source-colors'." + (interactive) + (with-helm-alive-p (helm-exit-and-execute-action 'helm-color-insert-rgb))) +(put 'helm-color-run-insert-rgb 'helm-only t) + +(defun helm-color-run-kill-rgb () + "Kill RGB of color from `helm-source-colors'." + (interactive) + (with-helm-alive-p (helm-exit-and-execute-action 'helm-color-kill-rgb))) +(put 'helm-color-run-kill-rgb 'helm-only t) + +(defvar helm-color-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c n") #'helm-color-run-insert-name) + (define-key map (kbd "C-c N") #'helm-color-run-kill-name) + (define-key map (kbd "C-c r") #'helm-color-run-insert-rgb) + (define-key map (kbd "C-c R") #'helm-color-run-kill-rgb) + map)) + +(defvar helm-source-colors + (helm-build-in-buffer-source "Colors" + :init 'helm-colors-init + :get-line 'buffer-substring + :keymap helm-color-map + :persistent-help "Kill entry in RGB format." + :persistent-action 'helm-color-kill-rgb + :help-message 'helm-colors-help-message + :action + '(("Copy Name (C-c N)" . helm-color-kill-name) + ("Copy RGB (C-c R)" . helm-color-kill-rgb) + ("Insert Name (C-c n)" . helm-color-insert-name) + ("Insert RGB (C-c r)" . helm-color-insert-rgb)))) + +(defun helm-colors-get-name (candidate) + "Get color name." + (replace-regexp-in-string + " " "" + (with-temp-buffer + (insert (capitalize candidate)) + (goto-char (point-min)) + (search-forward-regexp "\\s-\\{2,\\}") + (delete-region (point) (point-max)) + (buffer-string)))) + +(defun helm-colors-get-rgb (candidate) + "Get color RGB." + (replace-regexp-in-string + " " "" + (with-temp-buffer + (insert (capitalize candidate)) + (goto-char (point-max)) + (search-backward-regexp "\\s-\\{2,\\}") + (delete-region (point) (point-min)) + (buffer-string)))) + +;;;###autoload +(defun helm-colors () + "Preconfigured `helm' for color." + (interactive) + (helm :sources '(helm-source-colors helm-source-customize-face) + :buffer "*helm colors*")) + +(provide 'helm-color) + +;;; helm-color.el ends here diff --git a/code/elpa/helm-20220822.659/helm-comint.el b/code/elpa/helm-20220822.659/helm-comint.el new file mode 100644 index 0000000..e7dff8d --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-comint.el @@ -0,0 +1,230 @@ +;;; helm-comint.el --- Comint prompt navigation for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2020 Pierre Neidhardt + +;; 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 . + +;;; Commentary: +;; +;; You can bind this as follows in .emacs: +;; +;; (add-hook 'comint-mode-hook +;; (lambda () +;; (define-key comint-mode-map (kbd "M-s f") 'helm-comint-prompts-all))) + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-lib) +(require 'helm-help) +(require 'helm-elisp) + +;;; Comint prompts +;; +(defface helm-comint-prompts-promptidx + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + (:foreground "cyan"))) + "Face used to highlight comint prompt index." + :group 'helm-comint-faces) + +(defface helm-comint-prompts-buffer-name + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + (:foreground "green"))) + "Face used to highlight comint buffer name." + :group 'helm-comint-faces) + +(defcustom helm-comint-prompts-promptidx-p t + "Show prompt number." + :group 'helm-comint + :type 'boolean) + +(defcustom helm-comint-mode-list '(comint-mode slime-repl-mode sly-mrepl-mode sql-interactive-mode) + "Supported modes for prompt navigation. +Derived modes (e.g., Geiser's REPL) are automatically supported." + :group 'helm-comint + :type '(repeat (choice symbol))) + +(defcustom helm-comint-next-prompt-function '((sly-mrepl-mode . (lambda () + (sly-mrepl-next-prompt) + (point)))) + "Alist of (MODE . NEXT-PROMPT-FUNCTION) to use. + If the current major mode is a key in this list, the associated + function will be used to navigate the prompts. + The function must return the point after the prompt. + Otherwise (comint-next-prompt 1) will be used." + :group 'helm-comint + :type '(alist :key-type symbol :value-type function)) + +(defcustom helm-comint-max-offset 400 + "Max number of chars displayed per candidate in comint-input-ring browser. +When t, don't truncate candidate, show all. +By default it is approximatively the number of bits contained in +five lines of 80 chars each i.e 80*5. +Note that if you set this to nil multiline will be disabled, i.e +you will not have anymore separators between candidates." + :type '(choice (const :tag "Disabled" t) + (integer :tag "Max candidate offset")) + :group 'helm-misc) + +(defvar helm-comint-prompts-keymap + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c o") #'helm-comint-prompts-other-window) + (define-key map (kbd "C-c C-o") #'helm-comint-prompts-other-frame) + map) + "Keymap for `helm-comint-prompt-all'.") + +(defun helm-comint-prompts-list (mode &optional buffer) + "List the prompts in BUFFER in mode MODE. + +Return a list of (\"prompt\" (point) (buffer-name) prompt-index)) +E.g. (\"ls\" 162 \"*shell*\" 3). +If BUFFER is nil, use current buffer." + (with-current-buffer (or buffer (current-buffer)) + (when (derived-mode-p mode) + (save-excursion + (goto-char (point-min)) + (let (result (count 1)) + (save-mark-and-excursion + (helm-awhile (and (not (eobp)) + (helm-aif (alist-get major-mode helm-comint-next-prompt-function) + (funcall it) + (comint-next-prompt 1))) + (push (list (buffer-substring-no-properties + it (point-at-eol)) + it (buffer-name) count) + result) + (setq count (1+ count)))) + (nreverse result)))))) + +(defun helm-comint-prompts-list-all (mode) + "List the prompts of all buffers in mode MODE. +See `helm-comint-prompts-list'." + (cl-loop for b in (buffer-list) + append (helm-comint-prompts-list mode b))) + +(defun helm-comint-prompts-transformer (candidates &optional all) + ;; ("ls" 162 "*shell*" 3) => ("*shell*:3:ls" . ("ls" 162 "*shell*" 3)) + (cl-loop for (prt pos buf id) in candidates + collect `(,(concat + (when all + (concat (propertize + buf + 'face 'helm-comint-prompts-buffer-name) + ":")) + (when helm-comint-prompts-promptidx-p + (concat (propertize + (number-to-string id) + 'face 'helm-comint-prompts-promptidx) + ":")) + prt) + . ,(list prt pos buf id)))) + +(defun helm-comint-prompts-all-transformer (candidates) + (helm-comint-prompts-transformer candidates t)) + +(cl-defun helm-comint-prompts-goto (candidate &optional (action 'switch-to-buffer)) + ;; Candidate format: ("ls" 162 "*shell*" 3) + (let ((buf (nth 2 candidate))) + (unless (and (string= (buffer-name) buf) + (eq action 'switch-to-buffer)) + (funcall action buf)) + (goto-char (nth 1 candidate)) + (recenter))) + +(defun helm-comint-prompts-goto-other-window (candidate) + (helm-comint-prompts-goto candidate 'switch-to-buffer-other-window)) + +(defun helm-comint-prompts-goto-other-frame (candidate) + (helm-comint-prompts-goto candidate 'switch-to-buffer-other-frame)) + +(defun helm-comint-prompts-other-window () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-comint-prompts-goto-other-window))) +(put 'helm-comint-prompts-other-window 'helm-only t) + +(defun helm-comint-prompts-other-frame () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-comint-prompts-goto-other-frame))) +(put 'helm-comint-prompts-other-frame 'helm-only t) + +;;;###autoload +(defun helm-comint-prompts () + "Pre-configured `helm' to browse the prompts of the current comint buffer." + (interactive) + (if (apply #'derived-mode-p helm-comint-mode-list) + (helm :sources + (helm-build-sync-source "Comint prompts" + :candidates (helm-comint-prompts-list major-mode) + :candidate-transformer #'helm-comint-prompts-transformer + :action '(("Go to prompt" . helm-comint-prompts-goto))) + :buffer "*helm comint prompts*") + (message "Current buffer is not a comint buffer"))) + +;;;###autoload +(defun helm-comint-prompts-all () + "Pre-configured `helm' to browse the prompts of all comint sessions." + (interactive) + (if (apply #'derived-mode-p helm-comint-mode-list) + (helm :sources + (helm-build-sync-source "All comint prompts" + :candidates (helm-comint-prompts-list-all major-mode) + :candidate-transformer #'helm-comint-prompts-all-transformer + :action (quote (("Go to prompt" . helm-comint-prompts-goto) + ("Go to prompt in other window `C-c o`" . + helm-comint-prompts-goto-other-window) + ("Go to prompt in other frame `C-c C-o`" . + helm-comint-prompts-goto-other-frame))) + :keymap helm-comint-prompts-keymap) + :buffer "*helm comint all prompts*") + (message "Current buffer is not a comint buffer"))) + +;;; Comint history +;; +;; +(defun helm-comint-input-ring-action (candidate) + "Default action for comint history." + (with-helm-current-buffer + (delete-region (comint-line-beginning-position) (point-max)) + (insert candidate))) + +(defvar helm-source-comint-input-ring + (helm-build-sync-source "Comint history" + :candidates (lambda () + (with-helm-current-buffer + (cl-loop for elm in (ring-elements comint-input-ring) + unless (string= elm "") + collect elm))) + :action 'helm-comint-input-ring-action + ;; Multiline does not work for `shell' because of an Emacs bug. + ;; It works in other REPLs like Geiser. + :multiline 'helm-comint-max-offset) + "Source that provides Helm completion against `comint-input-ring'.") + +;;;###autoload +(defun helm-comint-input-ring () + "Preconfigured `helm' that provide completion of `comint' history." + (interactive) + (when (or (derived-mode-p 'comint-mode) + (member major-mode helm-comint-mode-list)) + (helm :sources 'helm-source-comint-input-ring + :input (buffer-substring-no-properties (comint-line-beginning-position) + (point-at-eol)) + :buffer "*helm comint history*"))) + +(provide 'helm-comint) + +;;; helm-comint.el ends here diff --git a/code/elpa/helm-20220822.659/helm-command.el b/code/elpa/helm-20220822.659/helm-command.el new file mode 100644 index 0000000..29491c7 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-command.el @@ -0,0 +1,413 @@ +;;; helm-command.el --- Helm execute-exended-command. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-mode) +(require 'helm-elisp) + + +(defvar helm-M-x-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-comp-read-map) + (define-key map (kbd "C-u") nil) + (define-key map (kbd "C-u") #'helm-M-x-universal-argument) + (define-key map (kbd "C-]") #'helm-M-x-toggle-short-doc) + map)) + + +(defgroup helm-command nil + "Emacs command related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-M-x-always-save-history nil + "`helm-M-x' save command in `extended-command-history' even when it fails." + :type 'boolean) + +(defcustom helm-M-x-reverse-history nil + "The history source of `helm-M-x' appear in second position when non-nil." + :type 'boolean) + +(defcustom helm-M-x-fuzzy-match t + "Helm-M-x fuzzy matching when non nil." + :type 'boolean) + +(defcustom helm-M-x-show-short-doc nil + "Show short docstring of command when non nil. +This value can be toggled with +\\\\[helm-M-x-toggle-short-doc] while in helm-M-x session." + :type 'boolean) + + +;;; Faces +;; +;; +(defgroup helm-command-faces nil + "Customize the appearance of helm-command." + :prefix "helm-" + :group 'helm-command + :group 'helm-faces) + +(defface helm-M-x-key + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "orange" :box (:line-width -1))) + "Face used in helm-M-x to show keybinding." + :group 'helm-command-faces) + +(defface helm-command-active-mode + '((t :inherit font-lock-builtin-face)) + "Face used by `helm-M-x' for activated modes." + :group 'helm-command-faces) + +(defface helm-M-x-short-doc + '((t :box (:line-width -1) :foreground "DimGray")) + "Face used by `helm-M-x' for short docstring." + :group 'helm-command-faces) + +(defvar helm-M-x-input-history nil) +(defvar helm-M-x-prefix-argument nil + "Prefix argument before calling `helm-M-x'.") +(defvar helm-M-x--timer nil) +(defvar helm-M-x--unwind-forms-done nil) + +(defun helm-M-x-get-major-mode-command-alist (mode-map) + "Return alist of MODE-MAP." + (when mode-map + (cl-loop for key being the key-seqs of mode-map using (key-bindings com) + for str-key = (key-description key) + for ismenu = (string-match "" str-key) + unless ismenu collect (cons str-key com)))) + +(defun helm-get-mode-map-from-mode (mode) + "Guess the mode-map name according to MODE. +Some modes don't use conventional mode-map name so we need to +guess mode-map name. E.g. `python-mode' ==> py-mode-map. +Return nil if no mode-map found." + (cl-loop ;; Start with a conventional mode-map name. + with mode-map = (intern-soft (format "%s-map" mode)) + with mode-string = (symbol-name mode) + with mode-name = (replace-regexp-in-string "-mode" "" mode-string) + while (not mode-map) + for count downfrom (length mode-name) + ;; Return when no result after parsing entire string. + when (eq count 0) return nil + for sub-name = (substring mode-name 0 count) + do (setq mode-map (intern-soft (format "%s-map" (concat sub-name "-mode")))) + finally return mode-map)) + +(defun helm-M-x-current-mode-map-alist () + "Return mode-map alist of current `major-mode'." + (let ((map-sym (helm-get-mode-map-from-mode major-mode))) + (when (and map-sym (boundp map-sym)) + (helm-M-x-get-major-mode-command-alist (symbol-value map-sym))))) + +(defun helm-M-x-toggle-short-doc () + "Toggle short doc display in helm-M-x." + (interactive) + (setq helm-M-x-show-short-doc (not helm-M-x-show-short-doc)) + (helm-force-update (concat "^" (helm-get-selection)) (helm-get-current-source))) +(put 'helm-M-x-toggle-short-doc 'no-helm-mx t) + +(defun helm-M-x-transformer-1 (candidates &optional sort ignore-props) + "Transformer function to show bindings in emacs commands. +Show global bindings and local bindings according to current +`major-mode'. +If SORT is non nil sort list with `helm-generic-sort-fn'. +Note that SORT should not be used when fuzzy matching because +fuzzy matching is running its own sort function with a different +algorithm." + (with-helm-current-buffer + (cl-loop with max-len = (when helm-M-x-show-short-doc + (buffer-local-value 'helm-candidate-buffer-longest-len + (get-buffer (helm-candidate-buffer)))) + with local-map = (helm-M-x-current-mode-map-alist) + for cand in candidates + for local-key = (car (rassq cand local-map)) + for key = (substitute-command-keys (format "\\[%s]" cand)) + for sym = (intern (if (consp cand) (car cand) cand)) + for doc = (when max-len + (helm-get-first-line-documentation (intern-soft cand))) + for disp = (if (or (eq sym major-mode) + (and (memq sym minor-mode-list) + (boundp sym) + (buffer-local-value sym helm-current-buffer))) + (propertize cand 'face 'helm-command-active-mode) + cand) + unless (and (null ignore-props) (or (get sym 'helm-only) (get sym 'no-helm-mx))) + collect + (cons (cond ((and (string-match "^M-x" key) local-key) + (format "%s%s%s %s" + disp + (if doc (make-string (+ 1 (- max-len (length cand))) ? ) "") + (if doc (propertize doc 'face 'helm-M-x-short-doc) "") + (propertize + " " 'display + (propertize local-key 'face 'helm-M-x-key)))) + ((string-match "^M-x" key) + (format "%s%s%s" + disp + (if doc (make-string (+ 1 (- max-len (length cand))) ? ) "") + (if doc (propertize doc 'face 'helm-M-x-short-doc) ""))) + (t (format "%s%s%s %s" + disp + (if doc (make-string (+ 1 (- max-len (length cand))) ? ) "") + (if doc (propertize doc 'face 'helm-M-x-short-doc) "") + (propertize + " " 'display + (propertize key 'face 'helm-M-x-key))))) + cand) + into ls + finally return + (if sort (sort ls #'helm-generic-sort-fn) ls)))) + +(defun helm-M-x-transformer (candidates _source) + "Transformer function for `helm-M-x' candidates." + ;; Generic sort function is handling helm-flex. + (helm-M-x-transformer-1 candidates (null helm--in-fuzzy))) + +(defun helm-M-x-transformer-no-sort (candidates _source) + "Transformer function for `helm-M-x' candidates." + (helm-M-x-transformer-1 candidates)) + +(defun helm-M-x-transformer-no-sort-no-props (candidates _source) + "Transformer function for `helm-M-x' candidates." + (helm-M-x-transformer-1 candidates nil t)) + +(defun helm-M-x--notify-prefix-arg () + ;; Notify a prefix-arg set AFTER calling M-x. + (when prefix-arg + (with-helm-window + (helm-display-mode-line (helm-get-current-source) 'force)))) + +(defun helm-cmd--get-current-function-name () + (save-excursion + (beginning-of-defun) + (cadr (split-string (buffer-substring-no-properties + (point-at-bol) (point-at-eol)))))) + +(defun helm-cmd--get-preconfigured-commands (&optional dir) + (let* ((helm-dir (or dir (helm-basedir (locate-library "helm")))) + (helm-autoload-file (expand-file-name "helm-autoloads.el" helm-dir)) + results) + (when (file-exists-p helm-autoload-file) + (with-temp-buffer + (insert-file-contents helm-autoload-file) + (while (re-search-forward "Preconfigured" nil t) + (push (substring (helm-cmd--get-current-function-name) 1) results)))) + results)) + +(defun helm-M-x-universal-argument () + "Same as `universal-argument' but for `helm-M-x'." + (interactive) + (if helm-M-x-prefix-argument + (progn (setq helm-M-x-prefix-argument nil) + (let ((inhibit-read-only t)) + (with-selected-window (minibuffer-window) + (save-excursion + (goto-char (point-min)) + (delete-char (- (minibuffer-prompt-width) (length "M-x ")))))) + (message "Initial prefix arg disabled")) + (setq prefix-arg (list 4)) + (universal-argument--mode))) +(put 'helm-M-x-universal-argument 'helm-only t) + +(defun helm-M-x-persistent-action (candidate) + (helm-elisp--persistent-help + candidate 'helm-describe-function)) + +(defun helm-M-x--move-selection-after-hook () + (setq current-prefix-arg nil)) + +(defun helm-M-x--before-action-hook () + (remove-hook 'helm-move-selection-after-hook + #'helm-M-x--move-selection-after-hook)) + +(defclass helm-M-x-class (helm-source-in-buffer helm-type-command) + ((requires-pattern :initform 0) + (must-match :initform t) + (filtered-candidate-transformer :initform 'helm-M-x-transformer-no-sort) + (persistent-help :initform "Describe this command") + (help-message :initform 'helm-M-x-help-message) + (nomark :initform t) + (cleanup :initform #'helm-M-x--unwind-forms) + (keymap :initform 'helm-M-x-map) + (resume :initform 'helm-M-x-resume-fn))) + +(defun helm-M-x-resume-fn () + (when (and helm-M-x--timer (timerp helm-M-x--timer)) + (cancel-timer helm-M-x--timer) + (setq helm-M-x--timer nil)) + (setq helm-M-x--timer (run-at-time 1 0.1 #'helm-M-x--notify-prefix-arg)) + (setq helm--mode-line-display-prefarg t) + ;; Prevent displaying a wrong prefix arg when helm-resume is called + ;; from prefix arg. + (setq current-prefix-arg nil)) + +(defun helm-M-x-read-extended-command (collection &optional predicate history) + "Read or execute action on command name in COLLECTION or HISTORY. + +When `helm-M-x-use-completion-styles' is used, Emacs +`completion-styles' mechanism is used, otherwise standard helm +completion and helm fuzzy matching are used together. + +Helm completion is not provided when executing or defining kbd +macros. + +Arg COLLECTION should be an `obarray' but can be any object +suitable for `try-completion'. Arg PREDICATE is a function that +default to `commandp' see also `try-completion'. Arg HISTORY +default to `extended-command-history'." + (setq helm--mode-line-display-prefarg t) + (let* ((pred (or predicate #'commandp)) + (helm-fuzzy-sort-fn (lambda (candidates _source) + ;; Sort on real candidate otherwise + ;; "symbol ()" is used when sorting. + (helm-fuzzy-matching-default-sort-fn-1 candidates t))) + (sources `(,(helm-make-source "Emacs Commands history" 'helm-M-x-class + :data (lambda () + (helm-comp-read-get-candidates + ;; History should be quoted to + ;; force `helm-comp-read-get-candidates' + ;; to use predicate against + ;; symbol and not string. + (or history 'extended-command-history) + ;; Ensure using empty string to + ;; not defeat helm matching fns [1] + pred nil nil "")) + :fuzzy-match helm-M-x-fuzzy-match) + ,(helm-make-source "Emacs Commands" 'helm-M-x-class + :data (lambda () + (helm-comp-read-get-candidates + ;; [1] Same comment as above. + collection pred nil nil "")) + :fuzzy-match helm-M-x-fuzzy-match))) + (prompt (concat (cond + ((eq helm-M-x-prefix-argument '-) "- ") + ((and (consp helm-M-x-prefix-argument) + (eq (car helm-M-x-prefix-argument) 4)) + "C-u ") + ((and (consp helm-M-x-prefix-argument) + (integerp (car helm-M-x-prefix-argument))) + (format "%d " (car helm-M-x-prefix-argument))) + ((integerp helm-M-x-prefix-argument) + (format "%d " helm-M-x-prefix-argument))) + "M-x "))) + (setq helm-M-x--timer (run-at-time 1 0.1 #'helm-M-x--notify-prefix-arg)) + ;; Fix Bug#2250, add `helm-move-selection-after-hook' which + ;; reset prefix arg to nil only for this helm session. + (add-hook 'helm-move-selection-after-hook + #'helm-M-x--move-selection-after-hook) + (add-hook 'helm-before-action-hook + #'helm-M-x--before-action-hook) + (when (and sources helm-M-x-reverse-history) + (setq sources (nreverse sources))) + (unwind-protect + (progn + (setq current-prefix-arg nil) + (helm :sources sources + :prompt prompt + :buffer "*helm M-x*" + :history 'helm-M-x-input-history + :truncate-lines t)) + (helm-M-x--unwind-forms)))) + +;; When running a command involving again helm from helm-M-x, the +;; unwind-protect UNWINDS forms are executed only once this helm +;; command exit leaving the helm-M-x timer running and other variables +;; and hooks not unset, so the timer is now in a global var and all +;; the forms that should normally run in unwind-protect are running as +;; well as soon as helm-M-x-execute-command is called. +(defun helm-M-x--unwind-forms (&optional done) + ;; helm-M-x--unwind-forms-done is non nil when it have been called + ;; once from helm-M-x-execute-command. + (unless helm-M-x--unwind-forms-done + (when (timerp helm-M-x--timer) + (cancel-timer helm-M-x--timer) + (setq helm-M-x--timer nil)) + (setq helm--mode-line-display-prefarg nil + helm-fuzzy-sort-fn (default-toplevel-value 'helm-fuzzy-sort-fn)) + ;; Be sure to remove it here as well in case of quit. + (remove-hook 'helm-move-selection-after-hook + #'helm-M-x--move-selection-after-hook) + (remove-hook 'helm-before-action-hook + #'helm-M-x--before-action-hook)) + ;; Reset helm-M-x--unwind-forms-done to nil when DONE is + ;; unspecified. + (setq helm-M-x--unwind-forms-done done)) + +(defun helm-M-x-execute-command (command) + "Execute COMMAND as an editor command. +COMMAND must be a symbol that satisfies the `commandp' predicate. +Save COMMAND to `extended-command-history'." + (helm-M-x--unwind-forms t) + (when command + ;; Avoid having `this-command' set to *exit-minibuffer. + (setq this-command command + ;; Handle C-x z (repeat) Bug#322 + real-this-command command) + ;; If helm-M-x is called with regular emacs completion (kmacro) + ;; use the value of arg otherwise use helm-current-prefix-arg. + (let ((prefix-arg (or helm-current-prefix-arg helm-M-x-prefix-argument)) + (command-name (symbol-name command))) + (condition-case-unless-debug err + (progn + (command-execute command 'record) + (add-to-history 'extended-command-history command-name)) + (error + (when helm-M-x-always-save-history + (add-to-history 'extended-command-history command-name)) + (signal (car err) (cdr err))))))) + +(defun helm-M-x--vanilla-M-x () + (helm-M-x-execute-command + (intern-soft + (if helm-mode + (unwind-protect + (progn + (helm-mode -1) + (read-extended-command)) + (helm-mode 1)) + (read-extended-command))))) + +;;;###autoload +(defun helm-M-x (_arg) + "Preconfigured `helm' for Emacs commands. +It is `helm' replacement of regular `M-x' +`execute-extended-command'. + +Unlike regular `M-x' Emacs vanilla `execute-extended-command' +command, the prefix args if needed, can be passed AFTER starting +`helm-M-x'. When a prefix arg is passed BEFORE starting +`helm-M-x', the first `C-u' while in `helm-M-x' session will +disable it. + +You can get help on each command by persistent action." + (interactive + (progn + (setq helm-M-x-prefix-argument current-prefix-arg) + (list current-prefix-arg))) + (if (or defining-kbd-macro executing-kbd-macro) + (helm-M-x--vanilla-M-x) + (helm-M-x-read-extended-command obarray))) +(put 'helm-M-x 'interactive-only 'command-execute) + +(provide 'helm-command) + +;;; helm-command.el ends here diff --git a/code/elpa/helm-20220822.659/helm-config.el b/code/elpa/helm-20220822.659/helm-config.el new file mode 100644 index 0000000..9a83a30 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-config.el @@ -0,0 +1,32 @@ +;;; helm-config.el --- Applications library for `helm.el' -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Commentary: +;; +;; Requiring this file is not needed when using a package manager to +;; install helm as this one will take care of creating and loading the +;; autoload file. + +;;; Code: + +;;; Load the autoload file generated by the make file. + +(load "helm-autoloads" nil t) + +(provide 'helm-config) + +;;; helm-config.el ends here diff --git a/code/elpa/helm-20220822.659/helm-dabbrev.el b/code/elpa/helm-20220822.659/helm-dabbrev.el new file mode 100644 index 0000000..cb85242 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-dabbrev.el @@ -0,0 +1,388 @@ +;;; helm-dabbrev.el --- Helm implementation of dabbrev. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'helm) +(require 'helm-lib) +(require 'helm-help) +(require 'helm-elisp) ; For show-completion. + +(defgroup helm-dabbrev nil + "Dabbrev related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-dabbrev-always-search-all t + "Always search in all buffers when non--nil. +Note that even if nil, a search in all buffers will occur if the +length of candidates is <= than +`helm-dabbrev-max-length-result'." + :type 'boolean) + +(defcustom helm-dabbrev-candidates-number-limit 1000 + "Maximum number of candidates to collect. + +The higher this number is, the slower the computation of +candidates will be. You can use safely a higher value with +emacs-26+. +Note that this have nothing to do with +`helm-candidate-number-limit', this means that computation of +candidates stop when this value is reached but only +`helm-candidate-number-limit' candidates are displayed in the +Helm buffer." + :type 'integer) + +(defcustom helm-dabbrev-ignored-buffers-regexps + '("\\*helm" "\\*Messages" "\\*Echo Area" "\\*Buffer List") + "List of regexps matching names of buffers that `helm-dabbrev' should not check." + :type '(repeat regexp)) + +(defcustom helm-dabbrev-related-buffer-fn #'helm-dabbrev--same-major-mode-p + "A function that decide if a buffer to search in its related to `current-buffer'. + +This is actually determined by comparing `major-mode' of the +buffer to search and the `current-buffer'. + +The function take one arg, the buffer which is current, look at +`helm-dabbrev--same-major-mode-p' for an example. + +When nil all buffers are considered related to `current-buffer'." + :type 'function) + +(defcustom helm-dabbrev-major-mode-assoc nil + "Major mode association alist. + +This allow helm-dabbrev searching in buffers with the associated +`major-mode'. +E.g. (emacs-lisp-mode . lisp-interaction-mode) + +will allow searching in the lisp-interaction-mode buffer when +`current-buffer' is an `emacs-lisp-mode' buffer and vice versa +i.e. no need to provide (lisp-interaction-mode . +emacs-lisp-mode) association. + +When nil check is the searched buffer has same `major-mode' than +the `current-buffer'. + +This has no effect when `helm-dabbrev-related-buffer-fn' is nil +or of course bound to a function that doesn't handle this var." + :type '(alist :key-type symbol :value-type symbol)) + +(defcustom helm-dabbrev-lineno-around 30 + "Search first in this number of lines before and after point." + :type 'integer) + +(defcustom helm-dabbrev-cycle-threshold 5 + "Number of time helm-dabbrev cycle before displaying helm completion. +When nil or 0 disable cycling." + :type '(choice (const :tag "Cycling disabled" nil) integer)) + +(defcustom helm-dabbrev-case-fold-search 'smart + "Set `case-fold-search' in `helm-dabbrev'. +Same as `helm-case-fold-search' but for `helm-dabbrev'. +Note that this is not affecting searching in Helm buffer, but the +initial search for all candidates in buffer(s)." + :type '(choice (const :tag "Ignore case" t) + (const :tag "Respect case" nil) + (other :tag "Smart" smart))) + +(defvaralias 'helm-dabbrev--regexp 'helm-dabbrev-separator-regexp) +(make-obsolete-variable 'helm-dabbrev--regexp + 'helm-dabbrev-separator-regexp "2.8.3") +;; Check for beginning of line should happen last (^\n\\|^). +(defvar helm-dabbrev-separator-regexp + "\\s-\\|\t\\|[(\\[\\{\"'`=<$;,@.#+]\\|\\s\\\\|^\n\\|^" + "Regexp matching the start of a dabbrev candidate.") + + +(defvar helm-dabbrev-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-/") #'helm-next-line) + (define-key map (kbd "M-:") #'helm-previous-line) + map)) + +;; Internal +(defvar helm-dabbrev--cache nil) +(defvar helm-dabbrev--data nil) +(cl-defstruct helm-dabbrev-info dabbrev limits iterator) +(defvar helm-dabbrev--already-tried nil) +(defvar helm-dabbrev--computing-cache nil + "[INTERNAL] Flag to notify helm-dabbrev is blocked. +Do nothing when non nil.") + +(defun helm-dabbrev--buffer-list () + (cl-loop for buf in (buffer-list) + unless (cl-loop for r in helm-dabbrev-ignored-buffers-regexps + thereis (string-match r (buffer-name buf))) + collect buf)) + +(defun helm-dabbrev--same-major-mode-p (start-buffer) + "Decide if current-buffer is related to START-BUFFER." + (helm-same-major-mode-p start-buffer helm-dabbrev-major-mode-assoc)) + +(defun helm-dabbrev--collect (str limit ignore-case all) + (let* ((case-fold-search ignore-case) + (buffer1 (current-buffer)) ; start buffer. + (minibuf (minibufferp buffer1)) + results pos-before pos-after) + (catch 'break + (dolist (buf (if all (helm-dabbrev--buffer-list) + (list (current-buffer)))) + (with-current-buffer buf + (when (or minibuf ; check against all buffers when in minibuffer. + (if helm-dabbrev-related-buffer-fn + (funcall helm-dabbrev-related-buffer-fn buffer1) + t)) + (save-excursion + ;; Start searching before thing before point. + (goto-char (- (point) (length str))) + ;; Search the last 30 lines BEFORE point and set POS-BEFORE. + (cl-multiple-value-bind (res _pa pb) + (helm-dabbrev--search-and-store str -2 limit results) + (setq results res + ;; No need to set POS-AFTER here. + pos-before pb))) + (save-excursion + ;; Search the next 30 lines AFTER point and set POS-AFTER. + (cl-multiple-value-bind (res pa _pb) + (helm-dabbrev--search-and-store str 2 limit results) + (setq results res + ;; No need to set POS-BEFORE, we keep the last + ;; value found. + pos-after pa))) + (save-excursion + ;; Search all BEFORE point maybe starting from + ;; POS-BEFORE to not search again what previously found. + ;; If limit is reached in previous call of + ;; `helm-dabbrev--search-and-store' POS-BEFORE is nil and + ;; goto-char will fail, so check it. + (when pos-before (goto-char pos-before)) + (cl-multiple-value-bind (res _pa _pb) + (helm-dabbrev--search-and-store str -1 limit results) + ;; No need to set POS-BEFORE and POS-AFTER here. + (setq results res))) + (save-excursion + ;; Search all AFTER point maybe starting from POS-AFTER. + ;; Same comment as above for POS-AFTER. + (when pos-after (goto-char pos-after)) + (cl-multiple-value-bind (res _pa _pb) + (helm-dabbrev--search-and-store str 1 limit results) + ;; No need to set POS-BEFORE and POS-AFTER here. + (setq results res))))) + (when (>= (length results) limit) (throw 'break nil)))) + (nreverse results))) + +(defun helm-dabbrev--search-and-store (pattern direction limit results) + "Search words or symbols matching PATTERN in DIRECTION up to LIMIT. +Finally returns all matched candidates appended to RESULTS. +Argument DIRECTION can be: + - (1): Search forward from point. + - (-1): Search backward from point. + - (2): Search forward from the + `helm-dabbrev-lineno-around' + lines after point. + - (-2): Search backward from the + `helm-dabbrev-lineno-around' + lines before point." + (let ((res results) + after before) + (while (and (<= (length res) limit) + (cl-case direction + (1 (search-forward pattern nil t)) + (-1 (search-backward pattern nil t)) + (2 (let ((pos + (save-excursion + (forward-line + helm-dabbrev-lineno-around) + (point)))) + (setq after pos) + (search-forward pattern pos t))) + (-2 (let ((pos + (save-excursion + (forward-line + (- helm-dabbrev-lineno-around)) + (point)))) + (setq before pos) + (search-backward pattern pos t))))) + (let* ((mb (match-beginning 0)) + (replace-regexp (concat "\\(" helm-dabbrev-separator-regexp + "\\)\\'")) + (match-word (helm-dabbrev--search + pattern mb replace-regexp))) + (when (and match-word (not (member match-word res))) + (push match-word res)))) + (list res after before))) + +(defun helm-dabbrev--search (pattern beg sep-regexp) + "Search word or symbol at point matching PATTERN. +Argument BEG is corresponding to the previous `match-beginning' +search. +The search starts at (1- BEG) with a regexp starting with +`helm-dabbrev-separator-regexp' followed by PATTERN followed by a +regexp matching syntactically any word or symbol. +The possible false positives matching SEP-REGEXP at end are +finally removed." + (let ((eol (point-at-eol))) + (save-excursion + (goto-char (1- beg)) + (when (re-search-forward + (concat "\\(" + helm-dabbrev-separator-regexp + "\\)" + "\\(?99:\\(" + (regexp-quote pattern) + "\\(\\sw\\|\\s_\\)+\\)\\)") + eol t) + (replace-regexp-in-string + sep-regexp "" + (match-string-no-properties 99)))))) + +(defun helm-dabbrev--get-candidates (dabbrev &optional limit) + (cl-assert dabbrev nil "[No Match]") + (helm-dabbrev--collect + dabbrev (or limit helm-dabbrev-candidates-number-limit) + (cl-case helm-dabbrev-case-fold-search + (smart (helm-set-case-fold-search-1 dabbrev)) + (t helm-dabbrev-case-fold-search)) + helm-dabbrev-always-search-all)) + +(defun helm-dabbrev-default-action (candidate) + (with-helm-current-buffer + (let* ((limits (helm-bounds-of-thing-before-point + helm-dabbrev-separator-regexp)) + (beg (car limits)) + (end (point))) + (run-with-timer + 0.01 nil + #'helm-insert-completion-at-point + beg end candidate)))) + +;;;###autoload +(cl-defun helm-dabbrev () + "Preconfigured helm for dynamic abbreviations." + (interactive) + (unless helm-dabbrev--computing-cache + (let ((dabbrev (helm-thing-before-point + nil helm-dabbrev-separator-regexp)) + (limits (helm-bounds-of-thing-before-point + helm-dabbrev-separator-regexp)) + (enable-recursive-minibuffers t) + (cycling-disabled-p (or (null helm-dabbrev-cycle-threshold) + (zerop helm-dabbrev-cycle-threshold))) + (helm-execute-action-at-once-if-one t) + (helm-quit-if-no-candidate + (lambda () + (message "[Helm-dabbrev: No expansion found]")))) + (cl-assert (and (stringp dabbrev) (not (string= dabbrev ""))) + nil "[Helm-dabbrev: Nothing found before point]") + (when (and + ;; have been called at least once. + (helm-dabbrev-info-p helm-dabbrev--data) + ;; But user have moved with some other command + ;; in the meaning time. + (not (eq last-command 'helm-dabbrev))) + (setq helm-dabbrev--data nil)) + ;; When candidates are requested in helm directly without cycling, + ;; we need them right now before running helm. + (when cycling-disabled-p + (message "Waiting for helm-dabbrev candidates...") + (setq helm-dabbrev--cache (helm-dabbrev--get-candidates dabbrev))) + (unless (or cycling-disabled-p + (helm-dabbrev-info-p helm-dabbrev--data)) + (setq helm-dabbrev--data + (make-helm-dabbrev-info + :dabbrev dabbrev + :limits limits + :iterator + (helm-iter-list + (cl-loop for i in (helm-dabbrev--get-candidates + dabbrev helm-dabbrev-cycle-threshold) + when (string-match-p + (concat "^" (regexp-quote dabbrev)) i) + collect i))))) + (let ((iter (and (helm-dabbrev-info-p helm-dabbrev--data) + (helm-dabbrev-info-iterator helm-dabbrev--data))) + deactivate-mark) + ;; Cycle until iterator is consumed. + (helm-aif (and iter (helm-iter-next iter)) + (progn + (helm-insert-completion-at-point + (car (helm-dabbrev-info-limits helm-dabbrev--data)) + ;; END is the end of the previous inserted string, not + ;; the end (apart for first insertion) of the initial string. + (cdr limits) it) + ;; Move already tried candidates to end of list. + (push it helm-dabbrev--already-tried)) + ;; Iterator is now empty, or cycling was disabled, maybe + ;; reset dabbrev to initial value and start helm completion. + (let* ((old-dabbrev (if (helm-dabbrev-info-p helm-dabbrev--data) + (helm-dabbrev-info-dabbrev helm-dabbrev--data) + dabbrev)) + (only-one (eq (length helm-dabbrev--already-tried) 1))) + (unless helm-dabbrev--cache ; Already computed when + ; cycling is disabled. + (message "Waiting for helm-dabbrev candidates...") + (setq helm-dabbrev--computing-cache t) + (setq helm-dabbrev--cache + (helm-dabbrev--get-candidates old-dabbrev)) + ;; If user continues typing M-/ while display is blocked by + ;; helm-dabbrev--get-candidates delete these events. + (setq unread-command-events nil)) + ;; If the length of candidates is only one when computed + ;; that's mean the unique matched item have already been + ;; inserted by the iterator, so no need to reinsert the old dabbrev, + ;; just let helm exiting with "No expansion found". + (unless (or only-one cycling-disabled-p) + (setq dabbrev old-dabbrev + limits (helm-dabbrev-info-limits helm-dabbrev--data)) + (setq helm-dabbrev--data nil) + (delete-region (car limits) (point)) + (insert dabbrev)) + (when (and (null cycling-disabled-p) only-one) + (setq helm-dabbrev--cache nil + helm-dabbrev--already-tried nil + helm-dabbrev--computing-cache nil) + (cl-return-from helm-dabbrev + (message "[Helm-dabbrev: No expansion found]"))) + (with-helm-show-completion (car limits) (cdr limits) + (unwind-protect + (helm :sources + (helm-build-in-buffer-source "Dabbrev Expand" + :data + (append + (cl-loop with lst = helm-dabbrev--cache + for cand in helm-dabbrev--already-tried + do (setq lst (delete cand lst)) + finally return lst) + helm-dabbrev--already-tried) + :persistent-action 'ignore + :persistent-help "DoNothing" + :keymap helm-dabbrev-map + :action 'helm-dabbrev-default-action + :group 'helm-dabbrev) + :buffer "*helm dabbrev*" + :input (concat "^" dabbrev " ") + :resume 'noresume + :allow-nest t) + (setq helm-dabbrev--computing-cache nil + helm-dabbrev--already-tried nil + helm-dabbrev--cache nil))))))))) + +(provide 'helm-dabbrev) + +;;; helm-dabbrev.el ends here diff --git a/code/elpa/helm-20220822.659/helm-easymenu.el b/code/elpa/helm-20220822.659/helm-easymenu.el new file mode 100644 index 0000000..690c1c1 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-easymenu.el @@ -0,0 +1,84 @@ +;;; helm-easymenu.el --- Helm easymenu definitions. -*- lexical-binding: t -*- + +;; Copyright (C) 2015 ~ 2020 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'easymenu) + +(easy-menu-add-item + nil '("Tools") + '("Helm" + ["Find any Files/Buffers" helm-multi-files t] + ["Helm Everywhere (Toggle)" helm-mode t] + ["Helm resume" helm-resume t] + "----" + ("Files" + ["Find files" helm-find-files t] + ["Recent Files" helm-recentf t] + ["Locate" helm-locate t] + ["Search Files with find" helm-find t] + ["Bookmarks" helm-filtered-bookmarks t]) + ("Buffers" + ["Find buffers" helm-buffers-list t]) + ("Projects" + ["Browse project" helm-browse-project] + ["Projects history" helm-projects-history]) + ("Commands" + ["Emacs Commands" helm-M-x t] + ["Externals Commands" helm-run-external-command t]) + ("Help" + ["Helm Apropos" helm-apropos t]) + ("Info" + ["Info at point" helm-info-at-point t] + ["Emacs Manual index" helm-info-emacs t] + ["Gnus Manual index" helm-info-gnus t] + ["Helm documentation" helm-documentation t]) + ("Elpa" + ["Elisp packages" helm-list-elisp-packages t] + ["Elisp packages no fetch" helm-list-elisp-packages-no-fetch t]) + ("Tools" + ["Occur" helm-occur t] + ["Grep current directory with AG" helm-do-grep-ag t] + ["Gid" helm-gid t] + ["Etags" helm-etags-select t] + ["Lisp complete at point" helm-lisp-completion-at-point t] + ["Browse Kill ring" helm-show-kill-ring t] + ["Browse register" helm-register t] + ["Mark Ring" helm-all-mark-rings t] + ["Regexp handler" helm-regexp t] + ["Colors & Faces" helm-colors t] + ["Show xfonts" helm-select-xfont t] + ["Ucs Symbols" helm-ucs t] + ["Imenu" helm-imenu t] + ["Imenu all" helm-imenu-in-all-buffers t] + ["Semantic or Imenu" helm-semantic-or-imenu t] + ["Google Suggest" helm-google-suggest t] + ["Eval expression" helm-eval-expression-with-eldoc t] + ["Calcul expression" helm-calcul-expression t] + ["Man pages" helm-man-woman t] + ["Top externals process" helm-top t] + ["Emacs internals process" helm-list-emacs-process t]) + "----" + ["Preferred Options" helm-configuration t]) + "Spell Checking") + +(easy-menu-add-item nil '("Tools") '("----") "Spell Checking") + + +(provide 'helm-easymenu) + +;;; helm-easymenu.el ends here diff --git a/code/elpa/helm-20220822.659/helm-elisp-package.el b/code/elpa/helm-20220822.659/helm-elisp-package.el new file mode 100644 index 0000000..692aadf --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-elisp-package.el @@ -0,0 +1,495 @@ +;;; helm-elisp-package.el --- helm interface for package.el -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'package) + +(defgroup helm-el-package nil + "helm elisp packages." + :group 'helm) + +(defcustom helm-el-package-initial-filter 'all + "Show only installed, upgraded or all packages at startup." + :type '(radio :tag "Initial filter for elisp packages" + (const :tag "Show all packages" all) + (const :tag "Show installed packages" installed) + (const :tag "Show not installed packages" uninstalled) + (const :tag "Show upgradable packages" upgrade))) + +(defcustom helm-el-truncate-lines t + "Truncate lines in `helm-buffer' when non-nil." + :type 'boolean) + + +(defcustom helm-el-package-upgrade-on-start nil + "Show package upgrades on startup when non nil." + :type 'boolean) + +(defcustom helm-el-package-autoremove-on-start nil + "Try to autoremove no more needed packages on startup. +See `package-autoremove'." + :type 'boolean) + +;; internals vars +(defvar helm-el-package--show-only 'all) +(defvar helm-el-package--initialized-p nil) +(defvar helm-el-package--tabulated-list nil) +(defvar helm-el-package--upgrades nil) +(defvar helm-el-package--removable-packages nil) + +;; Shutup bytecompiler for emacs-24* +(defvar package-menu-async) ; Only available on emacs-25. +(defvar helm-marked-buffer-name) +(declare-function async-byte-recompile-directory "ext:async-bytecomp.el") +(declare-function with-helm-display-marked-candidates "helm-utils.el") + + +(defun helm-el-package--init () + ;; In emacs-27 package-show-package-list returns an empty buffer + ;; until package-initialize have been called. + (unless (or package--initialized + (null (boundp 'package-quickstart))) + (package-initialize)) + (let (package-menu-async + (inhibit-read-only t)) + (when (null package-alist) + (setq helm-el-package--show-only 'all)) + (unless (consp package-selected-packages) + (helm-aif (package--find-non-dependencies) + (setq package-selected-packages it))) + (when (and (setq helm-el-package--removable-packages + (package--removable-packages)) + helm-el-package-autoremove-on-start) + (package-autoremove)) + (unwind-protect + (progn + (save-selected-window + (if helm-el-package--initialized-p + ;; Use this as `list-packages' doesn't work + ;; properly (empty buffer) when called from lisp + ;; with 'no-fetch (emacs-25 WA). + (package-show-package-list) + (when helm--force-updating-p (message "Refreshing packages list...")) + (list-packages helm-el-package--initialized-p)) + (setq helm-el-package--initialized-p t) + (message nil)) + (helm-init-candidates-in-buffer + 'global + (with-current-buffer (get-buffer "*Packages*") + (setq helm-el-package--tabulated-list tabulated-list-entries) + (remove-text-properties (point-min) (point-max) + '(read-only button follow-link category)) + (goto-char (point-min)) + (while (re-search-forward "^[ \t]+" nil t) + (replace-match "")) + (buffer-string))) + (setq helm-el-package--upgrades (helm-el-package-menu--find-upgrades)) + (if helm--force-updating-p + (if helm-el-package--upgrades + (message "Refreshing packages list done, [%d] package(s) to upgrade" + (length helm-el-package--upgrades)) + (message "Refreshing packages list done, no upgrades available")) + (setq helm-el-package--show-only (if (and helm-el-package-upgrade-on-start + helm-el-package--upgrades) + 'upgrade + helm-el-package-initial-filter)))) + (kill-buffer "*Packages*")))) + +(defun helm-el-package-describe (candidate) + (let ((id (get-text-property 0 'tabulated-list-id candidate))) + (describe-package (package-desc-name id)))) + +(defun helm-el-package-visit-homepage (candidate) + (let* ((id (get-text-property 0 'tabulated-list-id candidate)) + (pkg (package-desc-name id)) + (desc (cadr (assoc pkg package-archive-contents))) + (extras (package-desc-extras desc)) + (url (and (listp extras) (cdr-safe (assoc :url extras))))) + (if (stringp url) + (browse-url url) + (message "Package %s has no homepage" + (propertize (symbol-name pkg) + 'face 'font-lock-keyword-face))))) + +(defun helm-el-run-visit-homepage () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-el-package-visit-homepage))) +(put 'helm-el-run-visit-homepage 'helm-only t) + +(defun helm-elisp-package--pkg-name (pkg) + (if (package-desc-p pkg) + (package-desc-name pkg) + pkg)) + +(defun helm-el-package-install-1 (pkg-list) + (cl-loop with mkd = pkg-list + for p in mkd + for id = (get-text-property 0 'tabulated-list-id p) + for name = (helm-elisp-package--pkg-name id) + do (package-install id t) + when (helm-aand (assq name package-alist) + (package-desc-dir (cadr it)) + (file-exists-p it)) + collect id into installed-list and + do (unless (package--user-selected-p name) + (package--save-selected-packages + (cons name package-selected-packages))) + finally do (message (format "%d packages installed:\n(%s)" + (length installed-list) + (mapconcat #'package-desc-full-name + installed-list ", "))))) + +(defun helm-el-package-install (_candidate) + (helm-el-package-install-1 (helm-marked-candidates))) + +(defun helm-el-run-package-install () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-el-package-install))) +(put 'helm-el-run-package-install 'helm-only t) + +(defun helm-el-package-uninstall-1 (pkg-list &optional force) + (cl-loop with mkd = pkg-list + for p in mkd + for id = (get-text-property 0 'tabulated-list-id p) + do + (condition-case-unless-debug err + (package-delete id force) + (error (message (cadr err)))) + ;; Seems like package-descs are symbols with props instead of + ;; vectors in emacs-27, use package-desc-name to ensure + ;; compatibility in all emacs versions. + unless (assoc (package-desc-name id) package-alist) + collect id into delete-list + finally do (if delete-list + (message (format "%d packages deleted:\n(%s)" + (length delete-list) + (mapconcat #'package-desc-full-name + delete-list ", "))) + "No package deleted"))) + +(defun helm-el-package-uninstall (_candidate) + (helm-el-package-uninstall-1 (helm-marked-candidates) helm-current-prefix-arg)) + +(defun helm-el-run-package-uninstall () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-el-package-uninstall))) +(put 'helm-el-run-package-uninstall 'helm-only t) + +(defun helm-el-package-menu--find-upgrades () + (cl-loop for entry in helm-el-package--tabulated-list + for pkg-desc = (car entry) + for status = (package-desc-status pkg-desc) + ;; A dependency. + when (string= status "dependency") + collect pkg-desc into dependencies + ;; An installed package used as dependency (user have + ;; installed this package explicitely). + when (package--used-elsewhere-p pkg-desc) + collect pkg-desc into installed-as-dep + ;; An installed package. + when (member status '("installed" "unsigned")) + collect pkg-desc into installed + when (member status '("available" "new")) + collect (cons (package-desc-name pkg-desc) pkg-desc) into available + finally return + ;; Always try to upgrade dependencies before installed. + (cl-loop with all = (append dependencies installed-as-dep installed) + for pkg in all + for name = (package-desc-name pkg) + for avail-pkg = (assq name available) + when (and avail-pkg + (version-list-< + (package-desc-version pkg) + (package-desc-version (cdr avail-pkg)))) + collect avail-pkg))) + +(defun helm-el-package--user-installed-p (package) + "Return non-nil if PACKAGE is a user-installed package." + (let* ((assoc (assq package package-alist)) + (pkg-desc (and assoc (cadr assoc))) + (dir (and pkg-desc (package-desc-dir pkg-desc)))) + (when dir + (file-in-directory-p dir package-user-dir)))) + +(defun helm-el-package-upgrade-1 (pkg-list) + (cl-loop for p in pkg-list + for pkg-desc = (car p) + for pkg-name = (package-desc-name pkg-desc) + for upgrade = (cdr (assq pkg-name + helm-el-package--upgrades)) + do + (cond (;; Install. + (equal pkg-desc upgrade) + (message "Installing package `%s'" pkg-name) + (package-install pkg-desc t)) + (;; Do nothing. + (or (null upgrade) + ;; This may happen when a Elpa version of pkg + ;; is installed and need upgrade and pkg is as + ;; well a builtin package. + (package-built-in-p pkg-name)) + (ignore)) + (;; Delete. + t + (message "Deleting package `%s'" pkg-name) + (package-delete pkg-desc t t))))) + +(defun helm-el-package-upgrade (_candidate) + (helm-el-package-upgrade-1 + (cl-loop with pkgs = (helm-marked-candidates) + for p in helm-el-package--tabulated-list + for pkg = (car p) + if (member (symbol-name (package-desc-name pkg)) pkgs) + collect p))) + +(defun helm-el-run-package-upgrade () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-el-package-upgrade))) +(put 'helm-el-run-package-upgrade 'helm-only t) + +(defun helm-el-package-upgrade-all () + (if helm-el-package--upgrades + (with-helm-display-marked-candidates + helm-marked-buffer-name (helm-fast-remove-dups + (mapcar (lambda (x) (symbol-name (car x))) + helm-el-package--upgrades) + :test 'equal) + (when (y-or-n-p "Upgrade all packages? ") + (helm-el-package-upgrade-1 helm-el-package--tabulated-list))) + (message "No packages to upgrade actually!"))) + +(defun helm-el-package-upgrade-all-action (_candidate) + (helm-el-package-upgrade-all)) + +(defun helm-el-run-package-upgrade-all () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-el-package-upgrade-all-action))) +(put 'helm-el-run-package-upgrade-all 'helm-only t) + +(defun helm-el-package--transformer (candidates _source) + (cl-loop for c in candidates + for disp = (concat " " c) + for id = (get-text-property 0 'tabulated-list-id c) + for name = (and id (package-desc-name id)) + for desc = (package-desc-status id) + for built-in-p = (and (package-built-in-p name) + (not (member desc '("available" "new" + "installed" "dependency")))) + for installed-p = (member desc '("installed" "dependency")) + for upgrade-p = (assq name helm-el-package--upgrades) + for user-installed-p = (memq name package-selected-packages) + do (when (and user-installed-p (not upgrade-p)) + (put-text-property 0 2 'display "S " disp)) + do (when (or (memq name helm-el-package--removable-packages) + (and upgrade-p installed-p)) + (put-text-property 0 2 'display "U " disp) + (put-text-property + 2 (+ (length (symbol-name name)) 2) + 'face 'font-lock-variable-name-face disp)) + do (when (and upgrade-p (not installed-p) (not built-in-p)) + (put-text-property 0 2 'display "I " disp)) + for cand = (cons disp (car (split-string disp))) + when (or (and built-in-p + (eq helm-el-package--show-only 'built-in)) + (and upgrade-p + (eq helm-el-package--show-only 'upgrade)) + (and installed-p + (eq helm-el-package--show-only 'installed)) + (and (not installed-p) + (not built-in-p) + (eq helm-el-package--show-only 'uninstalled)) + (eq helm-el-package--show-only 'all)) + collect cand)) + +(defun helm-el-package-show-built-in () + (interactive) + (with-helm-alive-p + (setq helm-el-package--show-only 'built-in) + (helm-update))) +(put 'helm-el-package-show-built-in 'helm-only t) + +(defun helm-el-package-show-upgrade () + (interactive) + (with-helm-alive-p + (setq helm-el-package--show-only 'upgrade) + (helm-update))) +(put 'helm-el-package-show-upgrade 'helm-only t) + +(defun helm-el-package-show-installed () + (interactive) + (with-helm-alive-p + (setq helm-el-package--show-only 'installed) + (helm-update))) +(put 'helm-el-package-show-installed 'helm-only t) + +(defun helm-el-package-show-all () + (interactive) + (with-helm-alive-p + (setq helm-el-package--show-only 'all) + (helm-update))) +(put 'helm-el-package-show-all 'helm-only t) + +(defun helm-el-package-show-uninstalled () + (interactive) + (with-helm-alive-p + (setq helm-el-package--show-only 'uninstalled) + (helm-update))) +(put 'helm-el-package-show-uninstalled 'helm-only t) + +(defvar helm-el-package-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-I") #'helm-el-package-show-installed) + (define-key map (kbd "M-O") #'helm-el-package-show-uninstalled) + (define-key map (kbd "M-U") #'helm-el-package-show-upgrade) + (define-key map (kbd "M-B") #'helm-el-package-show-built-in) + (define-key map (kbd "M-A") #'helm-el-package-show-all) + (define-key map (kbd "C-c i") #'helm-el-run-package-install) + (define-key map (kbd "C-c r") #'helm-el-run-package-reinstall) + (define-key map (kbd "C-c d") #'helm-el-run-package-uninstall) + (define-key map (kbd "C-c u") #'helm-el-run-package-upgrade) + (define-key map (kbd "C-c U") #'helm-el-run-package-upgrade-all) + (define-key map (kbd "C-c @") #'helm-el-run-visit-homepage) + map)) + +(defvar helm-source-list-el-package nil) +(defclass helm-list-el-package-source (helm-source-in-buffer) + ((init :initform 'helm-el-package--init) + (get-line :initform 'buffer-substring) + (filtered-candidate-transformer :initform 'helm-el-package--transformer) + (action-transformer :initform 'helm-el-package--action-transformer) + (help-message :initform 'helm-el-package-help-message) + (keymap :initform 'helm-el-package-map) + (update :initform 'helm-el-package--update) + (candidate-number-limit :initform 9999) + (action :initform '(("Describe package" . helm-el-package-describe) + ("Visit homepage" . helm-el-package-visit-homepage))) + (find-file-target :initform #'helm-el-package-quit-an-find-file-fn) + (group :initform 'helm-el-package))) + +(defun helm-el-package-quit-an-find-file-fn (source) + (let* ((sel (helm-get-selection nil nil source)) + (pkg (and (stringp sel) + (get-text-property 0 'tabulated-list-id sel)))) + (when (and pkg (package-installed-p pkg)) + (expand-file-name (package-desc-dir pkg))))) + +(defun helm-el-package--action-transformer (actions candidate) + (let* ((pkg-desc (get-text-property 0 'tabulated-list-id candidate)) + (status (package-desc-status pkg-desc)) + (pkg-name (package-desc-name pkg-desc)) + (built-in (and (package-built-in-p pkg-name) + (not (member status '("available" "new" + "installed" "dependency"))))) + (acts (if helm-el-package--upgrades + (append actions '(("Upgrade all packages" + . helm-el-package-upgrade-all-action))) + actions))) + (cond (built-in '(("Describe package" . helm-el-package-describe))) + ((and (package-installed-p pkg-name) + (cdr (assq pkg-name helm-el-package--upgrades)) + (member status '("installed" "dependency"))) + (append '(("Upgrade package(s)" . helm-el-package-upgrade) + ("Uninstall package(s)" . helm-el-package-uninstall)) + acts)) + ((and (package-installed-p pkg-name) + (cdr (assq pkg-name helm-el-package--upgrades)) + (string= status "available")) + (append '(("Upgrade package(s)" . helm-el-package-upgrade)) + acts)) + ((and (package-installed-p pkg-name) + (or (null (package-built-in-p pkg-name)) + (and (package-built-in-p pkg-name) + (assq pkg-name package-alist)))) + (append acts '(("Reinstall package(s)" . helm-el-package-reinstall) + ("Recompile package(s)" . helm-el-package-recompile) + ("Uninstall package(s)" . helm-el-package-uninstall)))) + (t (append acts '(("Install packages(s)" . helm-el-package-install))))))) + +(defun helm-el-package--update () + (setq helm-el-package--initialized-p nil)) + +(defun helm-el-package-recompile (_pkg) + (cl-loop for p in (helm-marked-candidates) + do (helm-el-package-recompile-1 p))) + +(defun helm-el-package-recompile-1 (pkg) + (let* ((pkg-desc (get-text-property 0 'tabulated-list-id pkg)) + (dir (package-desc-dir pkg-desc))) + (async-byte-recompile-directory dir))) + +(defun helm-el-package-reinstall (_pkg) + (cl-loop for p in (helm-marked-candidates) + for pkg-desc = (get-text-property 0 'tabulated-list-id p) + do (helm-el-package-reinstall-1 pkg-desc))) + +(defun helm-el-package-reinstall-1 (pkg-desc) + (let ((name (package-desc-name pkg-desc))) + (package-delete pkg-desc 'force 'nosave) + ;; pkg-desc contain the description + ;; of the installed package just removed + ;; and is BTW no more valid. + ;; Use the entry in package-archive-content + ;; which is the non--installed package entry. + ;; For some reason `package-install' + ;; need a pkg-desc (package-desc-p) for the build-in + ;; packages already installed, the name (as symbol) + ;; fails with such packages. + (package-install + (cadr (assq name package-archive-contents)) t))) + +(defun helm-el-run-package-reinstall () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-el-package-reinstall))) +(put 'helm-el-run-package-reinstall 'helm-only t) + +;;;###autoload +(defun helm-list-elisp-packages (arg) + "Preconfigured `helm' for listing and handling Emacs packages." + (interactive "P") + (when arg (setq helm-el-package--initialized-p nil)) + (unless helm-source-list-el-package + (setq helm-source-list-el-package + (helm-make-source "list packages" 'helm-list-el-package-source))) + (helm :sources 'helm-source-list-el-package + :truncate-lines helm-el-truncate-lines + :full-frame t + :buffer "*helm list packages*")) + +;;;###autoload +(defun helm-list-elisp-packages-no-fetch (arg) + "Preconfigured Helm for Emacs packages. + +Same as `helm-list-elisp-packages' but don't fetch packages on +remote. Called with a prefix ARG always fetch packages on +remote." + (interactive "P") + (let ((helm-el-package--initialized-p (null arg))) + (helm-list-elisp-packages nil))) + +(provide 'helm-elisp-package) + +;;; helm-elisp-package.el ends here diff --git a/code/elpa/helm-20220822.659/helm-elisp.el b/code/elpa/helm-20220822.659/helm-elisp.el new file mode 100644 index 0000000..7f0455c --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-elisp.el @@ -0,0 +1,1047 @@ +;;; helm-elisp.el --- Elisp symbols completion for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-lib) +(require 'helm-help) +(require 'helm-types) +(require 'helm-utils) +(require 'helm-info) +(require 'helm-eval) +(require 'helm-files) + +(declare-function helm-describe-function "helm-lib") +(declare-function helm-describe-variable "helm-lib") +(declare-function helm-describe-face "helm-lib") +(declare-function helm-read-file-name "helm-mode") +(declare-function helm-comp-read "helm-mode") +(declare-function helm-M-x-transformer-no-sort-no-props "helm-command") +(defvar helm-M-x-show-short-doc) + +;;; Customizable values + +(defgroup helm-elisp nil + "Elisp related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-turn-on-show-completion t + "Display candidate in `current-buffer' while moving selection when non--nil." + :group 'helm-elisp + :type 'boolean) + +(defcustom helm-show-completion-min-window-height 7 + "Minimum completion window height used in show completion. +This is used in macro `with-helm-show-completion'." + :group 'helm-elisp + :type 'integer) + +(defcustom helm-lisp-quoted-function-list + '(funcall apply mapc cl-mapc mapcar cl-mapcar + callf callf2 cl-callf cl-callf2 fset + fboundp fmakunbound symbol-function) + "List of function where quoted function completion happen. +E.g. give only function names after (funcall \\='." + :group 'helm-elisp + :type '(repeat (choice symbol))) + +(defcustom helm-lisp-unquoted-function-list + '(function defadvice) + "List of function where unquoted function completion happen. +E.g. give only function names after (function ." + :group 'helm-elisp + :type '(repeat (choice symbol))) + +(defcustom helm-apropos-fuzzy-match nil + "Enable fuzzy matching for `helm-apropos' when non-nil." + :group 'helm-elisp + :type 'boolean) + +(defcustom helm-lisp-fuzzy-completion nil + "Enable fuzzy matching in emacs-lisp completion when non-nil. +NOTE: This enables fuzzy matching in Helm native implementation of +elisp completion, but not on helmized elisp completion, i.e. fuzzy +completion is not available in `completion-at-point'." + :group 'helm-elisp + :type 'boolean) + +(defcustom helm-apropos-function-list '(helm-def-source--emacs-commands + helm-def-source--emacs-functions + helm-def-source--eieio-classes + helm-def-source--eieio-generic + helm-def-source--emacs-variables + helm-def-source--emacs-faces) + "A list of functions that build helm sources to use in `helm-apropos'." + :group 'helm-elisp + :type '(repeat (choice symbol))) + +(defcustom helm-apropos-defaut-info-lookup-sources '(helm-source-info-elisp + helm-source-info-cl + helm-source-info-eieio) + "A list of sources to look into when searching info page of a symbol." + :group 'helm-elisp + :type '(repeat (choice symbol))) + +(defcustom helm-show-completion-display-function + (if (display-graphic-p) + #'helm-display-buffer-in-own-frame + #'helm-show-completion-default-display-function) + "The function used to display helm completion buffer. + +This function is used by `with-helm-show-completion', when nil +fallback to `helm-default-display-buffer'. + +Default is to use a separate frame on graphic display and +`helm-show-completion-default-display-function' on non graphic +display." + :group 'helm-elisp + :type 'function) + +;;; Faces +;; +;; +(defgroup helm-elisp-faces nil + "Customize the appearance of helm-elisp." + :prefix "helm-" + :group 'helm-elisp + :group 'helm-faces) + +(defface helm-lisp-show-completion + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "DarkSlateGray")) + "Face used for showing candidates in `helm-lisp-completion'." + :group 'helm-elisp-faces) + +(defface helm-lisp-completion-info + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "red")) + "Face used for showing info in `helm-lisp-completion'." + :group 'helm-elisp-faces) + +(defcustom helm-elisp-help-function + 'helm-elisp-show-help + "Function for displaying help for Lisp symbols." + :group 'helm-elisp + :type '(choice (function :tag "Open help for the symbol." + helm-elisp-show-help) + (function :tag "Show one liner in modeline." + helm-elisp-show-doc-modeline))) + +(defcustom helm-locate-library-fuzzy-match t + "Enable fuzzy-matching in `helm-locate-library' when non--nil." + :type 'boolean + :group 'helm-elisp) + + +;;; Show completion. +;; +;; Provide show completion with macro `with-helm-show-completion'. + +(defvar helm-show-completion-overlay nil) + +;; Called each time cursor move in helm-buffer. +(defun helm-show-completion () + (with-helm-current-buffer + (overlay-put helm-show-completion-overlay + 'display (substring-no-properties + (helm-get-selection))))) + +(defun helm-show-completion-init-overlay (beg end) + (setq helm-show-completion-overlay (make-overlay beg end)) + (overlay-put helm-show-completion-overlay + 'face 'helm-lisp-show-completion)) + +(defun helm-show-completion-default-display-function (buffer &rest _args) + "A special resized Helm window is used depending on position in BUFFER." + (with-selected-window (selected-window) + (if (window-dedicated-p) + (helm-default-display-buffer buffer) + (let* ((screen-size (+ (count-screen-lines (window-start) (point) t) + 1 ; mode-line + (if header-line-format 1 0))) ; header-line + (def-size (- (window-height) + helm-show-completion-min-window-height)) + (upper-height (max window-min-height (min screen-size def-size))) + split-window-keep-point) + (recenter -1) + (set-window-buffer (if (active-minibuffer-window) + (minibuffer-selected-window) + (split-window nil upper-height + helm-split-window-default-side)) + buffer))))) + +(defmacro with-helm-show-completion (beg end &rest body) + "Show Helm candidate in an overlay at point. +BEG and END are the beginning and end position of the current +completion in `helm-current-buffer'. +BODY is an Helm call where we want to enable show completion. +If `helm-turn-on-show-completion' is nil do nothing." + (declare (indent 2) (debug t)) + `(unwind-protect + (if helm-turn-on-show-completion + (let ((helm-move-selection-after-hook + (append (list 'helm-show-completion) + helm-move-selection-after-hook)) + (helm-split-window-default-side + (if (eq helm-split-window-default-side 'same) + 'below helm-split-window-default-side)) + helm-split-window-inside-p + helm-reuse-last-window-split-state) + (helm-set-local-variable + 'helm-display-function + (or helm-show-completion-display-function + 'helm-default-display-buffer)) + (helm-show-completion-init-overlay ,beg ,end) + ,@body) + ,@body) + (when (and helm-show-completion-overlay + (overlayp helm-show-completion-overlay)) + (delete-overlay helm-show-completion-overlay)))) + + +;;; Lisp symbol completion. +;; +;; +(defun helm-lisp-completion--predicate-at-point (beg) + ;; Return a predicate for `all-completions'. + (let ((fn-sym-p (lambda () + (or + (and (eq (char-before) ?\ ) + (save-excursion + (skip-syntax-backward " " (point-at-bol)) + (memq (symbol-at-point) + helm-lisp-unquoted-function-list))) + (and (eq (char-before) ?\') + (save-excursion + (forward-char -1) + (eq (char-before) ?\#))))))) + (save-excursion + (goto-char beg) + (if (or + ;; Complete on all symbols in non--lisp modes (logs mail etc..) + (not (memq major-mode '(emacs-lisp-mode + lisp-interaction-mode + inferior-emacs-lisp-mode))) + (not (or (funcall fn-sym-p) + (and (eq (char-before) ?\') + (save-excursion + (forward-char (if (funcall fn-sym-p) -2 -1)) + (skip-syntax-backward " " (point-at-bol)) + (memq (symbol-at-point) + helm-lisp-quoted-function-list))) + (eq (char-before) ?\())) ; no paren before str. + ;; Looks like we are in a let statement. + (condition-case nil + (progn (up-list -2) (forward-char 1) + (eq (char-after) ?\()) + (error nil))) + (lambda (sym) + (or (boundp sym) (fboundp sym) (symbol-plist sym))) + #'fboundp)))) + +(defun helm-thing-before-point (&optional limits regexp) + "Return symbol name before point. +If REGEXP is specified return what REGEXP find before point. +By default match the beginning of symbol before point. +With LIMITS arg specified return the beginning and end position +of symbol before point." + (save-excursion + (let (beg + (end (point)) + (boundary (field-beginning nil nil (point-at-bol)))) + (if (re-search-backward (or regexp "\\_<") boundary t) + (setq beg (match-end 0)) + (setq beg boundary)) + (unless (= beg end) + (if limits + (cons beg end) + (buffer-substring-no-properties beg end)))))) + +(defun helm-bounds-of-thing-before-point (&optional regexp) + "Get the beginning and end position of `helm-thing-before-point'. +Return a cons (beg . end)." + (helm-thing-before-point 'limits regexp)) + +(defun helm-insert-completion-at-point (beg end str) + ;; When there is no space after point + ;; we are completing inside a symbol or + ;; after a partial symbol with the next arg aside + ;; without space, in this case mark the region. + ;; deleting it would remove the + ;; next arg which is unwanted. + (delete-region beg end) + (insert str) + (let ((pos (cdr (or (bounds-of-thing-at-point 'symbol) + ;; needed for helm-dabbrev. + (bounds-of-thing-at-point 'filename))))) + (when (and pos (< (point) pos)) + (push-mark pos t t)))) + +(defvar helm-lisp-completion--cache nil) +(defvar helm-lgst-len nil) +;;;###autoload +(defun helm-lisp-completion-at-point () + "Preconfigured Helm for Lisp symbol completion at point." + (interactive) + (setq helm-lgst-len 0) + (let* ((target (helm-thing-before-point)) + (beg (car (helm-bounds-of-thing-before-point))) + (end (point)) + (pred (and beg (helm-lisp-completion--predicate-at-point beg))) + (loc-vars (and (fboundp 'elisp--local-variables) + (ignore-errors + (mapcar #'symbol-name (elisp--local-variables))))) + (glob-syms (and target pred (all-completions target obarray pred))) + (candidates (append loc-vars glob-syms)) + (helm-quit-if-no-candidate t) + (helm-execute-action-at-once-if-one t) + (enable-recursive-minibuffers t)) + (setq helm-lisp-completion--cache (cl-loop for sym in candidates + for len = (length sym) + when (> len helm-lgst-len) + do (setq helm-lgst-len len) + collect sym)) + (if candidates + (with-helm-show-completion beg end + ;; Overlay is initialized now in helm-current-buffer. + (helm + :sources (helm-build-in-buffer-source "Lisp completion" + :data helm-lisp-completion--cache + :persistent-action `(helm-lisp-completion-persistent-action . + ,(and (eq helm-elisp-help-function + 'helm-elisp-show-doc-modeline) + 'never-split)) + :nomark t + :match-part (lambda (c) (car (split-string c))) + :fuzzy-match helm-lisp-fuzzy-completion + :persistent-help (helm-lisp-completion-persistent-help) + :filtered-candidate-transformer + #'helm-lisp-completion-transformer + :action (lambda (candidate) + (with-helm-current-buffer + (run-with-timer + 0.01 nil + #'helm-insert-completion-at-point + beg end candidate)))) + :input (if helm-lisp-fuzzy-completion + target (concat target " ")) + :resume 'noresume + :truncate-lines t + :buffer "*helm lisp completion*" + :allow-nest t)) + (message "[No Match]")))) + +(defun helm-lisp-completion-persistent-action (candidate &optional name) + "Show documentation for the function. +Documentation is shown briefly in mode-line or completely in +other window according to the value of +`helm-elisp-help-function'." + (funcall helm-elisp-help-function candidate name)) + +(defun helm-lisp-completion-persistent-help () + "Return persistent-help according to the value of `helm-elisp-help-function'" + (cl-ecase helm-elisp-help-function + (helm-elisp-show-doc-modeline "Show brief doc in mode-line") + (helm-elisp-show-help "Toggle show help for the symbol"))) + +(defun helm-elisp--show-help-1 (candidate &optional name) + (let ((sym (intern-soft candidate))) + (pcase sym + ((and (pred fboundp) (pred boundp)) + (if (member name `(,helm-describe-function-function ,helm-describe-variable-function)) + (funcall (intern (format "helm-%s" name)) sym) + ;; When there is no way to know what to describe + ;; prefer describe-function. + (helm-describe-function sym))) + ((pred fboundp) (helm-describe-function sym)) + ((pred boundp) (helm-describe-variable sym)) + ((pred facep) (helm-describe-face sym))))) + +(defun helm-elisp-show-help (candidate &optional name) + "Show full help for the function CANDIDATE. +Arg NAME specifies the name of the top level function calling +Helm generic completion (e.g., \"describe-function\") which +allows calling the right function when CANDIDATE symbol refers at +the same time to variable and a function." + (helm-elisp--persistent-help + candidate 'helm-elisp--show-help-1 name)) + +(defun helm-elisp-show-doc-modeline (candidate &optional name) + "Show brief documentation for the function in the mode-line." + (let ((cursor-in-echo-area t) + mode-line-in-non-selected-windows) + (helm-show-info-in-mode-line + (propertize + (helm-get-first-line-documentation + (intern candidate) name) + 'face 'helm-lisp-completion-info)))) + +(defun helm-lisp-completion-transformer (candidates _source) + "Helm candidates transformer for Lisp completion." + (cl-loop for c in candidates + for sym = (intern c) + for annot = (pcase sym + ((pred commandp) " (Com)") + ((pred class-p) " (Class)") + ((pred cl-generic-p) " (Gen)") + ((pred fboundp) " (Fun)") + ((pred boundp) " (Var)") + ((pred facep) " (Face)")) + for spaces = (make-string (- helm-lgst-len (length c)) ? ) + collect (cons (concat c spaces annot) c) into lst + finally return (sort lst #'helm-generic-sort-fn))) + +(cl-defun helm-get-first-line-documentation (sym &optional + (name "describe-function") + (end-column 72)) + "Return first line documentation of symbol SYM truncated at END-COLUMN. +If SYM is not documented, return \"Not documented\". +Argument NAME allows specifiying what function to use to display +documentation when SYM name is the same for function and variable." + (let ((doc (condition-case _err + (pcase sym + ((and (pred fboundp) (pred boundp)) + (pcase name + ("describe-function" + (documentation sym t)) + ("describe-variable" + (documentation-property sym 'variable-documentation t)) + (_ (documentation sym t)))) + ((pred fboundp) (documentation sym t)) + ((pred boundp) (documentation-property + sym 'variable-documentation t)) + ((pred facep) (face-documentation sym))) + (void-function "Void function -- Not documented")))) + (if (and doc (not (string= doc "")) + ;; `documentation' return "\n\n(args...)" + ;; for CL-style functions. + (not (string-match-p "^\n\n" doc))) + ;; Some commands specify key bindings in their first line. + (truncate-string-to-width + (substitute-command-keys (car (split-string doc "\n"))) + end-column nil nil t) + "Not documented"))) + +;;; File completion. +;; +;; Complete file name at point. + +;;;###autoload +(defun helm-complete-file-name-at-point (&optional force) + "Preconfigured Helm to complete file name at point." + (interactive) + (require 'helm-mode) + (let* ((tap (or (thing-at-point 'filename) "")) + beg + (init (and tap + (or force + (save-excursion + (end-of-line) + (search-backward tap (point-at-bol) t) + (setq beg (point)) + (looking-back "[^'`( ]" (1- (point))))) + (expand-file-name + (substring-no-properties tap)))) + (end (point)) + (helm-quit-if-no-candidate t) + (helm-execute-action-at-once-if-one t) + completion) + (with-helm-show-completion beg end + (setq completion (helm-read-file-name "FileName: " + :initial-input init))) + (when (and completion (not (string= completion ""))) + (delete-region beg end) (insert (if (string-match "^~" tap) + (abbreviate-file-name completion) + completion))))) + +;;;###autoload +(defun helm-lisp-indent () + ;; It is meant to use with `helm-define-multi-key' which + ;; does not support args for functions yet, so use `current-prefix-arg' + ;; for now instead of (interactive "P"). + (interactive) + (let ((tab-always-indent (or (eq tab-always-indent 'complete) + tab-always-indent))) + (indent-for-tab-command current-prefix-arg))) + +;;;###autoload +(defun helm-lisp-completion-or-file-name-at-point () + "Preconfigured Helm to complete Lisp symbol or filename at point. +Filename completion happens if string start after or between a +double quote." + (interactive) + (let* ((tap (thing-at-point 'filename))) + (if (and tap (save-excursion + (end-of-line) + (search-backward tap (point-at-bol) t) + (looking-back "[^'`( ]" (1- (point))))) + (helm-complete-file-name-at-point) + (helm-lisp-completion-at-point)))) + + +;;; Apropos +;; +;; +(defvar helm-apropos-history nil) + +(defcustom helm-apropos-show-short-doc nil + "Show short docstring of symbols when non nil. + +NOTE: When displaying helm-apropos in a frame, i.e. when +`helm-apropos' is member of `helm-commands-using-frame' setting this +to non nil have no effect, you have first to remove `helm-apropos' +from `helm-commands-using-frame'." + :group 'helm-elisp + :type 'boolean) + +(defvar helm-apropos-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-]") #'helm-apropos-toggle-details) + map)) + +(defun helm-apropos-init (test default &optional fn) + "Setup `helm-candidate-buffer' for `helm-apropos' sources. +A list of symbols fetched with FN is inserted in +`helm-candidate-buffer', if FN is not provided symbols are fetched +against obarray with predicate TEST. When FN is provided predicate TEST +is only used to test DEFAULT." + (require 'helm-help) + (helm-init-candidates-in-buffer 'global + (let ((default-symbol (and (stringp default) + (intern-soft default))) + (symbols (if fn (funcall fn) (all-completions "" obarray test)))) + (if (and default-symbol (funcall test default-symbol)) + (cons default-symbol symbols) + symbols)))) + +(defun helm-apropos-short-doc-transformer (candidates _source) + (if helm-apropos-show-short-doc + (cl-loop with max-len = (buffer-local-value 'helm-candidate-buffer-longest-len + (get-buffer (helm-candidate-buffer))) + for cand in candidates + for doc = (helm-get-first-line-documentation (intern-soft cand)) + collect (cons (format "%s%s%s" + cand + (if doc + (make-string (+ 1 (if (zerop max-len) + max-len + (- max-len (string-width cand)))) + ? ) + "") + (if doc (propertize doc 'face 'helm-M-x-short-doc) "")) + cand)) + candidates)) + +(defun helm-apropos-default-sort-fn (candidates _source) + (if (string= helm-pattern "") + candidates + (sort candidates #'helm-generic-sort-fn))) + +(defun helm-apropos-clean-history-variable (candidate) + (with-helm-current-buffer ; var is maybe local + (let* ((sym (intern-soft candidate)) + (cands (symbol-value sym)) + (mkds (and (listp cands) + (helm-comp-read "Delete entry: " + cands :marked-candidates t)))) + (cl-assert (listp mkds) nil "Variable value is not a list") + (cl-loop for elm in mkds do + (if (local-variable-p sym) + (set (make-local-variable sym) + (setq cands (delete elm cands))) + (set sym (setq cands (delete elm cands)))))))) + +(defun helm-apropos-clean-ring (candidate) + (with-helm-current-buffer ; var is maybe local + (let* ((sym (intern-soft candidate)) + (val (symbol-value sym)) + (cands (and (ring-p val) (ring-elements val))) + (mkds (and cands (helm-comp-read + "Delete entry: " + cands :marked-candidates t)))) + (when mkds + (cl-loop for elm in mkds do + (ring-remove + val (helm-position + elm + (ring-elements val) + :test 'equal)) + and do (if (local-variable-p sym) + (set (make-local-variable sym) val) + (set sym val))))))) + +(defun helm-apropos-action-transformer (actions candidate) + (let* ((sym (helm-symbolify candidate)) + (val (with-helm-current-buffer (symbol-value sym)))) + (cond ((custom-variable-p sym) + (append + actions + (let ((standard-value (eval (car (get sym 'standard-value)) t))) + (unless (equal standard-value (symbol-value sym)) + `(("Reset Variable to default value" + . ,(lambda (candidate) + (let ((sym (helm-symbolify candidate))) + (set sym standard-value))))))) + '(("Customize variable" . + (lambda (candidate) + (customize-option (helm-symbolify candidate))))))) + ((and val (with-helm-current-buffer (ring-p (symbol-value sym)))) + (append actions + '(("Clean ring" . helm-apropos-clean-ring)))) + ((and (string-match-p "history" candidate) (listp val)) + (append actions + '(("Clean variable" . + helm-apropos-clean-history-variable)))) + (t actions)))) + +(defun helm-def-source--emacs-variables (&optional default) + (helm-build-in-buffer-source "Variables" + :init (lambda () + (helm-apropos-init + (lambda (x) (and (boundp x) (not (keywordp x)))) default)) + :fuzzy-match helm-apropos-fuzzy-match + :filtered-candidate-transformer + (delq nil (list (and (null helm-apropos-fuzzy-match) + 'helm-apropos-default-sort-fn) + (and (null (memq 'helm-apropos helm-commands-using-frame)) + #'helm-apropos-short-doc-transformer))) + :nomark t + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + candidate 'helm-describe-variable)) + :persistent-help "Toggle describe variable" + :keymap helm-apropos-map + :action '(("Describe variable" . helm-describe-variable) + ("Find variable" . helm-find-variable) + ("Info lookup" . helm-info-lookup-symbol) + ("Set variable" . helm-set-variable)) + :action-transformer 'helm-apropos-action-transformer)) + +(defun helm-def-source--emacs-faces (&optional default) + "Create `helm' source for faces to be displayed with +`helm-apropos'." + (helm-build-in-buffer-source "Faces" + :init (lambda () (helm-apropos-init 'facep default #'face-list)) + :fuzzy-match helm-apropos-fuzzy-match + :filtered-candidate-transformer + (delq nil (list + (and (null helm-apropos-fuzzy-match) + #'helm-apropos-default-sort-fn) + (lambda (candidates _source) + (cl-loop for c in candidates + collect (propertize c 'face (intern c)))) + (and (null (memq 'helm-apropos helm-commands-using-frame)) + #'helm-apropos-short-doc-transformer))) + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + candidate 'helm-describe-face)) + :persistent-help "Toggle describe face" + :keymap helm-apropos-map + :action '(("Describe face" . helm-describe-face) + ("Find face" . helm-find-face-definition) + ("Customize face" . (lambda (candidate) + (customize-face (helm-symbolify candidate))))))) + +(defun helm-def-source--emacs-commands (&optional default) + (require 'helm-command) + (helm-build-in-buffer-source "Commands" + :init (lambda () + (helm-apropos-init 'commandp default)) + :fuzzy-match helm-apropos-fuzzy-match + :filtered-candidate-transformer + (append (list #'helm-M-x-transformer-no-sort-no-props) + (and (null helm-apropos-fuzzy-match) + '(helm-apropos-default-sort-fn))) + :display-to-real 'helm-symbolify + :nomark t + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + candidate 'helm-describe-function)) + :persistent-help "Toggle describe command" + :keymap helm-apropos-map + :action 'helm-type-function-actions)) + +(defun helm-def-source--emacs-functions (&optional default) + (helm-build-in-buffer-source "Functions" + :init (lambda () + (helm-apropos-init (lambda (x) + (and (fboundp x) + (not (commandp x)) + (not (cl-generic-p x)) + (not (class-p x)))) + default)) + :fuzzy-match helm-apropos-fuzzy-match + :filtered-candidate-transformer + (delq nil (list (and (null helm-apropos-fuzzy-match) + 'helm-apropos-default-sort-fn) + (and (null (memq 'helm-apropos helm-commands-using-frame)) + #'helm-apropos-short-doc-transformer))) + :display-to-real 'helm-symbolify + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + candidate 'helm-describe-function)) + :persistent-help "Toggle describe function" + :keymap helm-apropos-map + :nomark t + :action 'helm-type-function-actions)) + +(defun helm-def-source--eieio-classes (&optional default) + (helm-build-in-buffer-source "Classes" + :init (lambda () + (helm-apropos-init (lambda (x) + (class-p x)) + default)) + :fuzzy-match helm-apropos-fuzzy-match + :filtered-candidate-transformer + (delq nil (list (and (null helm-apropos-fuzzy-match) + 'helm-apropos-default-sort-fn) + (and (null (memq 'helm-apropos helm-commands-using-frame)) + #'helm-apropos-short-doc-transformer))) + :nomark t + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + candidate 'helm-describe-class)) + :persistent-help "Toggle describe class" + :keymap helm-apropos-map + :action '(("Describe Class" . helm-describe-class) + ("Find Class" . helm-find-function) + ("Info lookup" . helm-info-lookup-symbol)))) + +(defun helm-def-source--eieio-generic (&optional default) + (helm-build-in-buffer-source "Generic functions" + :init (lambda () + (helm-apropos-init (lambda (x) + (cl-generic-p x)) + default)) + :fuzzy-match helm-apropos-fuzzy-match + :filtered-candidate-transformer + (delq nil (list (and (null helm-apropos-fuzzy-match) + 'helm-apropos-default-sort-fn) + (and (null (memq 'helm-apropos helm-commands-using-frame)) + #'helm-apropos-short-doc-transformer))) + :nomark t + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + candidate 'helm-describe-function)) + :persistent-help "Toggle describe generic function" + :keymap helm-apropos-map + :action '(("Describe function" . helm-describe-function) + ("Find function" . helm-find-function) + ("Info lookup" . helm-info-lookup-symbol)))) + +(defun helm-info-lookup-fallback-source (candidate) + (let ((sym (helm-symbolify candidate)) + src-name fn) + (cond ((class-p sym) + (setq fn #'helm-describe-function + src-name "Describe class")) + ((cl-generic-p sym) + (setq fn #'helm-describe-function + src-name "Describe generic function")) + ((fboundp sym) + (setq fn #'helm-describe-function + src-name "Describe function")) + ((facep sym) + (setq fn #'helm-describe-face + src-name "Describe face")) + (t + (setq fn #'helm-describe-variable + src-name "Describe variable"))) + (helm-build-sync-source src-name + :candidates (list candidate) + :persistent-action (lambda (candidate) + (helm-elisp--persistent-help + candidate fn)) + :persistent-help src-name + :nomark t + :action fn))) + +(defun helm-info-lookup-symbol-1 (c) + (let ((helm-execute-action-at-once-if-one 'current-source)) + (helm :sources (append helm-apropos-defaut-info-lookup-sources + (list (helm-info-lookup-fallback-source c))) + :resume 'noresume + :buffer "*helm lookup*" + :input (helm-stringify c)))) + +(defun helm-info-lookup-symbol (candidate) + ;; ???:Running an idle-timer allows not catching RET when exiting + ;; with the fallback source. + ;; (run-with-idle-timer 0.01 nil #'helm-info-lookup-symbol-1 candidate) + (helm-info-lookup-symbol-1 candidate)) + +(defun helm-apropos-toggle-details () + "Toggle details in `helm-apropos'." + (interactive) + (with-helm-buffer + (unless (memq 'helm-apropos helm-commands-using-frame) + (setq helm-M-x-show-short-doc (not helm-M-x-show-short-doc) + helm-apropos-show-short-doc (not helm-apropos-show-short-doc)) + (helm-force-update (concat "^" (helm-stringify (helm-get-selection))) + (helm-get-current-source))))) + +;;;###autoload +(defun helm-apropos (default) + "Preconfigured Helm to describe commands, functions, variables and faces. +In non interactives calls DEFAULT argument should be provided as +a string, i.e. the `symbol-name' of any existing symbol." + (interactive (list (with-syntax-table emacs-lisp-mode-syntax-table + (thing-at-point 'symbol)))) + (let ((helm-M-x-show-short-doc + (and helm-apropos-show-short-doc + (null (memq 'helm-apropos helm-commands-using-frame))))) + (helm :sources + (mapcar (lambda (func) + (funcall func default)) + helm-apropos-function-list) + :history 'helm-apropos-history + :buffer "*helm apropos*" + :preselect (and default (concat "^\\_<" (regexp-quote default) "\\_>")) + :truncate-lines t))) + + +;;; Advices +;; +;; +(defvar ad-advised-functions) +(defvar ad-advice-classes) +(declare-function ad-make-single-advice-docstring "advice") +(declare-function ad-get-advice-info-field "advice") +(declare-function ad-advice-set-enabled "advice") +(declare-function ad-advice-set-enabled "advice") +(declare-function ad-advice-enabled "advice") + +(defvar helm-source-advice + (helm-build-sync-source "Function Advice" + :init (lambda () (require 'advice)) + :candidates 'helm-advice-candidates + :action (helm-make-actions "Toggle Enable/Disable" 'helm-advice-toggle) + :persistent-action 'helm-advice-persistent-action + :nomark t + :multiline t + :persistent-help "Toggle describe function / C-u C-j: Toggle advice")) + +(defun helm-advice-candidates () + (cl-loop for (fname) in ad-advised-functions + for function = (intern fname) + append + (cl-loop for class in ad-advice-classes append + (cl-loop for advice in (ad-get-advice-info-field function class) + for enabled = (ad-advice-enabled advice) + collect + (cons (format + "%s %s %s" + (if enabled "Enabled " "Disabled") + (propertize fname 'face 'font-lock-function-name-face) + (ad-make-single-advice-docstring advice class nil)) + (list function class advice)))))) + +(defun helm-advice-persistent-action (func-class-advice) + (if current-prefix-arg + (helm-advice-toggle func-class-advice) + (describe-function (car func-class-advice)))) + +(defun helm-advice-toggle (func-class-advice) + (cl-destructuring-bind (function _class advice) func-class-advice + (cond ((ad-advice-enabled advice) + (ad-advice-set-enabled advice nil) + (message "Disabled")) + (t + (ad-advice-set-enabled advice t) + (message "Enabled"))) + (ad-activate function) + (and helm-in-persistent-action + (helm-advice-update-current-display-string)))) + +(defun helm-advice-update-current-display-string () + (helm-edit-current-selection + (let ((newword (cond ((looking-at "Disabled") "Enabled") + ((looking-at "Enabled") "Disabled")))) + (when newword + (delete-region (point) (progn (forward-word 1) (point))) + (insert newword))))) + +;;;###autoload +(defun helm-manage-advice () + "Preconfigured `helm' to disable/enable function advices." + (interactive) + (helm-other-buffer 'helm-source-advice "*helm advice*")) + + +;;; Locate elisp library +;; +;; +(defun helm-locate-library-scan-list () + (cl-loop for dir in load-path + with load-suffixes = '(".el") + when (file-directory-p dir) + append (directory-files + dir t (concat (regexp-opt (get-load-suffixes)) + "\\'")))) + +;;;###autoload +(defun helm-locate-library () + "Preconfigured helm to locate elisp libraries." + (interactive) + (helm :sources (helm-build-in-buffer-source "Elisp libraries (Scan)" + :data #'helm-locate-library-scan-list + :fuzzy-match helm-locate-library-fuzzy-match + :keymap helm-generic-files-map + :search (unless helm-locate-library-fuzzy-match + (lambda (regexp) + (re-search-forward + (if helm-ff-transformer-show-only-basename + (replace-regexp-in-string + "\\`\\^" "" regexp) + regexp) + nil t))) + :match-part (lambda (candidate) + (with-helm-buffer + (if helm-ff-transformer-show-only-basename + (helm-basename candidate) candidate))) + :filter-one-by-one (lambda (c) + (with-helm-buffer + (if helm-ff-transformer-show-only-basename + (cons (helm-basename c) c) c))) + :action (helm-actions-from-type-file)) + :ff-transformer-show-only-basename nil + :buffer "*helm locate library*")) + +(defun helm-set-variable (var) + "Set VAR value interactively." + (let* ((sym (helm-symbolify var)) + (val (default-value sym))) + (set-default sym (eval-minibuffer + (format "Set `%s': " var) + (if (or (stringp val) + (memq val '(nil t)) + (numberp val)) + (prin1-to-string val) + (format "'%s" (prin1-to-string val))))))) + + +;;; Elisp Timers. +;; +;; +(defclass helm-absolute-time-timers-class (helm-source-sync helm-type-timers) + ((candidates :initform 'timer-list) + (allow-dups :initform t) + (candidate-transformer + :initform + (lambda (candidates) + (cl-loop for timer in candidates + collect (cons (helm-elisp--format-timer timer) timer)))))) + +(defvar helm-source-absolute-time-timers + (helm-make-source "Absolute Time Timers" 'helm-absolute-time-timers-class)) + +(defclass helm-idle-time-timers-class (helm-source-sync helm-type-timers) + ((candidates :initform 'timer-idle-list) + (allow-dups :initform t) + (candidate-transformer + :initform + (lambda (candidates) + (cl-loop for timer in candidates + collect (cons (helm-elisp--format-timer timer) timer)))))) + +(defvar helm-source-idle-time-timers + (helm-make-source "Idle Time Timers" 'helm-idle-time-timers-class)) + +(defun helm-elisp--format-timer (timer) + (format "%s repeat=%s %s(%s)" + (let ((time (timer--time timer))) + (if (timer--idle-delay timer) + (format "idle-for=[%s]" + (format-seconds "%dd %hh %mmin %z%,3ss" + (time-convert time t))) + (format-time-string "%m/%d %T" time))) + (or (timer--repeat-delay timer) "nil") + (mapconcat #'identity (split-string + (prin1-to-string (timer--function timer)) + "\n") + " ") + (mapconcat #'prin1-to-string (timer--args timer) " "))) + +;;;###autoload +(defun helm-timers () + "Preconfigured `helm' for timers." + (interactive) + (helm :sources '(helm-source-absolute-time-timers + helm-source-idle-time-timers) + :buffer "*helm timers*")) + + +;;; Complex command history +;; +;; + +(defvar helm-sexp--last-sexp nil) +;; This wont work compiled. +(defun helm-sexp-eval-1 () + (interactive) + (unwind-protect + (progn + ;; Trick called-interactively-p into thinking that `cand' is + ;; an interactive call, See `repeat-complex-command'. + (add-hook 'called-interactively-p-functions + #'helm-complex-command-history--called-interactively-skip) + (eval (read helm-sexp--last-sexp) t)) + (remove-hook 'called-interactively-p-functions + #'helm-complex-command-history--called-interactively-skip))) + +(defun helm-complex-command-history--called-interactively-skip (i _frame1 frame2) + (and (eq 'eval (cadr frame2)) + (eq 'helm-sexp-eval-1 + (cadr (backtrace-frame (+ i 2) #'called-interactively-p))) + 1)) + +(defun helm-sexp-eval (_candidate) + (call-interactively #'helm-sexp-eval-1)) + +(defvar helm-source-complex-command-history + (helm-build-sync-source "Complex Command History" + :candidates (lambda () + ;; Use cdr to avoid adding + ;; `helm-complex-command-history' here. + (cl-loop for i in command-history + unless (equal i '(helm-complex-command-history)) + collect (prin1-to-string i))) + :action (helm-make-actions + "Eval" (lambda (candidate) + (and (boundp 'helm-sexp--last-sexp) + (setq helm-sexp--last-sexp candidate)) + (let ((command (read candidate))) + (unless (equal command (car command-history)) + (setq command-history (cons command command-history)))) + (run-with-timer 0.1 nil #'helm-sexp-eval candidate)) + "Edit and eval" (lambda (candidate) + (edit-and-eval-command "Eval: " (read candidate)))) + :persistent-action #'helm-sexp-eval + :multiline t)) + +;;;###autoload +(defun helm-complex-command-history () + "Preconfigured `helm' for complex command history." + (interactive) + (helm :sources 'helm-source-complex-command-history + :buffer "*helm complex commands*")) + +(provide 'helm-elisp) + +;;; helm-elisp.el ends here diff --git a/code/elpa/helm-20220822.659/helm-epa.el b/code/elpa/helm-20220822.659/helm-epa.el new file mode 100644 index 0000000..bc3968a --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-epa.el @@ -0,0 +1,254 @@ +;;; helm-epa.el --- helm interface for epa/epg -*- lexical-binding: t; -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + + +;;; Code: + +(require 'helm) + +(eval-when-compile (require 'epg)) +(defvar epa-protocol) +(defvar epa-last-coding-system-specified) +(defvar epg-key-validity-alist) +(defvar mail-header-separator) +(declare-function epg-list-keys "epg") +(declare-function epg-make-context "epg") +(declare-function epg-key-sub-key-list "epg") +(declare-function epg-sub-key-id "epg") +(declare-function epg-key-user-id-list "epg") +(declare-function epg-user-id-string "epg") +(declare-function epg-user-id-validity "epg") +(declare-function epa-sign-region "epa") +(declare-function epa--read-signature-type "epa") +(declare-function epa-display-error "epa") +(declare-function epg-export-keys-to-string "epg") +(declare-function epg-context-armor "epg") +(declare-function epg-context-set-armor "epg") +(declare-function epg-delete-keys "epg") +(declare-function helm-read-file-name "helm-mode") + +(defvar helm-epa--list-only-secrets nil + "[INTERNAL] Used to pass MODE argument to `epg-list-keys'.") + +(defcustom helm-epa-actions '(("Show key" . epa--show-key) + ("encrypt file with key" . helm-epa-encrypt-file) + ("Copy keys to kill ring" . helm-epa-kill-keys-armor) + ("Delete keys" . helm-epa-delete-keys)) + "Actions for `helm-epa-list-keys'." + :type '(alist :key-type string :value-type symbol) + :group 'helm-misc) + +(defclass helm-epa (helm-source-sync) + ((init :initform (lambda () + (require 'epg) + (require 'epa))) + (candidates :initform 'helm-epa-get-key-list) + (keymap :initform 'helm-comp-read-map) + (mode-line :initform 'helm-comp-read-mode-line)) + "Allow building helm sources for GPG keys.") + +(defun helm-epa-get-key-list (&optional keys) + "Build candidate list for `helm-epa-list-keys'." + (cl-loop with all-keys = (or keys (epg-list-keys (epg-make-context epa-protocol) + nil helm-epa--list-only-secrets)) + for key in all-keys + for sublist = (car (epg-key-sub-key-list key)) + for subkey-id = (epg-sub-key-id sublist) + for uid-list = (epg-key-user-id-list key) + for uid = (epg-user-id-string (car uid-list)) + for validity = (epg-user-id-validity (car uid-list)) + collect (cons (format " %s %s %s" + (helm-aif (rassq validity epg-key-validity-alist) + (string (car it)) + "?") + (propertize + subkey-id + 'face (cl-case validity + (none 'epa-validity-medium) + ((revoked expired) + 'epa-validity-disabled) + (t 'epa-validity-high))) + (propertize + uid 'face 'font-lock-warning-face)) + key))) + +(defun helm-epa--select-keys (prompt keys) + "A helm replacement for `epa--select-keys'." + (let ((result (helm :sources (helm-make-source "Epa select keys" 'helm-epa + :candidates (lambda () + (helm-epa-get-key-list keys))) + :prompt (and prompt (helm-epa--format-prompt prompt)) + :buffer "*helm epa*"))) + (unless (equal result "") + result))) + +(defun helm-epa--format-prompt (prompt) + (let ((split (split-string prompt "\n"))) + (if (cdr split) + (format "%s\n(%s): " + (replace-regexp-in-string "\\.[\t ]*\\'" "" (car split)) + (replace-regexp-in-string "\\.[\t ]*\\'" "" (cadr split))) + (format "%s: " (replace-regexp-in-string "\\.[\t ]*\\'" "" (car split)))))) + +(defun helm-epa--read-signature-type () + "A helm replacement for `epa--read-signature-type'." + (let ((answer (helm-read-answer "Signature type: +(n - Create a normal signature) +(c - Create a cleartext signature) +(d - Create a detached signature)" + '("n" "c" "d")))) + (helm-acase answer + ("n" 'normal) + ("c" 'clear) + ("d" 'detached)))) + +(defun helm-epa-collect-keys-from-candidates (candidates) + (cl-loop for c in candidates + collect (epg-sub-key-id + (car (epg-key-sub-key-list c))))) + +(defun helm-epa-collect-id-from-candidates (candidates) + (cl-loop for c in candidates + collect (epg-user-id-string + (car (epg-key-user-id-list c))))) + +(defun helm-epa-success-message (str keys ids) + (message str + (mapconcat (lambda (pair) + (concat (car pair) " " (cdr pair))) + (cl-loop for k in keys + for i in ids + collect (cons k i)) + "\n"))) + +;;;###autoload +(define-minor-mode helm-epa-mode + "Enable helm completion on gpg keys in epa functions." + :group 'helm-misc + :global t + (require 'epa) + (if helm-epa-mode + (progn + (advice-add 'epa--select-keys :override #'helm-epa--select-keys) + (advice-add 'epa--read-signature-type :override #'helm-epa--read-signature-type)) + (advice-remove 'epa-select-keys #'helm-epa--select-keys) + (advice-remove 'epa--read-signature-type #'helm-epa--read-signature-type))) + +(defun helm-epa-action-transformer (actions _candidate) + "Helm epa action transformer function." + (cond ((with-helm-current-buffer + (derived-mode-p 'message-mode 'mail-mode)) + (helm-append-at-nth + actions '(("Sign mail with key" . helm-epa-mail-sign) + ("Encrypt mail with key" . helm-epa-mail-encrypt)) + 3)) + (t actions))) + +(defun helm-epa-delete-keys (_candidate) + "Delete gpg marked keys from helm-epa." + (let ((context (epg-make-context epa-protocol)) + (keys (helm-marked-candidates))) + (message "Deleting gpg keys..") + (condition-case error + (epg-delete-keys context keys) + (error + (epa-display-error context) + (signal (car error) (cdr error)))) + (message "Deleting gpg keys done"))) + +(defun helm-epa-encrypt-file (_candidate) + "Select a file to encrypt with key CANDIDATE." + (let* ((file (helm-read-file-name "Encrypt file: ")) + (cands (helm-marked-candidates)) + (keys (helm-epa-collect-keys-from-candidates cands)) + (ids (helm-epa-collect-id-from-candidates cands))) + (epa-encrypt-file file cands) + (helm-epa-success-message "File encrypted with key(s):\n %s" + keys ids))) + +(defun helm-epa-kill-keys-armor (_candidate) + "Copy marked keys to kill ring." + (let ((keys (helm-marked-candidates)) + (context (epg-make-context epa-protocol))) + (with-no-warnings + (setf (epg-context-armor context) t)) + (condition-case error + (kill-new (epg-export-keys-to-string context keys)) + (error + (epa-display-error context) + (signal (car error) (cdr error)))))) + +(defun helm-epa-mail-sign (candidate) + "Sign email with key CANDIDATE." + (let ((key (epg-sub-key-id (car (epg-key-sub-key-list candidate)))) + (id (epg-user-id-string (car (epg-key-user-id-list candidate)))) + start end mode) + (save-excursion + (goto-char (point-min)) + (if (search-forward mail-header-separator nil t) + (forward-line)) + (setq epa-last-coding-system-specified + (or coding-system-for-write + (select-safe-coding-system (point) (point-max)))) + (let ((verbose current-prefix-arg)) + (setq start (point) + end (point-max) + mode (if verbose + (epa--read-signature-type) + 'clear)))) + ;; TODO Make non-interactive functions to replace epa-sign-region + ;; and epa-encrypt-region and inline them. + (with-no-warnings + (epa-sign-region start end candidate mode)) + (message "Mail signed with key `%s %s'" key id))) + +(defun helm-epa-mail-encrypt (_candidate) + "Encrypt email with key CANDIDATE." + (let ((cands (helm-marked-candidates)) + start end) + (save-excursion + (goto-char (point-min)) + (when (search-forward mail-header-separator nil t) + (forward-line)) + (setq start (point) + end (point-max)) + (setq epa-last-coding-system-specified + (or coding-system-for-write + (select-safe-coding-system start end)))) + ;; Don't let some read-only text stop us from encrypting. + (let ((inhibit-read-only t) + (keys (helm-epa-collect-keys-from-candidates cands)) + (ids (helm-epa-collect-id-from-candidates cands))) + (with-no-warnings + (epa-encrypt-region start end cands nil nil)) + (helm-epa-success-message "Mail encrypted with key(s):\n %s" + keys ids)))) + +;;;###autoload +(defun helm-epa-list-keys () + "List all gpg keys. +This is the helm interface for `epa-list-keys'." + (interactive) + (helm :sources + (helm-make-source "Epg list keys" 'helm-epa + :action-transformer 'helm-epa-action-transformer + :action 'helm-epa-actions) + :buffer "*helm epg list keys*")) + +(provide 'helm-epa) + +;;; helm-epa.el ends here diff --git a/code/elpa/helm-20220822.659/helm-eshell.el b/code/elpa/helm-20220822.659/helm-eshell.el new file mode 100644 index 0000000..c41b191 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-eshell.el @@ -0,0 +1,498 @@ +;;; helm-eshell.el --- pcomplete and eshell completion for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Commentary: +;; +;; Enable like this in .emacs: +;; (add-hook 'eshell-mode-hook +;; (lambda () +;; (eshell-cmpl-initialize) +;; (define-key eshell-mode-map [remap eshell-pcomplete] 'helm-esh-pcomplete) +;; (define-key eshell-mode-map (kbd "M-s f") 'helm-eshell-prompts-all))) +;; (define-key eshell-mode-map (kbd "M-r") 'helm-eshell-history))) + + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-lib) +(require 'helm-help) +(require 'helm-elisp) + +(declare-function eshell-read-aliases-list "em-alias") +(declare-function eshell-send-input "esh-mode" (&optional use-region queue-p no-newline)) +(declare-function eshell-bol "esh-mode") +(declare-function eshell-parse-arguments "esh-arg" (beg end)) +(declare-function eshell-backward-argument "esh-mode" (&optional arg)) +(declare-function helm-quote-whitespace "helm-lib") +(declare-function eshell-skip-prompt "em-prompt") +(defvar eshell-special-chars-outside-quoting) + + +(defgroup helm-eshell nil + "Helm completion and history for Eshell." + :group 'helm) + + +(defcustom helm-eshell-fuzzy-match nil + "Enable fuzzy matching in `helm-esh-pcomplete' when non-nil." + :type 'boolean) + + +(defvar helm-eshell-history-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-p") #'helm-next-line) + map) + "Keymap for `helm-eshell-history'.") + +(defvar helm-esh-completion-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "TAB") #'helm-next-line) + map) + "Keymap for `helm-esh-pcomplete'.") + +(defvar helm-eshell--quit-flag nil) + + +;; Internal. +(defvar helm-ec-target "") +(defun helm-ec-insert (_candidate) + "Replace text at point with CANDIDATE. +The function that call this should set `helm-ec-target' to thing +at point." + (set (make-local-variable 'comint-file-name-quote-list) + eshell-special-chars-outside-quoting) + (let ((pt (point))) + (when (and helm-ec-target + (search-backward helm-ec-target nil t) + (string= (buffer-substring (point) pt) helm-ec-target)) + (delete-region (point) pt))) + (when (string-match "\\`\\*" helm-ec-target) (insert "*")) + (let ((marked (helm-marked-candidates))) + (prog1 t ;; Makes helm returns t on action. + (insert + (mapconcat + (lambda (x) + (cond ((string-match "\\`~/" helm-ec-target) + ;; Strip out the first escape char added by + ;; `comint-quote-filename' before "~" (Bug#1803). + (substring (comint-quote-filename (abbreviate-file-name x)) 1)) + ((string-match "\\`/" helm-ec-target) + (comint-quote-filename x)) + (t + (concat (and (string-match "\\`[.]/" helm-ec-target) "./") + (comint-quote-filename + (file-relative-name x)))))) + marked " ") + (or (helm-aand (car (last marked)) + (string-match-p "/\\'" it) + "") + " "))))) + +(defun helm-esh-transformer (candidates _sources) + (cl-loop + for i in candidates + collect + (cond ((string-match "\\`~/?" helm-ec-target) + (abbreviate-file-name i)) + ((string-match "\\`/" helm-ec-target) i) + (t + (file-relative-name i))) + into lst + finally return (sort lst #'helm-generic-sort-fn))) + +(defclass helm-esh-source (helm-source-sync) + ((init :initform (lambda () + (setq pcomplete-current-completions nil + pcomplete-last-completion-raw nil) + ;; Eshell-command add this hook in all minibuffers + ;; Remove it for the helm one. (Fixed in Emacs24) + (remove-hook 'minibuffer-setup-hook 'eshell-mode))) + (candidates :initform 'helm-esh-get-candidates) + ;(nomark :initform t) + (persistent-action :initform 'ignore) + (nohighlight :initform t) + (filtered-candidate-transformer :initform #'helm-esh-transformer) + (action :initform 'helm-ec-insert)) + "Helm class to define source for Eshell completion.") + +(defun helm-esh-get-candidates () + "Get candidates for Eshell completion using `pcomplete'." + (catch 'pcompleted + (with-helm-current-buffer + (let* ((pcomplete-stub) + pcomplete-seen pcomplete-norm-func + pcomplete-args pcomplete-last pcomplete-index + (pcomplete-autolist pcomplete-autolist) + (pcomplete-suffix-list pcomplete-suffix-list) + (table (pcomplete-completions)) + (entry (or (try-completion helm-pattern + (pcomplete-entries)) + helm-pattern))) + (cl-loop ;; expand entry too to be able to compare it with file-cand. + with exp-entry = (and (stringp entry) + (not (string= entry "")) + (file-name-as-directory + (expand-file-name entry default-directory))) + with comps = (all-completions pcomplete-stub table) + unless comps return (prog1 nil + ;; Don't add final space when + ;; there is no completion (Bug#1990). + (setq helm-eshell--quit-flag t) + (message "No completions of %s" pcomplete-stub)) + for i in comps + ;; Transform the relative names to abs names. + for file-cand = (and exp-entry + (if (file-remote-p i) i + (expand-file-name + i (file-name-directory + (if (directory-name-p pcomplete-stub) + entry + (directory-file-name entry)))))) + ;; Compare them to avoid dups. + for file-entry-p = (and (stringp exp-entry) + (stringp file-cand) + ;; Fix :/tmp/foo/ $ cd foo + (not (file-directory-p file-cand)) + (file-equal-p exp-entry file-cand)) + if (and file-cand (or (file-remote-p file-cand) + (file-exists-p file-cand)) + (not file-entry-p)) + collect file-cand into ls + else + ;; Avoid adding entry here. + unless file-entry-p collect i into ls + finally return + (if (and exp-entry + (file-directory-p exp-entry) + ;; If the car of completion list is + ;; an executable, probably we are in + ;; command completion, so don't add a + ;; possible file related entry here. + (and ls (not (executable-find (car ls)))) + ;; Don't add entry if already in prompt. + (not (file-equal-p exp-entry pcomplete-stub))) + (append (list exp-entry) + ;; Entry should not be here now but double check. + (remove entry ls)) + ls)))))) + +;;; Eshell history. +;; +;; +(defclass helm-eshell-history-source (helm-source-sync) + ((init :initform + (lambda () + ;; Same comment as in `helm-source-esh'. + (remove-hook 'minibuffer-setup-hook 'eshell-mode))) + (candidates + :initform + (lambda () + (with-helm-current-buffer + (cl-loop for c from 0 to (ring-length eshell-history-ring) + for elm = (eshell-get-history c) + unless (and (member elm lst) + eshell-hist-ignoredups) + collect elm into lst + finally return lst)))) + (nomark :initform t) + (multiline :initform t) + (keymap :initform 'helm-eshell-history-map) + (candidate-number-limit :initform 9999) + (action :initform (lambda (candidate) + (eshell-kill-input) + (insert candidate)))) + "Helm class to define source for Eshell history.") + + +(defun helm-esh-pcomplete-input (target users-comp last) + (if (and (stringp last) + (not (string= last "")) + (not users-comp) + ;; Fix completion on "../" see Bug#1832. + (or (file-exists-p last) + (helm-aand + (file-name-directory last) + (file-directory-p it)))) + (if (and (file-directory-p last) + (string-match "\\`[~.]*.*/[.]\\'" target)) + ;; Fix completion on "~/.", "~/[...]/.", and "../." + (expand-file-name + (concat (helm-basedir (file-name-as-directory last)) + (regexp-quote (helm-basename target)))) + (expand-file-name last)) + ;; Don't add "~" to input to provide completion on all users instead of only + ;; on current $HOME (#1832). + (unless users-comp last))) + +(defun helm-esh-pcomplete-default-source () + "Make and return the default source for Eshell completion." + (helm-make-source "Eshell completions" 'helm-esh-source + :fuzzy-match helm-eshell-fuzzy-match + :keymap helm-esh-completion-map)) + +(defvar helm-esh-pcomplete-build-source-fn #'helm-esh-pcomplete-default-source + "Function that builds a source or a list of sources.") + +(defun helm-esh-pcomplete--make-helm (&optional input) + (helm :sources (funcall helm-esh-pcomplete-build-source-fn) + :buffer "*helm pcomplete*" + :resume 'noresume + :input input)) + +;;;###autoload +(defun helm-esh-pcomplete () + "Preconfigured `helm' to provide Helm completion in Eshell." + (interactive) + (let* ((helm-quit-if-no-candidate t) + (helm-execute-action-at-once-if-one t) + (end (point-marker)) + (beg (save-excursion (eshell-bol) (point))) + (args (catch 'eshell-incomplete + (eshell-parse-arguments beg end))) + (target + (or (and (looking-back " " (1- (point))) " ") + (buffer-substring-no-properties + (save-excursion + (eshell-backward-argument 1) (point)) + end))) + (users-comp (string= target "~")) + (first (car args)) ; Maybe lisp delimiter "(". + last ; Will be the last but parsed by pcomplete. + del-space + del-dot) + (setq helm-ec-target (or target " ") + end (point) + ;; Reset beg for `with-helm-show-completion'. + beg (or (and target (not (string= target " ")) + (- end (length target))) + ;; Nothing at point. + (progn (insert " ") (setq del-space t) (point)))) + (when (string-match "\\`[~.]*.*/[.]\\'" target) + ;; Fix completion on + ;; "~/.", "~/[...]/.", and "../." + (delete-char -1) (setq del-dot t) + (setq helm-ec-target (substring helm-ec-target 0 (1- (length helm-ec-target))))) + (cond ((eq first ?\() + (helm-lisp-completion-or-file-name-at-point)) + ;; In eshell `pcomplete-parse-arguments' is called + ;; with `pcomplete-parse-arguments-function' + ;; locally bound to `eshell-complete-parse-arguments' + ;; which is calling `lisp-complete-symbol', + ;; calling it before would popup the + ;; *completions* buffer. + (t (setq last (replace-regexp-in-string + "\\`\\*" "" + (car (last (ignore-errors + (pcomplete-parse-arguments)))))) + ;; Set helm-eshell--quit-flag to non-nil only on + ;; quit, this tells to not add final suffix when quitting + ;; helm. + (add-hook 'helm-quit-hook #'helm-eshell--quit-hook-fn) + (with-helm-show-completion beg end + (unwind-protect + (or (helm-esh-pcomplete--make-helm + (helm-esh-pcomplete-input target users-comp last)) + ;; Delete removed dot on quit + (and del-dot (prog1 t (insert "."))) + ;; A space is needed to have completion, remove + ;; it when nothing found. + (and del-space (looking-back "\\s-" (1- (point))) + (delete-char -1)) + (if (and (null helm-eshell--quit-flag) + (and (stringp last) (file-directory-p last)) + (looking-back "\\([.]\\{1,2\\}\\|[^/]\\)\\'" + (1- (point)))) + (prog1 t (insert "/")) + ;; We need another flag for space here, but + ;; global to pass it to `helm-quit-hook', this + ;; space is added when point is just after + ;; previous completion and there is no + ;; more completion, see Bug#1832. + (unless (or helm-eshell--quit-flag + (looking-back "/\\'" (1- (point)))) + (prog1 t (insert " "))) + (when (and helm-eshell--quit-flag + (string-match-p "[.]\\{2\\}\\'" last)) + (insert "/")))) + (remove-hook 'helm-quit-hook #'helm-eshell--quit-hook-fn) + (setq helm-eshell--quit-flag nil))))))) + +(defun helm-eshell--quit-hook-fn () + (setq helm-eshell--quit-flag t)) + +;;;###autoload +(defun helm-eshell-history () + "Preconfigured Helm for Eshell history." + (interactive) + (let* ((end (point)) + (beg (save-excursion (eshell-bol) (point))) + (input (buffer-substring beg end)) + flag-empty) + (when (eq beg end) + (insert " ") + (setq flag-empty t) + (setq end (point))) + (unwind-protect + (with-helm-show-completion beg end + (helm :sources (helm-make-source "Eshell history" + 'helm-eshell-history-source + :fuzzy-match helm-eshell-fuzzy-match) + :buffer "*helm eshell history*" + :resume 'noresume + :input input)) + (when (and flag-empty + (looking-back " " (1- (point)))) + (delete-char -1))))) + + +;;; Eshell prompts +;; +(defface helm-eshell-prompts-promptidx + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "cyan")) + "Face used to highlight Eshell prompt index.") + +(defface helm-eshell-prompts-buffer-name + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "green")) + "Face used to highlight Eshell buffer name.") + +(defcustom helm-eshell-prompts-promptidx-p t + "Show prompt number." + :type 'boolean) + +(defvar helm-eshell-prompts-keymap + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c o") #'helm-eshell-prompts-other-window) + (define-key map (kbd "C-c C-o") #'helm-eshell-prompts-other-frame) + map) + "Keymap for `helm-eshell-prompt-all'.") + +(defvar eshell-prompt-regexp) +(defvar eshell-highlight-prompt) + +(defun helm-eshell-prompts-list (&optional buffer) + "List the prompts in Eshell BUFFER. + +Return a list of (\"prompt\" (point) (buffer-name) prompt-index)) +E.g. (\"ls\" 162 \"*eshell*\" 3). +If BUFFER is nil, use current buffer." + (with-current-buffer (or buffer (current-buffer)) + (when (eq major-mode 'eshell-mode) + (save-excursion + (goto-char (point-min)) + (let (result (count 1)) + (helm-awhile (re-search-forward eshell-prompt-regexp nil t) + (when (or (and eshell-highlight-prompt + (get-text-property (match-beginning 0) 'read-only)) + (null eshell-highlight-prompt)) + (push (list (buffer-substring-no-properties + it (point-at-eol)) + it (buffer-name) count) + result) + (setq count (1+ count)))) + (nreverse result)))))) + +(defun helm-eshell-prompts-list-all () + "List the prompts of all Eshell buffers. +See `helm-eshell-prompts-list'." + (cl-loop for b in (buffer-list) + append (helm-eshell-prompts-list b))) + +(defun helm-eshell-prompts-transformer (candidates &optional all) + ;; ("ls" 162 "*eshell*" 3) => ("*eshell*:3:ls" . ("ls" 162 "*eshell*" 3)) + (cl-loop for (prt pos buf id) in candidates + collect `(,(concat + (when all + (concat (propertize + buf + 'face 'helm-eshell-prompts-buffer-name) + ":")) + (when helm-eshell-prompts-promptidx-p + (concat (propertize + (number-to-string id) + 'face 'helm-eshell-prompts-promptidx) + ":")) + prt) + . ,(list prt pos buf id)))) + +(defun helm-eshell-prompts-all-transformer (candidates) + (helm-eshell-prompts-transformer candidates t)) + +(cl-defun helm-eshell-prompts-goto (candidate &optional (action 'switch-to-buffer)) + ;; Candidate format: ("ls" 162 "*eshell*" 3) + (let ((buf (nth 2 candidate))) + (unless (and (string= (buffer-name) buf) + (eq action 'switch-to-buffer)) + (funcall action buf)) + (goto-char (nth 1 candidate)) + (recenter))) + +(defun helm-eshell-prompts-goto-other-window (candidate) + (helm-eshell-prompts-goto candidate 'switch-to-buffer-other-window)) + +(defun helm-eshell-prompts-goto-other-frame (candidate) + (helm-eshell-prompts-goto candidate 'switch-to-buffer-other-frame)) + +(defun helm-eshell-prompts-other-window () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-eshell-prompts-goto-other-window))) +(put 'helm-eshell-prompts-other-window 'helm-only t) + +(defun helm-eshell-prompts-other-frame () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-eshell-prompts-goto-other-frame))) +(put 'helm-eshell-prompts-other-frame 'helm-only t) + +;;;###autoload +(defun helm-eshell-prompts () + "Pre-configured `helm' to browse the prompts of the current Eshell." + (interactive) + (if (eq major-mode 'eshell-mode) + (helm :sources + (helm-build-sync-source "Eshell prompts" + :candidates (helm-eshell-prompts-list) + :candidate-transformer 'helm-eshell-prompts-transformer + :action '(("Go to prompt" . helm-eshell-prompts-goto))) + :buffer "*helm Eshell prompts*") + (message "Current buffer is not an Eshell buffer"))) + +;;;###autoload +(defun helm-eshell-prompts-all () + "Pre-configured `helm' to browse the prompts of all Eshell sessions." + (interactive) + (helm :sources + (helm-build-sync-source "All Eshell prompts" + :candidates (helm-eshell-prompts-list-all) + :candidate-transformer 'helm-eshell-prompts-all-transformer + :action '(("Go to prompt" . helm-eshell-prompts-goto) + ("Go to prompt in other window `C-c o`" . + helm-eshell-prompts-goto-other-window) + ("Go to prompt in other frame `C-c C-o`" . + helm-eshell-prompts-goto-other-frame)) + :keymap helm-eshell-prompts-keymap) + :buffer "*helm Eshell all prompts*")) + +(provide 'helm-eshell) + +;;; helm-eshell ends here diff --git a/code/elpa/helm-20220822.659/helm-eval.el b/code/elpa/helm-20220822.659/helm-eval.el new file mode 100644 index 0000000..72b7884 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-eval.el @@ -0,0 +1,215 @@ +;;; helm-eval.el --- eval expressions from helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'eldoc) +(require 'edebug) + +(declare-function helm-lisp-completion-at-point "helm-elisp.el") + + +(defgroup helm-eval nil + "Eval related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-eldoc-in-minibuffer-show-fn + 'helm-show-info-in-mode-line + "A function to display eldoc info. +Should take one arg: the string to display." + :group 'helm-eval + :type 'symbol) + +(defcustom helm-show-info-in-mode-line-delay 12 + "Eldoc will show info in mode-line during this delay if user is idle." + :type 'integer + :group 'helm-eval) + + +;;; Eldoc compatibility between emacs-24 and emacs-25 +;; +(if (require 'elisp-mode nil t) ; emacs-25 + ;; Maybe the eldoc functions have been + ;; already aliased by eldoc-eval. + (cl-loop for (f . a) in '((eldoc-current-symbol . + elisp--current-symbol) + (eldoc-fnsym-in-current-sexp . + elisp--fnsym-in-current-sexp) + (eldoc-get-fnsym-args-string . + elisp-get-fnsym-args-string) + (eldoc-get-var-docstring . + elisp-get-var-docstring)) + unless (fboundp f) + do (defalias f a)) + ;; Emacs-24. + (declare-function eldoc-current-symbol "eldoc") + (declare-function eldoc-get-fnsym-args-string "eldoc" (sym &optional index)) + (declare-function eldoc-get-var-docstring "eldoc" (sym)) + (declare-function eldoc-fnsym-in-current-sexp "eldoc")) + +;;; Evaluation Result +;; +;; +;; Internal +(defvar helm-eldoc-active-minibuffers-list nil) + +(defvar helm-eval-expression-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "") #'helm-eval-new-line-and-indent) + (define-key map (kbd "") #'lisp-indent-line) + (define-key map (kbd "") #'helm-lisp-completion-at-point) + (define-key map (kbd "C-p") #'previous-line) + (define-key map (kbd "C-n") #'next-line) + (define-key map (kbd "") #'previous-line) + (define-key map (kbd "") #'next-line) + (define-key map (kbd "") #'forward-char) + (define-key map (kbd "") #'backward-char) + map)) + +(defun helm-build-evaluation-result-source () + (helm-build-dummy-source "Evaluation Result" + :multiline t + :mode-line "C-RET: nl-and-indent, M-tab: reindent, C-tab:complete, C-p/n: next/prec-line." + :filtered-candidate-transformer + (lambda (_candidates _source) + (list + (condition-case nil + (with-helm-current-buffer + (pp-to-string + (if edebug-active + (edebug-eval-expression + (read helm-pattern)) + (eval (read helm-pattern) t)))) + (error "Error")))) + :nohighlight t + :keymap helm-eval-expression-map + :action '(("Copy result to kill-ring" . (lambda (candidate) + (kill-new + (replace-regexp-in-string + "\n" "" candidate)) + (message "Result copied to kill-ring"))) + ("copy sexp to kill-ring" . (lambda (_candidate) + (kill-new helm-input) + (message "Sexp copied to kill-ring")))))) + +(defun helm-eval-new-line-and-indent () + (interactive) + (newline) (lisp-indent-line)) + +(defun helm-eldoc-store-minibuffer () + "Store minibuffer buffer name in `helm-eldoc-active-minibuffers-list'." + (with-selected-window (minibuffer-window) + (push (current-buffer) helm-eldoc-active-minibuffers-list))) + +;; From emacs-28.1: As the eldoc API is nowaday a pain to use, try to +;; provide some eldoc in mode-line the best as possible (may break at +;; some point). +(defun helm-eldoc-show-in-eval () + "Return eldoc in mode-line for current minibuffer input." + (let ((buf (window-buffer (active-minibuffer-window)))) + (condition-case err + (when (member buf helm-eldoc-active-minibuffers-list) + (with-current-buffer buf + (let* ((info-fn (eldoc-fnsym-in-current-sexp)) + (vsym (eldoc-current-symbol)) + (sym (car info-fn)) + (vardoc (eldoc-get-var-docstring vsym)) + (doc (or vardoc + (eldoc-get-fnsym-args-string + sym (cadr info-fn)))) + (all (format "%s: %s" + (propertize + (symbol-name (if vardoc vsym sym)) + 'face (if vardoc + 'font-lock-variable-name-face + 'font-lock-function-name-face)) + doc))) + (when doc (funcall helm-eldoc-in-minibuffer-show-fn all))))) + (error (message "Eldoc in minibuffer error: %S" err) nil)))) + +(defun helm-show-info-in-mode-line (str) + "Display string STR in mode-line." + (save-selected-window + (with-helm-window + (let ((mode-line-format (concat " " str))) + (force-mode-line-update) + (sit-for helm-show-info-in-mode-line-delay)) + (force-mode-line-update)))) + +;;; Calculation Result +;; +;; +(defvar helm-source-calculation-result + (helm-build-dummy-source "Calculation Result" + :filtered-candidate-transformer (lambda (_candidates _source) + (list + (condition-case err + (let ((result (calc-eval helm-pattern))) + (if (listp result) + (error "At pos %s: %s" + (car result) (cadr result)) + result)) + (error (cdr err))))) + :nohighlight t + :action '(("Copy result to kill-ring" . (lambda (candidate) + (kill-new candidate) + (message "Result \"%s\" copied to kill-ring" + candidate))) + ("Copy operation to kill-ring" . (lambda (_candidate) + (kill-new helm-input) + (message "Calculation copied to kill-ring")))))) + +;;;###autoload +(defun helm-eval-expression (arg) + "Preconfigured `helm' for `helm-source-evaluation-result'." + (interactive "P") + (let ((helm-elisp-help-function #'helm-elisp-show-doc-modeline)) + (helm :sources (helm-build-evaluation-result-source) + :input (when arg (thing-at-point 'sexp)) + :buffer "*helm eval*" + :echo-input-in-header-line nil + :history 'read-expression-history))) + +(defvar eldoc-idle-delay) +;;;###autoload +(defun helm-eval-expression-with-eldoc () + "Preconfigured `helm' for `helm-source-evaluation-result' with `eldoc' support." + (interactive) + (let ((timer (run-with-idle-timer + eldoc-idle-delay 'repeat + #'helm-eldoc-show-in-eval))) + (unwind-protect + (minibuffer-with-setup-hook + #'helm-eldoc-store-minibuffer + (call-interactively 'helm-eval-expression)) + (and timer (cancel-timer timer)) + (setq helm-eldoc-active-minibuffers-list + (cdr helm-eldoc-active-minibuffers-list))))) + +;;;###autoload +(defun helm-calcul-expression () + "Preconfigured `helm' for `helm-source-calculation-result'." + (interactive) + (helm :sources 'helm-source-calculation-result + :buffer "*helm calcul*")) + +(provide 'helm-eval) + +;;; helm-eval.el ends here diff --git a/code/elpa/helm-20220822.659/helm-external.el b/code/elpa/helm-20220822.659/helm-external.el new file mode 100644 index 0000000..b780836 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-external.el @@ -0,0 +1,258 @@ +;;; helm-external.el --- Run Externals commands within Emacs with helm completion. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-net) + +(declare-function helm-comp-read "helm-mode") + + +(defgroup helm-external nil + "External related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-raise-command nil + "A shell command to jump to a window running specific program. +Need external program wmctrl. +This will be use with `format', so use something like \"wmctrl -xa %s\"." + :type 'string + :group 'helm-external) + +(defcustom helm-external-programs-associations nil + "Alist to store externals programs associated with file extension. +This variable overhide setting in .mailcap file. +E.g.: \\='((\"jpg\" . \"gqview\") (\"pdf\" . \"xpdf\")) " + :type '(alist :key-type string :value-type string) + :group 'helm-external) + +(defcustom helm-default-external-file-browser "nautilus" + "Default external file browser for your system. +Directories will be opened externally with it when opening file +externally in `helm-find-files'. +Set to nil if you do not have an external file browser or do not +want to use it. +Windows users should set that to \"explorer.exe\"." + :group 'helm-external + :type 'string) + + +;;; Internals +(defvar helm-external-command-history nil) +(defvar helm-external-commands-list nil + "A list of all external commands the user can execute. +If this variable is not set by the user, it will be calculated +automatically.") + +(defun helm-external-commands-list-1 (&optional sort) + "Returns a list of all external commands the user can execute. +If `helm-external-commands-list' is non-nil it will return its +contents. Else it calculates all external commands and sets +`helm-external-commands-list'." + (helm-aif helm-external-commands-list + it + (setq helm-external-commands-list + (cl-loop + for dir in (split-string (getenv "PATH") path-separator) + when (and (file-exists-p dir) (file-accessible-directory-p dir)) + for lsdir = (cl-loop for i in (directory-files dir t) + for bn = (file-name-nondirectory i) + when (and (not (member bn completions)) + (not (file-directory-p i)) + (file-executable-p i)) + collect bn) + append lsdir into completions + finally return + (if sort (sort completions 'string-lessp) completions))))) + +(defun helm-run-or-raise (exe &optional files detached) + "Run asynchronously EXE or jump to the application window. +If EXE is already running just jump to his window if +`helm-raise-command' is non-nil. +When FILES argument is provided run EXE with FILES. +When argument DETACHED is non nil, detach process from Emacs." + (let* ((proc-name (replace-regexp-in-string + "(" "" (car (split-string exe)))) + (fmt-file (lambda (file) + (shell-quote-argument + (if (eq system-type 'windows-nt) + (helm-w32-prepare-filename file) + (expand-file-name file))))) + (file-arg (and files (mapconcat fmt-file files " "))) + process-connection-type proc) + (when (and files detached (not (string-match "%s &)\\'" exe))) + (setq exe (format "(%s &)" exe))) + (when (member proc-name helm-external-commands-list) + ;; Allow adding more files to the current process if it is + ;; already running (i.e. Don't just raise it without sending + ;; files) we assume program doesn't start a new + ;; process (like firefox, transmission etc...). + (if files + (cond ((string-match "%s &)\\'" exe) + (message "Starting and detaching `%s' from Emacs" proc-name) + (call-process-shell-command (format exe file-arg))) + (t + (message "Starting %s..." proc-name) + (setq proc + (start-process-shell-command + proc-name nil (if (string-match "%s" exe) + (format exe file-arg) + (format "%s %s" exe file-arg)))))) + ;; Just jump to the already running program instance or start + ;; a new process. + (if (get-process proc-name) + (if helm-raise-command + (run-at-time 0.1 nil #'shell-command + (format helm-raise-command proc-name)) + (error "Error: %s is already running" proc-name)) + (if (and detached (not (memq system-type '(windows-nt ms-dos)))) + (progn + (message "Starting and detaching `%s' from Emacs" proc-name) + (call-process-shell-command (format "(%s &)" exe))) + (when detached + (user-error "Detaching programs not supported on `%s'" system-type)) + (setq proc (start-process-shell-command proc-name nil exe))))) + (when proc + (set-process-sentinel + proc + (lambda (process event) + (when (and (string= event "finished\n") + helm-raise-command + (not (helm-get-pid-from-process-name proc-name))) + (shell-command (format helm-raise-command "emacs"))) + (message "%s process...Finished." process)))) + ;; Move command on top list. + (setq helm-external-commands-list + (cons proc-name + (delete proc-name helm-external-commands-list)))))) + +(defun helm-get-mailcap-for-file (filename) + "Get the command to use for FILENAME from mailcap files." + (mailcap-parse-mailcaps) + (let* ((ext (file-name-extension filename)) + (mime (when ext (mailcap-extension-to-mime ext))) + (result (when mime (mailcap-mime-info mime)))) + ;; If elisp file have no associations in .mailcap + ;; `mailcap-maybe-eval' is returned, in this case just return nil. + (when (stringp result) (helm-basename result)))) + +(defun helm-get-default-program-for-file (filename) + "Try to find a default program to open FILENAME. +Try first in `helm-external-programs-associations' and then in +mailcap file. If nothing found return nil." + (let* ((ext (file-name-extension filename)) + (def-prog (assoc-default ext helm-external-programs-associations))) + (cond ((and def-prog (not (string= def-prog ""))) def-prog) + ((and helm-default-external-file-browser (file-directory-p filename)) + helm-default-external-file-browser) + (t (helm-get-mailcap-for-file filename))))) + +(defun helm-open-file-externally (_file) + "Open FILE with an external program. +Try to guess which program to use with +`helm-get-default-program-for-file'. +If not found or a prefix arg is given query the user which tool +to use." + (let* ((files (helm-marked-candidates :with-wildcard t)) + (fname (expand-file-name (car files))) + (collection (helm-external-commands-list-1 'sort)) + (def-prog (helm-get-default-program-for-file fname)) + (program (if (or helm-current-prefix-arg (not def-prog)) + ;; Prefix arg or no default program. + (prog1 + (helm-comp-read + "Program: " collection + :must-match t + :name "Open file Externally" + :history 'helm-external-command-history) + ;; Always prompt to set this program as default. + (setq def-prog nil)) + ;; No prefix arg or default program exists. + def-prog))) + (unless (or def-prog ; Association exists, no need to record it. + ;; Don't try to record non--filenames associations (e.g urls). + (not (file-exists-p fname))) + (when + (y-or-n-p + (format + "Do you want to make `%s' the default program for this kind of files? " + program)) + (helm-aif (assoc (file-name-extension fname) + helm-external-programs-associations) + (setq helm-external-programs-associations + (delete it helm-external-programs-associations))) + (push (cons (file-name-extension fname) + (helm-read-string + "Program (Add args maybe and confirm): " program)) + helm-external-programs-associations) + (customize-save-variable 'helm-external-programs-associations + helm-external-programs-associations))) + (helm-run-or-raise program files) + (setq helm-external-command-history + (cl-loop for i in helm-external-command-history + when (executable-find i) collect i)))) + +(defun helm-run-external-command-action (candidate &optional detached) + (helm-run-or-raise candidate nil detached) + (setq helm-external-command-history + (cons candidate + (delete candidate + helm-external-command-history)))) + +(defclass helm-external-commands (helm-source-in-buffer) + ((filtered-candidate-transformer + :initform (lambda (candidates _source) + (cl-loop for c in candidates + if (get-process c) + collect (propertize c 'face 'font-lock-type-face) + else collect c))) + (must-match :initform t) + (nomark :initform t) + (action :initform + (helm-make-actions + "Run program" 'helm-run-external-command-action + (lambda () + (unless (memq system-type '(windows-nt ms-dos)) + "Run program detached")) + (lambda (candidate) + (helm-run-external-command-action candidate 'detached)))))) + +;;;###autoload +(defun helm-run-external-command () + "Preconfigured `helm' to run External PROGRAM asyncronously from Emacs. +If program is already running try to run `helm-raise-command' if +defined otherwise exit with error. You can set your own list of +commands with `helm-external-commands-list'." + (interactive) + (helm :sources `(,(helm-make-source "External Commands history" 'helm-external-commands + :data helm-external-command-history) + ,(helm-make-source "External Commands" 'helm-external-commands + :data (helm-external-commands-list-1 'sort))) + :buffer "*helm externals commands*" + :prompt "RunProgram: ") + ;; Remove from history no more valid executables. + (setq helm-external-command-history + (cl-loop for i in helm-external-command-history + when (executable-find i) collect i))) + + +(provide 'helm-external) + +;;; helm-external ends here diff --git a/code/elpa/helm-20220822.659/helm-fd.el b/code/elpa/helm-20220822.659/helm-fd.el new file mode 100644 index 0000000..69d1116 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-fd.el @@ -0,0 +1,128 @@ +;;; helm-fd.el --- helm interface for fd command line tool. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'helm) +(require 'helm-types) + +(declare-function ansi-color-apply "ansi-color.el") + +(defvar helm-fd-executable "fd" + "The fd shell command executable.") + +(defcustom helm-fd-switches '("--no-ignore" "--hidden" "--type" "f" "--type" "d" "--color" "always") + "A list of options to pass to fd shell command." + :type '(repeat string) + :group 'helm-files) + +(defface helm-fd-finish + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Green")) + "Face used in mode line when fd process ends." + :group 'helm-grep-faces) + +(defvar helm-fd-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-generic-files-map) + (define-key map (kbd "C-]") 'undefined) + (define-key map (kbd "DEL") 'helm-delete-backward-no-update) + (define-key map (kbd "M-") 'helm-fd-next-directory) + (define-key map (kbd "M-") 'helm-fd-previous-directory) + map)) + +(defun helm-fd-next-directory-1 (arg) + (with-helm-window + (let ((cur-dir (helm-basedir (helm-get-selection)))) + (while (equal cur-dir (helm-basedir (helm-get-selection))) + (if (> arg 0) + (helm-next-line) + (helm-previous-line)))))) + +(defun helm-fd-next-directory () + "Move to next directory in a helm-fd source." + (interactive) + (with-helm-alive-p + (helm-fd-next-directory-1 1))) + +(defun helm-fd-previous-directory () + "Move to previous directory in a helm-fd source." + (interactive) + (with-helm-alive-p + (helm-fd-next-directory-1 -1))) + +(defclass helm-fd-class (helm-source-async) + ((candidates-process :initform 'helm-fd-process) + (requires-pattern :initform 2) + (candidate-number-limit :initform 20000) + (nohighlight :initform t) + (help-message :initform 'helm-fd-help-message) + (filtered-candidate-transformer :initform 'helm-fd-fct) + (action :initform 'helm-type-file-actions) + (keymap :initform 'helm-fd-map))) + +(defun helm-fd-process () + "Initialize fd process in an helm async source." + (let* (process-connection-type + (cmd (append helm-fd-switches (split-string helm-pattern " "))) + (proc (apply #'start-process "fd" nil helm-fd-executable cmd)) + (start-time (float-time)) + (fd-version (replace-regexp-in-string + "\n" "" + (shell-command-to-string (concat helm-fd-executable " --version"))))) + (helm-log "Fd command:\nfd %s" (mapconcat 'identity cmd " ")) + (helm-log "VERSION: %s" fd-version) + (prog1 + proc + (set-process-sentinel + proc (lambda (_process event) + (if (string= event "finished\n") + (with-helm-window + (setq mode-line-format + `(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format + "[%s process finished in %.2fs - (%s results)] " + ,fd-version + ,(- (float-time) start-time) + (helm-get-candidate-number)) + 'face 'helm-fd-finish)))) + (force-mode-line-update)) + (helm-log "Error: Fd %s" + (replace-regexp-in-string "\n" "" event)))))))) + +(defun helm-fd-fct (candidates _source) + "The filtered-candidate-transformer function for helm-fd." + (cl-loop for i in candidates + collect (ansi-color-apply i))) + +(defun helm-fd-1 (directory) + "Run fd shell command on DIRECTORY with helm interface." + (cl-assert (executable-find helm-fd-executable) nil "Could not find fd executable") + (cl-assert (not (file-remote-p directory)) nil "Fd not supported on remote directories") + (let ((default-directory directory)) + (helm :sources (helm-make-source + (format "fd (%s)" + (abbreviate-file-name default-directory)) + 'helm-fd-class) + :buffer "*helm fd*"))) + + +(provide 'helm-fd) + +;;; helm-fd.el ends here diff --git a/code/elpa/helm-20220822.659/helm-files.el b/code/elpa/helm-20220822.659/helm-files.el new file mode 100644 index 0000000..4ede240 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-files.el @@ -0,0 +1,6493 @@ +;;; helm-files.el --- helm file browser and related. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-types) +(require 'helm-utils) +(require 'helm-grep) +(require 'helm-help) +(require 'helm-locate) +(require 'helm-tags) +(require 'helm-buffers) +(require 'tramp) +(eval-when-compile + (require 'thingatpt) + (require 'ffap) + (require 'dired-aux) + (require 'dired-x)) +(require 'filenotify) +(require 'image-mode) +(require 'image-dired) + +(declare-function find-library-name "find-func.el" (library)) +(declare-function w32-shell-execute "ext:w32fns.c" (operation document &optional parameters show-flag)) +(declare-function gnus-dired-attach "ext:gnus-dired.el" (files-to-attach)) +(declare-function eshell-read-aliases-list "em-alias") +(declare-function eshell-send-input "esh-mode" (&optional use-region queue-p no-newline)) +(declare-function eshell-kill-input "esh-mode") +(declare-function eshell-bol "esh-mode") +(declare-function eshell-reset "esh-mode.el") +(declare-function eshell/cd "em-dirs.el") +(declare-function eshell-next-prompt "em-prompt.el") +(declare-function eshell-resume-eval "esh-cmd") +(declare-function helm-ls-git "ext:helm-ls-git") +(declare-function helm-hg-find-files-in-project "ext:helm-ls-hg") +(declare-function helm-gid "helm-id-utils.el") +(declare-function helm-find-1 "helm-find") +(declare-function helm-fd-1 "helm-fd") +(declare-function helm-get-default-program-for-file "helm-external") +(declare-function helm-open-file-externally "helm-external") +(declare-function helm-comp-read "helm-mode") +(declare-function helm-read-file-name "helm-mode") +(declare-function term-line-mode "term") +(declare-function term-char-mode "term") +(declare-function term-send-input "term") +(declare-function term-next-prompt "term") +(declare-function term-process-mark "term") +(declare-function bookmark-prop-get "bookmark") +(declare-function comint-next-prompt "comint") +(declare-function comint-delete-input "comint") +(declare-function comint-send-input "comint") +(declare-function comint-goto-process-mark "comint") +(declare-function tramp-dissect-file-name "tramp") +(declare-function tramp-get-completion-function "tramp") +(declare-function seconds-to-time "time-date") +(declare-function ffap-fixup-url "ffap") +(declare-function ffap-url-at-point "ffap") +(declare-function ffap-file-at-point "ffap") +(declare-function dired-create-files "dired-aux") +(declare-function dired-goto-file "dired") +(declare-function dired-move-to-filename "dired") +(declare-function dired-move-to-end-of-filename "dired") +(declare-function dired-get-filename "dired") +(declare-function dired-get-marked-files "dired") +(declare-function tramp-list-connections "tramp-cache") +(declare-function tramp-get-connection-process "tramp") +(declare-function tramp-buffer-name "tramp") +(declare-function tramp-make-tramp-file-name "tramp") +(declare-function tramp-cleanup-connection "tramp-cmds") +(declare-function dired-async-processes "ext:dired-async.el") +(declare-function all-the-icons-icon-for-file "ext:all-the-icons.el") +(declare-function all-the-icons-octicon "ext:all-the-icons.el") +(declare-function all-the-icons-match-to-alist "ext:all-the-icons.el") +(declare-function helm-adaptive-sort "ext:helm-adaptive.el") + +(defvar all-the-icons-dir-icon-alist) +(defvar term-char-mode-point-at-process-mark) +(defvar term-char-mode-buffer-read-only) +(defvar recentf-list) +(defvar helm-mm-matching-method) +(defvar dired-async-mode) +(defvar org-directory) +(defvar eshell-current-command) +(defvar eshell-debug-command) +(defvar eshell-current-command) +(defvar tramp-archive-enabled) +(defvar password-cache) + + +;;; Internal vars +;; +(defvar helm-ff-last-expanded-candidate-regexp "^[[:multibyte:] ]*%s$" + "Regexp that retrieve previous candidate when going up one level. +The default value matching a multibyte char at bol allows prefixing +candidate with an icon. The format part will be replaced by the +display part of the candidate regexp quoted.") + +(defvar helm-find-files-doc-header " (\\\\[helm-find-files-up-one-level]: Go up one level)" + "*The doc that is inserted in the Name header of a find-files or dired source.") +(defvar helm-ff-auto-update-flag nil + "Internal, flag to turn on/off auto-update in `helm-find-files'. +Don't set it directly, use instead `helm-ff-auto-update-initial-value'.") +(defvar helm-ff-last-expanded nil + "Store last expanded directory or file.") +(defvar helm-ff-default-directory nil) +(defvar helm-ff-history nil) +(defvar helm-ff-url-regexp + "\\`\\(news\\(post\\)?:\\|nntp:\\|mailto:\\|file:\\|\\(ftp\\|https?\\|telnet\\|gopher\\|www\\|wais\\):/?/?\\).*" + "Same as `ffap-url-regexp' but match earlier possible url.") +;; helm-tramp-file-name-regexp is based on old version of +;; tramp-file-name-regexp i.e. "\\`/\\([^[/:]+\\|[^/]+]\\):" but it +;; seems it is wrong and a simpler regexp is enough, let's try it and +;; watch out! +(defvar helm-tramp-file-name-regexp "\\`/\\([^/:|]+\\):") +(defvar helm-ff-tramp-method-regexp "[/|]:\\([^:]*\\)") +(defvar helm-marked-buffer-name "*helm marked*") +(defvar helm-ff--auto-update-state nil) +(defvar helm-ff--deleting-char-backward nil) +(defvar helm-multi-files--toggle-locate nil) +(defvar helm-ff--move-to-first-real-candidate t) +(defvar helm-find-files--toggle-bookmark nil) +(defvar helm-ff--tramp-methods nil) +(defvar helm-ff--directory-files-length (make-hash-table :test 'equal) + "Used to count number of candidates in directory. +candidate-number-limit is set to this value if this value is bigger +than `helm-candidate-number-limit'.") +(defvar helm-ff--list-directory-cache (make-hash-table :test 'equal) + "Cache for `helm-find-files' candidates.") +(defvar helm-ff--file-notify-watchers (make-hash-table :test 'equal) + "File-notify watchers for `helm-find-files' are stored here.") +(defvar helm-ff-history-buffer-name "*helm-find-files history*") +(defvar helm-rsync-command-history nil) +(defvar helm-rsync--last-progress-bar-alist nil + "Used to store last valid rsync progress bar.") +(defvar helm-rsync-process-buffer "*helm-rsync*") +(defvar helm-rsync-progress-str-alist nil) +(defvar helm-ff--trash-directory-regexp "\\.?Trash[/0-9]+files/?\\'") +(defvar helm-ff--show-directories-only nil) +(defvar helm-ff--show-files-only nil) +(defvar helm-ff--trashed-files nil + "[INTERNAL] Files already trashed are stored here during file deletion. +This is used only as a let binding.") +(defvar helm-ff--show-thumbnails nil) +(defvar helm-ff--thumbnailed-directories nil) + + +;;; Helm-find-files - The helm file browser. +;; +;; Keymaps + +(defvar helm-find-files-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "RET") 'helm-ff-RET) + (define-key map (kbd "C-]") 'helm-ff-run-toggle-basename) + (define-key map (kbd "C-x C-f") 'helm-ff-run-locate) + (define-key map (kbd "C-x C-d") 'helm-ff-run-browse-project) + (define-key map (kbd "C-x r m") 'helm-ff-bookmark-set) + (define-key map (kbd "C-x r b") 'helm-find-files-switch-to-bookmark) + (define-key map (kbd "C-x C-q") 'helm-ff-run-marked-files-in-dired) + (define-key map (kbd "C-s") 'helm-ff-run-grep) + (define-key map (kbd "M-g s") 'helm-ff-run-grep) + (define-key map (kbd "M-g p") 'helm-ff-run-pdfgrep) + (define-key map (kbd "M-g z") 'helm-ff-run-zgrep) + (define-key map (kbd "M-g a") 'helm-ff-run-grep-ag) + (define-key map (kbd "M-g g") 'helm-ff-run-git-grep) + (define-key map (kbd "M-g i") 'helm-ff-run-gid) + (define-key map (kbd "M-.") 'helm-ff-run-etags) + (define-key map (kbd "M-R") 'helm-ff-run-rename-file) + (define-key map (kbd "M-C") 'helm-ff-run-copy-file) + (when (executable-find "rsync") + (define-key map (kbd "M-V") 'helm-ff-run-rsync-file)) + (define-key map (kbd "M-B") 'helm-ff-run-byte-compile-file) + (define-key map (kbd "M-L") 'helm-ff-run-load-file) + (define-key map (kbd "M-S") 'helm-ff-run-symlink-file) + (define-key map (kbd "M-Y") 'helm-ff-run-relsymlink-file) + (define-key map (kbd "M-H") 'helm-ff-run-hardlink-file) + (define-key map (kbd "M-D") 'helm-ff-run-delete-file) + (define-key map (kbd "M-K") 'helm-ff-run-kill-buffer-persistent) + (define-key map (kbd "M-T") 'helm-ff-run-touch-files) + (define-key map (kbd "C-c d") 'helm-ff-persistent-delete) + (define-key map (kbd "M-e") 'helm-ff-run-switch-to-shell) + (define-key map (kbd "C-c i") 'helm-ff-run-complete-fn-at-point) + (define-key map (kbd "C-c o") 'helm-ff-run-switch-other-window) + (define-key map (kbd "C-c C-o") 'helm-ff-run-switch-other-frame) + (define-key map (kbd "C-c C-x") 'helm-ff-run-open-file-externally) + (define-key map (kbd "C-c C-v") 'helm-ff-run-preview-file-externally) + (define-key map (kbd "C-c X") 'helm-ff-run-open-file-with-default-tool) + (define-key map (kbd "C-c t") 'helm-ff-toggle-thumbnails) + (define-key map (kbd "M-!") 'helm-ff-run-eshell-command-on-file) + (define-key map (kbd "M-@") 'helm-ff-run-query-replace-fnames-on-marked) + (define-key map (kbd "M-%") 'helm-ff-run-query-replace) + (define-key map (kbd "C-M-%") 'helm-ff-run-query-replace-regexp) + (define-key map (kbd "C-c =") 'helm-ff-run-ediff-file) + (define-key map (kbd "M-=") 'helm-ff-run-ediff-merge-file) + (define-key map (kbd "M-p") 'helm-find-files-history) + (define-key map (kbd "C-c h") 'helm-ff-file-name-history) + (define-key map (kbd "M-i") 'helm-ff-properties-persistent) + (define-key map (kbd "C-}") 'helm-narrow-window) + (define-key map (kbd "C-{") 'helm-enlarge-window) + (define-key map (kbd "C-") 'helm-ff-run-toggle-auto-update) + (define-key map (kbd "C-c ") 'helm-ff-run-toggle-auto-update) + (define-key map (kbd "C-c C-a") 'helm-ff-run-mail-attach-files) + (define-key map (kbd "C-c p") 'helm-ff-run-print-file) + (define-key map (kbd "C-c /") 'helm-ff-run-find-sh-command) + (define-key map (kbd "C-/") 'helm-ff-run-fd) + ;; Next 2 have no effect if candidate is not an image file. + (define-key map (kbd "M-l") 'helm-ff-rotate-left-persistent) + (define-key map (kbd "M-r") 'helm-ff-rotate-right-persistent) + (define-key map (kbd "M-+") 'helm-ff-increase-image-size-persistent) + (define-key map (kbd "M--") 'helm-ff-decrease-image-size-persistent) + (define-key map (kbd "C-l") 'helm-find-files-up-one-level) + (define-key map (kbd "C-:") 'helm-ff-complete-tramp-methods) + (define-key map (kbd "C-_") 'helm-ff-undo) + (define-key map (kbd "C-r") 'helm-find-files-down-last-level) + (define-key map (kbd "C-c r") 'helm-ff-run-find-file-as-root) + (define-key map (kbd "C-x C-v") 'helm-ff-run-find-alternate-file) + (define-key map (kbd "C-c @") 'helm-ff-run-insert-org-link) + (define-key map (kbd "S-") 'helm-ff-sort-alpha) + (define-key map (kbd "S-") 'helm-ff-sort-by-newest) + (define-key map (kbd "S-") 'helm-ff-sort-by-size) + (define-key map (kbd "S-") 'helm-ff-toggle-dirs-only) + (define-key map (kbd "S-") 'helm-ff-toggle-files-only) + (define-key map (kbd "S-") 'helm-ff-sort-by-ext) + (helm-define-key-with-subkeys map (kbd "DEL") ?\d 'helm-ff-delete-char-backward + '((C-backspace . helm-ff-run-toggle-auto-update) + ([C-c DEL] . helm-ff-run-toggle-auto-update)) + nil 'helm-ff-delete-char-backward--exit-fn) + (when (fboundp 'tab-bar-mode) + (define-key map (kbd "C-c C-t") 'helm-ff-find-file-other-tab)) + map) + "Keymap for `helm-find-files'.") + +(defvar helm-read-file-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "") 'helm-cr-empty-string) + (define-key map (kbd "M-RET") 'helm-cr-empty-string) + (define-key map (kbd "C-]") 'helm-ff-run-toggle-basename) + (define-key map (kbd "C-.") 'helm-find-files-up-one-level) + (define-key map (kbd "C-l") 'helm-find-files-up-one-level) + (define-key map (kbd "C-:") 'helm-ff-complete-tramp-methods) + (define-key map (kbd "C-_") 'helm-ff-undo) + (define-key map (kbd "C-r") 'helm-find-files-down-last-level) + (define-key map (kbd "C-c h") 'helm-ff-file-name-history) + (define-key map (kbd "C-") 'helm-ff-run-toggle-auto-update) + (define-key map (kbd "C-c ") 'helm-ff-run-toggle-auto-update) + (define-key map (kbd "RET") 'helm-ff-RET) + (helm-define-key-with-subkeys map (kbd "DEL") ?\d 'helm-ff-delete-char-backward + '((C-backspace . helm-ff-run-toggle-auto-update) + ([C-c DEL] . helm-ff-run-toggle-auto-update)) + nil 'helm-ff-delete-char-backward--exit-fn) + map) + "Keymap for `helm-read-file-name'.") + +;;; User variables +;; +(defgroup helm-files nil + "Files applications and libraries for Helm." + :group 'helm) + +(defcustom helm-tramp-verbose 0 + "Just like `tramp-verbose' but specific to Helm. +When set to 0 don't show tramp messages in Helm. +If you want to have the default tramp messages set it to 3." + :type 'integer) + +(defcustom helm-ff-auto-update-initial-value nil + "Auto update when only one candidate directory is matched. +Default value when starting `helm-find-files' is nil to not +confuse new users. +For a better experience with `helm-find-files' set this to +non-nil and use C- to toggle it." + :type 'boolean) + +(defcustom helm-ff-history-max-length 100 + "Number of elements shown in `helm-find-files' history." + :type 'integer) + +(defcustom helm-ff-fuzzy-matching t + "Enable fuzzy matching for `helm-find-files' when non--nil. +See `helm-ff--transform-pattern-for-completion' for more info." + :type 'boolean) + +(defcustom helm-ff-exif-data-program "exiftran" + "Program used to extract exif data of an image file." + :type 'string) + +(defcustom helm-ff-exif-data-program-args "-d" + "Arguments used for `helm-ff-exif-data-program'." + :type 'string) + +(defcustom helm-ff-newfile-prompt-p t + "Whether Prompt or not when creating new file. +This set `ffap-newfile-prompt'." + :type 'boolean) + +(defcustom helm-ff-avfs-directory "~/.avfs" + "The default avfs directory, usually \\='~/.avfs'. +When this is set you will be able to expand archive filenames +with `C-j' inside an avfs directory mounted with mountavfs. +See ." + :type 'string) + +(defcustom helm-ff-file-compressed-list '("gz" "bz2" "zip" "7z") + "Minimal list of compressed files extension." + :type '(repeat (choice string))) + +(defcustom helm-ff-printer-list nil + "A list of available printers on your system. +When non-nil let you choose a printer to print file. +Otherwise when nil the variable `printer-name' will be used. +On Unix based systems (lpstat command needed) you don't need to +set this, `helm-ff-find-printers' will find a list of available +printers for you." + :type '(repeat (choice string))) + +(defcustom helm-ff-transformer-show-only-basename t + "Show only basename of candidates in `helm-find-files'. +This can be toggled at anytime from `helm-find-files' with \ +\\\\[helm-ff-run-toggle-basename]." + :type 'boolean) + +(defcustom helm-ff-signal-error-on-dot-files t + "Signal error when file is `.' or `..' on file deletion when non-nil. +Default is non-nil. +WARNING: Setting this to nil is unsafe and can cause deletion of +a whole tree." + :type 'boolean) + +(defcustom helm-ff-search-library-in-sexp nil + "Search for library in `require' and `declare-function' sexp." + :type 'boolean) + +(defcustom helm-tooltip-hide-delay 25 + "Hide tooltips automatically after this many seconds." + :type 'integer) + +(defcustom helm-ff-file-name-history-use-recentf nil + "Use `recentf-list' instead of `file-name-history' in `helm-find-files'." + :type 'boolean) + +(defcustom helm-ff-skip-boring-files nil + "Non-nil to skip boring files. +I.e. the files matching regexps in `helm-boring-file-regexp-list'. +This takes effect in `helm-find-files' and file completion used by +`helm-mode' i.e. `helm-read-file-name'. +Note that when non-nil this will slow down slightly `helm-find-files'." + :type 'boolean) + +(defcustom helm-ff-skip-git-ignored-files nil + "Non-nil to skip git ignored files. +This take effect only in `helm-find-files'. +Check is not done on remote files. +Note that when non-nil this will slow down slightly +`helm-find-files'." + :type 'boolean) + +(defcustom helm-ff-candidate-number-limit 5000 + "The `helm-candidate-number-limit' for `helm-find-files' and friends. +Note that when going one level up with +`\\\\[helm-find-files-up-one-level]' the +length of directory will be used instead if it is higher than +this value. This is to avoid failing to preselect the previous +directory/file if this one is situated lower than +`helm-ff-candidate-number-limit' num candidate." + :type 'integer) + +(defcustom helm-ff-up-one-level-preselect t + "Always preselect previous directory when going one level up. + +When non-nil `candidate-number-limit' source value is modified +dynamically when going one level up if the position of previous +candidate in its directory is > to +`helm-ff-candidate-number-limit'. + +It can be helpful to disable this and reduce +`helm-ff-candidate-number-limit' if you often navigate across +very large directories." + :type 'boolean) + +(defcustom helm-files-save-history-extra-sources + '("Find" "Locate" "Recentf" + "Files from Current Directory" "File Cache") + "Extras source that save candidate to `file-name-history'." + :type '(repeat (choice string))) + +(defcustom helm-find-files-before-init-hook nil + "Hook that run before initialization of `helm-find-files'." + :type 'hook) + +(defcustom helm-find-files-after-init-hook nil + "Hook that run after initialization of `helm-find-files'." + :type 'hook) + +(defcustom helm-find-files-bookmark-prefix nil + "bookmark name prefix of `helm-find-files' sessions." + :type 'string) + +(defcustom helm-ff-guess-ffap-filenames nil + "Use ffap to guess local filenames at point in `helm-find-files'. +This doesn't disable url or mail at point, see +`helm-ff-guess-ffap-urls' for this." + :type 'boolean) + +(defcustom helm-ff-guess-ffap-urls t + "Use ffap to guess local urls at point in `helm-find-files'. +This doesn't disable guessing filenames at point, see +`helm-ff-guess-ffap-filenames' for this. +See also `ffap-url-unwrap-remote' that may override this +variable." + :type 'boolean) + +(defcustom helm-ff-no-preselect nil + "When non-nil `helm-find-files' starts at root of current directory." + :type 'boolean) + +(defcustom helm-ff-allow-non-existing-file-at-point nil + "Use non existing file-at-point as initial input in `helm-find-files'." + :type 'boolean) + +(defcustom helm-find-files-ignore-thing-at-point nil + "Use only `default-directory' as default input in `helm-find-files'. +I.e. text under cursor in `current-buffer' is ignored. +Note that when non-nil you will be unable to complete filename at +point in `current-buffer'." + :type 'boolean) + +(defcustom helm-substitute-in-filename-stay-on-remote nil + "Don't switch back to local filesystem when expanding pattern with / or ~/." + :type 'boolean) + +(defcustom helm-ff-goto-first-real-dired-exceptions '(dired-goto-file) + "Dired commands that are allowed moving to first real candidate." + :type '(repeat (choice symbol))) + +(defcustom helm-mounted-network-directories nil + "A list of directories used for mounting remotes filesystem. + +When nil `helm-file-on-mounted-network-p' always return nil +otherwise check if a file is in one of these directories. + +Remote filesystem are generally mounted with sshfs." + :type '(repeat string)) + +(defcustom helm-browse-project-default-find-files-fn + (cond ((executable-find "fd") + #'helm-browse-project-fd-find-files) + ((executable-find "rg") + #'helm-browse-project-rg-find-files) + ((executable-find "ag") + #'helm-browse-project-ag-find-files) + (t #'helm-browse-project-walk-directory)) + "The default function to retrieve files in a non-vc directory. + +A function that takes a directory name as only arg." + :type 'function) + +(defcustom helm-ff-kill-or-find-buffer-fname-fn + #'helm-ff-kill-or-find-buffer-fname + "Default function used to expand non-directory filenames in `helm-find-files'. + +This variable will take effect only in `helm-find-files'. It +affects the behavior of persistent-action on filenames and +non-existing filenames. + +The default is to expand filename on first hit on +\\\\[helm-execute-persistent-action], pop buffer in +other window on second hit and finally kill this buffer on third +hit. This is very handy to create several new buffers, or when +navigating, show quickly the buffer of file to see its contents +briefly before killing it and continue navigating. + +However some users may not want this, so to disable this behaviour +just set this to `ignore' function. + +Of course you can also write your own function to do something +else." + :type 'function) + +(defcustom helm-modes-using-escaped-strings + '(eshell-mode shell-mode term-mode) + "Modes that requires string's insertion to be escaped." + :type '(repeat symbol)) + +(defcustom helm-ff-allow-recursive-deletes nil + "When \\='always don't prompt for recursive deletion of directories. +When nil, will ask for recursive deletion. +Note that when deleting multiple directories you can answer ! +when prompted to avoid being asked for next directories, so it +is probably better to not modify this variable." + :type '(choice + (const :tag "Delete non-empty directories" t) + (const :tag "Confirm for each directory" nil))) + +(defcustom helm-ff-delete-files-function #'helm-delete-marked-files + "The function to use by default to delete files. + +Default is to delete files synchronously, other choice is to +delete files asynchronously. + +BE AWARE that when deleting async you will not be warned about +recursive deletion of directories, IOW non-empty directories will +be deleted with no warnings in background!!! + +It is the function that will be used when using +`\\\\[helm-ff-run-delete-file]' from +`helm-find-files'." + :type '(choice (function :tag "Delete files synchronously." + helm-delete-marked-files) + (function :tag "Delete files asynchronously." + helm-delete-marked-files-async))) + +(defcustom helm-trash-remote-files nil + "Allow trashing remote files when non-nil. + +Trashing remote files with tramp doesn't work out of the box +unless the \\='trash-cli' package is installed. This is why trashing +remote files from Helm is disabled by default. + +Tramp is using external \\='trash' command in its `delete-file' and +`delete-directory' handlers when using +`delete-by-moving-to-trash', which is documented nowhere in +Emacs. + +If you want to enable this you will have to install the \\='trash' +command on remote (and/or locally if you want to trash as root). +On Ubuntu-based distributions it is \\='trash-cli'." + :type 'boolean) + +(defcustom helm-list-directory-function + (cl-case system-type + (gnu/linux #'helm-list-dir-external) + (berkeley-unix #'helm-list-dir-lisp) + (windows-nt #'helm-list-dir-lisp) + (t #'helm-list-dir-lisp)) + "The function used in `helm-find-files' to list remote directories. + +Actually Helm provides two functions to do this: +`helm-list-dir-lisp' and `helm-list-dir-external'. + +Using `helm-list-dir-external' will provide a similar display to +what is provided with local files i.e. colorized symlinks, +executables files etc., whereas using `helm-list-dir-lisp' will +allow colorizing only directories but it is more portable. + +NOTE: `helm-list-dir-external' needs ls and awk as dependencies. +Also the ls version installed on the remote side should support +the same arguments as the GNU/ls version, which are -A -1 -F -b +and -Q. So even if you are using a GNU/ls version locally and you +want to connect e.g. on a Freebsd server, you may have failures +due to the incompatible ls version installed on remote server. In +such case use `helm-list-dir-lisp' which works everywhere but is +slower and less featured (only directories colorized)." + :type 'function) + +(defcustom helm-ff-initial-sort-method nil + "Sort method to use when initially listing a directory. + +It is better to keep this nil globally and turn it on only when needed +otherwise it may be slightly slower specially with `ext' method which +BTW is not provided on remote files (helm will fallback on nil in such +case). +Note that this have no effect as soon as you start narrowing directory +i.e. filtering filenames inside directory." + :type '(choice + (const :tag "alphabetically" nil) + (const :tag "newest" newest) + (const :tag "size" size) + (const :tag "extensions" ext))) + +(defcustom helm-ff-rotate-image-program "exiftran" + "External program used to rotate images. +When nil and `helm-ff-display-image-native' is enabled, fallback to +`image-rotate' without modification of exif data i.e. rotation is not +persistent otherwise an error is returned when not using +`helm-ff-display-image-native' i.e. using image-dired." + :type '(choice + (const :tag "Mogrify" "mogrify") + (const :tag "Exiftran" "exiftran") + (const :tag "Jpegtran" "jpegtran"))) + +(defcustom helm-ff-rotate-image-switch '("-i") + "Options used with `helm-ff-rotate-image-program'. +If you are using Mogrify or Jpegtran mandatory option is +\"-rotate\", with Exiftran mandatory option is \"-i\"." + :type '(repeat string)) + +(defcustom helm-ff-preferred-shell-mode 'eshell-mode + "Shell to use to switch to a shell buffer from `helm-find-files'. +Possible values are `shell-mode', `eshell-mode' and `term-mode'. +This affects `\\\\[helm-ff-run-switch-to-shell]' keybinding." + :type '(choice + (const :tag "Use Eshell" eshell-mode) + (const :tag "Use Shell" shell-mode) + (const :tag "Use Shell" term-mode))) + +(defcustom helm-rsync-no-mode-line-update nil + "When non nil don't update mode-line when rsync is running. +This is useful if you display the progress bar somewhere else, +e.g. with minibuffer-line in minibuffer, in this case updating +mode-line may create flickering in other frame's mode-line." + :type 'boolean) + +(defcustom helm-rsync-switches '("-a" "-z" "-h" "-s" "--info=all2") + "Rsync options to use with HFF Rsync action. +Note: Using \"--info=all2\" allows having the name of the file +currently transfered in an help-echo in mode-line, if you use +\"--info=progress2\" you will not have this information." + :type '(repeat string)) + +(defcustom helm-rsync-percent-sign "%" + "Percentage unicode sign to use in Rsync reporter." + :type 'string) + +(defcustom helm-trash-default-directory nil + "The default trash directory. +You probably don't need to set this when using a Linux system using +standard settings. +Should be the directory file name i.e. don't add final slash. +When nil helm will compute a default value according to freedesktop +specs. +It is generally \"~/.local/share/Trash\"." + :type 'string) + +(defcustom helm-ff-lynx-style-map t + "Use arrow keys to navigate with `helm-find-files'. +Note that if you define this variable with `setq' your change +will have no effect, use customize instead." + :type 'boolean + :set (lambda (var val) + (set var val) + (if val + (progn + (define-key helm-find-files-map (kbd "") 'helm-execute-persistent-action) + (define-key helm-find-files-map (kbd "") 'helm-find-files-up-one-level) + (define-key helm-read-file-map (kbd "") 'helm-execute-persistent-action) + (define-key helm-read-file-map (kbd "") 'helm-find-files-up-one-level)) + (define-key helm-find-files-map (kbd "") nil) + (define-key helm-find-files-map (kbd "") nil) + (define-key helm-read-file-map (kbd "") nil) + (define-key helm-read-file-map (kbd "") nil)))) + +(defcustom helm-ff-DEL-up-one-level-maybe nil + "Use DEL to maybe go up one level when non nil. + +Going up one level works only when pattern is a directory endings +with \"/\", otherwise this command deletes char backward. + +When nil always delete char backward." + :type 'boolean) + +(defcustom helm-ff-display-image-native t + "Use native `image-mode' when non nil. + +You should use this only with Emacs>= 27 and `image-auto-resize' +enabled to have images resized properly. When this is enabled, +you have new commands to zoom in/out images. See +`image-transform-resize' and `image-auto-resize'. Otherwise, +when nil `image-dired' is used, using imagemagick as backend. +NOTE: On Emacs-29 `image-dired' is no more using external program +image-magick to display image, so this is used inconditionally even +when value is nil." + :type 'boolean) + +(defcustom helm-ff-reset-filters-on-update t + "Reset filter variables when changing directory. +When filtering directories/files only, switch back to a \"show all\" view +when moving out of directory when non nil." + :type 'boolean) + +(defcustom helm-ff-eshell-unwanted-aliases nil + "A list of eshell aliases to not display." + :type '(repeat string)) + +(defcustom helm-find-files-actions + (helm-make-actions + "Find File" 'helm-find-file-or-marked + "Find file in Dired" 'helm-point-file-in-dired + "View file" 'view-file + "Query replace fnames on marked `M-@'" 'helm-ff-query-replace-fnames-on-marked + "Marked files in dired `C-x C-q, C-u wdired'" 'helm-marked-files-in-dired + "Query replace contents on marked `M-%'" 'helm-ff-query-replace + "Query replace regexp contents on marked `C-M-%'" 'helm-ff-query-replace-regexp + "Attach file(s) to mail buffer `C-c C-a'" 'helm-ff-mail-attach-files + "Serial rename files" 'helm-ff-serial-rename + "Serial rename by symlinking files" 'helm-ff-serial-rename-by-symlink + "Serial rename by copying files" 'helm-ff-serial-rename-by-copying + "Open file with default tool" 'helm-open-file-with-default-tool + "Find file in hex dump" 'hexl-find-file + "Browse project `C-x C-d'" 'helm-ff-browse-project + "Complete at point `C-c i'" 'helm-insert-file-name-completion-at-point + "Insert as org link `C-c @'" 'helm-files-insert-as-org-link + "Find shell command `C-c /'" 'helm-ff-find-sh-command + "Fd shell command (C-/)" 'helm-ff-fd + "Find files in file" 'helm-find-files-in-file + "Add marked files to file-cache" 'helm-ff-cache-add-file + "Open file externally `C-c C-x, C-u to choose'" 'helm-open-file-externally + "Grep File(s) `C-s, C-u Recurse'" 'helm-find-files-grep + "Grep current directory with AG `M-g a, C-u select type'" 'helm-find-files-ag + "Git grep `M-g g, C-u from root'" 'helm-ff-git-grep + "Zgrep File(s) `M-g z, C-u Recurse'" 'helm-ff-zgrep + "Pdf Grep File(s)" 'helm-ff-pdfgrep + "Gid `M-g i'" 'helm-ff-gid + "Switch to Eshell `M-e'" 'helm-ff-switch-to-shell + "Etags `M-., C-u reload tag file'" 'helm-ff-etags-select + "Eshell command on file(s) `M-!, C-u take all marked as arguments.'" + 'helm-find-files-eshell-command-on-file + "Find file as root `C-c r'" 'helm-find-file-as-root + "Find alternate file `C-x C-v'" 'find-alternate-file + "Ediff File `C-c ='" 'helm-find-files-ediff-files + "Ediff Merge File `M-='" 'helm-find-files-ediff-merge-files + (lambda () (format "Delete File(s)%s `M-D' (C-u reverse trash)" + (if (eq helm-ff-delete-files-function + 'helm-delete-marked-files-async) + " async" ""))) + 'helm-ff-delete-files + "Touch File(s) `M-T'" 'helm-ff-touch-files + "Copy file(s) `M-C, C-u to follow'" 'helm-find-files-copy + (lambda () + (and (executable-find "rsync") + "Rsync file(s) `M-V' (C-u edit command)")) + 'helm-find-files-rsync + "Rename file(s) `M-R, C-u to follow'" 'helm-find-files-rename + "Backup files" 'helm-find-files-backup + "Symlink files(s) `M-S, C-u to follow'" 'helm-find-files-symlink + "Relsymlink file(s) `M-Y, C-u to follow'" 'helm-find-files-relsymlink + "Hardlink file(s) `M-H, C-u to follow'" 'helm-find-files-hardlink + "Find file other window `C-c o'" 'helm-find-files-other-window + "Find file other frame `C-c C-o'" 'find-file-other-frame + (lambda () (and (fboundp 'tab-bar-mode) + "Find file other tab `C-c C-t'")) + 'find-file-other-tab + "Print File `C-c p, C-u to refresh'" 'helm-ff-print + "Locate `C-x C-f, C-u to specify locate db'" 'helm-ff-locate) + "Actions for `helm-find-files'." + :type '(alist :key-type string :value-type function)) + +(defcustom helm-dwim-target nil + "Default target directory for file actions. + +Define the directory where you want to start navigating for the +target directory when copying, renaming, etc.. You can use the +`default-directory' of `next-window', the visited directory, the +current `default-directory' or have completion on all the +directories belonging to each visible windows." + :type '(radio :tag "Define default target directory for file actions." + (const :tag "Directory belonging to next window" + next-window) + (const :tag "Completion on directories belonging to each window" + completion) + (const :tag "Use initial directory or `default-directory'" + default-directory) + (const :tag "Use visited directory" + nil))) + +(defcustom helm-ff-use-notify t + "Watch directories visited with `helm-find-files' when non nil. +If your system have no file notification package available turn this +to nil to avoid error messages when using `helm-find-files'." + :type 'boolean + :set (lambda (var val) + (set-default var val) + (unless (symbol-value var) + (cl-loop for dir being the hash-keys of helm-ff--file-notify-watchers + do (remhash dir helm-ff--list-directory-cache))))) + +(defcustom helm-ff-inotify-unsupported-methods '("adb") + "Tramp methods unsupported by file-notify." + :type '(repeat string)) + +(defcustom helm-ff-image-cache-max-len 5 + "The last seen image number to keep in cache." + :type 'integer) + +(defcustom helm-ff-image-cache-max-len 5 + "The last seen image number to keep in cache." + :type 'integer) + +(defcustom helm-ff-slideshow-default-delay 3 + "Delay in seconds between each image in slideshow." + :type 'integer) + +(defcustom helm-file-name-history-hide-deleted nil + "Hide deleted files in file-name-history when non nil. + +This can be toggled at any time from `helm-ff-file-name-history' with \ +\\\\[helm-file-name-history-show-or-hide-deleted]." + :type 'boolean) + +;;; Faces +;; +;; +(defgroup helm-files-faces nil + "Customize the appearance of helm-files." + :prefix "helm-" + :group 'helm-files + :group 'helm-faces) + +(defface helm-ff-prefix + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "yellow" :foreground "black")) + "Face used to prefix new file or url paths in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-executable + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "green")) + "Face used for executable files in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-suid + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "red" :foreground "white")) + "Face used for suid files in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-directory + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "DarkRed" :background "LightGray")) + "Face used for directories in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-dotted-directory + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "black" :background "DimGray")) + "Face used for dotted directories in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-dotted-symlink-directory + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "DarkOrange" :background "DimGray")) + "Face used for dotted symlinked directories in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-symlink + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-comment-face)) + "Face used for symlinks in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-invalid-symlink + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "black" :background "red")) + "Face used for invalid symlinks in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-denied + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "red" :background "black")) + "Face used for non accessible files in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-file + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-builtin-face)) + "Face used for file names in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-nofile + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit helm-ff-file)) + "Face used for file names in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-truename + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-string-face)) + "Face used for symlink truenames in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-dirs + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-function-name-face)) + "Face used for file names in recursive dirs completion in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-socket + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "DeepPink")) + "Face used for socket files in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-pipe + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "yellow" :background "black")) + "Face used for named pipes and character device files in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-file-extension + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "magenta")) + "Face used for file extensions in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-ff-backup-file + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "DimGray")) + "Face used for backup files in `helm-find-files'." + :group 'helm-files-faces) + +(defface helm-history-deleted + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit helm-ff-invalid-symlink)) + "Face used for deleted files in `file-name-history'." + :group 'helm-files-faces) + +(defface helm-history-remote + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Indianred1")) + "Face used for remote files in `file-name-history'." + :group 'helm-files-faces) + +(defface helm-delete-async-message + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "yellow")) + "Face used for mode-line message." + :group 'helm-files-faces) + +(defface helm-ff-rsync-progress + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-warning-face)) + "Face used for rsync mode-line indicator." + :group 'helm-files-faces) + + +;;; Helm-find-files +;; +;; +(defvar helm-source-find-files nil + "The main source to browse files. +Should not be used among other sources.") + +(defclass helm-source-ffiles (helm-source-sync) + ((header-name + :initform (lambda (name) + (concat name (substitute-command-keys + helm-find-files-doc-header)))) + (init + :initform (lambda () + (setq helm-ff-auto-update-flag + helm-ff-auto-update-initial-value) + (setq helm-ff--auto-update-state + helm-ff-auto-update-flag) + (helm-set-local-variable 'bookmark-make-record-function + #'helm-ff-make-bookmark-record) + (require 'helm-external))) + (candidates :initform 'helm-find-files-get-candidates) + (update :initform (lambda () + (remhash helm-ff-default-directory + helm-ff--list-directory-cache))) + (match-on-real :initform t) + (filtered-candidate-transformer + :initform '(helm-ff-fct + helm-ff-maybe-show-thumbnails + ;; These next two have to be called after + ;; `helm-ff-fct' as they use only cons cell candidates. + helm-ff-directories-only + helm-ff-files-only + helm-ff-sort-candidates)) + (persistent-action-if :initform 'helm-find-files-persistent-action-if) + (persistent-help :initform "Hit1 Expand Candidate, Hit2 or (C-u) Find file") + (help-message :initform 'helm-ff-help-message) + (mode-line :initform (list "File(s)" helm-mode-line-string)) + (volatile :initform t) + (cleanup :initform 'helm-find-files-cleanup) + (migemo :initform t) + (nohighlight :initform t) + (keymap :initform 'helm-find-files-map) + (candidate-number-limit :initform 'helm-ff-candidate-number-limit) + (action-transformer + :initform 'helm-find-files-action-transformer) + (action :initform 'helm-find-files-actions) + (before-init-hook :initform 'helm-find-files-before-init-hook) + (after-init-hook :initform 'helm-find-files-after-init-hook) + (group :initform 'helm-files))) + +;; Bookmark handlers. +;; +(defun helm-ff-make-bookmark-record () + "The `bookmark-make-record-function' for `helm-find-files'." + (with-helm-buffer + `((filename . ,helm-ff-default-directory) + (presel . ,(helm-get-selection)) + (handler . helm-ff-bookmark-jump)))) + +(defun helm-ff-bookmark-jump (bookmark) + "bookmark handler for `helm-find-files'." + (let ((fname (bookmark-prop-get bookmark 'filename)) + (presel (bookmark-prop-get bookmark 'presel))) + ;; Force tramp connection with `file-directory-p' before lauching + ;; hff otherwise the directory name is inserted on top before + ;; tramp starts and display candidates. FNAME is here always a + ;; directory. + (when (file-directory-p fname) + (helm-find-files-1 fname + (format helm-ff-last-expanded-candidate-regexp + (if helm-ff-transformer-show-only-basename + (regexp-quote (helm-basename presel)) + (regexp-quote presel))))))) + +(defun helm-ff-bookmark-set () + "Record `helm-find-files' session in bookmarks." + (interactive) + (with-helm-alive-p + (with-helm-buffer + (bookmark-set + (concat helm-find-files-bookmark-prefix + (abbreviate-file-name helm-ff-default-directory)))) + (message "Helm find files session bookmarked! "))) +(put 'helm-ff-bookmark-set 'helm-only t) + +(defun helm-dwim-target-directory () + "Try to return a suitable directory according to `helm-dwim-target'." + (with-selected-window (or + ;; Try next-window if current-buffer has been + ;; killed during this session probably by C-d. + (get-buffer-window helm-current-buffer) + (next-window (helm-window) 1)) + (let ((wins (remove (get-buffer-window helm-marked-buffer-name) + (window-list)))) + (expand-file-name + (cond (;; Provide completion on all the directory belonging to + ;; visible windows if some. + (and (cdr wins) + (eq helm-dwim-target 'completion)) + (helm-comp-read "Browse target starting from: " + (append (list (or (car-safe helm-ff-history) + default-directory) + default-directory) + (cl-loop for w in wins collect + (with-selected-window w + default-directory))))) + ;; Use default-directory of next-window. + ((and (cdr wins) + (eq helm-dwim-target 'next-window)) + (with-selected-window (next-window) + default-directory)) + ;; Always use default-directory when only one window. + ((and (null (cdr wins)) + (eq helm-dwim-target 'default-directory)) + default-directory) + ;; Use the visited directory. + ((or (null (cdr wins)) + (null helm-dwim-target)) + ;; Using the car of *ff-history allow + ;; staying in the directory visited instead of + ;; current. + (or (car-safe helm-ff-history) default-directory))))))) + +(defsubst helm-ff--file-directory-p (file) + (if (file-remote-p file) + (get-text-property 1 'helm-ff-dir file) + (file-directory-p file))) + +(defun helm-ff--count-and-collect-dups (files) + (cl-loop with dups = (make-hash-table :test 'equal) + for f in files + for file = (if (helm-ff--file-directory-p f) + (concat (helm-basename f) "/") + (helm-basename f)) + for count = (gethash file dups) + if count do (puthash file (1+ count) dups) + else do (puthash file 1 dups) + finally return (cl-loop for k being the hash-keys in dups + using (hash-value v) + if (> v 1) + collect (format "%s(%s)" k v) + else + collect k))) + +(defun helm-find-files-do-action (action &optional target) + "Generic function for creating actions from `helm-source-find-files'. +ACTION can be `rsync' or any action supported by `helm-dired-action'." + (require 'dired-async) + (when (eq action 'rsync) + (cl-assert (executable-find "rsync") nil "No command named rsync")) + (let* ((rsync-switches + (when (and (eq action 'rsync) + helm-current-prefix-arg) + (cdr (split-string + (read-string "Run rsync like this: " + (mapconcat + 'identity + (cons "rsync" helm-rsync-switches) " ") + 'helm-rsync-command-history))))) + (ifiles (mapcar 'expand-file-name ; Allow modify '/foo/.' -> '/foo' + (helm-marked-candidates :with-wildcard t))) + (cand (unless (cdr ifiles) (helm-get-selection))) ; preselection. + (prefarg helm-current-prefix-arg) + (prompt (format "%s %s file(s) %s: " + (if (and (and (fboundp 'dired-async-mode) + dired-async-mode) + (not (eq action 'rsync)) + (null prefarg)) + (concat "Async " (symbol-name action)) + (capitalize (symbol-name action))) + (length ifiles) + (if (memq action '(symlink relsymlink hardlink)) + "from" "to"))) + helm-ff--move-to-first-real-candidate + helm-display-source-at-screen-top ; prevent setting window-start. + helm-ff-auto-update-initial-value + ;; It is not possible to rename a file to a boring name when + ;; helm-ff-skip-boring-files is enabled + helm-ff-skip-boring-files + ;; If HFF is using a frame use a frame as well. + (helm-actions-inherit-frame-settings t) + helm-use-frame-when-more-than-two-windows + (dest (or target + (with-helm-display-marked-candidates + helm-marked-buffer-name + (helm-ff--count-and-collect-dups ifiles) + (with-helm-current-buffer + (helm-read-file-name + prompt + :preselect (when cand + (format helm-ff-last-expanded-candidate-regexp + (regexp-quote + (if helm-ff-transformer-show-only-basename + (helm-basename cand) cand)))) + :initial-input (helm-dwim-target-directory) + :history (helm-find-files-history nil :comp-read nil)))))) + (dest-dir-p (file-directory-p dest)) + (dest-dir (helm-basedir dest))) + (unless (or dest-dir-p (file-directory-p dest-dir)) + (when (y-or-n-p (format "Create directory `%s'? " dest-dir)) + (make-directory dest-dir t))) + (if (eq action 'rsync) + (helm-rsync-copy-files ifiles dest rsync-switches) + (helm-dired-action + dest :files ifiles :action action :follow prefarg)))) + +;; Rsync +;; +(defun helm-rsync-remote2rsync (file) + (if (file-remote-p file) + (let ((localname (directory-file-name + (expand-file-name (file-remote-p file 'localname)))) + (user (file-remote-p file 'user)) + ;; Tramp name may contain port e.g. /ssh:host#2222:/foo. + (host (replace-regexp-in-string + "#[0-9]+" "" (file-remote-p file 'host)))) + (if user + (format "%s@%s:%s" user host (shell-quote-argument localname)) + (format "%s:%s" host (shell-quote-argument localname)))) + (shell-quote-argument + (directory-file-name + (expand-file-name file))))) + +(defun helm-rsync-format-mode-line-str (proc) + (helm-aif (and (process-live-p proc) + (assoc-default proc helm-rsync-progress-str-alist)) + (progn + ;; When rsync progress bar stop for some reason (e.g. rsync + ;; takes time to finalize writing file to disk), no output is + ;; coming from filter process, as a result the progress bar + ;; disapear for a while giving no information to user while + ;; the rsync process continues, so keep printing the last valid + ;; progress bar (stored in `helm-rsync--last-progress-bar-alist') + ;; instead of sending empty string. + (unless (equal it "") + (push (cons proc it) helm-rsync--last-progress-bar-alist)) + (format " [%s]" (propertize + (or (assoc-default proc helm-rsync--last-progress-bar-alist) + ;; Avoid (wrong-type-argument stringp + ;; nil) when process is not ready. + "") + 'face 'helm-ff-rsync-progress))))) + +(defun helm-rsync-mode-line (proc) + "Add Rsync progress to the mode line." + (or global-mode-string (setq global-mode-string '(""))) + (unless (member `(:eval (helm-rsync-format-mode-line-str ,proc)) + global-mode-string) + (setq global-mode-string + (append global-mode-string + `((:eval (helm-rsync-format-mode-line-str ,proc))))))) + +(defun helm-rsync-restore-mode-line (proc) + "Restore the mode line when Rsync finishes." + (setq global-mode-string + (remove `(:eval (helm-rsync-format-mode-line-str ,proc)) + global-mode-string)) + (setq helm-rsync--last-progress-bar-alist nil) + (force-mode-line-update)) + +(defun helm-rsync-copy-files (files dest &optional switches) + "Send FILES to DEST using Rsync with SWITCHES as arguments. + +DEST must be a directory. SWITCHES when unspecified default to +`helm-rsync-switches'." + (cl-assert (file-directory-p dest) t) + (setq files (cl-loop for f in files + collect (helm-rsync-remote2rsync f)) + dest (helm-rsync-remote2rsync dest)) + (let* ((buf (generate-new-buffer-name helm-rsync-process-buffer)) + (port (when (helm-aand (file-remote-p dest 'host) + (string-match "#\\([0-9]+\\)" it)) + (match-string 1))) + (proc (start-process-shell-command + "rsync" buf + (format "rsync %s" + (mapconcat + 'identity + (append (or switches helm-rsync-switches) + (and port + ;; Add automatically port + ;; specified in tramp name + ;; unless user already specified + ;; it himself with the -e option + ;; by editing command. + (and switches + (cl-loop for arg in switches never + (string-match-p + "\\`-e" arg))) + (list (format "-e 'ssh -p %s'" + port))) + files (list dest)) + " "))))) + (helm-rsync-mode-line proc) + (set-process-sentinel + proc `(lambda (process event) + (cond ((string= event "finished\n") + (message "%s copied %s files" + (capitalize (process-name process)) + ,(length files))) + (t (error "Process %s %s with code %s" + (process-name process) + (process-status process) + (process-exit-status process)))) + (setq helm-rsync-progress-str-alist + (delete (assoc process helm-rsync-progress-str-alist) + helm-rsync-progress-str-alist)) + (helm-rsync-restore-mode-line process) + (force-mode-line-update))) + (set-process-filter proc #'helm-rsync-process-filter))) + +(defun helm-rsync-process-filter (proc output) + "Filter process function used by `helm-rsync-copy-files'." + (let ((inhibit-read-only t) + fname progbar) + (with-current-buffer (process-buffer proc) + (when (string-match comint-password-prompt-regexp output) + ;; FIXME: Fully not tested and + ;; use an agent or auth-source + ;; or whatever to get password if + ;; available. + (process-send-string + proc (concat (read-passwd (match-string 0 output)) "\n"))) + ;; Extract the progress bar. + (with-temp-buffer + (insert output) + (when (re-search-backward "[[:cntrl:]]" nil t) + (setq progbar (buffer-substring-no-properties + (match-end 0) (point-max))))) + ;; Insert the text, advancing the process marker. + (save-excursion + (goto-char (process-mark proc)) + (insert output) + (set-marker (process-mark proc) (point))) + (goto-char (process-mark proc)) + ;; Extract the file name currently + ;; copied (Imply --info=all2 or all1). + (save-excursion + (when (re-search-backward "^[^[:cntrl:]]" nil t) + (setq fname (helm-basename + (buffer-substring-no-properties + (point) (point-at-eol)))))) + ;; Now format the string for the mode-line. + (let ((ml-str (mapconcat 'identity + (split-string + (replace-regexp-in-string + "%" helm-rsync-percent-sign + progbar) + " " t) + " "))) + (setq ml-str (propertize ml-str 'help-echo + (format "%s->%s" (process-name proc) fname))) + ;; Now associate the formatted + ;; progress-bar string with process. + (helm-aif (assoc proc helm-rsync-progress-str-alist) + (setcdr it ml-str) + (setq helm-rsync-progress-str-alist + (push (cons proc ml-str) helm-rsync-progress-str-alist))))) + ;; Finally update mode-line. + (unless helm-rsync-no-mode-line-update + (force-mode-line-update)))) + +(defun helm-ff-kill-rsync-process (process) + "Kill rsync process PROCESS. + +When called interactively prompt user with completion when more than +one process." + (interactive (list (get-process + (helm-comp-read + "Kill rsync process: " + (mapcar (lambda (x) + (process-name (car x))) + helm-rsync-progress-str-alist) + :exec-when-only-one t)))) + (with-current-buffer (process-buffer process) + (delete-process process) + (kill-buffer)) + (setq helm-rsync-progress-str-alist + (delete (assoc process helm-rsync-progress-str-alist) + helm-rsync-progress-str-alist))) + +(defun helm-find-files-rsync (_candidate) + "Rsync files from `helm-find-files'." + (helm-find-files-do-action 'rsync)) + +(defun helm-find-files-copy (_candidate) + "Copy files from `helm-find-files'." + (helm-find-files-do-action 'copy)) + +(defun helm-find-files-backup (_candidate) + "Backup files from `helm-find-files'. +This reproduce the behavior of \"cp --backup=numbered from to\"." + (cl-assert (and (fboundp 'dired-async-mode) dired-async-mode) nil + "Backup only available when `dired-async-mode' is enabled") + (helm-find-files-do-action 'backup)) + +(defun helm-find-files-rename (_candidate) + "Rename files from `helm-find-files'." + (helm-find-files-do-action 'rename)) + +(defun helm-find-files-symlink (_candidate) + "Symlink files from `helm-find-files'." + (helm-find-files-do-action 'symlink)) + +(defun helm-find-files-relsymlink (_candidate) + "Relsymlink files from `helm-find-files'." + (helm-find-files-do-action 'relsymlink)) + +(defun helm-find-files-hardlink (_candidate) + "Hardlink files from `helm-find-files'." + (helm-find-files-do-action 'hardlink)) + +(defun helm-find-files-other-window (_candidate) + "Keep current-buffer and open files in separate windows. +When a prefix arg is detected files are opened in a vertical +windows layout." + (let* ((files (helm-marked-candidates)) + (buffers (mapcar 'find-file-noselect files))) + (helm-window-show-buffers buffers t))) + +(defun helm-find-files-byte-compile (_candidate) + "Byte compile elisp files from `helm-find-files'." + (let ((files (helm-marked-candidates :with-wildcard t)) + (parg helm-current-prefix-arg)) + (cl-loop for fname in files + do (condition-case _err + (with-no-warnings + (byte-compile-file fname parg)) + (wrong-number-of-arguments + ;; Emacs-28 accepts only one arg. + (byte-compile-file fname) + (when parg (load fname))))))) + +(defun helm-find-files-load-files (_candidate) + "Load elisp files from `helm-find-files'." + (let ((files (helm-marked-candidates :with-wildcard t))) + (cl-loop for fname in files + do (load fname)))) + +(defun helm-find-files-ediff-files-1 (candidate &optional merge) + "Generic function to ediff/merge files in `helm-find-files'." + (let* ((helm-dwim-target 'next-window) + (bname (helm-basename candidate)) + (marked (helm-marked-candidates :with-wildcard t)) + (prompt (if merge "Ediff Merge `%s' With File: " + "Ediff `%s' With File: ")) + (fun (if merge 'ediff-merge-files 'ediff-files)) + (input (helm-dwim-target-directory)) + (presel (if helm-ff-transformer-show-only-basename + (helm-basename candidate) + (expand-file-name + (helm-basename candidate) + input)))) + (if (= (length marked) 2) + (funcall fun (car marked) (cadr marked)) + (funcall fun candidate (helm-read-file-name + (format prompt bname) + :initial-input input + :preselect presel))))) + +(defun helm-find-files-ediff-files (candidate) + (helm-find-files-ediff-files-1 candidate)) + +(defun helm-find-files-ediff-merge-files (candidate) + (helm-find-files-ediff-files-1 candidate 'merge)) + +(defun helm-find-files-grep (_candidate) + "Default action to grep files from `helm-find-files'." + (helm-do-grep-1 (helm-marked-candidates :with-wildcard t) + helm-current-prefix-arg)) + +(defun helm-ff-git-grep (_candidate) + "Default action to git-grep `helm-ff-default-directory'." + (helm-grep-git-1 helm-ff-default-directory helm-current-prefix-arg)) + +(defun helm-find-files-ag (_candidate) + (helm-grep-ag helm-ff-default-directory + helm-current-prefix-arg)) + +(defun helm-ff-zgrep (_candidate) + "Default action to zgrep files from `helm-find-files'." + (helm-ff-zgrep-1 (helm-marked-candidates :with-wildcard t) helm-current-prefix-arg)) + +(defun helm-ff-pdfgrep (_candidate) + "Default action to pdfgrep files from `helm-find-files'." + (let* ((recurse nil) + (cands (cl-loop for file in (helm-marked-candidates :with-wildcard t) + for dir = (file-directory-p file) + when dir do (setq recurse t) + when (or dir + (string= (file-name-extension file) "pdf") + (string= (file-name-extension file) "PDF")) + collect file))) + (when cands + (helm-do-pdfgrep-1 cands recurse)))) + +(defun helm-ff-etags-select (candidate) + "Default action to jump to etags from `helm-find-files'." + (when (get-buffer helm-action-buffer) + (kill-buffer helm-action-buffer)) + (let* ((source-name (assoc-default 'name (helm-get-current-source))) + (default-directory (if (string= source-name "Find Files") + helm-ff-default-directory + (file-name-directory candidate)))) + (helm-etags-select helm-current-prefix-arg))) + +;;; Eshell command on file +;; +(defvar eshell-command-aliases-list nil) +(defvar helm-eshell-command-on-file-input-history nil) +(defvar helm-eshell-command-on-file-history nil) +(cl-defun helm-find-files-eshell-command-on-file-1 (&optional map) + "Run `eshell-command' on CANDIDATE or marked candidates. +This is done possibly with an Eshell alias. If no alias found, +you can type in an Eshell command. + +Only aliases accepting a file as argument at the end of command +line are collected, i.e. aliases ending with \"$1\" or \"$*\". + +Basename of CANDIDATE can be a wild-card. +E.g. you can do \"eshell-command command *.el\" +Where \"*.el\" is the CANDIDATE. + +It is possible to do eshell-command command like this: \"command %s some more args\". + +If MAP is given run `eshell-command' on all marked files at once, +Otherwise, run `eshell-command' on each marked files. +In other terms, with a prefix arg do on the three marked files +\"foo\" \"bar\" \"baz\": + +\"eshell-command command foo bar baz\" + +otherwise do + +\"eshell-command command foo\" +\"eshell-command command bar\" +\"eshell-command command baz\" + +Note: +You have to setup some aliases in Eshell with the `alias' command +or by editing yourself the file `eshell-aliases-file' to make +this working." + (require 'helm-adaptive) + (require 'em-alias) (eshell-read-aliases-list) + (unless (> emacs-major-version 27) + ;; This advice have been merged in emacs-28. + (advice-add 'eshell-eval-command :override #'helm--advice-eshell-eval-command)) + (when (or eshell-command-aliases-list + (y-or-n-p "No eshell aliases found, run eshell-command without alias anyway? ")) + (let* ((cand-list (helm-marked-candidates :with-wildcard t)) + (default-directory (or helm-ff-default-directory + ;; If candidate is an url *-ff-default-directory is nil + ;; so keep value of default-directory. + default-directory)) + helm-display-source-at-screen-top + (helm-actions-inherit-frame-settings t) + helm-use-frame-when-more-than-two-windows + (command + (with-helm-display-marked-candidates + helm-marked-buffer-name + (helm-ff--count-and-collect-dups + (mapcar 'helm-basename cand-list)) + (with-helm-current-buffer + (helm-comp-read + "Command: " + (cl-loop with len = 0 + with aliases = + (cl-loop for (a c) in (eshell-read-aliases-list) + for len-key = (length a) + when + (and (string-match + "\\(\\$1\\|\\$\\*\\)" + c) + (not (member a helm-ff-eshell-unwanted-aliases))) + do (when (> len-key len) (setq len len-key)) + and collect (list a c)) + for (a c) in aliases + collect (cons + (concat (propertize + a 'face 'font-lock-keyword-face) + (make-string (1+ (- len (length a))) ? ) + c) + a)) + :fc-transformer #'helm-adaptive-sort + :buffer "*helm eshell on file*" + :name "Eshell command" + :mode-line + '("Eshell alias" + "C-h m: Help, \\[universal-argument]: Insert output at point") + :help-message 'helm-esh-help-message + :history 'helm-eshell-command-on-file-history + :raw-history t + :input-history + 'helm-eshell-command-on-file-input-history)))) + (alias-value (car (assoc-default command eshell-command-aliases-list))) + cmd-line) + (if (or (equal helm-current-prefix-arg '(16)) + (equal map '(16))) + ;; Two time C-u from `helm-comp-read' mean print to current-buffer. + ;; i.e `eshell-command' will use this value. + (setq current-prefix-arg '(16)) + ;; Else reset the value of `current-prefix-arg' + ;; to avoid printing in current-buffer. + (setq current-prefix-arg nil)) + (if (and (or + ;; One prefix-arg have been passed before `helm-comp-read'. + ;; If map have been set with C-u C-u (value == '(16)) + ;; ignore it. + (and map (equal map '(4))) + ;; One C-u from `helm-comp-read'. + (equal helm-current-prefix-arg '(4)) + ;; An alias that finish with $* + (and alias-value + ;; If command is an alias be sure it accept + ;; more than one arg i.e $*. + (string-match "\\$\\*" alias-value))) + (cdr cand-list) + (and alias-value + ;; Command is an alias and accept only one arg. + (not (string-match "\\$1" alias-value)))) + + ;; Run eshell-command with ALL marked files as argument. + ;; This wont work on remote files, because tramp handlers depend + ;; on `default-directory' (limitation). + (let ((mapfiles (mapconcat 'shell-quote-argument cand-list " "))) + (if (string-match "%s" command) + (setq cmd-line (format command mapfiles)) ; See [1] + (setq cmd-line (format "%s %s" command mapfiles))) + (eshell-command cmd-line)) + + ;; Run eshell-command sequencially on EACH marked files. + ;; To work with tramp handler we have to call + ;; COMMAND on basename of each file, using + ;; its basedir as `default-directory'. + (unwind-protect + (progn + (cl-loop for f in cand-list + for n from 1 + for dir = (and (not (string-match helm--url-regexp f)) + (helm-basedir f)) + ;; We can use basename here as the command will run + ;; under default-directory. + ;; This allows running e.g. + ;; "tar czvf test.tar.gz %s/*" without creating + ;; an archive expanding from /home. + for file = (shell-quote-argument + (if (string-match helm--url-regexp f) + f (helm-basename f))) + ;; \@ => placeholder for file without extension. + ;; \# => placeholder for incremental number. + for fcmd = (helm-aand command + (replace-regexp-in-string + "\\\\#" (format "%03d" n) + it t t) + (replace-regexp-in-string + "\\\\@" + (regexp-quote + (file-name-sans-extension file)) + it t t)) + for com = (if (string-match "%s" fcmd) + ;; [1] This allows to enter other args AFTER filename + ;; i.e + (format fcmd file) + (format "%s %s" fcmd file)) + do (let ((default-directory (or dir default-directory))) + (eshell-command com)))) + ;; Async process continues running but doesn't need anymore + ;; the advice at this point (see the `eshell-eval-command' + ;; call in `eshell-command'). + (unless (> emacs-major-version 27) + (advice-remove 'eshell-eval-command #'helm--advice-eshell-eval-command))))))) + +(defun helm--advice-eshell-eval-command (command &optional input) + "Fix return value when command ends with \"&\"." + ;; Fix this emacs commit which is plain wrong as it returns + ;; either nil or an error (double because format spec doesn't + ;; always match specifier) whereas it should return either a + ;; single element (CAR DELIM) or DELIM itself if the car of + ;; DELIM is a process. + ;; This prevent running eshell-command async when needed i.e. when + ;; command ends with "&". + ;; + ;; UPDATE: This have now been merged in Emacs-28. + ;; + ;; 6b6f91b357f6fe2f1e0d72f046a1b8d8a2d6d8c3 + ;; Author: John Wiegley + ;; AuthorDate: Fri May 27 02:57:18 2005 +0000 + ;; Commit: John Wiegley + ;; CommitDate: Fri May 27 02:57:18 2005 +0000 + (if eshell-current-command + ;; we can just stick the new command at the end of the current + ;; one, and everything will happen as it should + (setcdr (last (cdr eshell-current-command)) + (list `(let ((here (and (eobp) (point)))) + ,(and input + `(insert-and-inherit ,(concat input "\n"))) + (if here + (eshell-update-markers here)) + (eshell-do-eval ',command)))) + (and eshell-debug-command + (with-current-buffer (get-buffer-create "*eshell last cmd*") + (erase-buffer) + (insert "command: \"" input "\"\n"))) + (setq eshell-current-command command) + (let* ((delim (catch 'eshell-incomplete + (eshell-resume-eval))) + (val (car-safe delim))) + ;; If the return value of `eshell-resume-eval' is wrapped in a + ;; list, it indicates that the command was run asynchronously. + ;; In that case, unwrap the value before checking the delimiter + ;; value. + (if (and val + (not (processp val)) + (not (eq val t))) + (error "Unmatched delimiter: %S" val) + ;; Eshell-command expect a list like () to know if the + ;; command should be async or not. + (or (and (processp val) delim) val))))) + +(defun helm-find-files-eshell-command-on-file (_candidate) + "Run `eshell-command' on CANDIDATE or marked candidates. +See `helm-find-files-eshell-command-on-file-1' for more info." + (helm-find-files-eshell-command-on-file-1 helm-current-prefix-arg)) + +(defun helm-ff--shell-interactive-buffer-p (buffer &optional mode) + (with-current-buffer buffer + (when (eq major-mode (or mode 'eshell-mode)) + (let ((next-prompt-fn (cl-case major-mode + (shell-mode #'comint-next-prompt) + (eshell-mode #'eshell-next-prompt) + (term-mode #'term-next-prompt)))) + (save-excursion + (goto-char (point-min)) + (funcall next-prompt-fn 1) + (null (eql (point) (point-min)))))))) + +(defun helm-ff-switch-to-shell (_candidate) + "Switch to a shell buffer and cd to `helm-ff-default-directory'. +Set your preferred shell mode in `helm-ff-preferred-shell-mode'. + +With a numeric prefix arg switch to numbered shell buffer, if no +prefix arg provided and more than one shell buffer exists, provide +completions on those buffers. If only one shell buffer exists, +switch to this one, if no shell buffer exists or if the numeric +prefix arg shell buffer doesn't exists, create it and switch to it." + ;; Reproduce the Emacs-25 behavior to be able to edit and send + ;; command in term buffer. + (let (term-char-mode-buffer-read-only ; Emacs-25 behavior. + term-char-mode-point-at-process-mark ; Emacs-25 behavior. + (cd-eshell (lambda () + (eshell/cd helm-ff-default-directory) + (eshell-reset))) + (cd-shell + (lambda () + (goto-char (point-max)) + (when (eq helm-ff-preferred-shell-mode 'shell-mode) + (comint-delete-input)) + (insert (format "cd %s" + (shell-quote-argument + (or (file-remote-p + helm-ff-default-directory 'localname) + helm-ff-default-directory)))) + (cl-case helm-ff-preferred-shell-mode + (shell-mode (comint-send-input)) + (term-mode (progn (term-char-mode) (term-send-input)))))) + (bufs (cl-loop for b in (mapcar 'buffer-name (buffer-list)) + when (helm-ff--shell-interactive-buffer-p + b helm-ff-preferred-shell-mode) + collect b))) + ;; Jump to a shell buffer or open a new session. + (helm-aif (and (not helm-current-prefix-arg) + (if (cdr bufs) + (helm-comp-read "Switch to shell buffer: " bufs + :must-match t) + (car bufs))) + ;; Display in same window by default to preserve the + ;; historical behaviour + (pop-to-buffer it '(display-buffer-same-window)) + (cl-case helm-ff-preferred-shell-mode + (eshell-mode + (eshell helm-current-prefix-arg)) + (shell-mode + (shell (helm-aif (and helm-current-prefix-arg + (prefix-numeric-value + helm-current-prefix-arg)) + (format "*shell<%s>*" it)))) + (term-mode + (progn + (ansi-term (getenv "SHELL") + (helm-aif (and helm-current-prefix-arg + (prefix-numeric-value + helm-current-prefix-arg)) + (format "*ansi-term<%s>*" it))) + (term-line-mode))))) + ;; Now cd into directory. + (helm-aif (and (memq major-mode '(shell-mode term-mode)) + (get-buffer-process (current-buffer))) + (accept-process-output it 0.1)) + (unless (helm-ff-shell-alive-p major-mode) + (funcall + (if (eq major-mode 'eshell-mode) cd-eshell cd-shell))))) + +(defun helm-ff-shell-alive-p (mode) + "Returns non nil when a process is running inside `shell-mode' buffer." + (cl-ecase mode + (shell-mode + (save-excursion + (comint-goto-process-mark) + (or (null comint-last-prompt) + (not (eql (point) + (marker-position (cdr comint-last-prompt))))))) + (eshell-mode + (get-buffer-process (current-buffer))) + (term-mode + (save-excursion + (goto-char (term-process-mark)) + (not (looking-back "\\$ " (- (point) 2))))))) + +(defun helm-ff-touch-files (_candidate) + "The touch files action for helm-find-files." + (let* ((files (helm-marked-candidates)) + (split (cl-loop for f in files + for spt = (unless helm-current-prefix-arg + (cons (helm-basedir f) + (split-string f ", ?"))) + if spt + append (cl-loop with dir = (car spt) + for ff in (cdr spt) + collect (expand-file-name ff dir)) + else collect f)) + (timestamp (helm-comp-read + "Timestamp (default Now): " + (cl-loop for f in split + for time = (file-attributes f) + for date = (and time + (format-time-string + "%Y-%m-%d %H:%M:%S" + (nth 5 time))) + when date + collect (cons (format "%s: %s" + (helm-basename f) date) + date)) + :default + (format-time-string "%Y-%m-%d %H:%M:%S" + (current-time)))) + (failures + (cl-loop with default-directory = helm-ff-default-directory + for f in split + for file = (or (file-remote-p f 'localname) f) + when (> (process-file + "touch" nil nil nil "-d" timestamp file) + 0) + collect f))) + (when failures + (message "Failed to touch *%s files:\n%s" + (length failures) + (mapconcat (lambda (f) (format "- %s\n" f)) failures ""))))) + +(defun helm-ff-run-touch-files () + "Used to interactively run touch file action from keyboard." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-touch-files))) +(put 'helm-ff-run-touch-files 'helm-only t) + +(defun helm-ff-sort-by-size () + (interactive) + (let ((helm-ff-initial-sort-method 'size)) + (helm-force-update + (concat (regexp-quote (helm-get-selection + nil helm-ff-transformer-show-only-basename)) + "$")) + (message "Sorting by size"))) +(put 'helm-ff-sort-by-size 'helm-only t) + +(defun helm-ff-sort-by-newest () + (interactive) + (let ((helm-ff-initial-sort-method 'newest)) + (helm-force-update + (concat (regexp-quote (helm-get-selection + nil helm-ff-transformer-show-only-basename)) + "$")) + (message "Sorting by newest"))) +(put 'helm-ff-sort-by-newest 'helm-only t) + +(defun helm-ff-sort-by-ext () + (interactive) + (let ((helm-ff-initial-sort-method 'ext)) + (helm-force-update + (concat (regexp-quote (helm-get-selection + nil helm-ff-transformer-show-only-basename)) + "$")) + (message "Sorting by extensions"))) +(put 'helm-ff-sort-by-ext 'no-helm-mx t) + +(defun helm-ff-sort-alpha () + (interactive) + (let ((helm-ff-initial-sort-method nil)) + (helm-force-update + (concat (regexp-quote (helm-get-selection + nil helm-ff-transformer-show-only-basename)) + "$")) + (message "Sorting alphabetically"))) +(put 'helm-ff-sort-alpha 'helm-only t) + +(defun helm-ff-directories-only (candidates _source) + (if helm-ff--show-directories-only + (cl-loop for (d . r) in candidates + when (file-directory-p r) + ;; We can use this as long as this filtering function + ;; is called after `helm-ff-fct' otherwise candidates + ;; may not be cons cell at first call [1]. + collect (cons d r)) + candidates)) + +(defun helm-ff-files-only (candidates _source) + (if helm-ff--show-files-only + (cl-loop for (d . r) in candidates + unless (file-directory-p r) + ;; Same comment as in [1] above. + collect (cons d r)) + candidates)) + +(defun helm-ff-toggle-dirs-only () + "Show only directories in helm-find-files." + (interactive) + (with-helm-alive-p + (setq helm-ff--show-directories-only (not helm-ff--show-directories-only)) + (setq helm-ff--show-files-only nil) + (helm-update (helm-get-selection nil t)))) +(put 'helm-ff-toggle-dirs-only 'helm-only t) + +(defun helm-ff-toggle-files-only () + "Show only files in helm-find-files." + (interactive) + (with-helm-alive-p + (setq helm-ff--show-files-only (not helm-ff--show-files-only)) + (setq helm-ff--show-directories-only nil) + (helm-update (helm-get-selection nil t)))) +(put 'helm-ff-toggle-files-only 'helm-only t) + +(defun helm-ff-after-persistent-show-all () + (when helm-ff-reset-filters-on-update + (setq helm-ff--show-directories-only nil + helm-ff--show-files-only nil))) + +(defun helm-ff-serial-rename-action (method) + "Rename all marked files in `helm-ff-default-directory' with METHOD. +See `helm-ff-serial-rename-1'." + (let* ((helm--reading-passwd-or-string t) + (cands (helm-marked-candidates :with-wildcard t)) + (def-name (car cands)) + (name (helm-read-string "NewName: " + (replace-regexp-in-string + "[0-9]+$" "" + (helm-basename + def-name + (file-name-extension def-name))))) + (start (read-number "StartAtNumber: ")) + (extension (helm-read-string "Extension: " + (file-name-extension (car cands)))) + (dir (expand-file-name + (helm-read-file-name + "Serial Rename to directory: " + :initial-input + (expand-file-name helm-ff-default-directory) + :test 'file-directory-p + :must-match t))) + done) + (with-helm-display-marked-candidates + helm-marked-buffer-name (helm-ff--count-and-collect-dups cands) + (if (y-or-n-p + (format "Rename %s file(s) to <%s> like this ?\n%s " + (length cands) dir (format "%s <-> %s%s.%s" + (helm-basename (car cands)) + name start extension))) + (progn + (helm-ff-serial-rename-1 + dir cands name start extension :method method) + (setq done t) + (message nil)))) + (if done + (with-helm-current-buffer (helm-find-files-1 dir)) + (message "Operation aborted")))) + +(defun helm-ff-member-directory-p (file directory) + (let ((dir-file (expand-file-name + (file-name-as-directory (file-name-directory file)))) + (cur-dir (expand-file-name (file-name-as-directory directory)))) + (string= dir-file cur-dir))) + +(cl-defun helm-ff-serial-rename-1 + (directory collection new-name start-at-num extension &key (method 'rename)) + "Rename files in COLLECTION to DIRECTORY with the prefix name NEW-NAME. +Rename start at number START-AT-NUM - ex: prefixname-01.jpg. +EXTENSION is the file extension to use. In empty prompt, reuse +the original extension of file. +METHOD can be one of rename, copy or symlink. +Files will be renamed if they are files of current directory, +otherwise they will be treated with METHOD. +Default METHOD is rename." + ;; Maybe remove directories selected by error in collection. + (setq collection (cl-remove-if 'file-directory-p collection)) + (let* ((tmp-dir (file-name-as-directory + (concat (file-name-as-directory directory) + (symbol-name (cl-gensym "tmp"))))) + (fn (cl-case method + (copy 'copy-file) + (symlink 'make-symbolic-link) + (rename 'rename-file) + (t (error "Error: Unknown method %s" method))))) + (make-directory tmp-dir) + (unwind-protect + (progn + ;; Rename all files to tmp-dir with new-name. + ;; If files are not from start directory, use method + ;; to move files to tmp-dir. + (cl-loop for i in collection + for count from start-at-num + for fnum = (if (< count 10) "0%s" "%s") + for nname = (concat tmp-dir new-name (format fnum count) + (if (not (string= extension "")) + (format ".%s" (replace-regexp-in-string + "[.]" "" extension)) + (file-name-extension i 'dot))) + do (if (helm-ff-member-directory-p i directory) + (rename-file i nname) + (funcall fn i nname))) + ;; Now move all from tmp-dir to destination. + (cl-loop with dirlist = (directory-files + tmp-dir t directory-files-no-dot-files-regexp) + for f in dirlist do + (if (file-symlink-p f) + (make-symbolic-link (file-truename f) + (concat (file-name-as-directory directory) + (helm-basename f))) + (rename-file f directory)))) + (delete-directory tmp-dir t)))) + +(defun helm-ff-serial-rename (_candidate) + "Serial rename all marked files to `helm-ff-default-directory'. +Rename only file of current directory, and symlink files coming from +other directories. +See `helm-ff-serial-rename-1'." + (helm-ff-serial-rename-action 'rename)) + +(defun helm-ff-serial-rename-by-symlink (_candidate) + "Serial rename all marked files to `helm-ff-default-directory'. +Rename only file of current directory, and symlink files coming +from other directories. +See `helm-ff-serial-rename-1'." + (helm-ff-serial-rename-action 'symlink)) + +(defun helm-ff-serial-rename-by-copying (_candidate) + "Serial rename all marked files to `helm-ff-default-directory'. +Rename only file of current directory, and copy files coming from +other directories. +See `helm-ff-serial-rename-1'." + (helm-ff-serial-rename-action 'copy)) + +(defvar helm-ff-query-replace-fnames-history-from nil) +(defvar helm-ff-query-replace-fnames-history-to nil) +(defun helm-ff-query-replace-on-filenames (candidates) + "Query replace on filenames of CANDIDATES. +This doesn't replace inside the files, only modify filenames." + (with-helm-display-marked-candidates + helm-marked-buffer-name + (mapcar 'helm-basename candidates) + (let* ((regexp (read-string "Replace regexp on filename(s): " + nil 'helm-ff-query-replace-history-from + (helm-basename (car candidates)))) + (rep (read-string (format "Replace regexp `%s' with: " regexp) + nil 'helm-ff-query-replace-history-to))) + (cl-loop with query = "y" + with count = 0 + for old in candidates + for new = (helm-ff--query-replace-in-fname-set-new-name + old regexp rep count) + ;; If `regexp' is not matched in `old' + ;; `replace-regexp-in-string' will + ;; return `old' unmodified. + unless (string= old new) + do (progn + (when (file-exists-p new) + (setq new (concat (file-name-sans-extension new) + (format "(%s)" count) + (file-name-extension new t)))) + (unless (string= query "!") + (setq query (helm-read-answer (format + "Replace `%s' by `%s' [!,y,n,q]" + (helm-basename old) + (helm-basename new)) + '("y" "n" "!" "q")))) + (when (string= query "q") + (cl-return (message "Operation aborted"))) + (unless (string= query "n") + (rename-file old new) + (cl-incf count))) + finally (message "%d Files renamed" count)))) + ;; This fix the emacs bug where "Emacs-Lisp:" is sent + ;; in minibuffer (not the echo area). + (sit-for 0.1) + (with-current-buffer (window-buffer (minibuffer-window)) + (delete-minibuffer-contents))) + +(defun helm-ff--query-replace-in-fname-set-new-name (old regexp rep count) + "Setup a new name for OLD replacing part matching REGEXP with REP. +COUNT is used for incrementing new name if needed." + (let (subexp target) + (concat (helm-basedir old) + (helm--replace-regexp-in-buffer-string + (save-match-data + (cond ((string= regexp "%.") + (setq subexp 1) + (helm-ff--prepare-str-with-regexp + (setq target (helm-basename old t)))) + ((string= regexp ".%") + (setq subexp 1) + (helm-ff--prepare-str-with-regexp + (setq target (file-name-extension old)))) + ((string= regexp "%") + (regexp-quote + (setq target (helm-basename old)))) + ((string-match "%:\\([0-9]+\\):\\([0-9]+\\)" regexp) + (setq subexp 1) + (let ((beg (match-string 1 regexp)) + (end (match-string 2 regexp)) + (str (helm-basename old))) + (setq target (substring str + (string-to-number beg) + (string-to-number end))) + (helm-ff--prepare-str-with-regexp str beg end))) + (t regexp))) + (save-match-data + (cond (;; Handle incremental + ;; replacement with \# in + ;; search and replace + ;; feature in placeholder \@. + (string-match + "\\\\@/\\(.*\\)/\\(\\(?99:.*\\)\\\\#\\)/" + rep) + (replace-regexp-in-string + (match-string 1 rep) + (concat (match-string 99 rep) + (format "%03d" (1+ count))) + target)) + ;; Incremental replacement + ;; before or after \@. + ((and (string-match-p "\\\\#" rep) + (string-match "\\\\@" rep)) + (replace-regexp-in-string + "\\\\#" (format "%03d" (1+ count)) + (replace-match target t t rep))) + ;; Simple incremental replacement. + ((string-match "\\\\#" rep) + (replace-match + (format "%03d" (1+ count)) t t rep)) + ;; Substring replacement in placeholder. + ((string-match + "\\\\@:\\([0-9]*\\):\\([0-9]*\\)" rep) + (replace-match (substring + target + (string-to-number + (match-string 1 rep)) + (pcase (match-string 2 rep) + ((pred (string= "")) + (length target)) + (res (string-to-number res)))) + t t rep)) + ;; Search and replace in + ;; placeholder. Doesn't + ;; handle incremental here. + ((string-match "\\\\@/\\(.*\\)/\\(.*\\)/" rep) + (replace-match (replace-regexp-in-string + (match-string 1 rep) + (match-string 2 rep) + target t) + t t rep)) + ;; Simple replacement by placeholder. + ((string-match "\\\\@" rep) + (replace-match target t t rep)) + ;; Replacement with + ;; upcase, downcase or + ;; capitalized text. + ((string= rep "%u") #'upcase) + ((string= rep "%d") #'downcase) + ((string= rep "%c") #'capitalize) + ;; Simple replacement with + ;; whole replacement regexp. + (t rep))) + (helm-basename old) t nil subexp)))) + +(defun helm-ff--prepare-str-with-regexp (str &optional rep1 rep2) + ;; This is used in `helm-ff-query-replace-on-filenames' to prepare + ;; STR when REGEXP is specified as substring e.g %:1:3 in this case + ;; substring from 1 to 3 in STR will be enclosed with parenthesis to + ;; match this substring as a subexp e.g %:1:3 on string "emacs" will + ;; be replaced by "e\\(ma\\)cs" using subexp 1 like this: + ;; (helm--replace-regexp-in-buffer-string "e\\(ma\\)cs" "fo" "emacs" nil t 1) + ;; => "efocs" + ;; ^^ + ;; Where "1" and "3" will be strings extracted with match-string + ;; from regexp and refered respectively in this function as REP1 and + ;; REP2. + (let* ((from (or (and rep1 (string-to-number rep1)) 0)) + (to (or (and rep2 (string-to-number rep2)) (length str))) + (subexp (concat "\\(" (regexp-quote (substring str from to)) "\\)")) + (before-str (unless (zerop from) + (regexp-quote (substring str 0 from)))) + (after-str (unless (= to (length str)) + (regexp-quote (substring str to (length str)))))) + (concat before-str subexp after-str))) + +;; The action. +(defun helm-ff-query-replace-fnames-on-marked (_candidate) + (let ((marked (helm-marked-candidates :with-wildcard t))) + (helm-ff-query-replace-on-filenames marked))) + +;; The command for `helm-find-files-map'. +(defun helm-ff-run-query-replace-fnames-on-marked () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-query-replace-fnames-on-marked))) +(put 'helm-ff-run-query-replace-fnames-on-marked 'helm-only t) + +(defun helm-ff-query-replace (_candidate) + (let ((bufs (cl-loop for f in (helm-marked-candidates :with-wildcard t) + collect (buffer-name (find-file-noselect f))))) + (helm-buffer-query-replace-1 nil bufs))) + +(defun helm-ff-query-replace-regexp (_candidate) + (let ((bufs (cl-loop for f in (helm-marked-candidates :with-wildcard t) + collect (buffer-name (find-file-noselect f))))) + (helm-buffer-query-replace-1 'regexp bufs))) + +(defun helm-ff-run-query-replace () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-query-replace))) +(put 'helm-ff-run-query-replace 'helm-only t) + +(defun helm-ff-run-query-replace-regexp () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-query-replace-regexp))) +(put 'helm-ff-run-query-replace-regexp 'helm-only t) + +(defun helm-ff-toggle-auto-update (_candidate) + (if helm-ff--deleting-char-backward + (progn + (message "[Auto expansion disabled]") + (sit-for 1) (message nil) + (setq helm-ff--auto-update-state nil)) + (setq helm-ff-auto-update-flag (not helm-ff-auto-update-flag)) + (setq helm-ff--auto-update-state helm-ff-auto-update-flag) + (message "[Auto expansion %s]" + (if helm-ff-auto-update-flag "enabled" "disabled")))) + +(defun helm-ff-run-toggle-auto-update () + (interactive) + (with-helm-alive-p + (helm-set-attr 'toggle-auto-update '(helm-ff-toggle-auto-update . never-split)) + (helm-execute-persistent-action 'toggle-auto-update))) +(put 'helm-ff-run-toggle-auto-update 'helm-only t) + +(defun helm-ff-delete-char-backward () + "Go up one level or disable HFF auto update and delete char backward. + +Going up one level works only when pattern is a directory endings +with \"/\", otherwise this command deletes char backward. + +Going up one level can be disabled if necessary by deleting \"/\" +at end of pattern using \\\\[backward-char] and +\\[helm-delete-minibuffer-contents]." + (interactive) + (with-helm-alive-p + (if (and helm-ff-DEL-up-one-level-maybe + (string-match "/\\'" helm-pattern) + (file-directory-p helm-pattern)) + (call-interactively 'helm-find-files-up-one-level) + (setq helm-ff-auto-update-flag nil) + (setq helm-ff--deleting-char-backward t) + (call-interactively + (lookup-key (current-global-map) + (read-kbd-macro "DEL"))) + (helm--update-header-line)))) +(put 'helm-ff-delete-char-backward 'helm-only t) + +(defun helm-ff-delete-char-backward--exit-fn () + (setq helm-ff-auto-update-flag helm-ff--auto-update-state) + (setq helm-ff--deleting-char-backward nil)) + +(defvar helm-ff--RET-disabled nil) +(defun helm-ff-RET-1 (&optional must-match) + "Used for RET action in `helm-find-files'. +See `helm-ff-RET' for details. +If MUST-MATCH is specified exit with +`helm-confirm-and-exit-minibuffer' which handle must-match mechanism." + (let ((sel (helm-get-selection)) + ;; Ensure `file-directory-p' works on remote files. + non-essential) + (cl-assert sel nil "Trying to exit with no candidates") + (if (and (or (file-directory-p sel) + (helm-ff--invalid-tramp-name-p sel)) + ;; Allows exiting with default action when a prefix arg + ;; is specified. + (null current-prefix-arg) + (null helm-ff--RET-disabled) + (or (and (file-remote-p sel) + (string= "." (helm-basename sel)) + (string-match-p "\\`[/].*:.*:\\'" + helm-pattern)) + (not (string= "." (helm-basename sel))))) + (helm-execute-persistent-action) + (if must-match + (helm-confirm-and-exit-minibuffer) + (helm-maybe-exit-minibuffer))))) + +(defun helm-ff-RET () + "Default action for RET in `helm-find-files'. + +Behave differently depending on `helm-selection': + +- candidate basename is \".\" => open it in dired. +- candidate is a directory => expand it. +- candidate is a file => open it." + (interactive) + (helm-ff-RET-1)) +(put 'helm-ff-RET 'helm-only t) + +(defun helm-ff-TAB-1 (&optional force-menu) + "Used for TAB action in `helm-find-files'." + (let ((sel (helm-get-selection))) + (if (and (null force-menu) + (file-directory-p sel) + (not (string= "." (helm-basename sel)))) + (helm-execute-persistent-action) + (helm-select-action)))) + +(defun helm-ff-TAB (arg) + "Default action for TAB in `helm-find-files'. + +Behave differently depending on `helm-selection': + +- candidate basename is \".\" => open the action menu. +- candidate is a directory => expand it. +- candidate is a file => open action menu. + +Called with a prefix arg open menu unconditionally." + (interactive "P") + (helm-ff-TAB-1 arg)) +(put 'helm-ff-TAB 'helm-only t) + +(defun helm-ff-RET-must-match () + "Same as `helm-ff-RET' but used in must-match map." + (interactive) + (helm-ff-RET-1 t)) + +(defun helm-ff-run-grep () + "Run Grep action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-grep))) +(put 'helm-ff-run-grep 'helm-only t) + +(defun helm-ff-run-git-grep () + "Run git-grep action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-git-grep))) +(put 'helm-ff-run-git-grep 'helm-only t) + +(defun helm-ff-run-grep-ag () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-ag))) +(put 'helm-ff-run-grep-ag 'helm-only t) + +(defun helm-ff-run-pdfgrep () + "Run Pdfgrep action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-pdfgrep))) +(put 'helm-ff-run-pdfgrep 'helm-only t) + +(defun helm-ff-run-zgrep () + "Run Grep action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-zgrep))) +(put 'helm-ff-run-zgrep 'helm-only t) + +(defun helm-ff-run-copy-file () + "Run Copy file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-copy))) +(put 'helm-ff-run-copy-file 'helm-only t) + +(defun helm-ff-run-rsync-file () + "Run Rsync file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-rsync))) +(put 'helm-ff-run-rsync-file 'helm-only t) + +(defun helm-ff-run-rename-file () + "Run Rename file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-rename))) +(put 'helm-ff-run-rename-file 'helm-only t) + +(defun helm-ff-run-byte-compile-file () + "Run Byte compile file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-byte-compile))) +(put 'helm-ff-run-byte-compile-file 'helm-only t) + +(defun helm-ff-run-load-file () + "Run Load file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-load-files))) +(put 'helm-ff-run-load-file 'helm-only t) + +(defun helm-ff-run-eshell-command-on-file () + "Run eshell command on file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action + 'helm-find-files-eshell-command-on-file))) +(put 'helm-ff-run-eshell-command-on-file 'helm-only t) + +(defun helm-ff-run-ediff-file () + "Run Ediff file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-ediff-files))) +(put 'helm-ff-run-ediff-file 'helm-only t) + +(defun helm-ff-run-ediff-merge-file () + "Run Ediff merge file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action + 'helm-find-files-ediff-merge-files))) +(put 'helm-ff-run-ediff-merge-file 'helm-only t) + +(defun helm-ff-run-symlink-file () + "Run Symlink file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-symlink))) +(put 'helm-ff-run-symlink-file 'helm-only t) + +(defun helm-ff-run-relsymlink-file () + "Run Symlink file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-relsymlink))) +(put 'helm-ff-run-relsymlink-file 'helm-only t) + +(defun helm-ff-run-hardlink-file () + "Run Hardlink file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-hardlink))) +(put 'helm-ff-run-hardlink-file 'helm-only t) + +(defun helm-ff-delete-files (candidate) + "Delete files default action." + (funcall helm-ff-delete-files-function candidate)) + +(defun helm-ff-run-delete-file () + "Run Delete file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action #'helm-ff-delete-files))) +(put 'helm-ff-run-delete-file 'helm-only t) + +(defun helm-ff-run-complete-fn-at-point () + "Run complete file name action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action + 'helm-insert-file-name-completion-at-point))) +(put 'helm-ff-run-complete-fn-at-point 'helm-only t) + +(defun helm-ff-run-switch-to-shell () + "Run switch to eshell action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-switch-to-shell))) +(put 'helm-ff-run-switch-to-shell 'helm-only t) + +(defun helm-ff-run-switch-other-window () + "Run switch to other window action from `helm-source-find-files'. +When a prefix arg is provided, split is done vertically." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-files-other-window))) +(put 'helm-ff-run-switch-other-window 'helm-only t) + +(defun helm-ff-run-switch-other-frame () + "Run switch to other frame action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'find-file-other-frame))) +(put 'helm-ff-run-switch-other-frame 'helm-only t) + +(defun helm-ff-run-open-file-externally () + "Run open file externally command action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-open-file-externally))) +(put 'helm-ff-run-open-file-externally 'helm-only t) + +(defun helm-ff-run-open-file-with-default-tool () + "Run open file externally command action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-open-file-with-default-tool))) +(put 'helm-ff-run-open-file-with-default-tool 'helm-only t) + +(defun helm-ff-locate (candidate) + "Locate action function for `helm-find-files'." + (helm-locate-set-command) + (let ((default (concat (helm-basename + (expand-file-name + candidate + helm-ff-default-directory)) + (unless (or + ;; "-b" is already added when fuzzy matching. + helm-locate-fuzzy-match + ;; The locate '-b' option doesn't exists + ;; in everything (es). + (and (eq system-type 'windows-nt) + (string-match "^es" helm-locate-command))) + " -b")))) + (helm-locate-1 helm-current-prefix-arg nil 'from-ff default))) + +(defun helm-ff-run-locate () + "Run locate action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-locate))) +(put 'helm-ff-run-locate 'helm-only t) + +(defun helm-files-insert-as-org-link (candidate) + (insert (format "[[%s][]]" candidate)) + (goto-char (- (point) 2))) + +(defun helm-ff-run-insert-org-link () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-files-insert-as-org-link))) +(put 'helm-ff-run-insert-org-link 'helm-only t) + +(defun helm-ff-run-find-file-as-root () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-find-file-as-root))) +(put 'helm-ff-run-find-file-as-root 'helm-only t) + +(defun helm-ff-run-find-alternate-file () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'find-alternate-file))) +(put 'helm-ff-run-find-alternate-file 'helm-only t) + +(defun helm-ff-run-mail-attach-files () + "Run mail attach files command action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-mail-attach-files))) +(put 'helm-ff-run-mail-attach-files 'helm-only t) + +(defun helm-ff-run-etags () + "Run Etags command action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-etags-select))) +(put 'helm-ff-run-etags 'helm-only t) + +(defvar lpr-printer-switch) +(defun helm-ff-print (_candidate) + "Print marked files. + +You may to set in order variables `lpr-command',`lpr-switches' +and/or `printer-name', but with no settings Helm should detect +your printer(s) and print with the default `lpr' settings. + +NOTE: DO NOT set the \"-P\" flag in `lpr-switches'. If you really +have to modify this, do it in `lpr-printer-switch'. + +Same as `dired-do-print' but for Helm." + (require 'lpr) + (when (or helm-current-prefix-arg + (not helm-ff-printer-list)) + (setq helm-ff-printer-list + (helm-ff-find-printers))) + (let* ((file-list (helm-marked-candidates :with-wildcard t)) + (len (length file-list)) + (printer-name (if helm-ff-printer-list + (helm-comp-read + "Printer: " helm-ff-printer-list) + printer-name)) + (lpr-switches + (if (and (stringp printer-name) + (string< "" printer-name)) + (cons (concat lpr-printer-switch " " printer-name) + lpr-switches) + lpr-switches)) + (command (helm-read-string + (format "Print *%s File(s):\n%s with: " + len + (mapconcat + (lambda (f) (format "- %s\n" f)) + file-list "")) + (when (and lpr-command lpr-switches) + (mapconcat 'identity + (cons lpr-command + (if (stringp lpr-switches) + (list lpr-switches) + lpr-switches)) + " ")))) + (file-args (mapconcat #'shell-quote-argument + file-list " ")) + (cmd-line (concat command " " file-args))) + (if command + (start-process-shell-command "helm-print" nil cmd-line) + (error "Error: Please verify your printer settings in Emacs.")))) + +(defun helm-ff-run-print-file () + "Run Print file action from `helm-source-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-print))) +(put 'helm-ff-run-print-file 'helm-only t) + +(defun helm-ff-checksum (file) + "Calculate the checksum of FILE. +The checksum is copied to `kill-ring'. +Checksum is calculated with the md5sum, sha1sum, sha224sum, +sha256sum, sha384sum and sha512sum when available, otherwise the +Emacs function `secure-hash' is used but it is slow and may crash +Emacs and even the whole system as it eats all memory." + (cl-assert (file-regular-p file) + nil "`%s' is not a regular file" file) + (let* ((algo (intern (helm-comp-read + "Algorithm: " + '(md5 sha1 sha224 sha256 sha384 sha512)))) + (cmd (concat (symbol-name algo) "sum")) + (bn (helm-basename file)) + proc) + (message "Calculating %s checksum for %s..." algo bn) + (if (executable-find cmd) + (progn + (set-process-filter + (setq proc (start-file-process cmd nil cmd "-b" file)) + (lambda (_process output) + (when output (kill-new output)))) + (set-process-sentinel + proc + `(lambda (_process event) + (when (string= event "finished\n") + (message "Calculating %s checksum for `%s' done and copied to kill-ring" + ,(symbol-name algo) ,bn))))) + (async-let ((sum (with-temp-buffer + (insert-file-contents-literally file) + (secure-hash algo (current-buffer))))) + (kill-new sum) + (message "Calculating %s checksum for `%s' done and copied to kill-ring" + algo bn))))) + +(defun helm-ff-toggle-basename (_candidate) + (with-helm-buffer + (setq helm-ff-transformer-show-only-basename + (not helm-ff-transformer-show-only-basename)) + (let* ((cand (helm-get-selection nil t)) + (target (if helm-ff-transformer-show-only-basename + (helm-basename cand) cand))) + (helm-force-update (concat (regexp-quote target) "$"))))) + +(defun helm-ff-run-toggle-basename () + (interactive) + (with-helm-alive-p + (unless (helm-empty-source-p) + (helm-ff-toggle-basename nil)))) +(put 'helm-ff-run-toggle-basename 'helm-only t) + +(defun helm-reduce-file-name-1 (fname level) + ;; This is the old version of helm-reduce-file-name, we still use it + ;; with ftp fnames as expand-file-name is not working as expected + ;; with ftp fnames (emacs bug). + (cl-loop with result + with iter = (helm-iter-reduce-fname (expand-file-name fname)) + repeat level do (setq result (helm-iter-next iter)) + finally return (or result (expand-file-name "/")))) + +(defun helm-reduce-file-name-2 (fname level) + ;; This version comes from Bug#2004 (UNC paths) and should fix + ;; it. It works with local files and remote files as well but not + ;; with ftp, see helm-reduce-file-name-1. + (while (> level 0) + (unless (or (string= fname "/") + (string= (file-remote-p fname 'localname) "/")) + (setq fname (expand-file-name + (concat (expand-file-name fname) "/../")))) + (setq level (1- level))) + fname) + +(defun helm-reduce-file-name (fname level) + "Reduce FNAME by number LEVEL from end." + (if (helm-aand (file-remote-p fname 'method) + (string= it "ftp")) + (helm-reduce-file-name-1 fname level) + (helm-reduce-file-name-2 fname level))) + +(defun helm-iter-reduce-fname (fname) + "Yield FNAME reduced by one level at each call." + (let ((split (split-string fname "/" t))) + (unless (or (null split) + (string-match "\\`\\(~\\|[[:alpha:]]:\\)" (car split))) + (setq split (cons "/" split))) + (lambda () + (when (and split (cdr split)) + (cl-loop for i in (setq split (butlast split)) + concat (if (string= i "/") i (concat i "/"))))))) + +(defvar helm-find-files--level-tree nil) +(defvar helm-find-files--level-tree-iterator nil) +(defun helm-find-files-up-one-level (arg) + "Go up one level like unix command `cd ..'. +If prefix numeric arg is given go ARG level up." + (interactive "p") + (with-helm-alive-p + (helm-ff-after-persistent-show-all) + (let ((src (helm-get-current-source))) + (when (and (helm-file-completion-source-p src) + (not (helm-ff--invalid-tramp-name-p))) + (with-helm-window + (when (helm-follow-mode-p) + (helm-follow-mode -1) (message nil))) + ;; When going up one level we want to be at the line + ;; corresponding to actual directory, so store this info + ;; in `helm-ff-last-expanded'. + (let ((cur-cand (helm-get-selection nil nil src)) + (new-pattern (helm-reduce-file-name helm-pattern arg))) + ;; Ensure visibility on all candidates for preselection. + (helm-set-attr 'candidate-number-limit + (if helm-ff-up-one-level-preselect + (max (gethash new-pattern + helm-ff--directory-files-length + helm-ff-candidate-number-limit) + helm-ff-candidate-number-limit) + helm-ff-candidate-number-limit)) + (cond ((file-directory-p helm-pattern) + (setq helm-ff-last-expanded helm-ff-default-directory)) + ((file-exists-p helm-pattern) + (setq helm-ff-last-expanded helm-pattern)) + ((and cur-cand (file-exists-p cur-cand)) + (setq helm-ff-last-expanded cur-cand))) + (unless helm-find-files--level-tree + (setq helm-find-files--level-tree + (cons helm-ff-default-directory + helm-find-files--level-tree))) + (setq helm-find-files--level-tree-iterator nil) + (push new-pattern helm-find-files--level-tree) + (setq helm-ff--show-thumbnails + (member new-pattern helm-ff--thumbnailed-directories)) + (helm-set-pattern new-pattern helm-suspend-update-flag) + (with-helm-after-update-hook (helm-ff-retrieve-last-expanded))))))) +(put 'helm-find-files-up-one-level 'helm-only t) + +(defun helm-find-files-down-last-level () + "Retrieve previous paths reached by `C-l' in helm-find-files." + (interactive) + (with-helm-alive-p + (when (and (helm-file-completion-source-p) + (not (helm-ff--invalid-tramp-name-p))) + (unless helm-find-files--level-tree-iterator + (setq helm-find-files--level-tree-iterator + (helm-iter-list (cdr helm-find-files--level-tree)))) + (setq helm-find-files--level-tree nil) + (helm-aif (helm-iter-next helm-find-files--level-tree-iterator) + (progn + (setq helm-ff--show-thumbnails + (member it helm-ff--thumbnailed-directories)) + (helm-set-pattern it)) + (setq helm-find-files--level-tree-iterator nil))))) +(put 'helm-find-files-down-last-level 'helm-only t) + +(defun helm-find-files--reset-level-tree () + (setq helm-find-files--level-tree-iterator nil + helm-find-files--level-tree nil)) + +(add-hook 'helm-cleanup-hook 'helm-find-files--reset-level-tree) +(add-hook 'post-self-insert-hook 'helm-find-files--reset-level-tree) +(add-hook 'helm-after-persistent-action-hook 'helm-find-files--reset-level-tree) + +(defun helm-ff-retrieve-last-expanded () + "Move overlay to last visited directory `helm-ff-last-expanded'. +This happen after using `helm-find-files-up-one-level', or +hitting C-j on \"..\"." + (when helm-ff-last-expanded + (let ((presel (if helm-ff-transformer-show-only-basename + (helm-basename + (directory-file-name helm-ff-last-expanded)) + (directory-file-name helm-ff-last-expanded)))) + (with-helm-window + (when (re-search-forward + (format helm-ff-last-expanded-candidate-regexp + (regexp-quote presel)) + nil t) + (forward-line 0) + (helm-mark-current-line))) + (setq helm-ff-last-expanded nil)))) + +(defun helm-ff-move-to-first-real-candidate () + "When candidate is an incomplete file name move to first real candidate." + (let* ((src (helm-get-current-source)) + (name (assoc-default 'name src)) + ;; Ensure `helm-file-completion-source-p' returns nil on + ;; `helm-read-file-name' history. + minibuffer-completing-file-name) + (helm-aif (and (helm-file-completion-source-p src) + (not (helm-empty-source-p)) + ;; Prevent dired commands moving to first real + ;; (Bug#910). + (or (memq (intern-soft name) + helm-ff-goto-first-real-dired-exceptions) + (not (string-match "\\`[Dd]ired-" name))) + helm-ff--move-to-first-real-candidate + (helm-get-selection nil nil src)) + (unless (or (not (stringp it)) + (and (string-match helm-tramp-file-name-regexp it) + (not (file-remote-p it nil t))) + (string-match helm-ff-tramp-method-regexp it) + (file-exists-p it)) + (helm-next-line))))) + +(defun helm-ff-undo () + "Undo minibuffer in `helm-find-files'. +Ensure disabling `helm-ff-auto-update-flag' before undoing." + (interactive) + (let ((old--flag helm-ff-auto-update-flag)) + (setq helm-ff-auto-update-flag nil) + (setq helm-ff--auto-update-state nil) + (unwind-protect + (progn + (undo) + (helm-check-minibuffer-input)) + (setq helm-ff-auto-update-flag old--flag) + (setq helm-ff--auto-update-state helm-ff-auto-update-flag)))) + +;;; Auto-update - helm-find-files auto expansion of directories. +;; +;; +(defun helm-ff-update-when-only-one-matched () + "Expand to directory when sole completion. +When only one candidate is remaining and it is a directory, +expand to this directory. +This happen only when `helm-ff-auto-update-flag' is non-nil or +when `helm-pattern' is equal to \"~/\"." + (let ((src (helm-get-current-source))) + (when (and (helm-file-completion-source-p src) + (not (get-buffer-window helm-action-buffer 'visible)) + (not (helm-ff--invalid-tramp-name-p)) + (not (string-match-p "\\`[.]\\{2\\}[^/]+" + (helm-basename helm-pattern)))) + (with-helm-buffer + (let* ((history-p (string= (assoc-default 'name src) + "Read File Name History")) + (pat (helm-ff-set-pattern helm-pattern)) + (completed-p (string= (file-name-as-directory + (expand-file-name + (substitute-in-file-name pat))) + helm-ff-default-directory)) + (candnum (helm-get-candidate-number)) + (lt2-p (and (<= candnum 2) + (>= (string-width (helm-basename helm-pattern)) 2))) + (cur-cand (prog2 + (unless (or completed-p + (file-exists-p pat) + history-p (null lt2-p)) + ;; Only one non--existing candidate + ;; and one directory candidate, move to it, + ;; but not when renaming, copying etc..., + ;; so for this use + ;; `helm-ff-move-to-first-real-candidate' + ;; instead of `helm-next-line' (Bug#910). + (helm-ff-move-to-first-real-candidate)) + (helm-get-selection nil nil src))) + expand-to) + (when (and (or (and helm-ff-auto-update-flag + (null helm-ff--deleting-char-backward) + ;; Bug#295 + ;; File predicates are returning t + ;; with paths like //home/foo. + ;; So check it is not the case by regexp + ;; to allow user to do C-a / to start e.g + ;; entering a tramp method e.g /sudo::. + (not (string-match "\\`//" helm-pattern)) + (not (eq last-command 'helm-yank-text-at-point))) + ;; Fix Bug#542. + (string= helm-pattern "~/") + ;; Only one remaining directory, expand it. + (and (= candnum 1) + helm-ff--auto-update-state + (file-accessible-directory-p pat) + (null helm-ff--deleting-char-backward))) + (or + ;; Only one candidate remaining + ;; and at least 2 char in basename. + lt2-p + ;; Already completed. + completed-p) + (not history-p) ; Don't try to auto complete in history. + (stringp cur-cand) + (file-accessible-directory-p cur-cand)) + (if (and (not (helm-dir-is-dot cur-cand)) ; [1] + ;; Maybe we are here because completed-p is true + ;; but check this again to be sure. (Windows fix) + (<= candnum 2)) ; [2] + ;; If after going to next line the candidate + ;; is not one of "." or ".." [1] + ;; and only one candidate is remaining [2], + ;; assume candidate is a new directory to expand, and do it. + (progn + (setq expand-to (file-name-as-directory + (substring-no-properties cur-cand))) + (setq helm-ff--show-thumbnails + (member expand-to helm-ff--thumbnailed-directories)) + (helm-set-pattern expand-to) + ;; Reset flags to show all when changing dir. + (helm-ff-after-persistent-show-all)) + ;; The candidate is one of "." or ".." + ;; that mean we have entered the last letter of the directory name + ;; in prompt, so expansion is already done, just add the "/" at end + ;; of name unless helm-pattern ends with "." + ;; (i.e we are writing something starting with ".") + (unless (string-match "\\`.*[.]\\{1\\}\\'" helm-pattern) + ;; Need to expand-file-name to avoid e.g /ssh:host:./ in prompt. + (setq expand-to (expand-file-name (file-name-as-directory helm-pattern))) + (setq helm-ff--show-thumbnails + (member expand-to helm-ff--thumbnailed-directories)) + (helm-set-pattern expand-to))) + ;; When typing pattern in minibuffer, helm + ;; expand very fast to a directory matching pattern and + ;; don't let undo the time to set a boundary, the result + ;; is when e.g. going to root with "//" and undoing, undo + ;; doesn't undo to previous input. One fix for this is to + ;; advice `undo-auto--boundary-ensure-timer' so that it is + ;; possible to modify its delay (use a value of 1s for + ;; helm), a second fix is to run directly here `undo-boundary' + ;; inside a timer. + (run-at-time helm-input-idle-delay nil #'undo-boundary) + (helm-check-minibuffer-input))))))) + +(cl-defun helm-ff-auto-expand-to-home-or-root (&optional (pattern helm-pattern spattern)) + "Allow expanding to $HOME or \"/\" or text yanked after pattern. + +Argument PATTERN default to `helm-pattern' and should _not_ be used for +other purpose than debugging the second cond clause of this function. +When PATTERN is specified, specific helm functions are not called to +avoid errors when called outside helm for debugging purpose." + (when (or spattern + (and (helm-file-completion-source-p) + (with-current-buffer (window-buffer (minibuffer-window)) (eolp)) + (not (string-match helm-ff-url-regexp pattern)))) + (cond ((and (not (file-remote-p pattern)) + (null (file-exists-p pattern)) + (string-match-p + "\\`\\([.]\\)\\{2\\}[^/]+" + (helm-basename pattern)) + (string-match-p "/\\'" pattern) + (null spattern)) + (helm-ff-recursive-dirs pattern) + (helm-ff--maybe-set-pattern-and-update)) + ((string-match + "\\(?:\\`~/\\)\\|/?\\$.*/\\|/\\./\\|/\\.\\./\\|/~.*/\\|//\\|\\(/[[:alpha:]]:/\\)" + pattern) + (let* ((match (match-string 0 pattern)) + (input (cond ((string= match "/./") + (expand-file-name default-directory)) + ((string= pattern "/../") "/") + ((string-match-p "\\`/\\$" match) + (let ((sub (substitute-in-file-name match))) + (if (file-directory-p sub) + sub (replace-regexp-in-string "/\\'" "" sub)))) + (t (helm-ff--expand-substitued-pattern pattern))))) + ;; `file-directory-p' returns t on "/home/me/." (Bug#1844). + (if (and (file-directory-p input) + (not (string-match-p "[^.]\\.\\'" input))) + (progn + (setq helm-ff-default-directory + (setq input (file-name-as-directory input))) + ;; When changing directory ensure to show all. + (helm-ff-after-persistent-show-all)) + (setq helm-ff-default-directory (file-name-as-directory + (file-name-directory input)))) + (if spattern input (helm-ff--maybe-set-pattern-and-update input)))) + ((and (string-match "\\`/\\(-\\):.*" pattern) (null spattern)) + (helm-ff--maybe-set-pattern-and-update + (replace-match tramp-default-method t t pattern 1)))))) + +(defun helm-ff--maybe-set-pattern-and-update (&optional str) + (with-helm-window + (when str (helm-set-pattern str)) + (helm-check-minibuffer-input))) + +(defun helm-ff--expand-file-name-no-dot (name &optional directory) + "Prevent expanding \"/home/user/.\" to \"/home/user\"." + ;; Bug#1844 - If user enter "~/." to type an hidden filename + ;; don't expand to /home/him e.g. + ;; (expand-file-name "~/.") =>"/home/thierry" + ;; (helm-ff--expand-substitued-pattern "~/.") =>"/home/thierry/." + (concat (expand-file-name name directory) + (and (string-match "[^.]\\.\\'" name) "/."))) + +(defun helm-ff--expand-substitued-pattern (pattern) + ;; [Windows] On UNC paths "/" expand to current machine, + ;; so use the root of current Drive. (i.e "C:/") + (let* ((directory (and (memq system-type '(windows-nt ms-dos)) + (getenv "SystemDrive"))) + (subst (helm-substitute-in-filename pattern)) + ;; On Windows use a simple call to `expand-file-name' to + ;; avoid Bug#2004. + (expand-fn (if directory + #'expand-file-name + #'helm-ff--expand-file-name-no-dot))) + ;; Fix Bug#2223 with tilde in directory names e.g. "~/tmp/~test/". + (funcall expand-fn (if (string-match-p "\\`~[^/]" subst) + pattern subst) + ;; directory is nil on Nix. + directory))) + +(defun helm-substitute-in-filename (fname) + "Substitute all parts of FNAME from start up to \"~/\" or \"/\". +On windows system substitute from start up to \"/[[:lower:]]:/\". +This function is needed for `helm-ff-auto-expand-to-home-or-root' +and should be used carefully elsewhere, or not at all, using +`substitute-in-file-name' instead." + (cond ((and helm--url-regexp + (string-match-p helm--url-regexp fname)) + fname) + ((and (file-remote-p fname) + helm-substitute-in-filename-stay-on-remote) + (let ((sub (substitute-in-file-name fname))) + (if (file-directory-p sub) + sub (replace-regexp-in-string "/\\'" "" sub)))) + (t + (with-temp-buffer + (insert fname) + (goto-char (point-min)) + (when (memq system-type '(windows-nt ms-dos)) + (skip-chars-forward "/")) ;; Avoid infloop in UNC paths Bug#424 + (if (re-search-forward "~.*/?\\|//\\|/[[:alpha:]]:/" nil t) + (let ((match (match-string 0))) + (goto-char (if (or (string= match "//") + (string-match-p "/[[:alpha:]]:/" match)) + (1+ (match-beginning 0)) + (match-beginning 0))) + (buffer-substring-no-properties (point) (point-at-eol))) + fname))))) + +(defun helm-point-file-in-dired (file) + "Put point on filename FILE in dired buffer." + (unless (and helm--url-regexp + (string-match-p helm--url-regexp file)) + (let ((target (expand-file-name (helm-substitute-in-filename file)))) + (dired (file-name-directory target)) + (dired-goto-file target)))) + +(defun helm-marked-files-in-dired (_candidate) + "Open a dired buffer with only marked files. + +With a prefix arg toggle dired buffer to wdired mode." + (when (< emacs-major-version 29) + ;; Fix emacs bug + ;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2022-08/msg01994.html + ;; up to emacs-28.1 and should be fixed in emacs-29+. + (advice-add 'wdired-finish-edit :override #'helm--advice-wdired-finish-edit) + (advice-add 'wdired-get-filename :override #'helm--advice-wdired-get-filename)) + (let* ((marked (helm-marked-candidates :with-wildcard t)) + (current (car marked))) + (unless (and helm--url-regexp + (string-match-p helm--url-regexp current)) + (let ((target (expand-file-name (helm-substitute-in-filename current)))) + (dired (cons helm-ff-default-directory marked)) + (dired-goto-file target) + (when (or helm-current-prefix-arg current-prefix-arg) + (call-interactively 'wdired-change-to-wdired-mode)))))) + +(defun helm-ff-run-marked-files-in-dired () + "Execute `helm-marked-files-in-dired' interactively." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-marked-files-in-dired))) +(put 'helm-ff-run-marked-files-in-dired 'helm-only t) + +(defun helm-ff--create-tramp-name (fname) + "Build filename from `helm-pattern' like /su:: or /sudo::." + ;; `tramp-make-tramp-file-name' takes 7 args on emacs-26 whereas it + ;; takes only 5 args in emacs-24/25. + (apply #'tramp-make-tramp-file-name + ;; `tramp-dissect-file-name' returns a list in emacs-26 + ;; whereas in 24.5 it returns a vector, thus the car is a + ;; symbol (`tramp-file-name') which is not needed as argument + ;; for `tramp-make-tramp-file-name' so transform the cdr in + ;; vector, and for 24.5 use directly the returned value. + (cl-loop with v = (helm-ff--tramp-cons-or-vector + (tramp-dissect-file-name fname)) + for i across v collect i))) + +(defun helm-ff--tramp-cons-or-vector (vector-or-cons) + "Return VECTOR-OR-CONS as a vector." + (pcase vector-or-cons + (`(,_l . ,ll) (vconcat ll)) + ((and vec (pred vectorp)) vec))) + +(defun helm-ff--get-tramp-methods () + "Return a list of the car of `tramp-methods'." + (or helm-ff--tramp-methods + (setq helm-ff--tramp-methods (mapcar 'car tramp-methods)))) + +(defun helm-ff--previous-mh-tramp-method (str) + (save-match-data + (with-temp-buffer + (insert str) + (when (re-search-backward + (concat "\\([|]\\)\\(" + (mapconcat 'identity (helm-ff--get-tramp-methods) "\\|") + "\\):") + nil t) + (list + (buffer-substring-no-properties (point-at-bol) (match-beginning 2)) + (buffer-substring-no-properties (match-beginning 2) (match-end 2))))))) + +(defun helm-ff--get-host-from-tramp-invalid-fname (fname) + "Extract hostname from an incomplete tramp file name. +Return nil on valid file name remote or not." + ;; Check first if whole file is remote (file-remote-p is inefficient + ;; in this case) otherwise we are matching e.g. /home/you/ssh:foo/ + ;; which is not a remote name. + ;; FIXME this will not work with a directory or a file named like + ;; "ssh:foo" and located at root (/) but it seems there is no real + ;; solution apart disabling tramp-mode when a file/dir located at / + ;; is matching helm-tramp-file-name-regexp; This would prevent usage + ;; of tramp if one have such a directory at / (who would want to + ;; have such a dir at / ???) See emacs-bug#31489. + (when (string-match-p helm-tramp-file-name-regexp fname) + (let* ((bn (helm-basename fname)) + (bd (replace-regexp-in-string (regexp-quote bn) "" fname)) + (split (split-string bn ":" t)) + (meth (car (member (car split) + (helm-ff--get-tramp-methods))))) + (and meth (string= bd "/") (car (last split)))))) + +(cl-defun helm-ff--tramp-hostnames (&optional (pattern helm-pattern)) + "Get a list of hosts for tramp method found in `helm-pattern'. +Argument PATTERN default to `helm-pattern'. It is here only for +debugging purpose." + (when (string-match helm-tramp-file-name-regexp pattern) + (let* ((mh-method (helm-ff--previous-mh-tramp-method pattern)) + (method (or (cadr mh-method) (match-string 1 pattern)))) + (cl-loop with all-methods = (helm-ff--get-tramp-methods) + for (f . h) in (tramp-get-completion-function method) + append (cl-loop for e in (funcall f (car h)) + for host = (and (consp e) (cadr e)) + ;; On emacs-27 host may be + ;; ("root" t) in sudo method. + when (and (stringp host) + (not (member host all-methods))) + collect (helm-ff-filter-candidate-one-by-one + (concat (or (car mh-method) "/") + method ":" host))) + into comps + finally return + (helm-fast-remove-dups comps :test 'equal))))) + +(defun helm-ff-before-action-hook-fn () + "Exit Helm when user try to execute action on an invalid tramp fname." + (let* ((src (helm-get-current-source)) + (cand (helm-get-selection nil nil src))) + (when (and (helm-file-completion-source-p src) + (stringp cand) + (helm-ff--invalid-tramp-name-p cand) ; Check candidate. + (helm-ff--invalid-tramp-name-p)) ; check helm-pattern. + (error "Error: Unknown file or directory `%s'" cand)))) +(add-hook 'helm-before-action-hook 'helm-ff-before-action-hook-fn) + +(cl-defun helm-ff--invalid-tramp-name-p (&optional (pattern helm-pattern)) + "Return non-nil when PATTERN is an invalid tramp filename." + (or (string= (helm-ff-set-pattern pattern) + "@@TRAMP@@") + ;; Tramp methods completion. + (string-match helm-ff-tramp-method-regexp pattern))) + +(defun helm-ff--tramp-postfixed-p (str) + "Return non nil when tramp path STR is complete." + ;; E.g.: + ;; (helm-ff--tramp-postfixed-p "/ssh:foo") + ;; => nil + ;; (helm-ff--tramp-postfixed-p "/ssh:foo:") + ;; => 10 + ;; (helm-ff--tramp-postfixed-p "/ssh:foo|sudo:") + ;; => nil + ;; (helm-ff--tramp-postfixed-p "/ssh:foo|sudo::") + ;; => 16 + (let ((methods (helm-ff--get-tramp-methods)) + result) + (save-match-data + (with-temp-buffer + (save-excursion (insert str)) + (helm-awhile (search-forward ":" nil t) + (if (save-excursion + (forward-char -1) + (or (looking-back "[/|]" (1- (point))) + (looking-back + (mapconcat (lambda (m) (format "[/|]%s" m)) methods "\\|") + (point-at-bol)))) + (setq result nil) + (setq result it))))) + result)) + +(defun helm-ff--tramp-multihops-p (name) + (cl-loop for m in (helm-ff--get-tramp-methods) + thereis (string-match (format "\\`\\(/%s:.*[|]\\).*" m) name))) + +(defun helm-ff-complete-tramp-methods () + "Completion on tramp methods in a nested helm session." + (interactive) + (with-helm-alive-p + (let* (initial-input + (str helm-pattern) + (pattern (with-temp-buffer + (insert str) + (let ((end (point)) beg) + (when (re-search-backward "[/|]" nil t) + (setq beg (1+ (point))) + (unless (= beg end) + (setq initial-input + (buffer-substring beg end)) + (delete-region beg end)) + (buffer-string))))) + (collection (helm-ff--get-tramp-methods)) + (method (helm-comp-read + "Tramp methods: " + (sort collection #'string<) + :initial-input initial-input + :fc-transformer + (lambda (candidates _source) + (cl-loop for c in candidates + collect (propertize c 'face 'helm-ff-file))) + :allow-nest t + :must-match t))) + (helm-set-pattern (concat pattern method ":"))))) +(put 'helm-ff-complete-tramp-methods 'no-helm-mx t) + +(defun helm-ff-set-pattern (pattern) + "Handle tramp filenames in `helm-pattern'." + (let* ((methods (helm-ff--get-tramp-methods)) + ;; Returns the position of last ":" entered. + (postfixed (helm-ff--tramp-postfixed-p pattern)) + (reg "\\`/\\([^[/:]+\\|[^/]+]\\):.*:") + cur-method tramp-name) + (when (string-match "\\`/\\(-\\):" pattern) + (setq pattern (replace-match tramp-default-method t t pattern 1))) + ;; In some rare cases tramp can return a nil input, + ;; so be sure pattern is a string for safety (Bug#476). + (unless pattern (setq pattern "")) + (cond ((string-match helm-ff-url-regexp pattern) pattern) + ((string-match "\\`\\$" pattern) + (substitute-in-file-name pattern)) + ((string= pattern "") "") + ((string-match "\\`[.]\\{1,2\\}/\\'" pattern) + (expand-file-name pattern)) + ;; Directories ending by a dot (Bug#1940) + ((string-match "[^/][.]/\\'" pattern) + (expand-file-name pattern)) + ((string-match ".*\\(~?/?[.]\\{1\\}/\\)\\'" pattern) + (expand-file-name default-directory)) + ((string-match ".*\\(~//\\|//\\)\\'" pattern) + (expand-file-name "/")) ; Expand to "/" or "c:/" + ((string-match "\\`\\(~/\\|.*/~/\\)\\'" pattern) + (expand-file-name "~/")) + ((string-match "\\`~/" pattern) + (expand-file-name pattern)) + ((string-match helm-ff-tramp-method-regexp pattern) + pattern) + ;; Match "/method:maybe_hostname:~" + ((and (string-match (concat reg "~") pattern) + postfixed + (setq cur-method (match-string 1 pattern)) + (member cur-method methods)) + (setq tramp-name (expand-file-name + (helm-ff--create-tramp-name + (match-string 0 pattern)))) + (replace-match tramp-name nil t pattern)) + ;; Match "/method:maybe_hostname:" + ((and (string-match reg pattern) + postfixed + (setq cur-method (match-string 1 pattern)) + (member cur-method methods)) + (setq tramp-name (helm-ff--create-tramp-name + (match-string 0 pattern))) + (replace-match tramp-name nil t pattern)) + ;; Match "/hostname:" + ((and (string-match helm-tramp-file-name-regexp pattern) + postfixed + (setq cur-method (match-string 1 pattern)) + (and cur-method (not (member cur-method methods)))) + (setq tramp-name (helm-ff--create-tramp-name + (match-string 0 pattern))) + (replace-match tramp-name nil t pattern)) + ;; Match "/method:" in this case don't try to connect. + ((and (null postfixed) + (string-match helm-tramp-file-name-regexp pattern) + (member (match-string 1 pattern) methods)) + ;; A flag to notify tramp name is incomplete. + "@@TRAMP@@") + ;; Return PATTERN unchanged. + (t pattern)))) + +(defun helm-find-files-get-candidates (&optional require-match) + "Create candidate list for `helm-source-find-files'." + (let* ((path (helm-ff-set-pattern helm-pattern)) + (dir-p (file-accessible-directory-p path)) + basedir + invalid-basedir + non-essential + (tramp-verbose helm-tramp-verbose)) ; No tramp message when 0. + ;; Tramp check if path is valid without waiting a valid + ;; connection and may send a file-error. + (setq helm--ignore-errors (file-remote-p path)) + (set-text-properties 0 (length path) nil path) + ;; Bug#118 allow creation of newdir+newfile. + (unless (or + ;; A tramp file name not completed. + (string= path "@@TRAMP@@") + ;; An empty pattern + (string= path "") + (and (string-match-p ":\\'" path) + (helm-ff--tramp-postfixed-p path)) + ;; Check if base directory of PATH is valid. + (helm-aif (file-name-directory path) + ;; If PATH is a valid directory IT=PATH, + ;; else IT=basedir of PATH. + (file-directory-p it))) + ;; BASEDIR is invalid, that's mean user is starting + ;; to write a non--existing path in minibuffer + ;; probably to create a 'new_dir' or a 'new_dir+new_file'. + (setq invalid-basedir t)) + ;; Don't set now `helm-pattern' if `path' == "@@TRAMP@@" + ;; like that the actual value (e.g /ssh:) is passed to + ;; `helm-ff--tramp-hostnames'. + (unless (or (string= path "@@TRAMP@@") + invalid-basedir) ; Leave helm-pattern unchanged. + (setq helm-ff-auto-update-flag ; [1] + ;; Unless auto update is disabled start auto updating only + ;; at third char. + (unless (or (null helm-ff--auto-update-state) + ;; But don't enable auto update when + ;; deleting backward. + helm-ff--deleting-char-backward + (and dir-p (not (string-match-p "/\\'" path)))) + (or (>= (length (helm-basename path)) 3) dir-p))) + ;; At this point the tramp connection is triggered. + (helm-log + "Pattern=%S" + (setq helm-pattern (if (string-match helm-ff-tramp-method-regexp path) + ;; A tramp method, don't modify pattern. + helm-pattern + (helm-ff--transform-pattern-for-completion path)))) + ;; This have to be set after [1] to allow deleting char backward. + (setq basedir (or (helm-aand + (if (and dir-p helm-ff-auto-update-flag) + ;; Add the final "/" to path + ;; when `helm-ff-auto-update-flag' is enabled. + (file-name-as-directory path) + (if (string= path "") + "/" (file-name-directory path))) + (expand-file-name it)) + default-directory)) + (setq helm-ff-default-directory + (if (string= helm-pattern "") + (expand-file-name "/") ; Expand to "/" or "c:/" + ;; If path is an url *default-directory have to be nil. + (unless (or (string-match helm-ff-url-regexp path) + (and helm--url-regexp + (string-match helm--url-regexp path))) + basedir)))) + (when (and (string-match ":\\'" path) + (file-remote-p basedir nil t)) + (setq helm-pattern basedir)) + (cond ((string-match helm-ff-tramp-method-regexp path) ; Tramp methods + (mapcar (lambda (method) + (helm-ff-filter-candidate-one-by-one + (concat "/" ":" method))) + (helm-ff--get-tramp-methods))) + ((string= path "@@TRAMP@@") + (helm-ff--tramp-hostnames)) ; Hostnames completion. + ((or (and (file-regular-p path) + (eq last-repeatable-command 'helm-execute-persistent-action)) + ;; `ffap-url-regexp' don't match until url is complete. + (string-match helm-ff-url-regexp path) + invalid-basedir + (and (not (file-exists-p path)) (string-match "/$" path)) + (and helm--url-regexp (string-match helm--url-regexp path))) + ;; Do NOT filter boring files here (Bug#2330). + (list (helm-ff-filter-candidate-one-by-one path nil t))) + ((string= path "") (helm-ff-directory-files "/")) + ;; Check here if directory is accessible (not working on Windows). + ((and (file-directory-p path) (not (file-readable-p path))) + ;; Prefix error message with @@@@ for safety + ;; (some files may match file-error See bug#2400) + (list (cons (format "@@@@file-error: Opening directory permission denied `%s'" path) + path))) + ;; A fast expansion of PATH is made only if `helm-ff-auto-update-flag' + ;; is enabled. + ((and dir-p helm-ff-auto-update-flag) + (helm-ff-directory-files path)) + (t (append (unless (or (eq require-match t) + ;; Check here if path is an existing + ;; file before adding it to + ;; candidates, it was previously done + ;; in the sort function but this + ;; create a bug with remote files + ;; when path is at the same time a + ;; pattern matching a candidate and a + ;; real candidate e.g. ack and + ;; ack-grep in /usr/bin. This is due + ;; presumably to a latency more + ;; important with remote files which + ;; lead to a confusion with the + ;; pattern matching one candidate and + ;; the real candidate which is same + ;; as pattern. + (file-exists-p path) + ;; When `helm-ff-auto-update-flag' has been + ;; disabled, whe don't want PATH to be added on top + ;; if it is a directory. + dir-p) + ;; Do NOT filter boring files here (Bug#2330). + (list (helm-ff-filter-candidate-one-by-one path nil t))) + (helm-ff-directory-files basedir)))))) + +(defun helm-list-directory (directory &optional sel) + "List directory DIRECTORY. + +If DIRECTORY is remote use `helm-list-directory-function', +otherwise use `directory-files'. +SEL argument is only here for debugging purpose, it default to +`helm-get-selection'." + (let* ((remote (file-remote-p directory 'method)) + (helm-list-directory-function + (cond ((and remote (string= remote "ftp")) + #'helm-list-dir-lisp) + ((and remote (string= remote "adb")) + #'helm-list-dir-adb) + (t helm-list-directory-function))) + (remote-fn-p (eq helm-list-directory-function + 'helm-list-dir-external)) + (sort-method (cl-case helm-ff-initial-sort-method + (newest (if (and remote remote-fn-p) + "-t" #'file-newer-than-file-p)) + (size (if (and remote remote-fn-p) + "-S" #'helm-ff-file-larger-that-file-p)) + (ext (unless (and remote remote-fn-p) + #'helm-group-candidates-by)) + (t nil)))) + (cond (remote + (ignore-errors + (funcall helm-list-directory-function directory sort-method))) + ((memq helm-ff-initial-sort-method '(newest size)) + (sort (directory-files + directory t directory-files-no-dot-files-regexp) + sort-method)) + ((eq helm-ff-initial-sort-method 'ext) + (funcall sort-method + (directory-files + directory t directory-files-no-dot-files-regexp) + #'file-name-extension + (or sel (helm-get-selection) ""))) + (t (directory-files + directory t directory-files-no-dot-files-regexp))))) + +(defsubst helm-ff-file-larger-that-file-p (f1 f2) + (let ((attr1 (file-attributes f1)) + (attr2 (file-attributes f2))) + (> (nth 7 attr1) (nth 7 attr2)))) + +(defun helm-list-dir-lisp (directory &optional sort-method) + "List DIRECTORY with `file-name-all-completions' as backend. + +Add a `helm-ff-dir' property on each fname ending with \"/\"." + ;; NOTE: `file-name-all-completions' and `directory-files' and most + ;; tramp file handlers don't handle cntrl characters in fnames, so + ;; the displayed files will be plain wrong in this case, even worst + ;; the filenames will be splitted in two or more filenames. + (cl-loop for f in (sort (file-name-all-completions "" directory) + (or sort-method 'string-lessp)) + unless (or (string= f "") + (member f '("./" "../" "." ".."))) + if (and (helm--dir-name-p f) + (helm--dir-file-name f directory)) + collect (propertize it 'helm-ff-dir t) + else collect (propertize (expand-file-name f directory) + 'helm-ff-file t))) + +(defun helm-file-name-all-completions-internal (directory) + (let ((switches "-1F")) + (with-temp-buffer + (insert-directory (format "%s*" + (file-name-as-directory directory)) + switches t) + (split-string + (buffer-substring-no-properties (point-min) (point-max)) + "\n" t)))) + +(defun helm-list-dir-adb (directory &optional sort-method) + "List DIRECTORY with `helm-file-name-all-completions-internal' as backend. + +This is used for tramp adb backend. + +Add a `helm-ff-dir' property on each fname ending with \"/\"." + (cl-loop with files = (helm-file-name-all-completions-internal directory) + for f in (sort files (or sort-method 'string-lessp)) + for split = (split-string f "->" t) + for fname = (replace-regexp-in-string " $" "" (car split)) + for truename = (cadr split) + collect (cond ((string-match "/\\'" fname) + (propertize (helm--dir-file-name fname directory) + 'helm-ff-dir t)) + (truename + (propertize (expand-file-name + (substring fname 0 (1- (length fname))) + directory) + 'helm-ff-sym truename)) + (t (propertize (expand-file-name fname directory) + 'helm-ff-file t))))) + +(defun helm-list-dir-external (dir &optional sort-method) + "List directory DIR with external shell command as backend. + +This function is fast enough to be used for remote files and save +the type of files at the same time in a property for using it +later in the transformer." + (let ((default-directory (file-name-as-directory + (expand-file-name dir)))) + (with-temp-buffer + (when (eq (process-file-shell-command + (format + ;; -A remove dot files, -F append [*=@|/>] at eof + ;; and -Q quote the real filename. If not using -Q, + ;; there is no way to distinguish if foo* is a real + ;; file or if it is foo the executable file so with + ;; -Q we have "foo"* for the executable file foo and + ;; "foo*" for the real file foo. The downside is + ;; that we need an extra step to remove the quotes + ;; at the end which impact performances. + "ls -A -1 -F -b -Q %s | awk -v dir=%s '{print dir $0}'" + (or sort-method "") + (shell-quote-argument default-directory)) + nil t nil) + 0) + (goto-char (point-min)) + (save-excursion + (while (re-search-forward "[*=@|/>]$" nil t) + ;; A line looks like /home/you/"foo"@ + (helm-acase (match-string 0) + ("*" (replace-match "") + (put-text-property + (point-at-bol) (point-at-eol) 'helm-ff-exe t)) + ("@" (replace-match "") + (put-text-property + (point-at-bol) (point-at-eol) 'helm-ff-sym t)) + ("/" (replace-match "") + (put-text-property + (point-at-bol) (point-at-eol) 'helm-ff-dir t)) + (("=" "|" ">") (replace-match ""))))) + (while (re-search-forward "[\"]" nil t) + (replace-match "")) + (add-text-properties (point-min) (point-max) '(helm-ff-file t)) + (split-string (buffer-string) "\n" t))))) + +(defun helm-ff-directory-files (directory &optional force-update) + "List contents of DIRECTORY. +Argument FULL mean absolute path. +It is same as `directory-files' but always returns the dotted +filename \\='.' and \\='..' even on root directories in Windows +systems. +When FORCE-UPDATE is non nil recompute candidates even if DIRECTORY is +in cache." + (let ((method (file-remote-p directory 'method))) + (setq directory (file-name-as-directory + (expand-file-name directory))) + (or (and (not force-update) + (gethash directory helm-ff--list-directory-cache)) + (let* (file-error + (ls (condition-case err + (helm-list-directory directory) + ;; Handle file-error from here for Windows + ;; because predicates like `file-readable-p' and friends + ;; seem broken on emacs for Windows systems (always returns t). + ;; This should never be called on GNU/Linux/Unix + ;; as the error is properly intercepted in + ;; `helm-find-files-get-candidates' by `file-readable-p'. + (file-error + (prog1 + ;; Prefix error message with @@@@ for safety + ;; (some files may match file-error See bug#2400) + (list (format "@@@@%s:%s" + (car err) + (mapconcat 'identity (cdr err) " "))) + (setq file-error t))))) + (dot (concat directory ".")) + (dot2 (concat directory "..")) + (candidates (append (and (not file-error) (list dot dot2)) ls))) + (puthash directory (+ (length ls) 2) helm-ff--directory-files-length) + (prog1 + (puthash directory + (cl-loop for f in candidates + when (helm-ff-filter-candidate-one-by-one f) + collect it) + helm-ff--list-directory-cache) + ;; Put an inotify watcher to check directory modifications. + (unless (or (null helm-ff-use-notify) + (member method helm-ff-inotify-unsupported-methods) + (gethash directory helm-ff--file-notify-watchers)) + (condition-case-unless-debug err + (puthash directory + (file-notify-add-watch + directory + '(change attribute-change) + (helm-ff--inotify-make-callback directory)) + helm-ff--file-notify-watchers) + (file-notify-error (user-error "Error: %S %S" (car err) (cdr err)))))))))) + +(defun helm-ff--inotify-make-callback (directory) + "Return a callback for `file-notify-add-watch'." + (lambda (event) + (let ((desc (cadr event))) + ;; `attribute-changed' means permissions have changed, not + ;; file modifications like file changes, visit + ;; etc... AFAIU the desc for this is `changed' and for our + ;; use case we don't care of this. + (when (memq desc '(created deleted renamed attribute-changed)) + ;; When DIRECTORY is modified remove it from cache. + (remhash directory helm-ff--list-directory-cache) + ;; Remove watch as well in case of rename or delete. + (file-notify-rm-watch (gethash directory helm-ff--file-notify-watchers)) + (remhash directory helm-ff--file-notify-watchers))))) + +(defun helm-ff-tramp-cleanup-hook (vec) + "Remove remote directories related to VEC in helm-ff* caches. +Remove as well all related file-notify watchers. + +This is meant to run in `tramp-cleanup-connection-hook'." + (cl-loop for key being the hash-keys in helm-ff--list-directory-cache + when (equal (file-remote-p key 'method) (cadr vec)) + do (remhash key helm-ff--list-directory-cache)) + (cl-loop for key being the hash-keys in helm-ff--file-notify-watchers + when (equal (file-remote-p key 'method) (cadr vec)) + do (progn + (file-notify-rm-watch + (gethash key helm-ff--file-notify-watchers)) + (remhash key helm-ff--file-notify-watchers)))) +(add-hook 'tramp-cleanup-connection-hook #'helm-ff-tramp-cleanup-hook) + +(defun helm-ff-handle-backslash (fname) + ;; Allow creation of filenames containing a backslash. + (cl-loop with bad = '((92 . "")) + for i across fname + if (assq i bad) concat (cdr it) + else concat (string i))) + +(defun helm-ff-fuzzy-matching-p () + (and helm-ff-fuzzy-matching + (not (memq helm-mm-matching-method '(multi1 multi3p))))) + +(defun helm-ff--transform-pattern-for-completion (pattern) + "Maybe return PATTERN with it's basename modified as a regexp. +This happens only when `helm-ff-fuzzy-matching' is enabled. +This provides a similar behavior as `ido-enable-flex-matching'. +See also `helm--mapconcat-pattern'. +If PATTERN is an url return it unmodified. +When PATTERN contains a space fallback to multi-match. +If basename contains one or more space fallback to multi-match. +If PATTERN is a valid directory name, return PATTERN unchanged." + ;; handle bad filenames containing a backslash (no more needed in + ;; emacs-26, also prevent regexp matching with e.g. "\|"). + ;; (setq pattern (helm-ff-handle-backslash pattern)) + (let ((bn (helm-basename pattern)) + (bd (or (helm-basedir pattern) "")) + ;; Trigger tramp connection with file-directory-p. + (dir-p (file-directory-p pattern)) + (tramp-p (cl-loop for (m . f) in tramp-methods + thereis (string-match m pattern)))) + ;; Always regexp-quote base directory name to handle + ;; crap dirnames such e.g bookmark+ + (cond + ((or (and dir-p tramp-p (string-match ":\\'" pattern)) + (string= pattern "") + (and dir-p (<= (length bn) 2)) + ;; Fix Bug#541 when BD have a subdir similar + ;; to BN, don't switch to match plugin + ;; which will match both. + (and dir-p (string-match (regexp-quote bn) bd))) + ;; Use full PATTERN on e.g "/ssh:host:". + (regexp-quote pattern)) + ;; Prefixing BN with a space call multi-match completion. + ;; This allow showing all files/dirs matching BN (Bug#518). + ;; FIXME: some multi-match methods may not work here. + (dir-p (concat (regexp-quote bd) " " (regexp-quote bn))) + ((or (not (helm-ff-fuzzy-matching-p)) + (string-match "[ !]" bn)) ; Fall back to multi-match. + (concat (regexp-quote bd) " " bn)) + ((or (string-match "[*][.]?.*" bn) ; Allow entering wildcard. + (string-match "/\\'" pattern) ; Allow mkdir. + (string-match helm-ff-url-regexp pattern) + (and (string= helm-ff-default-directory "/") tramp-p)) + ;; Don't treat wildcards ("*") as regexp char. + ;; (e.g ./foo/*.el => ./foo/\\*\\.el) or ./foo/*.[ch] => + ;; ./foo/\\*\\.\\[ch] + (concat (regexp-quote bd) + ;; We were previously using + ;; (replace-regexp-in-string "[*]" "[*]" bn) but this + ;; doesn't handle wilcards like *.[ch], so regexp-quote + ;; bn as well. + (regexp-quote bn))) + (t (concat (regexp-quote bd) + (if (>= (length bn) 2) ; wait 2nd char before concating. + (helm--mapconcat-pattern bn) + (concat ".*" (regexp-quote bn)))))))) + +(defun helm-dir-is-dot (dir) + (string-match "\\(?:/\\|\\`\\)\\.\\{1,2\\}\\'" dir)) + +(defun helm-ff-save-history () + "Store the last value of `helm-ff-default-directory' in `helm-ff-history'. +Note that only existing directories are saved here." + (when (and helm-ff-default-directory + (helm-file-completion-source-p) + (file-directory-p helm-ff-default-directory)) + (set-text-properties 0 (length helm-ff-default-directory) + nil helm-ff-default-directory) + (push helm-ff-default-directory helm-ff-history))) +(add-hook 'helm-cleanup-hook 'helm-ff-save-history) + +(defun helm-ff-valid-symlink-p (file &optional link) + "Returns the truename of FILE if it exists. +If we already know the truename of FILE we can pass it with LINK arg +to avoid an unnecessary call to `file-truename'." + (helm-aif (condition-case-unless-debug nil + ;; `file-truename' send error + ;; on cyclic symlinks (Bug#692). + (or link (file-truename file)) + (error nil)) + (and (file-exists-p it) it))) + +(defun helm-get-default-mode-for-file (filename) + "Return the default mode to open FILENAME." + (let ((mode (cl-loop for (r . m) in auto-mode-alist + thereis (and (string-match r filename) m)))) + (or (and (symbolp mode) mode) "Fundamental"))) + +(defun helm-ff-properties (candidate) + "Show file properties of CANDIDATE in a tooltip or message." + (require 'helm-external) ; For `helm-get-default-program-for-file'. + (helm-aif (helm-file-attributes candidate) + (let* ((dired-line (helm-file-attributes + candidate :dired t :human-size t)) + (type (cl-getf it :type)) + (mode-type (cl-getf it :mode-type)) + (owner (cl-getf it :uid)) + (owner-right (cl-getf it :user t)) + (group (cl-getf it :gid)) + (group-right (cl-getf it :group)) + (other-right (cl-getf it :other)) + (trash (and (helm-ff-trash-file-p candidate) + (helm-ff--get-dest-file-from-trash + (helm-ff-trash-list) + (replace-regexp-in-string + "\\.trashinfo\\'" "" candidate)))) + (size (helm-file-human-size (cl-getf it :size))) + (modif (cl-getf it :modif-time)) + (access (cl-getf it :access-time)) + (ext (helm-get-default-program-for-file candidate)) + (tooltip-hide-delay (or helm-tooltip-hide-delay tooltip-hide-delay))) + (if (and (display-graphic-p) tooltip-mode) + (tooltip-show + (concat + (helm-basename candidate) "\n" + dired-line "\n" + (format "Mode: %s\n" (helm-get-default-mode-for-file candidate)) + (format "Ext prog: %s\n" (or (and ext (replace-regexp-in-string + " %s" "" ext)) + "Not defined")) + (format "Type: %s: %s\n" type mode-type) + (when (string= type "symlink") + (format "True name: '%s'\n" + (cond ((string-match "^\\.#" (helm-basename candidate)) + "Autosave symlink") + ((helm-ff-valid-symlink-p candidate)) + (t "Invalid Symlink")))) + (format "Owner: %s: %s\n" owner owner-right) + (format "Group: %s: %s\n" group group-right) + (format "Others: %s\n" other-right) + (format "Size: %s\n" size) + (when (string= type "directory") + (format "Size used in directory: %s\n" + (helm-directory-size + candidate current-prefix-arg t))) + (format "Modified: %s\n" modif) + (format "Accessed: %s\n" access) + (and (stringp trash) + (format "Trash: %s\n" + (abbreviate-file-name trash))))) + (message dired-line) (sit-for 5))) + (message "Permission denied, file not readable"))) + +(defun helm-ff-properties-persistent () + "Show properties without quitting helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'properties-action '(helm-ff-properties . never-split)) + (helm-execute-persistent-action 'properties-action))) +(put 'helm-ff-properties-persistent 'helm-only t) + +(defun helm-ff-persistent-delete () + "Delete current candidate without quitting." + (interactive) + (with-helm-alive-p + (helm-set-attr 'quick-delete '(helm-ff-quick-delete . never-split)) + (helm-execute-persistent-action 'quick-delete))) +(put 'helm-ff-persistent-delete 'helm-only t) + +(defun helm-ff-dot-file-p (file) + "Check if FILE is `.' or `..'." + (member (helm-basename file) '("." ".."))) + +(defun helm-ff-kill-buffer-fname (candidate) + (let* ((buf (get-file-buffer candidate)) + (buf-name (buffer-name buf))) + (cond ((and buf (eq buf (get-buffer helm-current-buffer))) + (user-error + "Can't kill `helm-current-buffer' without quitting session")) + (buf (kill-buffer buf) (message "Buffer `%s' killed" buf-name)) + (t (message "No buffer to kill"))))) + +(defun helm-ff-kill-or-find-buffer-fname (candidate) + "Find file CANDIDATE or kill its buffer if it is visible. +Never kill `helm-current-buffer'. +Never kill buffer modified. +This is called normally on third hit of \ +\\\\[helm-execute-persistent-action] +in `helm-find-files-persistent-action-if'." + (let* ((buf (get-file-buffer candidate)) + (buf-name (buffer-name buf)) + (win (get-buffer-window buf)) + (helm--reading-passwd-or-string t)) + (cond ((and buf win (eq buf (get-buffer helm-current-buffer))) + (user-error + "Can't kill `helm-current-buffer' without quitting session")) + ((and buf win (buffer-modified-p buf)) + (message "Can't kill modified buffer, please save it before")) + ((and buf win) + (kill-buffer buf) + (if (and helm-persistent-action-display-window + (window-dedicated-p (next-window win 1))) + (delete-window helm-persistent-action-display-window) + (set-window-buffer win helm-current-buffer)) + (message "Buffer `%s' killed" buf-name)) + (t (find-file candidate))))) + +(defun helm-ff-run-kill-buffer-persistent () + "Execute `helm-ff-kill-buffer-fname' without quitting." + (interactive) + (with-helm-alive-p + (helm-set-attr 'kill-buffer-fname 'helm-ff-kill-buffer-fname) + (helm-execute-persistent-action 'kill-buffer-fname))) +(put 'helm-ff-run-kill-buffer-persistent 'helm-only t) + +;; Preview with external tool +(defun helm-ff-persistent-open-file-externally (file) + (require 'helm-external) + (if (helm-get-default-program-for-file file) + (helm-open-file-externally file) + (message "Please configure an external program for `*%s' file in `helm-external-programs-associations'" + (file-name-extension file t)))) + +(defun helm-ff-run-preview-file-externally () + (interactive) + (with-helm-alive-p + (helm-set-attr 'open-file-externally '(helm-ff-persistent-open-file-externally . never-split)) + (helm-execute-persistent-action 'open-file-externally))) +(put 'helm-ff-run-preview-file-externally 'helm-only t) + +(defun helm-ff-prefix-filename (fname &optional file-or-symlinkp new-file) + "Add display property to FNAME. +Display property presents a string maybe prefixed with [?] or [@]. +If FILE-OR-SYMLINKP is non-nil this means we assume FNAME is an +existing filename or valid symlink and there is no need to test +it. +NEW-FILE when non-nil means FNAME is a non existing file and +return FNAME with display property prefixed with [?]." + (let* ((prefix-new (propertize + " " 'display + (propertize "[?]" 'face 'helm-ff-prefix))) + (prefix-url (propertize + " " 'display + (propertize "[@]" 'face 'helm-ff-prefix)))) + (cond (file-or-symlinkp fname) + ((or (string-match helm-ff-url-regexp fname) + (and helm--url-regexp (string-match helm--url-regexp fname))) + (concat prefix-url " " fname)) + (new-file (concat prefix-new " " fname))))) + +(defun helm-ff-score-candidate-for-pattern (real disp pattern) + (if (or (member real '("." "..")) + ;; Incomplete filenames are prefixed with two spaces, the + ;; first one beeing propertized with a 'display prop + ;; i.e. "[?] foo". + (and (string-match-p "\\`\\s-\\{2\\}" disp) + (string= real (substring-no-properties disp 2)))) + 900000 + (helm-score-candidate-for-pattern real pattern))) + +(defun helm-ff-sort-candidates-1 (candidates input) + "Sort function for `helm-source-find-files'. +Return candidates prefixed with basename of INPUT first." + (if (or (and (file-directory-p input) + (string-match "/\\'" input)) + (string-match "\\`\\$" input) + (null candidates)) + candidates + (let* ((memo-src (make-hash-table :test 'equal)) + (all (sort candidates + (lambda (s1 s2) + (let* ((score (lambda (disp real) + (helm-ff-score-candidate-for-pattern + disp real (helm-basename input)))) + ;; Reals + (r1 (helm-basename (if (consp s1) (cdr s1) s1))) + (r2 (helm-basename (if (consp s2) (cdr s2) s2))) + ;; Displays + (d1 (helm-basename (if (consp s1) (car s1) s1))) + (d2 (helm-basename (if (consp s2) (car s2) s2))) + (sc1 (or (gethash r1 memo-src) + (puthash r1 (funcall score r1 d1) memo-src))) + (sc2 (or (gethash r2 memo-src) + (puthash r2 (funcall score r2 d2) memo-src)))) + (cond ((= sc1 sc2) + (< (string-width r1) + (string-width r2))) + ((> sc1 sc2)))))))) + all))) + +(defun helm-ff-sort-candidates (candidates _source) + "Sort function for `helm-source-find-files'. +Return candidates prefixed with basename of `helm-input' first." + (helm-ff-sort-candidates-1 candidates helm-input)) + +(defun helm-ff-boring-file-p (file) + "Returns non nil when FILE is matching boring regexps." + ;; Prevent user doing silly thing like + ;; adding the dotted files to boring regexps (#924). + (and helm-ff-skip-boring-files + (not (string-match "\\.$" file)) + (string-match helm-ff--boring-regexp file))) + +(defvar helm-ff--git-found-p nil) +(defun helm-ff-git-ignored-p (file) + "Returns non nil when FILE is matched in \".gitignore\" file." + (and helm-ff-skip-git-ignored-files + (not (file-remote-p file)) + (or helm-ff--git-found-p + (setq helm-ff--git-found-p (executable-find "git"))) + (zerop (call-process "git" nil nil nil "check-ignore" "-q" file)))) + +(defun helm-ff-fct (candidates _source) + "Filter in charge of displaying basename or full path in HFF. +Because CANDIDATES are directly stored as (basename . full_path), when +`helm-ff-transformer-show-only-basename' is non nil do nothing and +return directly CANDIDATES." + (if (null helm-ff-transformer-show-only-basename) + (cl-loop for (_disp . real) in candidates + for fc = (helm-ff-filter-candidate-one-by-one real 'reverse) + when fc collect fc) + candidates)) + +(defun helm-ff-filter-candidate-one-by-one (file &optional reverse skip-boring-check) + "Transform file in a cons cell like (DISPLAY . REAL). +DISPLAY is shown as basename of FILE and REAL as full path of FILE. +If REVERSE is non nil DISPLAY is shown as full path. +If SKIP-BORING-CHECK is non nil don't filter boring files." + (let* ((basename (helm-basename file)) + (dot (helm-ff-dot-file-p file)) + (urlp (string-match-p helm-ff-url-regexp file)) + ;; Filename with cntrl chars e.g. foo^J + (disp (or (helm-ff--get-host-from-tramp-invalid-fname file) + (replace-regexp-in-string + "[[:cntrl:]]" "?" + (if (or reverse urlp) file basename)))) + (len (length disp)) + (backup (backup-file-name-p disp))) + (when (string-match "/\\'" file) + (setq disp (concat disp "/") + len (1+ len))) + ;; We want to filter boring files only on the files coming + ;; from the output of helm-ff-directory-files not on single + ;; candidate (Bug#2330). + (unless (and (not skip-boring-check) + (or (helm-ff-boring-file-p basename) + (helm-ff-git-ignored-p file))) + ;; Highlight extensions. + (helm-aif (and (not backup) + (not urlp) + (helm-file-name-extension disp)) + (when (condition-case _err + (string-match (format "\\.\\(%s\\)\\'" it) disp) + (invalid-regexp nil)) + (add-face-text-property + (match-beginning 1) (match-end 1) + 'helm-ff-file-extension t disp))) + ;; Handle tramp files with minimal highlighting. + (if (and (or (string-match-p helm-tramp-file-name-regexp helm-pattern) + (helm-file-on-mounted-network-p helm-pattern))) + (let* ((hostp (helm-ff--get-host-from-tramp-invalid-fname file))) + (helm-acond (;; Dot directories . and .. + dot + (cons (propertize file 'face 'helm-ff-dotted-directory) file)) + ;; Directories. + ((get-text-property 1 'helm-ff-dir file) + (cons (propertize disp 'face 'helm-ff-directory) file)) + ;; Backup files. + (backup + (cons (propertize disp 'face 'helm-ff-backup-file) file)) + ;; Executable files. + ((get-text-property 1 'helm-ff-exe file) + (add-face-text-property 0 len 'helm-ff-executable t disp) + (cons disp file)) + ;; Symlinks. + ((get-text-property 1 'helm-ff-sym file) + (add-face-text-property 0 len 'helm-ff-symlink t disp) + (if (stringp it) ; adb method. + (progn + (add-face-text-property 0 (length it) 'helm-ff-truename nil it) + (cons (propertize disp 'display (concat disp " ->" it)) file)) + (cons disp file))) + ;; Regular files. + ((get-text-property 1 'helm-ff-file file) + (add-face-text-property 0 len 'helm-ff-file t disp) + (cons disp file)) + ;; Tramp methods. + ((string-match helm-ff-tramp-method-regexp file) + (let ((method (match-string 1 file)) + (mh (helm-ff--tramp-multihops-p helm-pattern))) + (cons (propertize (concat (if mh "" "/") method) 'face 'helm-ff-file) + (if mh + (concat (match-string 1 helm-pattern) ":" method) + (concat "/:" method))))) + ;; non existing files. + (t + (add-face-text-property 0 len 'helm-ff-file t disp) + (cons (helm-ff-prefix-filename + disp + hostp (unless hostp 'new-file)) + file)))) + + ;; Highlight local files showing everything, symlinks, exe, + ;; dirs etc... + (let* ((attr (condition-case err + (file-attributes file) + (file-error + ;; Possible error not happening during listing + ;; but when calling file-attributes see error + ;; with sshfs bug#2405 + (message "%s:%s" (car err) (cdr err)) nil))) + (type (car attr)) + x-bit) + (cond (;; Not a file but the message error printed in + ;; helm-buffer. Such a message should not have a + ;; subdir so matching on bol should suffice, but to + ;; be sure use @@@@ as prefix in file-error message + ;; to be safe bug#2400. + (string-match "\\`@@@@file-error:" file) file) + (;; A dead symlink. + (and (stringp type) + (not (helm-ff-valid-symlink-p file)) + (not (string-match "^\\.#" basename))) + (add-face-text-property 0 len 'helm-ff-invalid-symlink t disp) + (cons disp file)) + ;; A dotted directory symlinked. + ((and dot (stringp type)) + (cons (propertize file 'face 'helm-ff-dotted-symlink-directory) file)) + ;; A dotted directory. + (dot + (cons (propertize file 'face 'helm-ff-dotted-directory) file)) + ;; Backup files. + (backup + (cons (propertize disp 'face 'helm-ff-backup-file) file)) + ;; A symlink. + ((stringp type) + (let* ((abbrev (abbreviate-file-name type)) + (len-abbrev (length abbrev))) + (helm-aif (helm-file-name-extension abbrev) + (when (string-match (format "\\.\\(%s\\)\\'" it) abbrev) + (add-face-text-property + (match-beginning 1) (match-end 1) + 'helm-ff-file-extension t abbrev))) + (add-face-text-property 0 len-abbrev 'helm-ff-truename t abbrev) + ;; Colorize extension only on truename. + (add-face-text-property 0 len 'helm-ff-symlink nil disp) + (cons (propertize disp 'display (concat disp " -> " abbrev)) + file))) + ;; A directory. + ((eq t type) + (cons (propertize disp 'face 'helm-ff-directory) file)) + ;; A character device file. + ((and attr (string-match + "\\`[cp]" (setq x-bit (substring (nth 8 attr) 0 4)))) + (add-face-text-property 0 len 'helm-ff-pipe t disp) + (cons disp file)) + ;; A socket file. + ((and attr (string-match "\\`[s]" x-bit)) + (add-face-text-property 0 len 'helm-ff-socket t disp) + (cons disp file)) + ;; An executable file. + ((and attr (string-match "x\\'" x-bit)) + (add-face-text-property 0 len 'helm-ff-executable t disp) + (cons disp file)) + ;; An executable file with suid + ((and attr (string-match "s\\'" x-bit)) + (add-face-text-property 0 len 'helm-ff-suid t disp) + (cons disp file)) + ;; A file. + ((and attr (null type)) + (add-face-text-property 0 len 'helm-ff-file t disp) + (cons disp file)) + ;; A tramp method + ;; At this point no need to handle multi hops syntax + ;; which is considered remote and handled in first + ;; cond before. + ((string-match helm-ff-tramp-method-regexp file) + (cons (propertize (concat "/" (match-string 1 file)) + 'face 'helm-ff-nofile) + (concat "/:" (match-string 1 file)))) + ;; A non--existing file. + (t + (add-face-text-property 0 len 'helm-ff-nofile t disp) + (cons (helm-ff-prefix-filename + disp nil 'new-file) + file)))))))) + +(defun helm-ff-icons-transformer (candidates _source) + "Transformer for HFF that prefix candidates with icons." + (cl-loop for (disp . fname) in candidates + for icon = (helm-ff-get-icon disp fname) + collect (cons (concat icon disp) fname))) + +(defun helm-ff-get-icon (disp file) + "Get icon from all-the-icons for FILE. +Arg DISP is the display part of the candidate." + (let ((icon (helm-acond (;; Non symlink directories. + (helm-ff--is-dir-from-disp disp) + (all-the-icons-octicon "file-directory")) + (;; All files, symlinks may be symlink directories. + (helm-ff--is-file-from-disp disp) + ;; Detect symlink directories. We must call + ;; `file-directory-p' here but it is + ;; limited to symlinks, so it should not + ;; degrade too much performances. + (if (and (memq it '(helm-ff-symlink + helm-ff-dotted-symlink-directory)) + (file-directory-p file)) + (let* ((icon (all-the-icons-match-to-alist + (helm-basename file) + all-the-icons-dir-icon-alist)) + (args (cdr icon))) + (apply #'all-the-icons-octicon + "file-symlink-directory" (cdr args))) + (all-the-icons-icon-for-file file)))))) + (when icon (concat icon " ")))) + +(defun helm-ff--is-dir-from-disp (disp) + "Return the face used for candidate when candidate is a directory." + (cl-loop for face in '(helm-ff-directory helm-ff-dotted-directory) + thereis (text-property-any 0 (length disp) 'face face disp))) + +(defun helm-ff--is-file-from-disp (disp) + "Return the face used for file's candidate or dotted-symlink dirs." + (cl-loop with len = (length disp) + for face in '(helm-ff-file + helm-ff-suid + helm-ff-executable + helm-ff-socket + helm-ff-pipe + helm-ff-symlink + helm-ff-dotted-symlink-directory + helm-ff-backup-file) + when (text-property-any 0 len 'face face disp) + return face)) + +;;;###autoload +(define-minor-mode helm-ff-icon-mode + "Display icons from `all-the-icons' package in HFF when enabled. + +NOTE: This mode is building `helm-source-find-files', so if you enable +it from your init file, ensure to call it _after_ your defmethod's +`helm-setup-user-source' definitions (if some) to ensure they are called." + :global t + :group 'helm-files + (require 'all-the-icons) + (if helm-ff-icon-mode + (progn + (unless helm-source-find-files + (setq helm-source-find-files + (helm-make-source + "Find Files" 'helm-source-ffiles))) + (let ((fct (helm-get-attr + 'filtered-candidate-transformer + helm-source-find-files))) + (unless (memq 'helm-ff-icons-transformer fct) + (helm-set-attr 'filtered-candidate-transformer + (append fct '(helm-ff-icons-transformer)) + helm-source-find-files)))) + (helm-set-attr 'filtered-candidate-transformer + (remove 'helm-ff-icons-transformer + (helm-get-attr + 'filtered-candidate-transformer + helm-source-find-files)) + helm-source-find-files))) + +(defun helm-find-files-action-transformer (actions candidate) + "Action transformer for `helm-source-find-files'." + (let ((str-at-point (with-helm-current-buffer + (buffer-substring-no-properties + (point-at-bol) (point-at-eol))))) + (when (file-regular-p candidate) + (setq actions (helm-append-at-nth + actions '(("Checksum File" . helm-ff-checksum)) 4))) + (cond ((and (file-exists-p candidate) + (string-match helm-ff--trash-directory-regexp + (helm-basedir (expand-file-name candidate))) + (not (member (helm-basename candidate) '("." ".."))) + (executable-find "trash")) + (helm-append-at-nth + actions + '(("Restore file(s) from trash" . helm-restore-file-from-trash) + ("Delete file(s) from trash" . helm-ff-trash-rm)) + 1)) + ((and helm--url-regexp + (not (string-match-p helm--url-regexp str-at-point)) + (not (with-helm-current-buffer (eq major-mode 'dired-mode))) + (string-match-p ":\\([0-9]+:?\\)" str-at-point)) + (append '(("Find file to line number" . helm-ff-goto-linum)) + actions)) + ((string-match (image-file-name-regexp) candidate) + (helm-append-at-nth + actions + '(("Rotate image right `M-r'" . helm-ff-rotate-image-right) + ("Rotate image left `M-l'" . helm-ff-rotate-image-left) + ("Start slideshow with marked" . helm-ff-start-slideshow-on-marked)) + 3)) + ((string-match "\\.el\\'" candidate) + (helm-append-at-nth + actions + '(("Byte compile lisp file(s) `M-B, C-u to load'" + . helm-find-files-byte-compile) + ("Load File(s) `M-L'" . helm-find-files-load-files)) + 2)) + ((string-match (concat (regexp-opt load-suffixes) "\\'") candidate) + (helm-append-at-nth + actions + '(("Load File(s) `M-L'" . helm-find-files-load-files)) + 2)) + ((and (string-match "\\.html?$" candidate) + (file-exists-p candidate)) + (helm-append-at-nth + actions '(("Browse url file" . browse-url-of-file)) 2)) + (t actions)))) + +;;; Trashing files +;; +(defun helm-ff-trash-action (fn names &rest args) + "Execute a trash action FN on marked files. + +Arg NAMES is a list of strings to pass to messages. +E.g. \\='(\"delete\" \"deleting\") + +ARGS are other arguments to be passed to FN." + (let ((mkd (helm-marked-candidates)) + errors aborted) + (with-helm-display-marked-candidates + helm-marked-buffer-name + (if (and args (string= (car names) "restore")) + (cl-loop for f in mkd + for bd = (helm-basename f) + for assoc = (assoc bd (car args)) + when assoc + collect (concat (truncate-string-to-width + (car assoc) 40 nil nil t) + " -> " + (truncate-string-to-width + (helm-basedir (cdr assoc)) 40 nil nil t))) + (helm-ff--count-and-collect-dups (mapcar 'helm-basename mkd))) + (if (y-or-n-p (format "%s %s files from trash? " + (capitalize (car names)) + (length mkd))) + (progn + (message "%s files from trash..." (capitalize (cadr names))) + (cl-loop for f in mkd do + (condition-case err + (apply fn f args) + (error (push (format "%s" (cadr err)) errors) + nil)))) + (message "%s files from trash aborted" (capitalize (cadr names))) + (setq aborted t))) + ;; Handle errors from outside the + ;; with-helm-display-marked-candidates block otherwise warning is + ;; never displayed. + (if errors + (progn + (display-warning 'helm + (with-temp-buffer + (insert (format-time-string "%Y-%m-%d %H:%M:%S\n" + (current-time))) + (insert (format + "Failed to %s %s/%s files from trash\n" + (car names) (length errors) (length mkd))) + (insert (mapconcat 'identity errors "\n") "\n ") + (buffer-string)) + :error + "*helm restore warnings*") + (message "%s files from trash aborted" (capitalize (cadr names)))) + (unless aborted + (message "%s %s files from trash done" + (capitalize (cadr names)) (length mkd)))))) + +(defun helm-ff-trash-rm (_candidate) + "Delete marked-files from a Trash directory. + +The Trash directory should be a directory compliant with + and each +file should have its \\='*.trashinfo' correspondent file in +Trash/info directory." + (helm-ff-trash-action 'helm-ff-trash-rm-1 '("delete" "deleting"))) + +(defun helm-ff-trash-rm-1 (file) + (let ((info-file (concat (helm-reduce-file-name file 2) + "info/" (helm-basename file "trashinfo") + ".trashinfo"))) + (cl-assert (file-exists-p file) + nil (format "No such file or directory `%s'" + file)) + (cl-assert (file-exists-p info-file) + nil (format "No such file or directory `%s'" + info-file)) + (if (file-directory-p file) + (delete-directory file t) + (delete-file file)) + (delete-file info-file))) + +(defun helm-restore-file-from-trash (_candidate) + "Restore marked-files from a Trash directory. + +The Trash directory should be a directory compliant with + and each +file should have its \\='*.trashinfo' corresponding file in +Trash/info directory." + (let* ((default-directory (file-name-as-directory + helm-ff-default-directory)) + (trashed-files (helm-ff-trash-list))) + (helm-ff-trash-action 'helm-restore-file-from-trash-1 + '("restore" "restoring") + trashed-files))) + +(defun helm-restore-file-from-trash-1 (file trashed-files) + "Restore FILE from a trash directory. +Arg TRASHED-FILES is an alist of (fname_in_trash . dest) obtained +with `helm-ff-trash-list'." + ;; Emacs trash duplicate files with a unique name + .trashinfo in + ;; the filename which is wrong, only files in info directory should + ;; end with .trashinfo, so fix the filename before looking for dest name. + (let* ((fname (replace-regexp-in-string "\\.trashinfo\\'" "" file)) + (info-file (concat (helm-reduce-file-name fname 2) + "info/" + (helm-basename fname) + ".trashinfo")) + (dest-file (helm-ff--get-dest-file-from-trash + trashed-files fname))) + (cl-assert (not (file-exists-p dest-file)) nil + (format "File `%s' already exists" dest-file)) + (cl-assert dest-file nil "No such file in trash") + (message "Restoring %s to %s..." (helm-basename file) (helm-basedir dest-file)) + (rename-file file dest-file) + (message "Restoring %s to %s done" (helm-basename file) (helm-basedir dest-file)) + (delete-file info-file))) + +(defun helm-ff-trash-file-p (file) + "Return t when FILE is a trashed file." + (and (file-exists-p file) + (string-match helm-ff--trash-directory-regexp (helm-basedir file)) + (not (member (helm-basename file) '("." ".."))))) + +(defun helm-ff--get-dest-file-from-trash (trashed-files file) + (assoc-default (helm-basename file) trashed-files)) + +(defun helm-ff-trash-list (&optional trash-dir) + "Return an alist of trashed files basename and dest name. +Assume the trash system in use is freedesktop compatible, see + +This function is intended to be used from a trash directory i.e. it +use `helm-ff-default-directory', but it may be used elsewhere by +specifying the trash directory with TRASH-DIR arg." + (unless (fboundp 'system-move-file-to-trash) + ;; Files owned by root are trashed in /root/.local/share/Trash. + ;; Files owned by user and trashed by root are trashed in + ;; /home/.Trash. + ;; Files owned by user and trashed by user are trashed in + ;; ~/.local/share/Trash. + (cl-loop for f in (directory-files + (expand-file-name + ;; helm-ff-default-directory is actually the + ;; trash directory. + "info" (helm-basedir (directory-file-name + (or trash-dir helm-ff-default-directory)))) + t directory-files-no-dot-files-regexp) + collect (cons (helm-basename (replace-regexp-in-string "\\.trashinfo\\'" "" f)) + (with-temp-buffer + (save-excursion + (insert-file-contents f)) + (when (re-search-forward "^path=" nil t) + (let ((path (helm-url-unhex-string + (buffer-substring-no-properties + (point) (point-at-eol))))) + (if (string-match "\\`/" path) + ;; path is absolute + path + ;; When path is relative, assume the + ;; trash directory is located at + ;; /home/.Trash and path is the + ;; relative name of file from /home. + (expand-file-name path "/home"))))))))) + +(defun helm-ff-goto-linum (candidate) + "Find file CANDIDATE and maybe jump to line number found in fname at point. +Line number should be added at end of fname preceded with \":\". +E.g. \"foo:12\"." + (let ((linum (with-helm-current-buffer + (let ((str (buffer-substring-no-properties + (point-at-bol) (point-at-eol)))) + (when (string-match ":\\([0-9]+:?\\)" str) + (match-string 1 str)))))) + (find-file candidate) + (and linum (not (string= linum "")) + (helm-goto-line (string-to-number linum) t)))) + +(defun helm-ff-mail-attach-files (_candidate) + "Run `mml-attach-file' on `helm-marked-candidates'." + (require 'mml) + (let ((flist (helm-marked-candidates :with-wildcard t)) + (dest-buf (and (derived-mode-p 'message-mode 'mail-mode) + (current-buffer))) + bufs) + (unless dest-buf + (setq bufs (cl-loop for b in (buffer-list) + when (with-current-buffer b + (derived-mode-p 'message-mode 'mail-mode)) + collect (buffer-name b))) + (if (and bufs (y-or-n-p "Attach files to existing mail composition buffer? ")) + (setq dest-buf + (if (cdr bufs) + (helm-comp-read "Attach to buffer: " bufs :nomark t) + (car bufs))) + (compose-mail) + (setq dest-buf (current-buffer)))) + (switch-to-buffer dest-buf) + (save-restriction + (widen) + (save-excursion + (goto-char (point-max)) + (cl-loop for f in flist + do (mml-attach-file f (or (mm-default-file-encoding f) + "application/octet-stream"))))))) + +(defvar image-dired-display-image-buffer) +(defun helm-ff-rotate-current-image-1 (file angle) + "Rotate current image at ANGLE degrees." + (cl-assert (and (file-exists-p file) + (string-match (image-file-name-regexp) file)) + nil "Can't rotate non image file") + (setq file (file-truename file)) ; For symlinked images. + (let ((default-directory (file-name-directory file)) + (basename (helm-basename file)) + ;; convert ANGLE to a suitable value for exiftran. + (num-arg (if (string= helm-ff-rotate-image-program "exiftran") + (cl-case angle + (90 "-9") ; 90 clockwise + (270 "-2")) ; 270 clockwise == -90 + (number-to-string angle))) + rotation-failed) + ;; Try to rotate image with exiftran even with helm-ff-display-image-native. + (if (and helm-ff-rotate-image-program + (executable-find helm-ff-rotate-image-program)) + (apply #'process-file helm-ff-rotate-image-program nil nil nil + (append helm-ff-rotate-image-switch + (list num-arg basename))) + (setq rotation-failed t)) + ;; Display image in image-mode. + (if (helm-ff-display-image-native-p) + (if rotation-failed + ;; When rotation fails fallback to `image-rotate' with no + ;; transformation of file. + (with-selected-window (helm-persistent-action-display-window) + (condition-case _err + (with-no-warnings (image-rotate angle)) + (wrong-number-of-arguments (image-rotate)))) + (helm-ff--display-image-native file)) + ;; Use image-dired to display image. + (when rotation-failed + (error "%s not found" (or helm-ff-rotate-image-program + "`helm-ff-rotate-image-program'"))) + (when (buffer-live-p image-dired-display-image-buffer) + (kill-buffer image-dired-display-image-buffer)) + (image-dired-display-image basename) + (message nil) + (display-buffer (get-buffer image-dired-display-image-buffer))))) + +(defun helm-ff-rotate-image-left (candidate) + "Rotate image file CANDIDATE left. +This affects directly file CANDIDATE." + (helm-ff-rotate-current-image-1 candidate 270)) + +(defun helm-ff-rotate-image-right (candidate) + "Rotate image file CANDIDATE right. +This affects directly file CANDIDATE." + (helm-ff-rotate-current-image-1 candidate 90)) + +(defun helm-ff-rotate-left-persistent () + "Rotate image left without quitting helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'image-action1 'helm-ff-rotate-image-left) + (helm-execute-persistent-action 'image-action1))) +(put 'helm-ff-rotate-left-persistent 'helm-only t) + +(defun helm-ff-rotate-right-persistent () + "Rotate image right without quitting helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'image-action2 'helm-ff-rotate-image-right) + (helm-execute-persistent-action 'image-action2))) +(put 'helm-ff-rotate-right-persistent 'helm-only t) + +(defun helm-ff-resize-image-1 (arg) + ;; `image-decrease-size' and `image-increase-size' are not usable + ;; because they run directly `image--change-size' in a timer without + ;; taking care of the selected-window. + (cl-assert (and (fboundp 'image--change-size) + (helm-ff-display-image-native-p)) + nil "Resizing image not available") + (if (> arg 0) + (run-with-idle-timer + 0.3 nil + (lambda () + (with-selected-window (helm-persistent-action-display-window) + (image--change-size 1.2)))) + (run-with-idle-timer + 0.3 nil + (lambda () + (with-selected-window (helm-persistent-action-display-window) + (image--change-size 0.8)))))) + +(defun helm-ff-increase-image-size (_candidate) + (helm-ff-resize-image-1 1)) + +(defun helm-ff-decrease-image-size (_candidate) + (helm-ff-resize-image-1 -1)) + +(defun helm-ff-increase-image-size-persistent () + "Increase image size without quitting helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'image-action3 'helm-ff-increase-image-size) + (helm-execute-persistent-action 'image-action3))) +(put 'helm-ff-increase-image-size-persistent 'helm-only t) + +(defun helm-ff-decrease-image-size-persistent () + "Decrease image size without quitting helm." + (interactive) + (with-helm-alive-p + (helm-set-attr 'image-action4 'helm-ff-decrease-image-size) + (helm-execute-persistent-action 'image-action4))) +(put 'helm-ff-decrease-image-size-persistent 'helm-only t) + +(defun helm-ff-exif-data (candidate) + "Extract exif data from file CANDIDATE using `helm-ff-exif-data-program'." + (if (and helm-ff-exif-data-program + (executable-find helm-ff-exif-data-program)) + (shell-command-to-string (format "%s %s %s" + helm-ff-exif-data-program + helm-ff-exif-data-program-args + candidate)) + (format "No program %s found to extract exif" + helm-ff-exif-data-program))) + +(defvar helm-ff-image-native-buffer "*image-native-display*") + +(defvar helm-ff-sound-file-extensions '("wav" "au")) + +(cl-defun helm-find-files-persistent-action-if (candidate) + "Open subtree CANDIDATE without quitting helm. +If CANDIDATE is not a directory expand CANDIDATE filename. +If CANDIDATE is alone, open file CANDIDATE filename. +That means: +First hit on C-j expands CANDIDATE, second hit opens file. +If a prefix arg is given or `helm-follow-mode' is on, then open +file." + (let* ((follow (or (helm-follow-mode-p) + helm--temp-follow-flag)) + (image-cand (string-match-p (image-file-name-regexp) candidate)) + (sound-cand (member (file-name-extension candidate) + helm-ff-sound-file-extensions)) + (selection (helm-get-selection)) + (insert-in-minibuffer (lambda (fname) + (with-selected-window (or (active-minibuffer-window) + (minibuffer-window)) + (unless follow + (delete-minibuffer-contents) + (set-text-properties 0 (length fname) + nil fname) + (insert fname)))))) + (helm-set-attr 'candidate-number-limit helm-ff-candidate-number-limit) + (unless image-cand + (when follow + (helm-follow-mode -1) + (cl-return-from helm-find-files-persistent-action-if + (prog1 + #'ignore + (message "Helm-follow-mode allowed only on images, disabling"))))) + (cond (;; Tramp methods completion. + (string-match helm-ff-tramp-method-regexp candidate) + (let ((method (match-string 1 candidate))) + (cons (lambda (candidate) + (funcall insert-in-minibuffer + (if (helm-ff--tramp-multihops-p candidate) + (concat (match-string 1 candidate) method ":") + (concat "/" method ":")))) + 'never-split))) + ((and (helm-ff--invalid-tramp-name-p) + (string-match helm-tramp-file-name-regexp candidate)) + (cons (lambda (_candidate) + ;; First hit insert hostname and + ;; second hit insert ":" and expand. + (if (string= candidate helm-pattern) + (funcall insert-in-minibuffer (concat candidate ":")) + (funcall insert-in-minibuffer candidate))) + 'never-split)) + (;; A symlink directory, expand it but not to its truename + ;; unless a prefix arg is given. + (and (file-directory-p candidate) (file-symlink-p candidate)) + (cons (lambda (_candidate) + (helm-ff-after-persistent-show-all) + (let ((new-dir (file-name-as-directory + (if current-prefix-arg + (file-truename (expand-file-name candidate)) + (expand-file-name candidate))))) + (setq helm-ff--show-thumbnails + (member new-dir helm-ff--thumbnailed-directories)) + (funcall insert-in-minibuffer new-dir))) + 'never-split)) + ;; A directory, open it. + ((file-directory-p candidate) + (cons (lambda (_candidate) + (helm-ff-after-persistent-show-all) + (when (string= (helm-basename candidate) "..") + (setq helm-ff-last-expanded helm-ff-default-directory)) + (let ((new-dir (file-name-as-directory + (expand-file-name candidate)))) + (setq helm-ff--show-thumbnails + (member new-dir helm-ff--thumbnailed-directories)) + (funcall insert-in-minibuffer new-dir)) + (with-helm-after-update-hook (helm-ff-retrieve-last-expanded))) + 'never-split)) + ;; A symlink file, expand to it's true name. (first hit) + ((and (file-symlink-p candidate) (not current-prefix-arg) (not follow)) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer (file-truename candidate)) + (helm-check-minibuffer-input)) ; Force update. + 'never-split)) + ;; A regular file, expand it, (first hit) + ((and (not (file-equal-p selection helm-pattern)) + (not current-prefix-arg) (not follow)) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer selection) + (helm-check-minibuffer-input)) ; Force update. + 'never-split)) + (sound-cand (lambda (candidate) (play-sound-file candidate))) + ;; An image file and it is the second hit on C-j, display it. + (image-cand + (if (helm-ff-display-image-native-p) + #'helm-ff--display-or-kill-image-native + (lambda (_candidate) + (require 'image-dired) + (let* ((win (get-buffer-window + image-dired-display-image-buffer 'visible)) + (fname (and win + (with-selected-window win + (get-text-property (point-min) + 'original-file-name)))) + (remove-buf-only (and win + fname + (with-helm-buffer + (file-equal-p candidate fname))))) + (when remove-buf-only + (with-helm-window + (if (and helm-persistent-action-display-window + (window-dedicated-p (next-window win 1))) + (delete-window helm-persistent-action-display-window) + (set-window-buffer win helm-current-buffer)))) + (when (buffer-live-p (get-buffer image-dired-display-image-buffer)) + (kill-buffer image-dired-display-image-buffer)) + (unless remove-buf-only + ;; Fix emacs bug never fixed upstream. + (unless (file-directory-p image-dired-dir) + (make-directory image-dired-dir)) + (switch-to-buffer image-dired-display-image-buffer) + (message "Resizing image...") + (cl-letf (((symbol-function 'message) #'ignore)) + (image-dired-display-image candidate)) + (message "Resizing image done") + (with-current-buffer image-dired-display-image-buffer + (let ((exif-data (helm-ff-exif-data candidate))) + (setq default-directory helm-ff-default-directory) + (image-dired-update-property 'help-echo exif-data)))))))) + ;; Allow browsing archive on avfs fs. + ;; Assume volume is already mounted with mountavfs. + ((helm-aand helm-ff-avfs-directory + (file-name-directory candidate) + (string-match + (regexp-quote (expand-file-name helm-ff-avfs-directory)) + it) + (helm-ff-file-compressed-p candidate)) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer (concat candidate "#/"))) + 'never-split)) + ;; File doesn't exists and basename starts with ".." or " ", + ;; Start a recursive search for directories. + ((and (not (file-exists-p candidate)) + (not (file-remote-p candidate)) + (string-match-p "\\`\\([.]\\|\\s-\\)\\{2\\}[^/]+" + (helm-basename candidate))) + ;; As soon as the final "/" is added the job is passed + ;; to `helm-ff-auto-expand-to-home-or-root'. + (cons (lambda (_candidate) + (funcall insert-in-minibuffer (concat candidate "/"))) + 'never-split)) + ;; File is not existing and have no basedir, typically when + ;; user hit C-k (minibuffer is empty) and then write foo and + ;; hit C-j. This make clear that when no basedir, helm will + ;; create the file in default-directory. + ((and (not (file-exists-p candidate)) + (not (helm-basedir candidate))) + (cons (lambda (_candidate) + (funcall insert-in-minibuffer + (expand-file-name candidate default-directory))) + 'never-split)) + ;; On second hit we open file. + ;; On Third hit we kill it's buffer maybe. + (t + (lambda (candidate) + (funcall helm-ff-kill-or-find-buffer-fname-fn candidate)))))) + +;; Native image display (with image-mode). +;; +(defvar helm-ff--image-cache nil) + +(defun helm-ff-display-image-native-p () + "Use `helm-ff-display-image-native' when returns `t'." + (or helm-ff-display-image-native + ;; Image-dired in emacs-29 uses image-mode but + ;; display is no more working with our old + ;; image-dired code, so force usage of + ;; helm-ff-display-image-native. + (fboundp 'image-dired-display-image-mode))) + +(defun helm-ff--display-or-kill-image-native (candidate) + ;; Display images in same buffer + ;; `helm-ff-image-native-buffer'. + (if (and (buffer-live-p (get-buffer helm-ff-image-native-buffer)) + (file-equal-p (buffer-file-name + (get-buffer helm-ff-image-native-buffer)) + candidate) + ;; Allow redisplaying + ;; `helm-ff-image-native-buffer' when it + ;; already exists and display same image as candidate. + (get-buffer-window helm-ff-image-native-buffer 'visible)) + (progn + (set-window-buffer + helm-persistent-action-display-window helm-current-buffer) + (kill-buffer helm-ff-image-native-buffer)) + (helm-ff--display-image-native candidate))) + +(defun helm-ff-clean-image-cache () + (when helm-ff--image-cache + (cl-loop for img in helm-ff--image-cache + do (clear-image-cache img) + finally do (setq helm-ff--image-cache nil)))) + +(defun helm-ff--display-image-native (candidate) + (when (buffer-live-p (get-buffer helm-ff-image-native-buffer)) + (kill-buffer helm-ff-image-native-buffer)) + ;; Avoid hight memory consumption see + ;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-11/msg00879.html. + (when (> (length helm-ff--image-cache) + (* helm-ff-image-cache-max-len 2)) + ;; Only keep the last `helm-ff-image-cache-max-len' images in cache. + (cl-loop for img in (butlast helm-ff--image-cache + (1+ helm-ff-image-cache-max-len)) + do (clear-image-cache img) + (setq helm-ff--image-cache + (delete img helm-ff--image-cache)))) + (cl-letf* (((symbol-function 'message) #'ignore) + (buf (find-file-noselect candidate t))) + ;; When going back reuse the cached images. + (unless (member candidate helm-ff--image-cache) + (setq helm-ff--image-cache + (append helm-ff--image-cache + (list (expand-file-name candidate))))) + (with-current-buffer buf + (rename-buffer helm-ff-image-native-buffer)) + (display-buffer buf))) + +;;; Slideshow action +;; +(defvar helm-ff--slideshow-iterator nil) +(defvar helm-ff--slideshow-sequence nil) +(defvar helm-ff--slideshow-in-pause nil) +(defvar helm-ff-slideshow-helper + "Type `\\[helm-ff-slideshow-pause-or-restart]' to %s, \ +`\\[helm-ff-slideshow-next]' for next, `\\[helm-ff-slideshow-previous]' for previous, \ +`\\[helm-ff-slideshow-quit]' to quit") + +(defvar helm-slideshow-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map image-mode-map) + (define-key map (kbd "SPC") 'helm-ff-slideshow-pause-or-restart) + (define-key map (kbd "q") 'helm-ff-slideshow-quit) + (define-key map (kbd "n") 'helm-ff-slideshow-next) + (define-key map (kbd "p") 'helm-ff-slideshow-previous) + map)) + +(define-derived-mode helm-slideshow-mode + image-mode "helm-image-mode" + "Mode to display images from helm-find-files. + +Special commands: +\\{helm-slideshow-mode-map} +") +(put 'helm-slideshow-mode 'no-helm-mx t) + +(defun helm-ff-start-slideshow-on-marked (_candidate) + "Start a slideshow on marked files." + (let ((marked (helm-marked-candidates :with-wildcard t))) + (cl-assert (cdr marked) nil "Can't start a slideshow on a single file") + (setq helm-ff--slideshow-sequence marked) + (setq helm-ff--slideshow-iterator (helm-iter-circular marked)) + (helm-ff--display-image-native (helm-iter-next helm-ff--slideshow-iterator)) + (delete-other-windows (get-buffer-window helm-ff-image-native-buffer)) + (cl-letf (((symbol-function 'message) #'ignore)) + (helm-slideshow-mode)) + (message (concat (format "(1/%s) " (length marked)) + (substitute-command-keys + (format helm-ff-slideshow-helper "pause")))) + (helm-ff-slideshow-loop helm-ff--slideshow-iterator))) + +(defun helm-ff-slideshow-state () + (format "(%s/%s) " + (1+ (cl-position + (buffer-file-name) helm-ff--slideshow-sequence + :test 'equal)) + (length helm-ff--slideshow-sequence))) + +(defun helm-ff-slideshow-sequence-from-current (&optional reverse) + (helm-reorganize-sequence-from-elm + helm-ff--slideshow-sequence (buffer-file-name) reverse)) + +(defun helm-ff-slideshow-loop (iterator) + (while (sit-for helm-ff-slideshow-default-delay) + (helm-ff--display-image-native (helm-iter-next iterator)) + (delete-other-windows (get-buffer-window helm-ff-image-native-buffer)) + (cl-letf (((symbol-function 'message) #'ignore)) + (helm-slideshow-mode)) + (message (concat (helm-ff-slideshow-state) + (substitute-command-keys + (format helm-ff-slideshow-helper "pause")))))) + +(defun helm-ff-slideshow-pause-or-restart () + (interactive) + (setq helm-ff--slideshow-in-pause (not helm-ff--slideshow-in-pause)) + (if helm-ff--slideshow-in-pause + (message (substitute-command-keys + (format helm-ff-slideshow-helper "restart"))) + (message "Helm Slideshow restarting...") + (setq helm-ff--slideshow-iterator + (helm-iter-circular (helm-ff-slideshow-sequence-from-current))) + (helm-ff-slideshow-loop helm-ff--slideshow-iterator))) +(put 'helm-ff-slideshow-pause-or-restart 'no-helm-mx t) + +(defun helm-ff-slideshow-next () + (interactive) + (setq helm-ff--slideshow-in-pause t) + (setq helm-ff--slideshow-iterator nil) + (helm-ff--display-image-native + (car (helm-ff-slideshow-sequence-from-current))) + (delete-other-windows (get-buffer-window helm-ff-image-native-buffer)) + (cl-letf (((symbol-function 'message) #'ignore)) + (helm-slideshow-mode)) + (message (concat (helm-ff-slideshow-state) + (substitute-command-keys + (format helm-ff-slideshow-helper "restart"))))) +(put 'helm-ff-slideshow-next 'no-helm-mx t) + +(defun helm-ff-slideshow-previous () + (interactive) + (setq helm-ff--slideshow-in-pause t) + (setq helm-ff--slideshow-iterator nil) + (helm-ff--display-image-native + (car (helm-ff-slideshow-sequence-from-current 'reverse))) + (delete-other-windows (get-buffer-window helm-ff-image-native-buffer)) + (cl-letf (((symbol-function 'message) #'ignore)) + (helm-slideshow-mode)) + (message (concat (helm-ff-slideshow-state) + (substitute-command-keys + (format helm-ff-slideshow-helper "restart"))))) +(put 'helm-ff-slideshow-previous 'no-helm-mx t) + +(defun helm-ff-slideshow-quit () + (interactive) + (setq helm-ff--slideshow-iterator nil) + (setq helm-ff--slideshow-in-pause nil) + (helm-ff-clean-image-cache) + (quit-window)) +(put 'helm-ff-slideshow-quit 'no-helm-mx t) + +;;; Thumbnails view +;; +(defun helm-ff-maybe-show-thumbnails (candidates _source) + (require 'image-dired) + (if (and helm-ff--show-thumbnails + (null (file-remote-p helm-ff-default-directory))) + (progn + (cl-pushnew helm-ff-default-directory + helm-ff--thumbnailed-directories :test 'equal) + (cl-loop for (disp . img) in candidates + for type = (helm-acase (file-name-extension img) + ("png" 'png) + (("jpg" "jpeg") 'jpeg)) + if type collect + (let ((thumbnail (plist-get + (cdr (helm-ff--image-dired-get-thumbnail-image img)) + :file))) + (cons (concat (propertize " " + 'display `(image + :type ,type + :margin 5 + :file ,thumbnail) + 'rear-nonsticky '(display)) + disp) + img)) + else collect (cons disp img))) + candidates)) + +;; Same as `image-dired-get-thumbnail-image' but use +;; `helm-ff--image-dired-thumb-name' which cache thumbnails for further use. +(defun helm-ff--image-dired-get-thumbnail-image (file) + "Return the image descriptor for a thumbnail of image file FILE." + (unless (string-match-p (image-file-name-regexp) file) + (error "%s is not a valid image file" file)) + (let* ((thumb-file (helm-ff--image-dired-thumb-name file)) + (thumb-attr (file-attributes thumb-file))) + (when (or (not thumb-attr) + (time-less-p (file-attribute-modification-time thumb-attr) + (file-attribute-modification-time + (file-attributes file)))) + (image-dired-create-thumb file thumb-file)) + (create-image thumb-file))) + +(defvar helm-ff-image-dired-thumbnails-cache (make-hash-table :test 'equal) + "Store associations of image_file/thumbnail_file.") +(defun helm-ff--image-dired-thumb-name (file) + (or (gethash file helm-ff-image-dired-thumbnails-cache) + (let ((thumb-name (image-dired-thumb-name file))) + (puthash file thumb-name helm-ff-image-dired-thumbnails-cache) + thumb-name))) + +(defun helm-ff-toggle-thumbnails () + (interactive) + (cl-assert (null (file-remote-p helm-ff-default-directory)) + nil "Thumbnails show not supported on remote files") + (setq helm-ff--show-thumbnails (not helm-ff--show-thumbnails)) + (when (and (null helm-ff--show-thumbnails) + (member helm-ff-default-directory + helm-ff--thumbnailed-directories)) + (setq helm-ff--thumbnailed-directories + (delete helm-ff-default-directory helm-ff--thumbnailed-directories))) + (helm-update (regexp-quote (replace-regexp-in-string + "\\`[[:multibyte:] ]*" "" (helm-get-selection nil t))))) +(put 'helm-ff-toggle-thumbnails 'no-helm-mx t) + +;;;###autoload +(defun helm-ff-cleanup-image-dired-dir-and-cache () + "Cleanup `image-dired-dir' directory. +Delete all thumb files that are no more associated with an existing +image file in `helm-ff-image-dired-thumbnails-cache'." + (interactive) + (cl-loop for key being the hash-keys in helm-ff-image-dired-thumbnails-cache + using (hash-value val) + unless (file-exists-p key) do + (progn + (message "Deleting %s" val) + (delete-file val) + (remhash key helm-ff-image-dired-thumbnails-cache)))) + +;;; Recursive dirs completion +;; +(defun helm-find-files-recursive-dirs (directory &optional input) + (when (string-match "\\([.]\\)\\{2\\}" input) + (setq input (replace-match "" nil t input))) + (message "Recursively searching %s from %s ..." + input (abbreviate-file-name directory)) + ;; Ensure to not create a new frame + (let (helm-actions-inherit-frame-settings) + (helm :sources + (helm-make-source + "Recursive directories" 'helm-locate-subdirs-source + :basedir (if (string-match-p + "\\`es" helm-locate-recursive-dirs-command) + directory + (shell-quote-argument directory)) + :subdir (shell-quote-argument input) + :candidate-transformer + `((lambda (candidates) + (cl-loop for c in candidates + when (and (file-directory-p c) + (null (helm-boring-directory-p + c helm-boring-file-regexp-list)) + (string-match-p ,(regexp-quote input) + (helm-basename c))) + collect (propertize c 'face 'helm-ff-dirs))) + helm-w32-pathname-transformer + (lambda (candidates) + (helm-ff-sort-candidates-1 candidates ,input))) + :persistent-action 'ignore + :action (lambda (c) + (helm-set-pattern + (file-name-as-directory (expand-file-name c))))) + :candidate-number-limit 999999 + :allow-nest t + :resume 'noresume + :ff-transformer-show-only-basename nil + :buffer "*helm recursive dirs*"))) + +(defun helm-ff-recursive-dirs (_candidate) + "Launch a recursive search in `helm-ff-default-directory'." + (with-helm-default-directory helm-ff-default-directory + (helm-find-files-recursive-dirs + (helm-current-directory) + (helm-basename (helm-get-selection))))) + +(defun helm-ff-file-compressed-p (candidate) + "Whether CANDIDATE is a compressed file or not." + (member (file-name-extension candidate) + helm-ff-file-compressed-list)) + +(defun helm-ff--fname-at-point () + "Try to guess fname at point." + (let ((end (point)) + (limit (helm-aif (bounds-of-thing-at-point 'filename) + (car it) + (point)))) + (save-excursion + (while (re-search-backward "\\(~\\|/\\|[[:lower:][:upper:]]:/\\)" + limit t)) + (buffer-substring-no-properties (point) end)))) + +(defun helm-insert-file-name-completion-at-point (_candidate) + "Insert file name completion at point. + +When completing i.e. there is already something at point, insert +filename abbreviated, relative or full according to initial +input, whereas when inserting i.e. there is nothing at point, +insert filename full, abbreviated or relative according to prefix +arg, respectively no prefix arg, one prefix arg or two prefix +arg." + (with-helm-current-buffer + (if buffer-read-only + (error "Error: Buffer `%s' is read-only" (buffer-name)) + (let* ((mkds (helm-marked-candidates :with-wildcard t)) + (candidate (car mkds)) + (end (point)) + (tap (helm-ffap-guesser)) + (guess (and (stringp tap) + (substring-no-properties tap))) + (beg (helm-aif (and guess + (save-excursion + (when (re-search-backward + (regexp-quote guess) + (point-at-bol) t) + (point)))) + it (point))) + (full-path-p (and (stringp guess) + (or (string-match-p + (concat "^" (getenv "HOME")) + guess) + (string-match-p + "\\`\\(/\\|[[:lower:][:upper:]]:/\\)" + guess)))) + (escape-fn (if (memq major-mode + helm-modes-using-escaped-strings) + #'shell-quote-argument #'identity))) + (when (and beg end) + (delete-region beg end)) + (insert + (funcall + escape-fn + (helm-ff--format-fname-to-insert + candidate beg end full-path-p guess + helm-current-prefix-arg)) + (if (cdr mkds) " " "") + (mapconcat escape-fn + (cl-loop for f in (cdr mkds) + collect (helm-ff--format-fname-to-insert + f nil nil nil nil + helm-current-prefix-arg)) + " ")))))) + +(defun helm-ff--format-fname-to-insert (candidate + &optional beg end full-path guess prefarg) + (set-text-properties 0 (length candidate) nil candidate) + (if (and beg end guess (not (string= guess "")) + (null prefarg) + (or (string-match + "^\\(~/\\|/\\|[[:lower:][:upper:]]:/\\)" + guess) + (file-exists-p candidate))) + (cond (full-path + (expand-file-name candidate)) + ((string= (match-string 1 guess) "~/") + (abbreviate-file-name candidate)) + (t (file-relative-name candidate))) + (helm-acase prefarg + ('(4) (abbreviate-file-name candidate)) + ('(16) (file-relative-name candidate)) + ('(64) (helm-basename candidate)) + (t candidate)))) + +(cl-defun helm-find-files-history (arg &key (comp-read t)) + "The `helm-find-files' history. +Show the first `helm-ff-history-max-length' elements of +`helm-ff-history' in an `helm-comp-read'." + (interactive "p") + (let ((history (when helm-ff-history + (helm-fast-remove-dups helm-ff-history + :test 'equal)))) + (when history + (setq helm-ff-history + (if (>= (length history) helm-ff-history-max-length) + (cl-subseq history 0 helm-ff-history-max-length) + history)) + (if comp-read + (let ((src (helm-build-sync-source "Helm Find Files History" + :candidates helm-ff-history + :fuzzy-match (helm-ff-fuzzy-matching-p) + :persistent-action 'ignore + :migemo t + :action (lambda (candidate) + (if arg + (helm-set-pattern + (expand-file-name candidate)) + (identity candidate)))))) + (helm :sources src + :resume 'noresume + :buffer helm-ff-history-buffer-name + :allow-nest t)) + helm-ff-history)))) +(put 'helm-find-files-history 'helm-only t) + +(defvar helm-ff-drag-mouse-1-default-action 'copy + "Default action when dragging files. +Possible values are `copy', `rsync' or `rename'.") + +(defvar helm-ff-drag-and-drop-default-directory nil + "Default directory where to drop files on a drag-and-drop action. +It is used when no suitable directory is found at drop place, +generally when dropping outside of an emacs frame. +You want generally to set this to your home desktop directory.") + +(defun helm-ff-drag-mouse-1-fn (event) + "Drag-and-drop function for `helm-find-files'. +Allows dropping marked files to another frame or window. +When dropping to another frame (i.e. not the selected one where helm +is running), you are asked for which directory you want to drop to when frame +displays more than one window. +When no suitable place to drop is found ask to drop to +`helm-ff-drag-and-drop-default-directory' if set." + (interactive "e") + (cl-assert (memq helm-ff-drag-mouse-1-default-action + '(copy rsync rename))) + (let* ((win-or-frame (posn-window (event-end event))) + (target-frame (when (framep win-or-frame) + (car (mouse-pixel-position)))) + (target (with-selected-window + (if target-frame + (frame-selected-window target-frame) + win-or-frame) + default-directory)) + (windows (and target-frame + (remove (helm-window) + (window-list target-frame 1))))) + (when windows + (setq target + (helm-acond ((cdr windows) + (x-popup-menu + t (list "Choose target" + (cons "" + (cl-loop for win in windows + for dir = (with-selected-window + win default-directory) + collect (cons dir dir)))))) + ((and (eql (window-buffer (car windows)) + helm-current-buffer) + helm-ff-drag-and-drop-default-directory) + (x-popup-menu + t (list "Choose target" + (cons "" + (list (cons it it)))))) + ((car windows) + (with-selected-window it default-directory))))) + (if (memq helm-ff-drag-mouse-1-default-action '(copy rsync)) + (helm-find-files-do-action + helm-ff-drag-mouse-1-default-action target) + (helm-run-after-exit + #'helm-find-files-do-action + helm-ff-drag-mouse-1-default-action target)))) + +(defun helm-find-files-1 (fname &optional preselect) + "Find FNAME filename with PRESELECT filename preselected. + +Use it for non-interactive calls of `helm-find-files'." + (require 'tramp) + ;; Resolve FNAME now outside of helm. + ;; [FIXME] When `helm-find-files-1' is used directly from lisp + ;; and FNAME is an abbreviated path, for some reasons + ;; `helm-update' is called many times before resolving + ;; the abbreviated path (Bug#1939) so be sure to pass a + ;; full path to helm-find-files-1. + (unless (string-match-p helm-ff-url-regexp fname) + (setq fname (expand-file-name (substitute-in-file-name fname)))) + (when (get-buffer helm-action-buffer) + (kill-buffer helm-action-buffer)) + (setq helm-find-files--toggle-bookmark nil) + (let* ( ;; Be sure we don't erase the precedent minibuffer if some. + (helm-ff-auto-update-initial-value + (and helm-ff-auto-update-initial-value + (not (minibuffer-window-active-p (minibuffer-window))))) + (tap (thing-at-point 'filename)) + (def (and tap (or (file-remote-p tap) + (expand-file-name tap)))) + ;; Ensure not being prompted for password each time we + ;; navigate to a directory. + (password-cache t)) + (helm-set-local-variable 'helm-follow-mode-persistent nil + 'helm-drag-mouse-1-fn 'helm-ff-drag-mouse-1-fn) + (unless helm-source-find-files + (setq helm-source-find-files (helm-make-source + "Find Files" 'helm-source-ffiles))) + (when (helm-get-attr 'follow helm-source-find-files) + (helm-set-attr 'follow -1 helm-source-find-files)) + (helm-ff-setup-update-hook) + (add-hook 'helm-resume-after-hook 'helm-ff--update-resume-after-hook) + (unwind-protect + (helm :sources 'helm-source-find-files + :input fname + :case-fold-search helm-file-name-case-fold-search + :preselect preselect + :ff-transformer-show-only-basename + helm-ff-transformer-show-only-basename + :default def + :prompt "Find files or url: " + :buffer "*helm find files*") + (helm-ff--update-resume-after-hook nil t) + (setq helm-ff-default-directory nil)))) + +(defun helm-ff--update-resume-after-hook (sources &optional nohook) + "Meant to be used in `helm-resume-after-hook'. +When NOHOOK is non-nil run inconditionally, otherwise only when +source is `helm-source-find-files'." + (when (or nohook (string= "Find Files" + (assoc-default 'name (car sources)))) + (helm-set-attr 'resume `(lambda () + (helm-ff-setup-update-hook) + (setq helm-ff-default-directory + ,helm-ff-default-directory + helm-ff-last-expanded + ,helm-ff-last-expanded)) + helm-source-find-files))) + +(defun helm-ff-clean-initial-input () + ;; When using hff in an external frame initial input is printed in + ;; the minibuffer of initial-frame, delete it. + (with-selected-frame helm-initial-frame + (helm-clean-up-minibuffer))) + +(defun helm-ff-setup-update-hook () + (dolist (hook '(helm-ff-clean-initial-input ; Add to be called first. + helm-ff-move-to-first-real-candidate + helm-ff-update-when-only-one-matched + helm-ff-auto-expand-to-home-or-root)) + (add-hook 'helm-after-update-hook hook))) + +(defun helm-find-files-cleanup () + (mapc (lambda (hook) + (remove-hook 'helm-after-update-hook hook)) + '(helm-ff-auto-expand-to-home-or-root + helm-ff-update-when-only-one-matched + helm-ff-move-to-first-real-candidate + helm-ff-clean-initial-input)) + (setq helm-ff--show-directories-only nil + helm-ff--show-files-only nil + helm-ff--show-thumbnails nil + helm-ff--thumbnailed-directories nil) + (helm-ff-clean-image-cache)) + +(defun helm-ff-bookmark () + (helm :sources 'helm-source-bookmark-helm-find-files + :buffer "*helm ff bookmarks*")) + +(defun helm-find-files-switch-to-bookmark () + "Switch to helm-bookmark for `helm-find-files' from `helm-find-files.'" + (interactive) + (require 'helm-bookmark) + (with-helm-alive-p + (helm-run-after-exit 'helm-ff-bookmark))) +(put 'helm-find-files-switch-to-bookmark 'helm-only t) + +(defun helm-find-files-initial-input (&optional input) + "Return INPUT if present, otherwise try to guess it." + (let ((guesser (helm-acase (helm-ffap-guesser) + ("" nil) + (t it)))) + (unless (eq major-mode 'image-mode) + (if input + (if (or (file-remote-p input) + (string-match helm-ff-url-regexp input)) + input + (expand-file-name input)) + (helm-find-files-input + (if (and helm-ff-allow-non-existing-file-at-point + guesser + (not (string-match ffap-url-regexp guesser))) + ;; Keep the ability of jumping to numbered lines even + ;; when allowing non existing filenames at point. + (helm-aand guesser + (thing-at-point 'filename) + (replace-regexp-in-string + ":[0-9]+\\'" "" it)) + guesser) + (thing-at-point 'filename)))))) + +(defun helm-ffap-guesser () + "Same as `ffap-guesser' but without gopher and machine support." + (require 'ffap) + ;; Avoid "Stack overflow in regexp matcher" error + ;; in evil `ffap-guesser' by removing crap `ffap-gopher-at-point' + ;; (bug fixed in emacs-26 http://debbugs.gnu.org/cgi/bugreport.cgi?bug=25391) . + ;; `ffap-machine-at-point' have been removed too as it was anyway + ;; disabled with `ffap-machine-p-known' bound to 'reject. + ;; `ffap-file-at-point' can be neutralized with + ;; `helm-ff-guess-ffap-filenames' and `ffap-url-at-point' with + ;; `helm-ff-guess-ffap-urls' + ;; Note also that `ffap-url-unwrap-remote' can override these + ;; variables. + (let ((ffap-alist (and helm-ff-guess-ffap-filenames ffap-alist)) + (ffap-url-regexp helm--url-regexp)) + (if (eq major-mode 'dired-mode) + (let ((beg (save-excursion (dired-move-to-filename))) + (end (save-excursion (dired-move-to-end-of-filename t)))) + (helm-aif (and beg end (member (buffer-substring beg end) + '("." ".."))) + (concat (file-name-as-directory + (expand-file-name dired-directory)) + (car it)) + (dired-get-filename 'no-dir t))) + (let* ((beg (and (use-region-p) (region-beginning))) + (end (and (use-region-p) (region-end))) + (str (and beg end (buffer-substring-no-properties beg end))) + (ffap (or (helm-aand helm-ff-guess-ffap-urls ffap-url-regexp + (ffap-url-at-point) + (ffap-fixup-url it) + (and (string-match ffap-url-regexp it) it)) + (ffap-file-at-point)))) + ;; Workaround emacs bugs: + ;; When the region is active and a file is detected + ;; `ffap-string-at-point' returns the region prefixed with + ;; "/", e.g. at a beginning of a patch (first bug) and make + ;; `file-remote-p' returning an error (second bug), so in such + ;; case returns the region itself instead of the region + ;; corrupted by ffap. + (if (and str ffap) str ffap))))) + +(defun helm-find-files-input (file-at-pt thing-at-pt) + "Try to guess a default input for `helm-find-files'." + (let* ((non-essential t) + (remp (or (and file-at-pt (file-remote-p file-at-pt)) + (and thing-at-pt (file-remote-p thing-at-pt)))) + (def-dir (helm-current-directory)) + (urlp (and file-at-pt helm--url-regexp + (string-match helm--url-regexp file-at-pt))) + (lib (when helm-ff-search-library-in-sexp + (helm-find-library-at-point))) + (hlink (helm-ff-find-url-at-point)) + (file-p (and file-at-pt + (not (string= file-at-pt "")) + (not remp) + (or (file-exists-p file-at-pt) + helm-ff-allow-non-existing-file-at-point) + (not urlp) + thing-at-pt + (not (string= thing-at-pt "")) + (file-exists-p + (file-name-directory + (expand-file-name thing-at-pt def-dir)))))) + (cond (lib) ; e.g we are inside a require sexp. + (hlink) ; String at point is an hyperlink. + (file-p ; a regular file + (and file-at-pt (if (not (member (helm-basename file-at-pt) + '("." ".."))) + (expand-file-name file-at-pt) + file-at-pt))) + (urlp (helm-html-decode-entities-string file-at-pt)) ; possibly an url or email. + ((and file-at-pt + (not remp) + (or helm-ff-allow-non-existing-file-at-point + (file-exists-p file-at-pt))) + (expand-file-name file-at-pt))))) + +(defun helm-ff-find-url-at-point () + "Try to find link to an url in text-property at point." + (let* ((he (get-text-property (point) 'help-echo)) + (ov (overlays-at (point))) + (ov-he (and ov (overlay-get + (car (overlays-at (point))) 'help-echo))) + (w3m-l (get-text-property (point) 'w3m-href-anchor)) + (nt-prop (get-text-property (point) 'nt-link))) + ;; Org link. + (when (and (stringp he) (string-match "^LINK: " he)) + (setq he (replace-match "" t t he))) + (cl-loop for i in (list he ov-he w3m-l nt-prop) + thereis (and (stringp i) helm--url-regexp (string-match helm--url-regexp i) i)))) + +(defun helm-find-library-at-point () + "Try to find library path at point. +Find inside `require' and `declare-function' sexp." + (require 'find-func) + (let* ((beg-sexp (save-excursion (search-backward "(" (point-at-bol) t))) + (end-sexp (save-excursion (ignore-errors (end-of-defun)) (point))) + (sexp (and beg-sexp end-sexp + (buffer-substring-no-properties + (1+ beg-sexp) (1- end-sexp))))) + (ignore-errors + (cond (;; Should work only when point is on the use-package line + ;; i.e. first line of sexp otherwise it prevents matching + ;; urls with helm-find-files (bug #2469). + (and sexp (string-match "use-package +\\([^ )\n]+\\)" sexp)) + (find-library-name (match-string 1 sexp))) + ((and sexp (string-match "require +[']\\([^ )]+\\)" sexp)) + ;; If require use third arg, ignore it, + ;; always use library path found in `load-path'. + (find-library-name (match-string 1 sexp))) + ;; Assume declare-function sexps are on one line. + ((and sexp (string-match "declare-function .+? \"\\(?:ext:\\)?\\([^ )]+\\)\"" sexp)) + (find-library-name (match-string 1 sexp))) + (t nil))))) + + +;;; Handle copy, rename, symlink, relsymlink and hardlink from helm. +;; +;; +(defun helm-ff--valid-default-directory () + (with-helm-current-buffer + (cl-loop for b in (buffer-list) + for cd = (with-current-buffer b default-directory) + when (eq (car (file-attributes cd)) t) + return cd))) + +(cl-defun helm-dired-action (destination + &key action follow (files (dired-get-marked-files))) + "Execute ACTION on FILES to DESTINATION. +Where ACTION is a symbol that can be one of: +\\='copy', \\='rename', \\='symlink', \\='relsymlink', \\='hardlink' or \\='backup'. +Argument FOLLOW when non-nil specifies to follow FILES to +DESTINATION for the actions copy and rename." + (require 'dired-async) + (require 'dired-x) ; For dired-keep-marker-relsymlink + (when (get-buffer dired-log-buffer) (kill-buffer dired-log-buffer)) + ;; When default-directory in current-buffer is an invalid directory, + ;; (e.g buffer-file directory have been renamed somewhere else) + ;; be sure to use a valid value to give to dired-create-file. + ;; i.e start-process is creating a process buffer based on default-directory. + (let ((default-directory (helm-ff--valid-default-directory)) + (fn (cl-case action + (copy 'dired-copy-file) + (rename 'dired-rename-file) + (symlink 'make-symbolic-link) + (relsymlink 'dired-make-relative-symlink) + (hardlink 'dired-hardlink) + (backup 'backup-file))) + (marker (cl-case action + ((copy rename backup) dired-keep-marker-copy) + (symlink dired-keep-marker-symlink) + (relsymlink dired-keep-marker-relsymlink) + (hardlink dired-keep-marker-hardlink))) + (dirflag (and (= (length files) 1) + (file-directory-p (car files)) + (not (file-directory-p destination)))) + (dired-async-state (if (and (boundp 'dired-async-mode) + dired-async-mode) + 1 -1))) + (and follow (fboundp 'dired-async-mode) (dired-async-mode -1)) + (when (and (cdr files) (not (file-directory-p destination))) + (error "%s: target `%s' is not a directory" action destination)) + (unwind-protect + (dired-create-files + fn (symbol-name action) files + (if (file-directory-p destination) + ;; When DESTINATION is a directory, build file-name in this directory. + ;; Else we use DESTINATION. + (lambda (from) + (expand-file-name (file-name-nondirectory from) destination)) + (lambda (_from) destination)) + marker) + (and (fboundp 'dired-async-mode) + (dired-async-mode dired-async-state))) + (push (file-name-as-directory + (if (file-directory-p destination) + (expand-file-name destination) + (file-name-directory destination))) + helm-ff-history) + ;; If follow is non--nil we should not be in async mode. + (when (and follow + (not (memq action '(symlink relsymlink hardlink))) + (not (get-buffer dired-log-buffer))) + (let ((target (directory-file-name destination)) + (cands-to-mark (helm-get-dest-fnames-from-list files destination dirflag))) + (with-helm-after-update-hook (helm-ff-maybe-mark-candidates cands-to-mark)) + ;; Wait for the notify callback ends before calling HFF. + (run-at-time + 0.1 nil + (lambda () + (if (and dirflag (eq action 'rename)) + (helm-find-files-1 (file-name-directory target) + (format helm-ff-last-expanded-candidate-regexp + (if helm-ff-transformer-show-only-basename + (helm-basename target) target))) + (helm-find-files-1 (if (file-directory-p destination) + (file-name-as-directory + (expand-file-name destination)) + (expand-file-name (helm-basedir destination))) + (format helm-ff-last-expanded-candidate-regexp + (if helm-ff-transformer-show-only-basename + (helm-basename (car files)) + (car files))))))))))) + +(defun helm-get-dest-fnames-from-list (flist dest-cand rename-dir-flag) + "Transform filenames of FLIST to abs of DEST-CAND. +If RENAME-DIR-FLAG is non-nil collect the `directory-file-name' +of transformed members of FLIST." + ;; At this point files have been renamed/copied at destination. + ;; That's mean DEST-CAND exists. + (cl-loop + with dest = (expand-file-name dest-cand) + for src in flist + for basename-src = (helm-basename src) + for fname = (cond (rename-dir-flag (directory-file-name dest)) + ((file-directory-p dest) + (concat (file-name-as-directory dest) basename-src)) + (t dest)) + when (file-exists-p fname) + collect fname into tmp-list + finally return (sort tmp-list 'string<))) + +(defun helm-ff-maybe-mark-candidates (seq) + "Add visible mark to all candidates in SEQ. +This is used when copying/renaming/symlinking etc. and following +files to destination." + (when (and (string= (assoc-default 'name (helm-get-current-source)) + (assoc-default 'name helm-source-find-files)) + seq) + (with-helm-window + (while seq + (if (string= (car seq) (helm-get-selection)) + (progn + (helm-make-visible-mark) + (helm-next-line) + (setq seq (cdr seq))) + (helm-next-line))) + (unless (helm-this-visible-mark) + (helm-prev-visible-mark))))) + + +;;; Delete and trash files +;; +;; +(defun helm-file-buffers (filename) + "Return a list of buffer names corresponding to FILENAME." + (cl-loop with name = (expand-file-name filename) + for buf in (buffer-list) + for bfn = (buffer-file-name buf) + when (and bfn (string= name bfn)) + collect (buffer-name buf))) + +(defun helm-ff--delete-by-moving-to-trash (file) + "Decide to trash or delete FILE. +Return non-nil when FILE needs to be trashed." + (let ((remote (file-remote-p file))) + (or + (and delete-by-moving-to-trash + (null helm-current-prefix-arg) + (null current-prefix-arg) + (or (and remote helm-trash-remote-files) + (null remote))) + (and (null delete-by-moving-to-trash) + (or helm-current-prefix-arg + current-prefix-arg) + (or (and remote helm-trash-remote-files) + (null remote)))))) + +(defun helm-trash-directory () + "Try to find a trash directory. +Return the files subdirectory of trash directory. +When `helm-trash-default-directory' is set use it as trash directory." + (let ((xdg-data-dir + (or helm-trash-default-directory + (directory-file-name + (expand-file-name "Trash" + (or (getenv "XDG_DATA_HOME") + "~/.local/share")))))) + (expand-file-name "files" xdg-data-dir))) + +(defun helm-ff-file-already-trashed (file &optional trash-alist) + "Return FILE when it is already in trash. + +Optional arg TRASH-ALIST should be an alist as what +`helm-ff-trash-list' returns." + (unless (fboundp 'system-move-file-to-trash) + (let ((trash-files-dir (helm-trash-directory))) + (cl-loop for (_bn . fn) in (or trash-alist + (helm-ff-trash-list trash-files-dir)) + thereis (file-equal-p file fn))))) + +(defun helm-ff-quick-delete (_candidate) + "Delete file CANDIDATE without quitting. + +When a prefix arg is given, meaning of +`delete-by-moving-to-trash' is the opposite." + (with-helm-window + (let* ((marked (helm-marked-candidates)) + (trash (helm-ff--delete-by-moving-to-trash (car marked))) + (helm-ff--trashed-files + (and trash (helm-ff-trash-list (helm-trash-directory))))) + (unwind-protect + (cl-loop for c in marked do + (progn (helm-preselect + (format helm-ff-last-expanded-candidate-regexp + (regexp-quote + (if (and helm-ff-transformer-show-only-basename + (not (helm-ff-dot-file-p c))) + (helm-basename c) c)))) + (when (y-or-n-p + (format "Really %s file `%s'? " + (if trash "Trash" "Delete") + (abbreviate-file-name c))) + (helm-acase (helm-delete-file + c helm-ff-signal-error-on-dot-files 'synchro trash) + (skip + ;; This happens only when trying to + ;; trash a file already trashed. + (helm-delete-visible-mark (helm-this-visible-mark)) + (if (helm-end-of-source-p) + (helm-previous-line) + (helm-next-line))) + (t (helm-delete-current-selection))) + (message nil) + (helm--remove-marked-and-update-mode-line c)))) + (setq helm-marked-candidates nil + helm-visible-mark-overlays nil) + (helm-force-update + (let ((presel (helm-get-selection))) + (when presel + (format helm-ff-last-expanded-candidate-regexp + (regexp-quote (if (and helm-ff-transformer-show-only-basename + (not (helm-ff-dot-file-p presel))) + (helm-basename presel) presel)))))))))) + +(defun helm-delete-file (file &optional error-if-dot-file-p synchro trash) + "Delete FILE after querying the user. + +When a prefix arg is given, meaning of +`delete-by-moving-to-trash' is the opposite. + +Return error when ERROR-IF-DOT-FILE-P is non-nil and user tries +to delete a dotted file i.e. \".\" or \"..\". + +Ask user when directory are not empty to allow recursive deletion +unless `helm-ff-allow-recursive-deletes' is non nil. +When user is asked and reply with \"!\" don't ask for remaining +directories. + +Ask to kill buffers associated with that file, too. + +When TRASH is non nil, trash FILE even if `delete-by-moving-to-trash' +is nil." + (require 'dired) + (cl-block nil + (when (and error-if-dot-file-p + (helm-ff-dot-file-p file)) + (error "Error: Cannot operate on `.' or `..'")) + (let ((buffers (helm-file-buffers file)) + (helm--reading-passwd-or-string t) + (file-attrs (file-attributes file)) + (trash (or trash (helm-ff--delete-by-moving-to-trash file))) + (delete-by-moving-to-trash trash) + (already-trashed + (and trash (helm-ff-file-already-trashed + file helm-ff--trashed-files)))) + (cond (already-trashed + ;; We use message here to avoid exiting loop when + ;; deleting more than one file. + (message "User error: `%s' is already trashed" file) + (sit-for 1.5) + (cl-return 'skip)) + ((and (eq (nth 0 file-attrs) t) + (directory-files file t directory-files-no-dot-files-regexp)) + ;; Synchro means persistent deletion from HFF. + (if synchro + (when (or helm-ff-allow-recursive-deletes + trash + (y-or-n-p (format "Recursive delete of `%s'? " + (abbreviate-file-name file)))) + (delete-directory file 'recursive trash)) + ;; Avoid using dired-delete-file really annoying in + ;; emacs-26 but allows using ! (instead of all) to not + ;; confirm anymore for recursive deletion of + ;; directory. This is not persistent for all session + ;; like emacs-26 does with dired-delete-file (think it + ;; is a bug). + (if (or helm-ff-allow-recursive-deletes trash) + (delete-directory file 'recursive trash) + (helm-acase (helm-read-answer (format "Recursive delete of `%s'? [y,n,!,q]" + (abbreviate-file-name file)) + '("y" "n" "!" "q")) + ("y" (delete-directory file 'recursive trash)) + ("!" (setq helm-ff-allow-recursive-deletes t) + (delete-directory file 'recursive trash)) + ("n" (cl-return 'skip)) + ("q" (throw 'helm-abort-delete-file + (progn + (message "Abort file deletion") (sleep-for 1)))))))) + ((eq (nth 0 file-attrs) t) + (delete-directory file nil trash)) + (t (delete-file file trash))) + (when buffers + (cl-dolist (buf buffers) + (when (y-or-n-p (format "Kill buffer %s, too? " buf)) + (kill-buffer buf))))))) + +(defun helm-delete-marked-files (_ignore) + "Delete marked files with `helm-delete-file'. + +When a prefix arg is given, meaning of +`delete-by-moving-to-trash' is the opposite." + (let* ((files (helm-marked-candidates :with-wildcard t)) + (len 0) + (trash (helm-ff--delete-by-moving-to-trash (car files))) + (helm-ff--trashed-files + (and trash (helm-ff-trash-list (helm-trash-directory)))) + (prmt (if trash "Trash" "Delete")) + (old--allow-recursive-deletes helm-ff-allow-recursive-deletes)) + (with-helm-display-marked-candidates + helm-marked-buffer-name + (helm-ff--count-and-collect-dups files) + (if (not (y-or-n-p (format "%s *%s File(s)" prmt (length files)))) + (message "(No deletions performed)") + (catch 'helm-abort-delete-file + (unwind-protect + (cl-dolist (i files) + (set-text-properties 0 (length i) nil i) + (let ((res (helm-delete-file + i helm-ff-signal-error-on-dot-files nil trash))) + (if (eq res 'skip) + (progn (message "Directory is not empty, skipping") + (sleep-for 1)) + (cl-incf len)))) + (setq helm-ff-allow-recursive-deletes old--allow-recursive-deletes))) + (message "%s File(s) %s" len (if trash "trashed" "deleted")))))) + +;;; Delete files async +;; +;; +(defvar helm-ff-delete-log-file + (locate-user-emacs-file "helm-delete-file.log") + "The file use to communicate with Emacs child when deleting files async.") + +(defvar helm-ff--trash-flag nil) + +(define-minor-mode helm-ff--delete-async-modeline-mode + "Notify mode-line that an async process run." + :group 'dired-async + :global t + ;; FIXME: Handle jobs like in dired-async, needs first to allow + ;; naming properly processes in async, they are actually all named + ;; emacs and running `async-batch-invoke', so if one copy a file and + ;; delete another file at the same time it may clash. + :lighter (:eval (propertize (format " %s file(s) async ..." + (if helm-ff--trash-flag + "Trashing" "Deleting")) + 'face 'helm-delete-async-message)) + (unless helm-ff--delete-async-modeline-mode + (let ((visible-bell t)) (ding)) + (setq helm-ff--trash-flag nil))) + +(defun helm-delete-async-mode-line-message (text face &rest args) + "Notify end of async operation in mode-line." + (message nil) + (let ((mode-line-format (concat + " " (propertize + (if args + (apply #'format text args) + text) + 'face face)))) + (force-mode-line-update) + (sit-for 3) + (force-mode-line-update))) + +(defun helm-delete-async-kill-process () + "Kill async process created by helm delete files async." + (interactive) + (let* ((processes (dired-async-processes)) + (proc (car (last processes)))) + (and proc (delete-process proc)) + (unless (> (length processes) 1) + (helm-ff--delete-async-modeline-mode -1)))) + +(defun helm-delete-marked-files-async (_ignore) + "Same as `helm-delete-marked-files' but async. + +When a prefix arg is given, meaning of +`delete-by-moving-to-trash' is the opposite. + +This function is not using `helm-delete-file' and BTW not asking +user for recursive deletion of directory, be warned that +directories are always deleted with no warnings." + (let* ((files (helm-marked-candidates :with-wildcard t)) + (trash (helm-ff--delete-by-moving-to-trash (car files))) + (prmt (if trash "Trash" "Delete")) + buffers callback already-trashed + ;; Workaround emacs-26 bug with tramp see + ;; https://github.com/jwiegley/emacs-async/issues/80. + (async-quiet-switch "-q")) + (cl-loop with trash-alist = (and trash (helm-ff-trash-list (helm-trash-directory))) + for f in files + for buf = (helm-file-buffers f) + for trashed = (helm-ff-file-already-trashed f trash-alist) + for dot-file-p = (helm-ff-dot-file-p f) + when (and helm-ff-signal-error-on-dot-files + dot-file-p) + do (cl-return (error "Error: Cannot operate on `.' or `..'")) + when buf + do (setq buffers (nconc buf buffers)) + when trashed + do (push trashed already-trashed)) + (setq callback (lambda (result) + (helm-ff--delete-async-modeline-mode -1) + (when (file-exists-p helm-ff-delete-log-file) + (display-warning 'helm + (with-temp-buffer + (insert-file-contents + helm-ff-delete-log-file) + (buffer-string)) + :error + "*helm delete files*") + (fit-window-to-buffer (get-buffer-window + "*helm delete files*")) + (delete-file helm-ff-delete-log-file)) + (when buffers + (helm-read-answer-dolist-with-action + "Kill buffer `%s', too? " + buffers #'kill-buffer)) + (run-with-timer + 0.1 nil + (lambda () + (helm-delete-async-mode-line-message + "%s (%s/%s) file(s) async done" + 'helm-delete-async-message + (if trash "Trashing" "Deleting") + result (length files)))))) + (setq helm-ff--trash-flag trash) + (with-helm-display-marked-candidates + helm-marked-buffer-name + (helm-ff--count-and-collect-dups files) + (if (not (y-or-n-p (format "%s *%s File(s)" prmt (length files)))) + (message "(No deletions performed)") + (async-start + `(lambda () + (require 'cl-lib) + ;; `delete-by-moving-to-trash' have to be set globally, + ;; using the TRASH argument of delete-file or + ;; delete-directory is not enough. + (setq delete-by-moving-to-trash ,trash) + (let ((result 0)) + (dolist (file ',files result) + (condition-case err + (cond ((and ,trash + (cl-loop for f in ',already-trashed + thereis (file-equal-p f file))) + (error (format "`%s' is already trashed" file))) + ((eq (nth 0 (file-attributes file)) t) + (delete-directory file 'recursive ,trash) + (setq result (1+ result))) + (t (delete-file file ,trash) + (setq result (1+ result)))) + (error (with-temp-file ,helm-ff-delete-log-file + (insert (format-time-string "%x:%H:%M:%S\n")) + (insert (format "%s:%s\n " + (car err) + (mapconcat 'identity (cdr err) " "))))))))) + callback) + (helm-ff--delete-async-modeline-mode 1))))) + +(defun helm-find-file-or-marked (candidate) + "Open file CANDIDATE or open helm marked files in separate windows. +Called with one prefix arg open files in separate windows in a +vertical split. +Called with two prefix arg open files in background without +selecting them." + (let ((marked (helm-marked-candidates :with-wildcard t)) + (url-p (and helm--url-regexp ; we should have only one candidate. + (string-match helm--url-regexp candidate))) + (ffap-newfile-prompt helm-ff-newfile-prompt-p) + (find-file-wildcards nil) + (helm--reading-passwd-or-string t)) + (if (cdr marked) + (if (equal helm-current-prefix-arg '(16)) + (mapcar 'find-file-noselect marked) + ;; If helm-current-prefix-arg is detected split is done + ;; vertically. + (helm-window-show-buffers (mapcar 'find-file-noselect marked))) + (let ((dir (and (not url-p) (helm-basedir candidate)))) + (cond ((and dir (file-directory-p dir)) + (find-file (substitute-in-file-name candidate))) + (url-p (find-file-at-point candidate)) + ;; A a non--existing filename ending with / + ;; Create a directory and jump to it. + ((and (not (file-exists-p candidate)) + (string-match "/$" candidate)) + (helm-ff--mkdir candidate 'helm-ff)) + ;; A non--existing filename NOT ending with / or + ;; an existing filename, create or jump to it. + ;; If the basedir of candidate doesn't exists, + ;; ask for creating it. + (dir + (helm-ff--mkdir dir) + (find-file candidate)) + ;; Find file at `default-directory' when basedir is + ;; unspecified e.g user hit C-k foo RET. + (t (find-file candidate))))))) + +(defun helm-ff-find-file-other-tab () + "Run find file in other tab action from `helm-source-buffers-list'." + (interactive) + (cl-assert (fboundp 'tab-bar-mode) nil "Tab-bar-mode not available") + (with-helm-alive-p + (helm-exit-and-execute-action 'find-file-other-tab))) +(put 'helm-ff-find-file-other-tab 'helm-only t) + +(defun helm-ff--new-dirs-to-update (path) + "Collect directories to update when creating new directory PATH." + (let ((result (list path))) + (helm-awhile (helm-reduce-file-name path 1) + (if (not (file-directory-p it)) + (progn (push it result) (setq path it)) + (push it result) + (cl-return))) + result)) + +(defun helm-ff--mkdir (dir &optional helm-ff) + (when (or (not confirm-nonexistent-file-or-buffer) + (y-or-n-p (format "Create directory `%s'? " + (abbreviate-file-name + (expand-file-name dir))))) + (let ((dirfname (directory-file-name dir)) + (to-update (and helm-ff (helm-ff--new-dirs-to-update dir)))) + (if (file-exists-p dirfname) + (error + "Mkdir: Unable to create directory `%s': file exists." + (helm-basename dirfname)) + (make-directory dir 'parent)) + (when helm-ff + ;; Refresh cache. + (mapc (lambda (x) (helm-ff-directory-files x t)) to-update) + ;; Allow having this new dir in history + ;; to be able to retrieve it immediately + ;; if we want to e.g copy a file from somewhere in it. + (setq helm-ff-default-directory + (file-name-as-directory (expand-file-name dir))) + (push helm-ff-default-directory helm-ff-history)) + (or (and helm-ff (helm-find-files-1 dir)) t)))) + +(defun helm-transform-file-load-el (actions candidate) + "Add action to load the file CANDIDATE if it is an Emacs Lisp +file. Else return ACTIONS unmodified." + (if (member (file-name-extension candidate) '("el" "elc")) + (append actions '(("Load Emacs Lisp File" . load-file))) + actions)) + +(defun helm-transform-file-browse-url (actions candidate) + "Add an action to browse the file CANDIDATE if it is a HTML file or URL. +Else return ACTIONS unmodified." + (let ((browse-action '("Browse with Browser" . browse-url))) + (cond ((string-match "^http\\|^ftp" candidate) + (cons browse-action actions)) + ((string-match "\\.html?$" candidate) + (append actions (list browse-action))) + (t actions)))) + +(defun helm-file-on-mounted-network-p (file) + "Return non-nil when FILE is part of a mounted remote directory. + +This function is checking `helm-mounted-network-directories' +list." + (when helm-mounted-network-directories + (cl-loop for dir in helm-mounted-network-directories + thereis (file-in-directory-p file dir)))) + +;; helm-find-files bindings for filecache +(defvar file-cache-alist) + +(defun helm-ff-cache-add-file (_candidate) + (require 'filecache) + (let ((mkd (helm-marked-candidates :with-wildcard t))) + (mapc 'file-cache-add-file mkd))) + +(defun helm-ff-file-cache-remove-file-1 (file) + "Remove FILE from `file-cache-alist'." + (let ((entry (assoc (helm-basename file) file-cache-alist)) + (dir (helm-basedir file)) + new-entry) + (setq new-entry (remove dir entry)) + (when (= (length entry) 1) + (setq new-entry nil)) + (setq file-cache-alist + (cons new-entry (remove entry file-cache-alist))))) + +(defun helm-ff-file-cache-remove-file (_file) + "Remove marked files from `file-cache-alist.'" + (let ((mkd (helm-marked-candidates))) + (mapc 'helm-ff-file-cache-remove-file-1 mkd))) + +;;; Find files in file +;; +;; +(defclass helm-find-files-in-file-class (helm-source-in-file helm-type-file) ()) +(cl-defmethod helm--setup-source ((source helm-find-files-in-file-class)) + (helm-aif (slot-value source 'candidate-transformer) + (setf (slot-value source 'candidate-transformer) + (append (helm-mklist it) + (list (lambda (candidates) + (cl-loop for c in candidates + when (and (not (string= c "")) + (file-exists-p c)) + collect c))))))) + +(defun helm-find-files-in-file-build-source (file) + (helm-make-source + (format "Find files in `%s'" (helm-basename file)) + 'helm-find-files-in-file-class + :candidates-file file)) + +(defun helm-find-files-in-file (_file) + "Helm action for listing filenames listed in marked files." + (require 'helm-for-files) + (let ((sources (cl-loop for f in (helm-marked-candidates) + collect (helm-find-files-in-file-build-source f)))) + (helm :sources sources + :quit-if-no-candidate (lambda () + (message "No files found in file(s)")) + :buffer "*helm find files in files*"))) + + +;;; File name history +;; +;; +(defun helm-files-save-file-name-history (&optional force) + "Save marked files to `file-name-history'." + (let* ((src (helm-get-current-source)) + (src-name (assoc-default 'name src))) + (when (or force (helm-file-completion-source-p src) + (member src-name helm-files-save-history-extra-sources)) + (let ((mkd (helm-marked-candidates :with-wildcard t)) + (history-delete-duplicates t)) + (cl-loop for sel in mkd + when (and sel + (stringp sel) + ;; If file was one of HFF candidates assume it + ;; is an existing file, so no need to call + ;; file-exists-p which is costly on remote candidates. + ;; (file-exists-p sel) + (not (helm-ff--file-directory-p sel)) + ;; When creating a new directory previous test + ;; check for file-directory-p BEFORE its + ;; creation, so check for ending slash as + ;; well to know if it is a future directory. + (not (string-match "/\\'" sel))) + do + ;; we use `abbreviate-file-name' here because + ;; other parts of Emacs seems to, + ;; and we don't want to introduce duplicates. + (add-to-history 'file-name-history + (abbreviate-file-name sel))))))) +(add-hook 'helm-exit-minibuffer-hook 'helm-files-save-file-name-history) + +(defvar helm-source-file-name-history + (helm-build-sync-source "File Name History" + :candidates 'file-name-history + :persistent-action #'ignore + :filtered-candidate-transformer #'helm-file-name-history-transformer + :action 'helm-type-file-actions)) + +(defun helm-file-name-history-show-or-hide-deleted () + (interactive) + (setq helm-file-name-history-hide-deleted + (not helm-file-name-history-hide-deleted)) + (helm-update)) +(put 'helm-file-name-history-show-or-hide-deleted 'helm-only t) + +(defvar helm-file-name-history-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c d") 'helm-file-name-history-show-or-hide-deleted) + (define-key map (kbd "C-x C-f") 'helm-ff-file-name-history-run-ff) + map)) + +(defun helm-file-name-history-transformer (candidates _source) + (cl-loop with lgst = (cl-loop for c in candidates maximize (length c)) + for c in candidates + for last-access = (format-time-string "%d/%m/%Y %X" + (nth 4 (file-attributes c))) + for disp = (cond ((or (file-remote-p c) + (and (fboundp 'tramp-archive-file-name-p) + (tramp-archive-file-name-p c))) + (propertize c 'face 'helm-history-remote)) + ((file-exists-p c) + (propertize + c 'display + (concat (propertize c 'face 'helm-ff-file) + (make-string (1+ (- lgst (length c))) ? ) + last-access))) + (t (unless helm-file-name-history-hide-deleted + (propertize c 'face 'helm-history-deleted)))) + when disp + collect (cons (if helm-ff-icon-mode + (concat (all-the-icons-icon-for-file c) " " disp) + disp) + c))) + +(defun helm-ff-file-name-history-ff (candidate) + (helm-set-pattern + (expand-file-name candidate))) + +(defun helm-ff-file-name-history-run-ff () + "Switch back to current HFF session with selection as preselect." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-file-name-history-ff))) + +(defun helm-ff-file-name-history-delete-item (_candidate) + (let ((files (helm-marked-candidates))) + (with-helm-display-marked-candidates + helm-marked-buffer-name + (helm-ff--count-and-collect-dups files) + (when (y-or-n-p "Delete file(s) from history? ") + (cl-loop for f in files do + (setq file-name-history (delete f file-name-history)))) + (message nil)))) + +(defun helm-ff-file-name-history () + "Switch to `file-name-history' without quitting `helm-find-files'." + (interactive) + (let ((src (helm-build-sync-source "File name history" + :init (lambda () + (with-helm-alive-p + (when helm-ff-file-name-history-use-recentf + (require 'recentf) + (or recentf-mode (recentf-mode 1))))) + :candidate-number-limit (or (get 'file-name-history 'history-length) + history-length) + :candidates (lambda () + (if helm-ff-file-name-history-use-recentf + recentf-list + file-name-history)) + :help-message 'helm-file-name-history-help-message + :fuzzy-match t + :persistent-action 'ignore + :migemo t + :filtered-candidate-transformer 'helm-file-name-history-transformer + :action (helm-make-actions + "Find file" (lambda (candidate) + (helm-set-pattern + (expand-file-name candidate)) + (with-helm-after-update-hook (helm-exit-minibuffer))) + "Find file in helm" 'helm-ff-file-name-history-ff + "Delete candidate(s)" 'helm-ff-file-name-history-delete-item) + :keymap helm-file-name-history-map))) + (with-helm-alive-p + (helm :sources src + :buffer "*helm-file-name-history*" + :allow-nest t + :resume 'noresume)))) +(put 'helm-ff-file-name-history 'helm-only t) + +;;; Browse project +;; Need dependencies: +;; +;; +;; Only hg and git are supported for now. +(defvar helm--browse-project-cache (make-hash-table :test 'equal)) +(defvar helm-buffers-in-project-p) + +(defvar helm-browse-project-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-generic-files-map) + (define-key map (kbd "M-g a") 'helm-browse-project-run-ag) + map)) + +(defun helm-browse-project-get-buffers (root-directory) + (cl-loop for b in (helm-buffer-list) + ;; FIXME: Why default-directory is root-directory + ;; for current-buffer when coming from helm-quit-and-find-file. + for cd = (with-current-buffer b default-directory) + for bn = (buffer-file-name (get-buffer b)) + if (or (and bn (file-in-directory-p bn root-directory)) + (and (null bn) + (not (file-remote-p cd)) + (file-in-directory-p cd root-directory))) + collect b)) + +(defun helm-browse-project-build-buffers-source (directory) + (helm-make-source "Buffers in project" 'helm-source-buffers + :header-name (lambda (name) + (format + "%s (%s)" + name (abbreviate-file-name directory))) + :buffer-list (lambda () (helm-browse-project-get-buffers directory)))) + +(defun helm-browse-project-walk-directory (directory) + "Default function for `helm-browse-project-default-find-files-fn'." + (helm-walk-directory + directory + :directories nil :path 'full :skip-subdirs t)) + +(defun helm-browse-project-find-files-1 (directory program) + "List files in DIRECTORY recursively with external PROGRAM." + (let ((cmd (cl-ecase program + (ag "ag --hidden -g '.*' %s") + (rg "rg --files --hidden -g '*' %s") + (fd "fd --hidden --type f --glob '*' %s")))) + (with-temp-buffer + (call-process-shell-command + (format cmd directory) + nil t nil) + (mapcar (lambda (f) (expand-file-name f directory)) + (split-string (buffer-string) "\n"))))) + +(defun helm-browse-project-ag-find-files (directory) + "A suitable function for `helm-browse-project-default-find-files-fn'. +Use AG as backend." + (helm-browse-project-find-files-1 directory 'ag)) + +(defun helm-browse-project-rg-find-files (directory) + "A suitable function for `helm-browse-project-default-find-files-fn'. +Use RG as backend." + (helm-browse-project-find-files-1 directory 'rg)) + +(defun helm-browse-project-fd-find-files (directory) + "A suitable function for `helm-browse-project-default-find-files-fn'. +Use FD as backend." + (helm-browse-project-find-files-1 directory 'fd)) + +(defun helm-browse-project-ag (_candidate) + "A `helm-grep' AG action for `helm-browse-project'." + (let ((dir (with-helm-buffer (helm-get-attr 'root-dir)))) + (helm-grep-ag dir helm-current-prefix-arg))) + +(defun helm-browse-project-run-ag () + "Run `helm-grep' AG from `helm-browse-project'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-browse-project-ag))) +(put 'helm-browse-project-run-ag 'helm-only t) + +(defclass helm-browse-project-override-inheritor (helm-type-file) ()) + +(defclass helm-browse-project-source (helm-source-in-buffer + helm-browse-project-override-inheritor) + ((root-dir :initarg :root-dir + :initform nil + :custom 'file) + (match-part :initform + (lambda (c) + (if (with-helm-buffer + helm-ff-transformer-show-only-basename) + (helm-basename c) c))) + (filter-one-by-one :initform + (lambda (c) + (if (with-helm-buffer + helm-ff-transformer-show-only-basename) + (cons (propertize (helm-basename c) + 'face 'helm-ff-file) + c) + (propertize c 'face 'helm-ff-file))))) + "Class to define a source in `helm-browse-project' handling non +VC handled directories.") + +(cl-defmethod helm--setup-source :after ((source helm-browse-project-override-inheritor)) + (let ((actions (slot-value source 'action))) + (setf (slot-value source 'action) + (helm-append-at-nth + (symbol-value actions) + '(("Grep project with AG `M-g a, C-u select type'" . helm-browse-project-ag)) + 7)) + (setf (slot-value source 'keymap) helm-browse-project-map))) + +(defun helm-browse-project-find-files (directory &optional refresh) + "Browse non VC handled directory DIRECTORY." + (when refresh (remhash directory helm--browse-project-cache)) + (unless (gethash directory helm--browse-project-cache) + (puthash directory (funcall helm-browse-project-default-find-files-fn + directory) + helm--browse-project-cache)) + (helm :sources `(,(helm-browse-project-build-buffers-source directory) + ,(helm-make-source "Browse project" + 'helm-browse-project-source + :root-dir directory + :data (gethash directory helm--browse-project-cache) + :header-name + (lambda (name) + (format + "%s (%s)" + name (abbreviate-file-name directory))))) + :ff-transformer-show-only-basename nil + :buffer "*helm browse project*")) + +(defvar helm-browse-project-history nil) + +;;;###autoload +(defun helm-projects-history (arg) + (interactive "P") + (helm :sources + (helm-build-sync-source "Project history" + :candidates helm-browse-project-history + :action (lambda (candidate) + (with-helm-default-directory candidate + (helm-browse-project + (or arg helm-current-prefix-arg))))) + :buffer "*helm browse project history*")) + +;;;###autoload +(defun helm-browse-project (arg) + "Preconfigured helm to browse projects. +Browse files and see status of project with its VCS. +Only HG and GIT are supported for now. +Fall back to `helm-browse-project-find-files' if current +directory is not under control of one of those VCS. +With a prefix ARG browse files recursively, with two prefix ARG +rebuild the cache. +If the current directory is found in the cache, start +`helm-browse-project-find-files' even with no prefix ARG. +NOTE: The prefix ARG have no effect on the VCS controlled +directories. + +Needed dependencies for VCS: + +and +." + (interactive "P") + (require 'helm-x-files) + (let ((helm-type-buffer-actions + (remove (assoc "Browse project from buffer" + helm-type-buffer-actions) + helm-type-buffer-actions)) + (helm-buffers-in-project-p t)) + (cl-flet ((push-to-hist (root) + (setq helm-browse-project-history + (cons root (delete root helm-browse-project-history))))) + (helm-acond ((and (require 'helm-ls-git nil t) + (fboundp 'helm-ls-git-root-dir) + (helm-ls-git-root-dir)) + (push-to-hist it) + (helm-ls-git)) + ((and (require 'helm-ls-hg nil t) + (fboundp 'helm-hg-root) + (helm-hg-root)) + (push-to-hist it) + (helm-hg-find-files-in-project)) + ((helm-browse-project-get--root-dir (helm-current-directory)) + (if (or arg (gethash it helm--browse-project-cache)) + (progn + (push-to-hist it) + (helm-browse-project-find-files it (equal arg '(16)))) + (helm :sources (helm-browse-project-build-buffers-source it) + :buffer "*helm browse project*"))))))) + +(defun helm-browse-project-get--root-dir (directory) + (cl-loop with dname = (file-name-as-directory directory) + while (and dname (not (gethash dname helm--browse-project-cache))) + if (file-remote-p dname) + do (setq dname nil) else + do (setq dname (helm-basedir (substring dname 0 (1- (length dname))))) + finally return (or dname (file-name-as-directory directory)))) + +(defun helm-ff-browse-project (_candidate) + "Browse project in current directory. +See `helm-browse-project'." + (with-helm-default-directory helm-ff-default-directory + (helm-browse-project helm-current-prefix-arg))) + +(defun helm-ff-run-browse-project () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-browse-project))) +(put 'helm-ff-run-browse-project 'helm-only t) + +;;; Actions calling helm and main interactive functions. +;; +;; +(defun helm-ff-gid (_candidate) + (with-helm-default-directory helm-ff-default-directory + (helm-gid))) + +(defun helm-ff-run-gid () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-gid))) +(put 'helm-ff-run-gid 'helm-only t) + +;; helm-find bindings for helm-find-files. +(defun helm-ff-find-sh-command (_candidate) + "Run `helm-find' from `helm-find-files'." + (require 'helm-find) + (helm-find-1 helm-ff-default-directory)) + +(defun helm-ff-run-find-sh-command () + "Run find shell command action with key from `helm-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-find-sh-command))) +(put 'helm-ff-run-find-sh-command 'helm-only t) + +;; helm-hd bindings for hff +(defun helm-ff-fd (_candidate) + "Run fd shell command from `helm-find-files'." + (require 'helm-fd) + (helm-fd-1 helm-ff-default-directory)) + +(defun helm-ff-run-fd () + "Run fd shell command action with key from `helm-find-files'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ff-fd))) +(put 'helm-ff-run-fd 'helm-only t) + +;;;###autoload +(defun helm-find-files (arg) + "Preconfigured `helm' for helm implementation of `find-file'. +Called with a prefix arg show history if some. +Don't call it from programs, use `helm-find-files-1' instead. +This is the starting point for nearly all actions you can do on +files." + (interactive "P") + (let* (tramp-archive-enabled ; Disable tramp-archive which is + ; kicking in unexpectedly. + (hist (and arg helm-ff-history (helm-find-files-history nil))) + (smart-input (or hist (helm-find-files-initial-input))) + (default-input (expand-file-name (helm-current-directory))) + (input (cond ((and (null hist) + helm-find-files-ignore-thing-at-point) + default-input) + ((and (eq major-mode 'org-agenda-mode) + org-directory + (not smart-input)) + (file-name-as-directory + (expand-file-name org-directory))) + ((and (eq major-mode 'dired-mode) smart-input) + (file-name-directory smart-input)) + ((and (not (string= smart-input "")) + smart-input)) + (t default-input))) + (input-as-presel (null (file-directory-p input))) + (presel (helm-aif (or hist + (and input-as-presel input) + (buffer-file-name (current-buffer)) + (and (eq major-mode 'dired-mode) + smart-input)) + (if (and helm-ff-transformer-show-only-basename + (null hist) + (not (string-match-p "[.]\\{1,2\\}\\'" it))) + (helm-basename it) it)))) + ;; Continue using the same display function as history which used + ;; probably itself the same display function as inner HFF call, + ;; i.e. if history was using frame use a frame otherwise use a window. + (when (and hist (buffer-live-p (get-buffer helm-ff-history-buffer-name))) + (helm-set-local-variable 'helm-display-function + (with-current-buffer helm-ff-history-buffer-name + helm-display-function) + 'helm--last-frame-parameters + (with-current-buffer helm-ff-history-buffer-name + helm--last-frame-parameters))) + (set-text-properties 0 (length input) nil input) + (setq current-prefix-arg nil) + ;; Allow next helm session to reuse helm--last-frame-parameters as + ;; resume would do. + (let ((helm--executing-helm-action (not (null hist)))) + (helm-find-files-1 input (and presel (null helm-ff-no-preselect) + (format helm-ff-last-expanded-candidate-regexp + (regexp-quote presel))))))) + + + +(provide 'helm-files) + +;;; helm-files.el ends here diff --git a/code/elpa/helm-20220822.659/helm-find.el b/code/elpa/helm-20220822.659/helm-find.el new file mode 100644 index 0000000..7626639 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-find.el @@ -0,0 +1,170 @@ +;;; helm-find.el --- helm interface for find command. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'helm-files) +(require 'helm-external) + +(defcustom helm-findutils-skip-boring-files t + "Ignore boring files in find command results." + :group 'helm-files + :type 'boolean) + +(defcustom helm-findutils-search-full-path nil + "Search in full path with shell command find when non-nil. +I.e. use the -path/ipath arguments of find instead of +-name/iname." + :group 'helm-files + :type 'boolean) + +(defcustom helm-find-noerrors nil + "Prevent showing error messages in helm buffer when non nil." + :group 'helm-files + :type 'boolean) + +(defvar helm-find-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-generic-files-map) + (define-key map (kbd "DEL") 'helm-delete-backward-no-update) + map)) + +(defvar helm-source-findutils + (helm-build-async-source "Find" + :header-name (lambda (name) + (concat name " in [" (helm-default-directory) "]")) + :candidates-process 'helm-find-shell-command-fn + :filtered-candidate-transformer 'helm-findutils-transformer + :action-transformer 'helm-transform-file-load-el + :persistent-action 'helm-ff-kill-or-find-buffer-fname + :action 'helm-type-file-actions + :help-message 'helm-generic-file-help-message + :keymap helm-find-map + :candidate-number-limit 9999 + :requires-pattern 3)) + +(defun helm-findutils-transformer (candidates _source) + (let (non-essential + (default-directory (helm-default-directory))) + (cl-loop for i in candidates + for abs = (expand-file-name + (helm-aif (file-remote-p default-directory) + (concat it i) i)) + for type = (car (file-attributes abs)) + for disp = (if (and helm-ff-transformer-show-only-basename + (not (string-match "[.]\\{1,2\\}$" i))) + (helm-basename abs) abs) + collect (cond ((eq t type) + (cons (propertize disp 'face 'helm-ff-directory) + abs)) + ((stringp type) + (cons (propertize disp 'face 'helm-ff-symlink) + abs)) + (t (cons (propertize disp 'face 'helm-ff-file) + abs)))))) + +(defun helm-find--build-cmd-line () + (require 'find-cmd) + (let* ((default-directory (or (file-remote-p default-directory 'localname) + default-directory)) + (patterns+options (split-string helm-pattern "\\(\\`\\| +\\)\\* +")) + (fold-case (helm-set-case-fold-search (car patterns+options))) + (patterns (split-string (car patterns+options))) + (additional-options (and (cdr patterns+options) + (list (concat (cadr patterns+options) " ")))) + (ignored-dirs ()) + (ignored-files (when helm-findutils-skip-boring-files + (cl-loop for f in completion-ignored-extensions + if (string-match "/$" f) + do (push (replace-match "" nil t f) + ignored-dirs) + else collect (concat "*" f)))) + (path-or-name (if helm-findutils-search-full-path + '(ipath path) '(iname name))) + (name-or-iname (if fold-case + (car path-or-name) (cadr path-or-name)))) + (find-cmd (and ignored-dirs + `(prune (name ,@ignored-dirs))) + (and ignored-files + `(not (name ,@ignored-files))) + `(and ,@(mapcar + (lambda (pattern) + `(,name-or-iname ,(concat "*" pattern "*"))) + patterns) + ,@additional-options)))) + +(defun helm-find-shell-command-fn () + "Asynchronously fetch candidates for `helm-find'. +Additional find options can be specified after a \"*\" +separator." + (let* (process-connection-type + non-essential + (cmd (concat (helm-find--build-cmd-line) + (if helm-find-noerrors "2> /dev/null" ""))) + (proc (start-file-process-shell-command "hfind" helm-buffer cmd))) + (helm-log "Find command:\n%s" cmd) + (prog1 proc + (set-process-sentinel + proc + (lambda (process event) + (helm-process-deferred-sentinel-hook + process event (helm-default-directory)) + (if (string= event "finished\n") + (helm-locate-update-mode-line "Find") + (helm-log "Error: Find %s" + (replace-regexp-in-string "\n" "" event)))))))) + +(defun helm-find-1 (dir) + (let ((default-directory (file-name-as-directory dir))) + (helm :sources 'helm-source-findutils + :buffer "*helm find*" + :ff-transformer-show-only-basename nil + :case-fold-search helm-file-name-case-fold-search))) + + +;;; Preconfigured commands +;; +;; +;;;###autoload +(defun helm-find (arg) + "Preconfigured `helm' for the find shell command. + +Recursively find files whose names are matched by all specified +globbing PATTERNs under the current directory using the external +program specified in `find-program' (usually \"find\"). Every +input PATTERN is silently wrapped into two stars: *PATTERN*. + +With prefix argument, prompt for a directory to search. + +When user option `helm-findutils-search-full-path' is non-nil, +match against complete paths, otherwise, against file names +without directory part. + +The (possibly empty) list of globbing PATTERNs can be followed by +the separator \"*\" plus any number of additional arguments that +are passed to \"find\" literally." + (interactive "P") + (let ((directory + (if arg + (file-name-as-directory + (read-directory-name "DefaultDirectory: ")) + default-directory))) + (helm-find-1 directory))) + +(provide 'helm-find) + +;;; helm-find.el ends here diff --git a/code/elpa/helm-20220822.659/helm-font.el b/code/elpa/helm-20220822.659/helm-font.el new file mode 100644 index 0000000..f3d4511 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-font.el @@ -0,0 +1,344 @@ +;;; helm-font --- Font and ucs selection for Helm -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) + +;; No warnings in Emacs built --without-x +(declare-function x-list-fonts "xfaces.c") + +(declare-function helm-generic-sort-fn "helm-utils") + +(defgroup helm-font nil + "Related applications to display fonts in Helm." + :group 'helm) + +(defcustom helm-ucs-recent-size 10 + "Number of recent chars to keep." + :type 'integer + :group 'helm-font) + +(defcustom helm-ucs-actions + '(("Insert character" . helm-ucs-insert-char) + ("Insert character name" . helm-ucs-insert-name) + ("Insert character code in hex" . helm-ucs-insert-code) + ("Kill marked characters" . helm-ucs-kill-char) + ("Kill name" . helm-ucs-kill-name) + ("Kill code" . helm-ucs-kill-code) + ("Describe char" . helm-ucs-describe-char)) + "Actions for `helm-source-ucs'." + :group 'helm-font + :type '(alist :key-type string :value-type function)) + +(defvar helm-ucs-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "") 'helm-ucs-persistent-delete) + (define-key map (kbd "") 'helm-ucs-persistent-backward) + (define-key map (kbd "") 'helm-ucs-persistent-forward) + (define-key map (kbd "C-c SPC") 'helm-ucs-persistent-insert-space) + map) + "Keymap for `helm-ucs'.") + +(defface helm-ucs-char + `((((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Gold")) + "Face used to display ucs characters." + :group 'helm-font) + +;;; Xfont selection +;; +;; +(defvar helm-xfonts-cache nil) +(defvar helm-previous-font nil) +(defvar helm-source-xfonts + (helm-build-sync-source "X Fonts" + :init (lambda () + (unless helm-xfonts-cache + (setq helm-xfonts-cache + (x-list-fonts "*"))) + ;; Save current font so it can be restored in cleanup + (setq helm-previous-font (cdr (assq 'font (frame-parameters))))) + :candidates 'helm-xfonts-cache + :action '(("Copy font to kill ring" . (lambda (elm) + (kill-new elm))) + ("Set font" . (lambda (elm) + (kill-new elm) + (set-frame-font elm 'keep-size) + (message "Font copied to kill ring")))) + :cleanup (lambda () + ;; Restore previous font + (set-frame-font helm-previous-font 'keep-size)) + :persistent-action (lambda (new-font) + (set-frame-font new-font 'keep-size) + (kill-new new-font)) + :persistent-help "Preview font and copy to kill-ring")) + + +;;; 𝕌𝕔𝕤 𝕊𝕪𝕞𝕓𝕠𝕝 𝕔𝕠𝕞𝕡𝕝𝕖𝕥𝕚𝕠𝕟 +;; +;; +(defvar helm-ucs--max-len nil) +(defvar helm-ucs--names nil) +(defvar helm-ucs-history nil) +(defvar helm-ucs-recent nil + "Ring of recent `helm-ucs' selections.") + +(defun helm-calculate-ucs-alist-max-len (names) + "Calculate the length of the longest NAMES list candidate." + (cl-loop for (_n . v) in names + maximize (length (format "#x%x:" v)) into code + maximize (max 1 (string-width (format "%c" v))) into char + finally return (cons code char))) + +(defun helm-calculate-ucs-hash-table-max-len (names) + "Calculate the length of the longest NAMES hash table candidate." + (cl-loop for _n being the hash-keys of names + using (hash-values v) + maximize (length (format "#x%x:" v)) into code + maximize (max 1 (string-width (format "%c" v))) into char + finally return (cons code char))) + +(defun helm-calculate-ucs-max-len () + "Calculate the length of the longest `ucs-names' candidate." + (let ((ucs-struct (ucs-names))) + (if (hash-table-p ucs-struct) + (helm-calculate-ucs-hash-table-max-len ucs-struct) + (helm-calculate-ucs-alist-max-len ucs-struct)))) + +(defun helm-ucs-collect-symbols-alist (names) + "Collect ucs symbols from the NAMES list." + (cl-loop with pr = (make-progress-reporter + "collecting ucs names" + 0 (length names)) + for (n . v) in names + for count from 1 + for xcode = (format "#x%x:" v) + for len = (length xcode) + for diff = (- (car helm-ucs--max-len) len) + for code = (format "(#x%x): " v) + for char = (propertize (format "%c" v) + 'face 'helm-ucs-char) + unless (or (string= "" n) + ;; `char-displayable-p' return a font object or + ;; t for some char that are displayable but have + ;; no special font (e.g 10) so filter out char + ;; with no font. + (not (fontp (char-displayable-p (read xcode))))) + collect + (concat code (make-string diff ? ) + char " " n) + and do (progress-reporter-update pr count))) + +(defun helm-ucs-collect-symbols-hash-table (names) + "Collect ucs symbols from the NAMES hash-table." + (cl-loop with pr = (make-progress-reporter + "collecting ucs names" + 0 (hash-table-count names)) + for n being the hash-keys of names + using (hash-values v) + for count from 1 + for xcode = (format "#x%x:" v) + for len = (length xcode) + for diff = (- (car helm-ucs--max-len) len) + for code = (format "(#x%x): " v) + for char = (propertize (format "%c" v) + 'face 'helm-ucs-char) + unless (or (string= "" n) + (not (fontp (char-displayable-p (read xcode))))) + collect + (concat code (make-string diff ? ) + char " " n) + and do (progress-reporter-update pr count))) + +(defun helm-ucs-collect-symbols (ucs-struct) + "Collect ucs symbols from UCS-STRUCT. + +Depending on the Emacs version, the variable `ucs-names' can +either be an alist or a hash-table." + (if (hash-table-p ucs-struct) + (helm-ucs-collect-symbols-hash-table ucs-struct) + (helm-ucs-collect-symbols-alist ucs-struct))) + +(defun helm-ucs-init () + "Initialize a Helm buffer with ucs symbols. +Only math* symbols are collected." + (unless helm-ucs--max-len + (setq helm-ucs--max-len + (helm-calculate-ucs-max-len))) + (or helm-ucs--names + (setq helm-ucs--names + (helm-ucs-collect-symbols (ucs-names))))) + +;; Actions (insertion) + +(defun helm-ucs-match (candidate n) + "Return the N part of an ucs CANDIDATE. +Where N=1 is the ucs code, N=2 the ucs char and N=3 the ucs +name." + (when (string-match + "^(\\(#x[a-f0-9]+\\)): *\\(.\\) *\\([^:]+\\)+" + candidate) + (match-string n candidate))) + +(defun helm-ucs-save-recentest (candidate) + (let ((lst (cons candidate (delete candidate helm-ucs-recent)))) + (setq helm-ucs-recent + (if (> (length lst) helm-ucs-recent-size) + (nbutlast lst 1) + lst)))) + +(defun helm-ucs-insert (candidate n) + "Insert the N part of CANDIDATE." + (with-helm-current-buffer + (helm-ucs-save-recentest candidate) + (insert (helm-ucs-match candidate n)))) + +(defun helm-ucs-insert-char (candidate) + "Insert ucs char part of CANDIDATE at point." + (helm-ucs-insert candidate 2)) + +(defun helm-ucs-insert-code (candidate) + "Insert ucs code part of CANDIDATE at point." + (helm-ucs-insert candidate 1)) + +(defun helm-ucs-insert-name (candidate) + "Insert ucs name part of CANDIDATE at point." + (helm-ucs-insert candidate 3)) + +;; Kill actions +(defun helm-ucs-kill-char (_candidate) + "Action that concatenate ucs marked chars." + (let ((marked (helm-marked-candidates))) + (cl-loop for candidate in marked + do (helm-ucs-save-recentest candidate)) + (kill-new (mapconcat (lambda (x) + (helm-ucs-match x 2)) + marked "")))) + +(defun helm-ucs-kill-code (candidate) + (helm-ucs-save-recentest candidate) + (kill-new (helm-ucs-match candidate 1))) + +(defun helm-ucs-kill-name (candidate) + (helm-ucs-save-recentest candidate) + (kill-new (helm-ucs-match candidate 3))) + +;; Describe char +(defun helm-ucs-describe-char (candidate) + "Describe char CANDIDATE." + (with-temp-buffer + (insert (helm-ucs-match candidate 2)) + (describe-char (point-min)))) + +;; Navigation in current-buffer (persistent) + +(defun helm-ucs-forward-char (_candidate) + (with-helm-current-buffer + (forward-char 1))) + +(defun helm-ucs-backward-char (_candidate) + (with-helm-current-buffer + (forward-char -1))) + +(defun helm-ucs-delete-backward (_candidate) + (with-helm-current-buffer + (delete-char -1))) + +(defun helm-ucs-insert-space (_candidate) + (with-helm-current-buffer + (insert " "))) + +(defun helm-ucs-persistent-forward () + (interactive) + (with-helm-alive-p + (helm-set-attr 'action-forward 'helm-ucs-forward-char) + (helm-execute-persistent-action 'action-forward))) +(put 'helm-ucs-persistent-forward 'helm-only t) + +(defun helm-ucs-persistent-backward () + (interactive) + (with-helm-alive-p + (helm-set-attr 'action-back 'helm-ucs-backward-char) + (helm-execute-persistent-action 'action-back))) +(put 'helm-ucs-persistent-backward 'helm-only t) + +(defun helm-ucs-persistent-delete () + (interactive) + (with-helm-alive-p + (helm-set-attr 'action-delete 'helm-ucs-delete-backward) + (helm-execute-persistent-action 'action-delete))) +(put 'helm-ucs-persistent-delete 'helm-only t) + +(defun helm-ucs-persistent-insert-space () + (interactive) + (with-helm-alive-p + (helm-set-attr 'action-insert-space 'helm-ucs-insert-space) + (helm-execute-persistent-action 'action-insert-space))) + +(defvar helm-source-ucs-recent + (helm-build-sync-source "Recent UCS" + :action 'helm-ucs-actions + :candidates (lambda () helm-ucs-recent) + :help-message helm-ucs-help-message + :keymap helm-ucs-map + :volatile t)) + +(defvar helm-source-ucs + (helm-build-in-buffer-source "UCS names" + :data #'helm-ucs-init + :get-line #'buffer-substring + :help-message 'helm-ucs-help-message + :filtered-candidate-transformer + (lambda (candidates _source) (sort candidates #'helm-generic-sort-fn)) + :action 'helm-ucs-actions + :persistent-action (lambda (candidate) + (helm-ucs-insert-char candidate) + (helm-force-update)) + :keymap helm-ucs-map) + "Source for collecting `ucs-names' math symbols.") + +;;;###autoload +(defun helm-select-xfont () + "Preconfigured `helm' to select Xfont." + (interactive) + (helm :sources 'helm-source-xfonts + :buffer "*helm select xfont*")) + +;;;###autoload +(defun helm-ucs (arg) + "Preconfigured `helm' for `ucs-names'. + +Called with a prefix arg force reloading cache." + (interactive "P") + (when arg + (setq helm-ucs--names nil + helm-ucs--max-len nil + ucs-names nil)) + (let ((char (helm-aif (char-after) (string it)))) + (helm :sources (list helm-source-ucs-recent helm-source-ucs) + :history 'helm-ucs-history + :input (and char (multibyte-string-p char) char) + :buffer "*helm ucs*"))) + +(provide 'helm-font) + +;;; helm-font.el ends here diff --git a/code/elpa/helm-20220822.659/helm-for-files.el b/code/elpa/helm-20220822.659/helm-for-files.el new file mode 100644 index 0000000..3587fc7 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-for-files.el @@ -0,0 +1,310 @@ +;;; helm-for-files.el --- helm-for-files and related. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'helm-files) +(require 'helm-external) +(require 'helm-bookmark) + +(defcustom helm-multi-files-toggle-locate-binding "C-c p" + "Default binding to switch back and forth locate in `helm-multi-files'." + :group 'helm-files + :type 'string) + +(defcustom helm-for-files-preferred-list + '(helm-source-buffers-list + helm-source-recentf + helm-source-bookmarks + helm-source-file-cache + helm-source-files-in-current-dir + helm-source-locate) + "Your preferred sources for `helm-for-files' and `helm-multi-files'. + +When adding a source here it is up to you to ensure the library +of this source is accessible and properly loaded." + :type '(repeat (choice symbol)) + :group 'helm-files) + +(defcustom helm-for-files-tramp-not-fancy t + "Colorize remote files when non nil. + +Be aware that a nil value will make tramp display very slow." + :group 'helm-files + :type 'boolean) + +;;; File Cache +;; +;; +(defvar file-cache-alist) + +(defclass helm-file-cache (helm-source-in-buffer helm-type-file) + ((init :initform (lambda () (require 'filecache))))) + +(defun helm-file-cache-get-candidates () + (cl-loop for item in file-cache-alist append + (cl-destructuring-bind (base &rest dirs) item + (cl-loop for dir in dirs collect + (concat dir base))))) + +(defvar helm-source-file-cache nil) + +(defcustom helm-file-cache-fuzzy-match nil + "Enable fuzzy matching in `helm-source-file-cache' when non--nil." + :group 'helm-files + :type 'boolean + :set (lambda (var val) + (set var val) + (setq helm-source-file-cache + (helm-make-source "File Cache" 'helm-file-cache + :fuzzy-match helm-file-cache-fuzzy-match + :data 'helm-file-cache-get-candidates)))) + +(cl-defun helm-file-cache-add-directory-recursively + (dir &optional match (ignore-dirs t)) + (require 'filecache) + (cl-loop for f in (helm-walk-directory + dir + :path 'full + :directories nil + :match match + :skip-subdirs ignore-dirs) + do (file-cache-add-file f))) + +(defun helm-transform-file-cache (actions _candidate) + (let ((source (helm-get-current-source))) + (if (string= (assoc-default 'name source) "File Cache") + (append actions + '(("Remove marked files from file-cache" + . helm-ff-file-cache-remove-file))) + actions))) + +;;; Recentf files +;; +;; +(defvar helm-recentf--basename-flag nil) + +(defun helm-recentf-pattern-transformer (pattern) + (let ((pattern-no-flag (replace-regexp-in-string " -b" "" pattern))) + (cond ((and (string-match " " pattern-no-flag) + (string-match " -b\\'" pattern)) + (setq helm-recentf--basename-flag t) + pattern-no-flag) + ((string-match "\\([^ ]*\\) -b\\'" pattern) + (prog1 (match-string 1 pattern) + (setq helm-recentf--basename-flag t))) + (t (setq helm-recentf--basename-flag nil) + pattern)))) + +(defcustom helm-turn-on-recentf t + "Automatically turn on `recentf-mode' when non-nil." + :group 'helm-files + :type 'boolean) + +(defclass helm-recentf-source (helm-source-sync helm-type-file) + ((init :initform (lambda () + (require 'recentf) + (when helm-turn-on-recentf (recentf-mode 1)))) + (candidates :initform (lambda () recentf-list)) + (pattern-transformer :initform 'helm-recentf-pattern-transformer) + (match-part :initform (lambda (candidate) + (if (or helm-ff-transformer-show-only-basename + helm-recentf--basename-flag) + (helm-basename candidate) candidate))) + (migemo :initform t) + (persistent-action :initform 'helm-ff-kill-or-find-buffer-fname))) + +(cl-defmethod helm--setup-source :after ((source helm-recentf-source)) + (setf (slot-value source 'action) + (append (symbol-value (helm-actions-from-type-file)) + '(("Delete file(s) from recentf" . + (lambda (_candidate) + (cl-loop for file in (helm-marked-candidates) + do (setq recentf-list (delete file recentf-list))))))))) + +(defvar helm-source-recentf nil + "See (info \"(emacs)File Conveniences\"). +Set `recentf-max-saved-items' to a bigger value if default is too +small.") + +(defcustom helm-recentf-fuzzy-match nil + "Enable fuzzy matching in `helm-source-recentf' when non-nil." + :group 'helm-files + :type 'boolean + :set (lambda (var val) + (set var val) + (let ((helm-fuzzy-sort-fn 'helm-fuzzy-matching-sort-fn-preserve-ties-order)) + (setq helm-source-recentf + (helm-make-source "Recentf" 'helm-recentf-source + :fuzzy-match val))))) + + +;;; Files in current dir +;; +;; +(defun helm-highlight-files (files _source) + "A basic transformer for helm files sources. +Colorize only symlinks, directories and files." + (cl-loop with mp-fn = (or (assoc-default + 'match-part (helm-get-current-source)) + 'identity) + for i in files + for disp = (if (and helm-ff-transformer-show-only-basename + (not (helm-dir-is-dot i)) + (not (and helm--url-regexp + (string-match helm--url-regexp i))) + (not (string-match helm-ff-url-regexp i))) + (helm-basename i) (abbreviate-file-name i)) + for isremote = (or (file-remote-p i) + (helm-file-on-mounted-network-p i)) + ;; Call file-attributes only if: + ;; - file is not remote + ;; - helm-for-files--tramp-not-fancy is nil and file is remote AND + ;; connected. (Bug#1679) + for type = (and (or (null isremote) + (and (null helm-for-files-tramp-not-fancy) + (file-remote-p i nil t))) + (car (file-attributes i))) + collect + (cond ((and (null type) isremote) (cons disp i)) + ((stringp type) + (cons (propertize disp + 'face 'helm-ff-symlink + 'match-part (funcall mp-fn disp) + 'help-echo (expand-file-name i)) + i)) + ((eq type t) + (cons (propertize disp + 'face 'helm-ff-directory + 'match-part (funcall mp-fn disp) + 'help-echo (expand-file-name i)) + i)) + (t (let* ((ext (helm-file-name-extension disp)) + (disp (propertize disp + 'face 'helm-ff-file + 'match-part (funcall mp-fn disp) + 'help-echo (expand-file-name i)))) + (when (condition-case _err + (string-match (format "\\.\\(%s\\)$" ext) disp) + (invalid-regexp nil)) + (add-face-text-property + (match-beginning 1) (match-end 1) + 'helm-ff-file-extension nil disp)) + (cons disp i)))))) + +(defclass helm-files-in-current-dir-source (helm-source-sync helm-type-file) + ((candidates :initform (lambda () + (with-helm-current-buffer + (let ((dir (helm-current-directory))) + (when (file-accessible-directory-p dir) + (directory-files dir t)))))) + (pattern-transformer :initform 'helm-recentf-pattern-transformer) + (match-part :initform (lambda (candidate) + (if (or helm-ff-transformer-show-only-basename + helm-recentf--basename-flag) + (helm-basename candidate) candidate))) + (fuzzy-match :initform t) + (migemo :initform t))) + +(defvar helm-source-files-in-current-dir + (helm-make-source "Files from Current Directory" + 'helm-files-in-current-dir-source)) + +;;;###autoload +(defun helm-for-files () + "Preconfigured `helm' for opening files. +Run all sources defined in `helm-for-files-preferred-list'." + (interactive) + (require 'helm-x-files) + (unless helm-source-buffers-list + (setq helm-source-buffers-list + (helm-make-source "Buffers" 'helm-source-buffers))) + (helm :sources helm-for-files-preferred-list + :ff-transformer-show-only-basename nil + :buffer "*helm for files*" + :truncate-lines helm-buffers-truncate-lines)) + +(defun helm-multi-files-toggle-to-locate () + (interactive) + (with-helm-alive-p + (with-helm-buffer + (if (setq helm-multi-files--toggle-locate + (not helm-multi-files--toggle-locate)) + (progn + (helm-set-sources (unless (memq 'helm-source-locate + helm-sources) + (cons 'helm-source-locate helm-sources))) + (helm-set-source-filter '(helm-source-locate))) + (helm-kill-async-processes) + (helm-set-sources (remove 'helm-source-locate + helm-for-files-preferred-list)) + (helm-set-source-filter nil))))) +(put 'helm-multi-files-toggle-to-locate 'helm-only t) + +;;;###autoload +(defun helm-multi-files () + "Preconfigured helm like `helm-for-files' but running locate only on demand. + +Allow toggling back and forth from locate to others sources with +`helm-multi-files-toggle-locate-binding' key. +This avoids launching locate needlessly when what you are +searching for is already found." + (interactive) + (require 'helm-x-files) + (unless helm-source-buffers-list + (setq helm-source-buffers-list + (helm-make-source "Buffers" 'helm-source-buffers))) + (setq helm-multi-files--toggle-locate nil) + (helm-locate-set-command) + (helm-set-local-variable 'helm-async-outer-limit-hook + (list (lambda () + (when (and helm-locate-fuzzy-match + (not (string-match-p + "\\s-" helm-pattern))) + (helm-redisplay-buffer))))) + (let ((sources (remove 'helm-source-locate helm-for-files-preferred-list)) + (helm-locate-command + (if helm-locate-fuzzy-match + (unless (string-match-p "\\`locate -b" helm-locate-command) + (replace-regexp-in-string + "\\`locate" "locate -b" helm-locate-command)) + helm-locate-command)) + (old-key (lookup-key + helm-map + (read-kbd-macro helm-multi-files-toggle-locate-binding)))) + (with-helm-temp-hook 'helm-after-initialize-hook + (define-key helm-map (kbd helm-multi-files-toggle-locate-binding) + 'helm-multi-files-toggle-to-locate)) + (unwind-protect + (helm :sources sources + :ff-transformer-show-only-basename nil + :buffer "*helm multi files*" + :truncate-lines helm-buffers-truncate-lines) + (define-key helm-map (kbd helm-multi-files-toggle-locate-binding) + old-key)))) + +;;;###autoload +(defun helm-recentf () + "Preconfigured `helm' for `recentf'." + (interactive) + (helm :sources 'helm-source-recentf + :ff-transformer-show-only-basename nil + :buffer "*helm recentf*")) + +(provide 'helm-for-files) + +;;; helm-for-files.el ends here diff --git a/code/elpa/helm-20220822.659/helm-global-bindings.el b/code/elpa/helm-20220822.659/helm-global-bindings.el new file mode 100644 index 0000000..896df8b --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-global-bindings.el @@ -0,0 +1,100 @@ +;;; helm-global-bindings.el --- Bind global helm commands -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'helm-lib) ; For helm-aif (bug #2520). + + +;;; Command Keymap +;; +;; +(defcustom helm-command-prefix-key + (helm-aif (car (where-is-internal 'Control-X-prefix (list global-map))) + (concat it [?c])) + "The key `helm-command-prefix' is bound to in the global map." + :type '(choice (string :tag "Key") (const :tag "no binding")) + :group 'helm-config + :set + (lambda (var key) + (when (and (boundp var) (symbol-value var)) + (define-key (current-global-map) + (read-kbd-macro (symbol-value var)) nil)) + (when key + (define-key (current-global-map) + (read-kbd-macro key) 'helm-command-prefix)) + (set var key))) + +(defvar helm-command-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "a") 'helm-apropos) + (define-key map (kbd "e") 'helm-etags-select) + (define-key map (kbd "l") 'helm-locate) + (define-key map (kbd "s") 'helm-surfraw) + (define-key map (kbd "r") 'helm-regexp) + (define-key map (kbd "m") 'helm-man-woman) + (define-key map (kbd "t") 'helm-top) + (define-key map (kbd "/") 'helm-find) + (define-key map (kbd "i") 'helm-imenu) + (define-key map (kbd "I") 'helm-imenu-in-all-buffers) + (define-key map (kbd "") 'helm-lisp-completion-at-point) + (define-key map (kbd "p") 'helm-list-emacs-process) + (define-key map (kbd "C-x r b") 'helm-filtered-bookmarks) + (define-key map (kbd "M-y") 'helm-show-kill-ring) + (define-key map (kbd "C-c ") 'helm-all-mark-rings) + (define-key map (kbd "C-x C-f") 'helm-find-files) + (define-key map (kbd "f") 'helm-multi-files) + (define-key map (kbd "C-:") 'helm-eval-expression-with-eldoc) + (define-key map (kbd "C-,") 'helm-calcul-expression) + (define-key map (kbd "M-x") 'helm-M-x) + (define-key map (kbd "M-s o") 'helm-occur) + (define-key map (kbd "M-g a") 'helm-do-grep-ag) + (define-key map (kbd "c") 'helm-colors) + (define-key map (kbd "F") 'helm-select-xfont) + (define-key map (kbd "8") 'helm-ucs) + (define-key map (kbd "C-c f") 'helm-recentf) + (define-key map (kbd "C-c g") 'helm-google-suggest) + (define-key map (kbd "h i") 'helm-info-at-point) + (define-key map (kbd "h r") 'helm-info-emacs) + (define-key map (kbd "h g") 'helm-info-gnus) + (define-key map (kbd "h h") 'helm-documentation) + (define-key map (kbd "C-x C-b") 'helm-buffers-list) + (define-key map (kbd "C-x r i") 'helm-register) + (define-key map (kbd "C-c C-x") 'helm-run-external-command) + (define-key map (kbd "b") 'helm-resume) + (define-key map (kbd "M-g i") 'helm-gid) + (define-key map (kbd "@") 'helm-list-elisp-packages) + map)) + +;; Don't override the keymap we just defined with an empty +;; keymap. This also protect bindings changed by the user. +(defvar helm-command-prefix) +(define-prefix-command 'helm-command-prefix) +(fset 'helm-command-prefix helm-command-map) +(setq helm-command-prefix helm-command-map) + + +;;; Menu + +(require 'helm-easymenu) + + +;;; Provide + +(provide 'helm-global-bindings) + +;;; helm-global-bindings.el ends here diff --git a/code/elpa/helm-20220822.659/helm-grep.el b/code/elpa/helm-20220822.659/helm-grep.el new file mode 100644 index 0000000..b222f96 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-grep.el @@ -0,0 +1,1801 @@ +;;; helm-grep.el --- Helm Incremental Grep. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'ansi-color) +(require 'cl-lib) +(require 'format-spec) +(require 'helm) +(require 'helm-help) +(require 'helm-regexp) + +;;; load wgrep proxy if it's available +(require 'wgrep-helm nil t) + +(declare-function helm-buffer-list "helm-buffers") +(declare-function View-quit "view") +(declare-function doc-view-goto-page "doc-view" (page)) +(declare-function pdf-view-goto-page "pdf-view" (page &optional window)) +(declare-function helm-mm-split-pattern "helm-multi-match") +(declare-function helm-comp-read "helm-mode") +(declare-function helm-occur "helm-occur") + +(defvar helm--ansi-color-regexp) +(defvar helm-ff-default-directory) +(defvar helm-tramp-verbose) +(defvar helm-grep-ack-types-cache) +(defvar helm-grep-git-grep-command) +(defvar helm-source-grep-git) +(defvar tramp-verbose) +(defvar helm-current-error) + +;;; Internals vars +;; +;; +(defvar helm-rzgrep-cache (make-hash-table :test 'equal)) +(defvar helm-grep-default-function 'helm-grep-init) +(defvar helm-zgrep-recurse-flag nil) +(defvar helm-grep-history nil) +(defvar helm-grep-ag-history nil) +(defvar helm-grep-last-targets nil) +(defvar helm-grep-include-files nil) +(defvar helm-grep-in-recurse nil) +(defvar helm-grep-use-zgrep nil) +(defvar helm-grep-default-directory-fn nil + "A function that should return a directory to expand candidate to. +It is intended to use as a let-bound variable, DON'T set this globaly.") +(defvar helm-pdfgrep-targets nil) +(defvar helm-grep-last-cmd-line nil) +(defvar helm-grep-split-line-regexp "^\\([[:lower:][:upper:]]?:?.*?\\):\\([0-9]+\\):\\(.*\\)") + + +;;; Keymaps +;; +;; +(defvar helm-grep-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-") 'helm-goto-next-file) + (define-key map (kbd "M-") 'helm-goto-precedent-file) + (define-key map (kbd "C-c o") 'helm-grep-run-other-window-action) + (define-key map (kbd "C-c C-o") 'helm-grep-run-other-frame-action) + (define-key map (kbd "C-x C-s") 'helm-grep-run-save-buffer) + (define-key map (kbd "DEL") 'helm-delete-backward-no-update) + map) + "Keymap used in Grep sources.") + +(defvar helm-pdfgrep-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-") 'helm-goto-next-file) + (define-key map (kbd "M-") 'helm-goto-precedent-file) + (define-key map (kbd "DEL") 'helm-delete-backward-no-update) + map) + "Keymap used in pdfgrep.") + +(defvar helm-grep-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "RET") 'helm-grep-mode-jump) + (define-key map (kbd "C-o") 'helm-grep-mode-jump-other-window) + (define-key map (kbd "") 'helm-grep-mode-jump-other-window-forward) + (define-key map (kbd "") 'helm-grep-mode-jump-other-window-backward) + (define-key map (kbd "") 'helm-gm-next-file) + (define-key map (kbd "") 'helm-gm-precedent-file) + (define-key map (kbd "M-n") 'helm-grep-mode-jump-other-window-forward) + (define-key map (kbd "M-p") 'helm-grep-mode-jump-other-window-backward) + (define-key map (kbd "M-N") 'helm-gm-next-file) + (define-key map (kbd "M-P") 'helm-gm-precedent-file) + map)) + + +(defgroup helm-grep nil + "Grep related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-grep-default-command + "grep --color=always -a -d skip %e -n%cH -e %p %f" + "Default grep format command for `helm-do-grep-1'. +Where: +'%e' format spec is for --exclude or --include grep options or + ack-grep --type option. (Not mandatory) + +'%c' format spec is for case-fold-search, + whether to use the -i option of grep. (Not mandatory) + When you specify this spec, helm grep will use smartcase + that is when a upcase character is found in pattern case will + be respected and no \\='-i' option will be used, otherwise, when + no upcase character is found in pattern always use \\='-i'. + If you don't want this behavior, don't use this spec and + specify or not the \\='-i' option. + Note that with ack-grep this is not needed, just specify + the \\='--smart-case' option. + +'%p' format spec is for pattern. (Mandatory) + +'%f' format spec is for filenames. (Mandatory) + +If your grep version doesn't support the --exclude/include args +don't specify the \\='%e' format spec. + +Helm also support ack-grep and git-grep. The following is a +default command example for ack-grep: + +\(setq helm-grep-default-command + \"ack-grep -Hn --color --smart-case --no-group %e -- %p %f\" + helm-grep-default-recurse-command + \"ack-grep -H --color --smart-case --no-group %e -- %p %f\") + +You can ommit the %e spec if you don't want to be prompted for +types. + +NOTE: Helm for ack-grep support ANSI sequences, so you can remove +the \"--no-color\" option safely (recommended). +However you should specify --color to enable multi matches highlighting +because ack disable it when output is piped. + +Same for grep you can use safely the option \"--color=always\" (default). +You can customize the color of matches using GREP_COLORS env var. +e.g: (setenv \"GREP_COLORS\" + \"ms=30;43:mc=30;43:sl=01;37:cx=:fn=35:ln=32:bn=32:se=36\") + +To enable ANSI color in git-grep just add \"--color=always\". +To customize the ANSI color in git-grep, GREP_COLORS have no effect, +you will have to setup this in your .gitconfig: + + [color \"grep\"] + match = black yellow + +Where \"black\" is the foreground and \"yellow\" the background. +See the git documentation for more infos. + +`helm-grep-default-command' and +`helm-grep-default-recurse-command' are independent, so you can +enable `helm-grep-default-command' with ack-grep and +`helm-grep-default-recurse-command' with grep if you want to be +faster on recursive grep. + +NOTE: Remote grepping is not available with ack-grep, and badly + supported with grep because tramp handles badly repeated + remote processes in a short delay (< to 5s)." + :type 'string) + +(defcustom helm-grep-default-recurse-command + "grep --color=always -a -d recurse %e -n%cH -e %p %f" + "Default recursive grep format command for `helm-do-grep-1'. +See `helm-grep-default-command' for format specs and infos about +ack-grep." + :type 'string) + +(defcustom helm-default-zgrep-command + "zgrep --color=always -a -n%cH -e %p %f" + "Default command for Zgrep. +See `helm-grep-default-command' for infos on format specs. +Option --color=always is supported and can be used safely to +replace the Helm internal match highlighting. See +`helm-grep-default-command' for more infos." + :type 'string) + +(defcustom helm-pdfgrep-default-command + "pdfgrep --color always -niH %s %s" + "Default command for pdfgrep. +Option \"--color always\" is supported starting Helm version +1.7.8. When used matches will be highlighted according to +GREP_COLORS env var." + :type 'string) + +(defcustom helm-pdfgrep-default-recurse-command + "pdfgrep --color always -rniH %s %s" + "Default recurse command for pdfgrep. +Option \"--color always\" is supported starting Helm version +1.7.8. When used matches will be highlighted according to +GREP_COLORS env var." + :type 'string) + +(defcustom helm-pdfgrep-default-read-command nil + "Default command to read pdf files from pdfgrep. +Where \\='%f' format spec is filename and \\='%p' is page number. +E.g. In Ubuntu you can set it to: + + \"evince --page-label=%p \\='%f'\" + +If set to nil either `doc-view-mode' or `pdf-view-mode' will be +used instead of an external command." + :type 'string) + +(defcustom helm-grep-max-length-history 100 + "Max number of elements to save in `helm-grep-history'." + :type 'integer) + +(defcustom helm-zgrep-file-extension-regexp + ".*\\(\\.gz\\|\\.bz\\|\\.xz\\|\\.lzma\\)$" + "Default file extensions zgrep will search in." + :type 'string) + +(defcustom helm-grep-preferred-ext nil + "This file extension will be preselected for grep." + :type 'string) + +(defcustom helm-grep-save-buffer-name-no-confirm nil + "When *hgrep* already exists, auto append suffix." + :type 'boolean) + +(defcustom helm-grep-ignored-files + (cons ".#*" (delq nil (mapcar (lambda (s) + (unless (string-match-p "/\\'" s) + (concat "*" s))) + completion-ignored-extensions))) + "List of file names which `helm-grep' shall exclude." + :type '(repeat string)) + +(defcustom helm-grep-ignored-directories + helm-walk-ignore-directories + "List of names of sub-directories which `helm-grep' shall not recurse into." + :type '(repeat string)) + +(defcustom helm-grep-truncate-lines t + "When nil the grep line that appears will not be truncated." + :type 'boolean) + +(defcustom helm-grep-file-path-style 'basename + "File path display style when grep results are displayed. +Possible value are: + basename: displays only the filename, none of the directory path + absolute: displays absolute path + relative: displays relative path from root grep directory." + :type '(choice (const :tag "Basename" basename) + (const :tag "Absolute" absolute) + (const :tag "Relative" relative))) + +(defcustom helm-grep-actions + (helm-make-actions + "Find File" 'helm-grep-action + "Find file other frame" 'helm-grep-other-frame + "Save results in grep buffer" 'helm-grep-save-results + "Find file other window (C-u vertically)" 'helm-grep-other-window) + "Actions for helm grep." + :type '(alist :key-type string :value-type function)) + +(defcustom helm-grep-pipe-cmd-switches nil + "A list of additional parameters to pass to grep pipe command. +This will be used to pipe command for multiple pattern matching +for grep, zgrep ack-grep and git-grep backends. +If you add extra args for ack-grep, use ack-grep options, for +others (grep, zgrep and git-grep) use grep options. +Here are the commands where you may want to add switches: + + grep --color=always + ack-grep --smart-case --color + +You probably don't need to use this unless you know what you are +doing." + :type '(repeat string)) + +(defcustom helm-grep-ag-pipe-cmd-switches nil + "A list of additional parameters to pass to grep-ag pipe command. +Use parameters compatibles with the backend you are using +\(i.e. AG for AG, PT for PT or RG for RG) +Here are the commands where you may want to add switches: + + ag -S --color + rg -N -S --color=? + +For RG the value of --color= is computed according to the --color= +value used in `helm-grep-ag-command'. + +Note also that by default the \"--\" option is always used, you don't +need to add it here. + +You probably don't need to use this unless you know what you are +doing." + :type '(repeat string)) + +(defcustom helm-grep-input-idle-delay 0.1 + "Idle time before updating, specified in seconds. +A lower value (default) means Helm will display the results +faster. Increasing it to a higher value (e.g. 0.6) prevents the +buffer from flickering when updating." + :type 'float) + +(defcustom helm-grep-use-ioccur-style-keys t + "Use Arrow keys to jump to occurences. +Note that if you define this variable with `setq' your change +will have no effect, use customize instead." + :type 'boolean + :set (lambda (var val) + (set var val) + (if val + (progn + (define-key helm-grep-map (kbd "") 'helm-execute-persistent-action) + (define-key helm-grep-map (kbd "") 'helm-grep-run-default-action)) + (define-key helm-grep-map (kbd "") nil) + (define-key helm-grep-map (kbd "") nil)))) + +(defcustom helm-grep-ag-command + "ag --line-numbers -S --color --nogroup %s -- %s %s" + "The default command for AG, PT or RG. + +Takes three format specs, the first for type(s), the second for +pattern and the third for directory. + +You can use safely \"--color\" (used by default) with AG RG and +PT. + +NOTE: Usage of \"--color=never\" is discouraged as it uses Elisp +to colorize matched items which is slower than using the native +colorization of backend, however it is still supported. + +For ripgrep here is the command line to use: + + rg --color=always --smart-case --no-heading --line-number %s -- %s %s + +And to customize colors (always for ripgrep) use something like this: + + rg --color=always --colors \\='match:bg:yellow' --colors \\='match:fg:black' +\--smart-case --no-heading --line-number %s -- %s %s + +This will change color for matched items from foreground red (the +default) to a yellow background with a black foreground. Note +that your color settings for RG will not work properly with +multiples pattern if you have configured colors in rg config file +instead of command line. For more enhanced settings of ansi +colors see https://github.com/emacs-helm/helm/issues/2313 + +You must use an output format that fit with helm grep, that is: + + \"filename:line-number:string\" + +The option \"--nogroup\" allow this. +The option \"--line-numbers\" is also mandatory except with +PT (not supported). +For RG the options \"--no-heading\" and \"--line-number\" are the +ones to use. + +When modifying the default colors of matches with e.g. +\"--color-match\" option of AG or \"--colors\" option of ripgrep +you may want to modify as well `helm-grep-ag-pipe-cmd-switches' +to have all matches colorized with the same color in multi +match. + +Of course you can use several other options, see the man page of the +backend you are using." + :type 'string) + +(defcustom helm-grep-git-grep-command + "git --no-pager grep -n%cH --color=always --full-name -e %p -- %f" + "The git grep default command line. +The option \"--color=always\" can be used safely. +The color of matched items can be customized in your .gitconfig +See `helm-grep-default-command' for more infos. + +The \"--exclude-standard\" and \"--no-index\" switches allow +skipping unwanted files specified in ~/.gitignore_global and +searching files not already staged (not enabled by default). + +You have also to enable this in global \".gitconfig\" with + \"git config --global core.excludesfile ~/.gitignore_global\"." + :type 'string) + + +;;; Faces +;; +;; +(defgroup helm-grep-faces nil + "Customize the appearance of helm-grep." + :prefix "helm-" + :group 'helm-grep + :group 'helm-faces) + +(defface helm-grep-match + `((((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "#b00000") + (((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "gold1")) + "Face used to highlight grep matches. +Have no effect when grep backend use \"--color=\"." + :group 'helm-grep-faces) + +(defface helm-grep-file + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "BlueViolet" + :underline t)) + "Face used to highlight grep results filenames." + :group 'helm-grep-faces) + +(defface helm-grep-lineno + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Darkorange1")) + "Face used to highlight grep number lines." + :group 'helm-grep-faces) + +(defface helm-grep-finish + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Green")) + "Face used in mode line when grep is finish." + :group 'helm-grep-faces) + +(defface helm-grep-cmd-line + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit font-lock-type-face)) + "Face used to highlight grep command line when no results." + :group 'helm-grep-faces) + + +;;; Init +;; +;; +(defun helm-grep-prepare-candidates (candidates in-directory) + "Prepare filenames and directories CANDIDATES for grep command line." + ;; If one or more candidate is a directory, search in all files + ;; of this candidate (e.g /home/user/directory/*). + ;; If r option is enabled search also in subdidrectories. + ;; We need here to expand wildcards to support crap windows filenames + ;; as grep doesn't accept quoted wildcards (e.g "dir/*.el"). + (if helm-zgrep-recurse-flag + (mapconcat 'shell-quote-argument candidates " ") + ;; When candidate is a directory, search in all its files. + ;; NOTE that `file-expand-wildcards' will return also + ;; directories, they will be ignored by grep but not + ;; by ack-grep that will grep all files of this directory + ;; without recursing in their subdirs though, see that as a one + ;; level recursion with ack-grep. + ;; So I leave it as it is, considering it is a feature. [1] + (cl-loop for i in candidates append + (cond ((string-match "^git" helm-grep-default-command) + (list i)) + ;; Candidate is a directory and we use recursion or ack. + ((and (file-directory-p i) + (or helm-grep-in-recurse + ;; ack-grep accept directory [1]. + (helm-grep-use-ack-p))) + (list (expand-file-name i))) + ;; Grep doesn't support directory only when not in recurse. + ((file-directory-p i) + (file-expand-wildcards + (concat (file-name-as-directory (expand-file-name i)) "*") t)) + ;; Candidate is a file or wildcard and we use recursion, use the + ;; current directory instead of candidate. + ((and (or (file-exists-p i) (string-match "[*]" i)) + helm-grep-in-recurse) + (list (expand-file-name + (directory-file-name ; Needed for windoze. + (file-name-directory (directory-file-name i)))))) + ;; Else should be one or more file/directory + ;; possibly marked. + ;; When real is a normal filename without wildcard + ;; file-expand-wildcards returns a list of one file. + ;; wildcards should have been already handled by + ;; helm-read-file-name or helm-find-files but do it from + ;; here too in case we are called from elsewhere. + (t (file-expand-wildcards i t))) into all-files ; [1] + finally return + (let ((files (if (file-remote-p in-directory) + ;; Grep don't understand tramp filenames + ;; use the local name. + (mapcar (lambda (x) + (file-remote-p x 'localname)) + all-files) + all-files))) + ;; When user mark files and use recursion with grep + ;; backend enabled, the loop collect on each marked + ;; candidate its `file-name-directory' and we endup with + ;; duplicates (Bug#1714). FIXME: For now as a quick fix + ;; I just remove dups here but I should handle this inside + ;; the cond above. + (setq files (helm-fast-remove-dups files :test 'equal)) + (if (string-match "^git" helm-grep-default-command) + (mapconcat 'identity files " ") + (mapconcat 'shell-quote-argument files " ")))))) + +(defun helm-grep-command (&optional recursive grep) + (let* ((com (if recursive + helm-grep-default-recurse-command + helm-grep-default-command)) + (exe (if grep + (symbol-name grep) + (and com (car (split-string com " ")))))) + (if (and exe (string= exe "git")) "git-grep" exe))) + +(cl-defun helm-grep-use-ack-p (&key where) + (let* ((rec-com (helm-grep-command t)) + (norm-com (helm-grep-command)) + (norm-com-ack-p (string-match "\\`ack" norm-com)) + (rec-com-ack-p (and rec-com (string-match "\\`ack" rec-com)))) + (cl-case where + (default (and norm-com norm-com-ack-p)) + (recursive (and rec-com rec-com-ack-p)) + (strict (and norm-com rec-com rec-com-ack-p norm-com-ack-p)) + (t (and (not (and norm-com (string= norm-com "git-grep"))) + (or (and norm-com norm-com-ack-p) + (and rec-com rec-com-ack-p))))))) + +(defun helm-grep--pipe-command-for-grep-command (smartcase pipe-switches &optional grep-cmd) + (pcase (or grep-cmd (helm-grep-command)) + ;; Use grep for GNU regexp based tools. + ((or "grep" "zgrep" "git-grep") + (format "grep --color=always%s %s" + (if smartcase " -i" "") + pipe-switches)) + ;; Use ack-grep for PCRE based tools. + ;; Sometimes ack-grep cmd is ack only. + ((and (pred (string-match-p "ack")) ack) + (format "%s --smart-case --color %s" ack pipe-switches)))) + +(defun helm-grep--prepare-cmd-line (only-files &optional include zgrep) + (let* ((default-directory (or helm-ff-default-directory + (helm-default-directory) + default-directory)) + (fnargs (helm-grep-prepare-candidates + only-files default-directory)) + (ignored-files (unless (helm-grep-use-ack-p) + (mapconcat + (lambda (x) + (concat "--exclude=" + (shell-quote-argument x))) + helm-grep-ignored-files " "))) + (ignored-dirs (unless (helm-grep-use-ack-p) + (mapconcat + ;; Need grep version >=2.5.4 + ;; of Gnuwin32 on windoze. + (lambda (x) + (concat "--exclude-dir=" + (shell-quote-argument x))) + helm-grep-ignored-directories " "))) + (exclude (unless (helm-grep-use-ack-p) + (let ((inc (and include + (concat include " "))) + (igfiles (and ignored-files + (concat ignored-files " "))) + (igdirs (and helm-grep-in-recurse + ignored-dirs))) + (concat inc igfiles igdirs)))) + (types (and (helm-grep-use-ack-p) + ;; When %e format spec is not specified + ;; in `helm-grep-default-command' + ;; we need to pass an empty string + ;; to types to avoid error. + (or include ""))) + (smartcase (if (helm-grep-use-ack-p) + "" + (unless (let ((case-fold-search nil)) + (string-match-p + "[[:upper:]]" helm-pattern)) + "i"))) + (helm-grep-default-command + (concat helm-grep-default-command " %m")) ; `%m' like multi. + (patterns (helm-mm-split-pattern helm-pattern t)) + (pipe-switches (mapconcat 'identity helm-grep-pipe-cmd-switches " ")) + (pipes + (helm-aif (cdr patterns) + (cl-loop with pipcom = (helm-grep--pipe-command-for-grep-command + smartcase pipe-switches) + for p in it concat + (format " | %s %s" pipcom (shell-quote-argument p))) + ""))) + (format-spec + helm-grep-default-command + (delq nil + (list (unless zgrep + (if types + (cons ?e types) + (cons ?e exclude))) + (cons ?c (or smartcase "")) + (cons ?p (shell-quote-argument (car patterns))) + (cons ?f fnargs) + (cons ?m pipes)))))) + +(defun helm-grep-init (cmd-line) + "Start an asynchronous grep process with CMD-LINE using ZGREP if non-nil." + (let* ((default-directory (or helm-ff-default-directory + (helm-default-directory) + default-directory)) + (zgrep (string-match "\\`zgrep" cmd-line)) + ;; Use pipe only with grep, zgrep or git-grep. + (process-connection-type (and (not zgrep) (helm-grep-use-ack-p))) + (tramp-verbose helm-tramp-verbose) + (start-time (float-time)) + (proc-name (if helm-grep-use-zgrep + "Zgrep" + (capitalize + (if helm-grep-in-recurse + (helm-grep-command t) + (helm-grep-command))))) + non-essential) + ;; Start grep process. + (helm-log "Starting Grep process in directory `%s'" default-directory) + (helm-log "Command line used was:\n\n%s" + (concat ">>> " (propertize cmd-line 'face 'helm-grep-cmd-line) "\n\n")) + (prog1 ; This function should return the process first. + (start-file-process-shell-command + proc-name helm-buffer cmd-line) + ;; Init sentinel. + (set-process-sentinel + (get-buffer-process helm-buffer) + (lambda (process event) + (let* ((err (process-exit-status process)) + (noresult (= err 1))) + (unless (and err (> err 0)) + (helm-process-deferred-sentinel-hook + process event (helm-default-directory))) + (cond ((and noresult + ;; This is a workaround for zgrep + ;; that exit with code 1 + ;; after a certain amount of results. + (with-helm-buffer (helm-empty-buffer-p))) + (with-helm-buffer + (insert (concat "* Exit with code 1, no result found," + " command line was:\n\n " + (propertize helm-grep-last-cmd-line + 'face 'helm-grep-cmd-line))) + (setq mode-line-format + `(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format + "[%s process finished - (no results)] " + ,proc-name) + 'face 'helm-grep-finish)))))) + ((or (string= event "finished\n") + (and noresult + ;; This is a workaround for zgrep + ;; that exit with code 1 + ;; after a certain amount of results. + (with-helm-buffer (not (helm-empty-buffer-p))))) + (helm-log "%s process finished with %s results in %fs" + proc-name + (helm-get-candidate-number) + (- (float-time) start-time)) + (helm-maybe-show-help-echo) + (with-helm-window + (setq mode-line-format + `(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format + "[%s process finished in %.2fs - (%s results)] " + ,proc-name + ,(- (float-time) start-time) + (helm-get-candidate-number)) + 'face 'helm-grep-finish)))) + (force-mode-line-update) + (when (and helm-allow-mouse helm-selection-point) + (helm--bind-mouse-for-selection helm-selection-point)))) + ;; Catch error output in log. + (t (helm-log + "Error: %s %s" + proc-name + (replace-regexp-in-string "\n" "" event)))))))))) + +(defun helm-grep-collect-candidates () + (let ((cmd-line (helm-grep--prepare-cmd-line + helm-grep-last-targets + helm-grep-include-files + helm-grep-use-zgrep))) + (set (make-local-variable 'helm-grep-last-cmd-line) cmd-line) + (funcall helm-grep-default-function cmd-line))) + + +;;; Actions +;; +;; +(defun helm-grep-action (candidate &optional where) + "Define a default action for `helm-do-grep-1' on CANDIDATE. +WHERE can be `other-window' or `other-frame'." + (let* ((split (helm-grep-split-line candidate)) + (split-pat (helm-mm-split-pattern helm-input)) + (lineno (string-to-number (nth 1 split))) + (loc-fname (or (with-current-buffer + (if (eq major-mode 'helm-grep-mode) + (current-buffer) + helm-buffer) + (get-text-property (point-at-bol) + 'helm-grep-fname)) + (car split))) + (tramp-fname (file-remote-p (or helm-ff-default-directory + default-directory))) + (fname (if tramp-fname + (concat tramp-fname loc-fname) + loc-fname))) + (helm-log "helm-grep-action fname: %s" fname ) + (cl-case where + (other-window (helm-window-show-buffers + (list (find-file-noselect fname)) t)) + (other-frame (find-file-other-frame fname)) + (grep (helm-grep-save-results-1)) + (pdf (if helm-pdfgrep-default-read-command + (helm-pdfgrep-action-1 split lineno (car split)) + (find-file (car split)) + (if (derived-mode-p 'pdf-view-mode) + (pdf-view-goto-page lineno) + (doc-view-goto-page lineno)))) + (t (find-file fname))) + (unless (or (eq where 'grep) (eq where 'pdf)) + (helm-goto-line lineno) + ;; Move point to the nearest matching regexp from bol. + (cl-loop for reg in split-pat + when (save-excursion + (condition-case _err + (if helm-migemo-mode + (helm-mm-migemo-forward reg (point-at-eol) t) + (re-search-forward reg (point-at-eol) t)) + (invalid-regexp nil))) + collect (match-beginning 0) into pos-ls + finally (when pos-ls (goto-char (apply #'min pos-ls)))) + ;; Save history + (unless (or helm-in-persistent-action + (eq major-mode 'helm-grep-mode) + (string= helm-pattern "")) + (setq helm-grep-history + (cons helm-pattern + (delete helm-pattern helm-grep-history))) + (when (> (length helm-grep-history) + helm-grep-max-length-history) + (setq helm-grep-history + (delete (car (last helm-grep-history)) + helm-grep-history))))))) + +(defun helm-grep-persistent-action (candidate) + "Persistent action for `helm-do-grep-1'. +With a prefix arg record CANDIDATE in `mark-ring'." + (helm-grep-action candidate) + (helm-highlight-current-line)) + +(defun helm-grep-other-window (candidate) + "Jump to result in other window from helm grep." + (helm-grep-action candidate 'other-window)) + +(defun helm-grep-other-frame (candidate) + "Jump to result in other frame from helm grep." + (helm-grep-action candidate 'other-frame)) + +(defun helm-goto-next-or-prec-file (n) + "Go to next or precedent candidate file in helm grep/etags buffers. +If N is positive go forward otherwise go backward." + (let* ((allow-mode (or (eq major-mode 'helm-grep-mode) + (eq major-mode 'helm-moccur-mode) + (eq major-mode 'helm-occur-mode))) + (sel (if allow-mode + (buffer-substring (point-at-bol) (point-at-eol)) + (helm-get-selection nil t))) + (current-line-list (helm-grep-split-line sel)) + (current-fname (nth 0 current-line-list)) + (bob-or-eof (if (eq n 1) 'eobp 'bobp)) + (mark-maybe (lambda () + (if allow-mode + (ignore) + (helm-mark-current-line))))) + (catch 'break + (while (not (funcall bob-or-eof)) + (forward-line n) ; Go forward or backward depending of n value. + ;; Exit when current-fname is not matched or in `helm-grep-mode' + ;; the line is not a grep line i.e 'fname:num:tag'. + (setq sel (buffer-substring (point-at-bol) (point-at-eol))) + (when helm-allow-mouse + (helm--mouse-reset-selection-help-echo)) + (unless (or (string= current-fname + (car (helm-grep-split-line sel))) + (and (eq major-mode 'helm-grep-mode) + (not (get-text-property (point-at-bol) 'helm-grep-fname)))) + (funcall mark-maybe) + (throw 'break nil)))) + (cond ((and (> n 0) (eobp)) + (re-search-backward ".") + (forward-line 0) + (funcall mark-maybe)) + ((and (< n 0) (bobp)) + (helm-aif (next-single-property-change (point-at-bol) 'helm-grep-fname) + (goto-char it) + (forward-line 1)) + (funcall mark-maybe))) + (unless allow-mode + (helm-follow-execute-persistent-action-maybe) + (helm-log-run-hook 'helm-move-selection-after-hook)))) + +;;;###autoload +(defun helm-goto-precedent-file () + "Go to previous file in Helm grep/etags buffers." + (interactive) + (with-helm-alive-p + (with-helm-window + (helm-goto-next-or-prec-file -1)))) +(put 'helm-goto-precedent-file 'helm-only t) + +;;;###autoload +(defun helm-goto-next-file () + "Go to previous file in Helm grep/etags buffers." + (interactive) + (with-helm-window + (helm-goto-next-or-prec-file 1))) + +(defun helm-grep-run-default-action () + "Run grep default action from `helm-do-grep-1'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-grep-action))) +(put 'helm-grep-run-default-action 'helm-only t) + +(defun helm-grep-run-other-window-action () + "Run grep goto other window action from `helm-do-grep-1'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-grep-other-window))) +(put 'helm-grep-run-other-window-action 'helm-only t) + +(defun helm-grep-run-other-frame-action () + "Run grep goto other frame action from `helm-do-grep-1'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-grep-other-frame))) +(put 'helm-grep-run-other-frame-action 'helm-only t) + +(defun helm-grep-run-save-buffer () + "Run grep save results action from `helm-do-grep-1'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-grep-save-results))) +(put 'helm-grep-run-save-buffer 'helm-only t) + +(defun helm-grep-quit-an-find-file-fn (source) + (let* ((sel (helm-get-selection nil nil source)) + (grep-line (and (stringp sel) + (helm-grep-split-line sel)))) + (if (and grep-line (file-exists-p (car grep-line))) + (expand-file-name (car grep-line)) + default-directory))) + +;;; helm-grep-mode +;; +;; +(defun helm-grep-save-results (candidate) + (helm-grep-action candidate 'grep)) + +(defvar helm-grep-mode-use-pcre nil) +(defun helm-grep-save-results-1 () + "Save Helm grep result in a `helm-grep-mode' buffer." + (let* ((buf "*hgrep*") + new-buf + (pattern (with-helm-buffer helm-input-local)) + (src (helm-get-current-source)) + (src-name (assoc-default 'name src))) + (when (get-buffer buf) + (if helm-grep-save-buffer-name-no-confirm + (setq new-buf (format "*hgrep|%s|-%s" pattern + (format-time-string "%H-%M-%S*"))) + (setq new-buf (helm-read-string "GrepBufferName: " buf)) + (cl-loop for b in (helm-buffer-list) + when (and (string= new-buf b) + (not (y-or-n-p + (format "Buffer `%s' already exists overwrite? " + new-buf)))) + do (setq new-buf (helm-read-string "GrepBufferName: " "*hgrep ")))) + (setq buf new-buf)) + (with-current-buffer (get-buffer-create buf) + (setq default-directory (or helm-ff-default-directory + (helm-default-directory) + default-directory)) + (setq-local helm-grep-mode-use-pcre (helm-get-attr 'pcre src)) + (setq buffer-read-only t) + (let ((inhibit-read-only t) + (map (make-sparse-keymap))) + (erase-buffer) + (insert "-*- mode: helm-grep -*-\n\n" + (format "%s Results for `%s':\n\n" src-name pattern)) + (save-excursion + (insert (with-current-buffer helm-buffer + (goto-char (point-min)) (forward-line 1) + (buffer-substring (point) (point-max))))) + (save-excursion + (while (not (eobp)) + (add-text-properties (point-at-bol) (point-at-eol) + `(keymap ,map + help-echo ,(concat + (get-text-property + (point) 'helm-grep-fname) + "\nmouse-1: set point\nmouse-2: jump to selection") + mouse-face highlight)) + (define-key map [mouse-1] 'mouse-set-point) + (define-key map [mouse-2] 'helm-grep-mode-mouse-jump) + (define-key map [mouse-3] 'ignore) + (forward-line 1)))) + (helm-grep-mode)) + (pop-to-buffer buf) + (setq next-error-last-buffer (get-buffer buf)) + (message "Helm %s Results saved in `%s' buffer" src-name buf))) + +(defun helm-grep-mode-mouse-jump (event) + (interactive "e") + (let* ((window (posn-window (event-end event))) + (pos (posn-point (event-end event)))) + (with-selected-window window + (when (eq major-mode 'helm-grep-mode) + (goto-char pos) + (helm-grep-mode-jump))))) +(put 'helm-grep-mode-mouse-jump 'helm-only t) + +(defun helm-grep-next-error (&optional argp reset) + "Goto ARGP position from a `helm-grep-mode' buffer. +RESET non-nil means rewind to the first match. +This is the `next-error-function' for `helm-grep-mode'." + (interactive "p") + (goto-char (cond (reset (point-min)) + ((and (< argp 0) helm-current-error) + (line-beginning-position)) + ((and (> argp 0) helm-current-error) + (line-end-position)) + ((point)))) + (let ((fun (if (> argp 0) + #'next-single-property-change + #'previous-single-property-change))) + (helm-aif (funcall fun (point) 'helm-grep-fname) + (progn + (goto-char it) + ;; `helm-current-error' is set in + ;; `helm-grep-mode-jump'. + (helm-grep-mode-jump)) + (user-error "No more matches")))) +(put 'helm-grep-next-error 'helm-only t) + +;;;###autoload +(defun helm-revert-next-error-last-buffer () + "Revert last `next-error' buffer from `current-buffer'. + +Accept to revert only `helm-grep-mode' or `helm-occur-mode' buffers. +Use this when you want to revert the `next-error' buffer after +modifications in `current-buffer'." + (interactive) + (let ((buffer (next-error-find-buffer)) + (linum (line-number-at-pos)) + (bufname (buffer-name))) + (if buffer + (with-current-buffer buffer + (helm-aif (memq major-mode '(helm-grep-mode helm-occur-mode)) + (progn (revert-buffer) + ;; helm-occur-mode revert fn is synchronous so + ;; reajust from here (it is done with + ;; helm-grep-mode in its sentinel). + (when (eq (car it) 'helm-occur-mode) + (helm-grep-goto-closest-from-linum linum bufname))) + (error "No suitable buffer to revert found"))) + (error "No suitable buffer to revert found")))) + +(define-derived-mode helm-grep-mode + special-mode "helm-grep" + "Major mode to provide actions in helm grep saved buffer. + +Special commands: +\\{helm-grep-mode-map}" + (set (make-local-variable 'helm-grep-last-cmd-line) + (with-helm-buffer helm-grep-last-cmd-line)) + (set (make-local-variable 'revert-buffer-function) + #'helm-grep-mode--revert-buffer-function) + (set (make-local-variable 'next-error-function) + #'helm-grep-next-error) + (set (make-local-variable 'helm-current-error) nil)) +(put 'helm-grep-mode 'helm-only t) + +(defun helm-grep-mode--revert-buffer-function (&optional _ignore-auto _noconfirm) + (goto-char (point-min)) + (when (re-search-forward helm-grep-split-line-regexp nil t) (forward-line 0)) + (let ((inhibit-read-only t)) + (delete-region (point) (point-max))) + (message "Reverting buffer...") + (let ((process-connection-type + ;; Git needs a nil value otherwise it tries to use a pager. + (null (string-match-p "\\`git" helm-grep-last-cmd-line)))) + (set-process-sentinel + (start-file-process-shell-command + "hgrep" (generate-new-buffer "*hgrep revert*") helm-grep-last-cmd-line) + 'helm-grep-mode--sentinel))) + +(defun helm-grep-mode--sentinel (process event) + (when (string= event "finished\n") + (with-current-buffer (if (eq major-mode 'helm-grep-mode) + (current-buffer) + (next-error-find-buffer)) + (let ((inhibit-read-only t)) + (save-excursion + (cl-loop for l in (with-current-buffer (process-buffer process) + (prog1 (split-string (buffer-string) "\n") + (kill-buffer))) + for line = (if (string-match-p helm--ansi-color-regexp l) + (ansi-color-apply l) l) + when (string-match helm-grep-split-line-regexp line) + do (insert (propertize + (car (helm-grep-filter-one-by-one + line helm-grep-mode-use-pcre)) + ;; needed for wgrep. + 'helm-realvalue line) + "\n")))) + (when (fboundp 'wgrep-cleanup-overlays) + (wgrep-cleanup-overlays (point-min) (point-max)))) + (unless (eq major-mode 'helm-grep-mode) + (let ((bufname (buffer-name)) + (linum (line-number-at-pos))) + (with-current-buffer (next-error-find-buffer) + (helm-grep-goto-closest-from-linum linum bufname)))) + (message "Reverting buffer done") + (when executing-kbd-macro (sit-for 1)))) + +(defun helm-grep-goto-closest-from-linum (linum bufname) + (goto-char (point-min)) + (catch 'break + (while (re-search-forward (format "^%s:\\([0-9]+\\):" (regexp-quote bufname)) nil t) + (let ((numline (string-to-number (match-string 1)))) + (when (< (- linum numline) 0) + (forward-line -1) + (throw 'break nil)))))) + +(defun helm-gm-next-file () + (interactive) + (helm-goto-next-or-prec-file 1)) + +(defun helm-gm-precedent-file () + (interactive) + (helm-goto-next-or-prec-file -1)) + +(defun helm-grep-mode-jump () + (interactive) + (setq next-error-last-buffer (current-buffer)) + (setq-local helm-current-error (point-marker)) + (helm-grep-action + (buffer-substring (point-at-bol) (point-at-eol))) + (helm-match-line-cleanup-pulse)) + +(defun helm-grep-mode-jump-other-window-1 (arg) + (condition-case nil + (progn + (when (or (eq last-command 'helm-grep-mode-jump-other-window-forward) + (eq last-command 'helm-grep-mode-jump-other-window-backward)) + (forward-line arg)) + (save-selected-window + (helm-grep-action (buffer-substring (point-at-bol) (point-at-eol)) + 'other-window) + (helm-match-line-cleanup-pulse) + (recenter))) + (error nil))) + +(defun helm-grep-mode-jump-other-window-forward (arg) + (interactive "p") + (helm-grep-mode-jump-other-window-1 arg)) + +(defun helm-grep-mode-jump-other-window-backward (arg) + (interactive "p") + (helm-grep-mode-jump-other-window-1 (- arg))) + +(defun helm-grep-mode-jump-other-window () + (interactive) + (setq next-error-last-buffer (current-buffer)) + (setq-local helm-current-error (point-marker)) + (let ((candidate (buffer-substring (point-at-bol) (point-at-eol)))) + (condition-case nil + (progn (helm-grep-action candidate 'other-window) + (helm-match-line-cleanup-pulse)) + (error nil)))) + + +;;; ack-grep types +;; +;; +(defun helm-grep-hack-types () + "Return a list of known ack-grep types." + (with-temp-buffer + ;; "--help-types" works with both 1.96 and 2.1+, while + ;; "--help types" works only with 1.96 Bug#422. + ;; `helm-grep-command' should return the ack executable + ;; when this function is used in the right context + ;; i.e After checking is we are using ack-grep with + ;; `helm-grep-use-ack-p'. + (call-process (helm-grep-command t) nil t nil "--help-types") + (goto-char (point-min)) + (cl-loop while (re-search-forward "^ +\\([^. ]+\\) +\\(.*\\)" nil t) + collect (cons (concat (match-string 1) + " [" (match-string 2) "]") + (match-string 1)) + collect (cons (concat "no" (match-string 1) + " [" (match-string 2) "]") + (concat "no" (match-string 1)))))) + +(defun helm-grep-ack-types-transformer (candidates _source) + (cl-loop for i in candidates + if (stringp i) + collect (rassoc i helm-grep-ack-types-cache) + else + collect i)) + +(defvar helm-grep-ack-types-cache nil) +(defun helm-grep-read-ack-type () + "Select types for the \\='--type' argument of ack-grep." + (require 'helm-mode) + (require 'helm-adaptive) + (setq helm-grep-ack-types-cache (helm-grep-hack-types)) + (let ((types (helm-comp-read + "Types: " helm-grep-ack-types-cache + :name "*Ack-grep types*" + :marked-candidates t + :must-match t + :fc-transformer '(helm-adaptive-sort + helm-grep-ack-types-transformer) + :buffer "*helm ack-types*"))) + (mapconcat (lambda (type) (concat "--type=" type)) types " "))) + + +;;; grep extensions +;; +;; +(defun helm-grep-guess-extensions (files) + "Try to guess file extensions in FILES list when using grep recurse. +These extensions will be added to command line with --include arg +of grep." + (cl-loop with ext-list = (list helm-grep-preferred-ext "*") + with lst = (if (file-directory-p (car files)) + (directory-files + (car files) nil + directory-files-no-dot-files-regexp) + files) + for i in lst + for ext = (file-name-extension i 'dot) + for glob = (and ext (not (string= ext "")) + (concat "*" ext)) + unless (or (not glob) + (and glob-list (member glob glob-list)) + (and glob-list (member glob ext-list)) + (and glob-list (member glob helm-grep-ignored-files))) + collect glob into glob-list + finally return (delq nil (append ext-list glob-list)))) + +(defun helm-grep-get-file-extensions (files) + "Try to return a list of file extensions to pass to \\='--include' arg of grep." + (require 'helm-adaptive) + (let* ((all-exts (helm-grep-guess-extensions + (mapcar 'expand-file-name files))) + (extensions (helm-comp-read "Search Only in: " all-exts + :marked-candidates t + :fc-transformer 'helm-adaptive-sort + :buffer "*helm grep exts*" + :name "*helm grep extensions*"))) + (when (listp extensions) ; Otherwise it is empty string returned by C-RET. + ;; If extensions is a list of one string containing spaces, + ;; assume user entered more than one glob separated by space(s) and + ;; split this string to pass it later to mapconcat. + ;; e.g '("*.el *.py") + (cl-loop for i in extensions + append (split-string-and-unquote i " "))))) + + +;;; Set up source +;; +;; +(defvar helm-grep-before-init-hook nil + "Hook that runs before initialization of the Helm buffer.") + +(defvar helm-grep-after-init-hook nil + "Hook that runs after initialization of the Helm buffer.") + +(defclass helm-grep-class (helm-source-async) + ((candidates-process :initform 'helm-grep-collect-candidates) + (filtered-candidate-transformer :initform #'helm-grep-fc-transformer) + (keymap :initform 'helm-grep-map) + (pcre :initarg :pcre :initform nil + :documentation + " Backend is using pcre regexp engine when non-nil.") + (nohighlight :initform t) + (nomark :initform t) + (backend :initarg :backend + :initform nil + :documentation + " The grep backend that will be used. + It is actually used only as an internal flag + and doesn't set the backend by itself. + You probably don't want to modify this.") + (candidate-number-limit :initform 9999) + (help-message :initform 'helm-grep-help-message) + (history :initform 'helm-grep-history) + (action :initform 'helm-grep-actions) + (persistent-action :initform 'helm-grep-persistent-action) + (persistent-help :initform "Jump to line (`C-u' Record in mark ring)") + (requires-pattern :initform 2) + (before-init-hook :initform 'helm-grep-before-init-hook) + (after-init-hook :initform 'helm-grep-after-init-hook) + (find-file-target :initform #'helm-grep-quit-an-find-file-fn) + (group :initform 'helm-grep))) + +(defvar helm-source-grep nil) + +(cl-defmethod helm--setup-source ((source helm-grep-class)) + (cl-call-next-method) + (helm-aif (and helm-follow-mode-persistent + (if (eq (slot-value source 'backend) 'git) + helm-source-grep-git + helm-source-grep)) + (setf (slot-value source 'follow) + (assoc-default 'follow it)))) + +(cl-defun helm-do-grep-1 (targets &optional recurse backend exts + default-input input (source 'helm-source-grep)) + "Launch helm using backend BACKEND on a list of TARGETS files. + +When RECURSE is given and BACKEND is \\='grep' use -r option of +BACKEND and prompt user for EXTS to set the --include args of +BACKEND. +Interactively you can give more than one arg separated by space +at prompt. +E.g.: + $Pattern: *.el *.py *.tex + +From Lisp use the EXTS argument as a list of extensions as above. +If you are using ack-grep, you will be prompted for --type +instead and EXTS will be ignored. If prompt is empty +`helm-grep-ignored-files' are added to --exclude. + +Argument DEFAULT-INPUT is use as `default' arg of `helm' and +INPUT is used as `input' arg of `helm'. See `helm' docstring. + +Arg BACKEND when non-nil specifies which backend to use. +It is used actually to specify \\='zgrep' or \\='git'. +When BACKEND \\='zgrep' is used don't prompt for a choice in +recurse, and ignore EXTS, search being made recursively on files +matching `helm-zgrep-file-extension-regexp' only." + (let* (non-essential + (ack-rec-p (helm-grep-use-ack-p :where 'recursive)) + (exts (and recurse + ;; [FIXME] I could handle this from helm-walk-directory. + (not (eq backend 'zgrep)) ; zgrep doesn't handle -r opt. + (not ack-rec-p) + (or exts (helm-grep-get-file-extensions targets)))) + (include-files + (and exts + (mapconcat (lambda (x) + (concat "--include=" + (shell-quote-argument x))) + (if (> (length exts) 1) + (remove "*" exts) + exts) " "))) + (types (and (not include-files) + (not (eq backend 'zgrep)) + recurse + ack-rec-p + ;; When %e format spec is not specified + ;; ignore types and do not prompt for choice. + (string-match "%e" helm-grep-default-command) + (helm-grep-read-ack-type))) + (src-name (capitalize (helm-grep-command recurse backend))) + (com (cond ((eq backend 'zgrep) helm-default-zgrep-command) + ((eq backend 'git) helm-grep-git-grep-command) + (recurse helm-grep-default-recurse-command) + ;; When resuming, the local value of + ;; `helm-grep-default-command' is used, only git-grep + ;; should need this. + (t helm-grep-default-command)))) + ;; When called as action from an other source e.g *-find-files + ;; we have to kill action buffer. + (when (get-buffer helm-action-buffer) + (kill-buffer helm-action-buffer)) + ;; If `helm-find-files' haven't already started, + ;; give a default value to `helm-ff-default-directory' + ;; and set locally `default-directory' to this value . See below [1]. + (unless helm-ff-default-directory + (setq helm-ff-default-directory default-directory)) + ;; We need to store these vars locally + ;; to pass infos later to `helm-resume'. + (helm-set-local-variable + 'helm-zgrep-recurse-flag (and recurse (eq backend 'zgrep)) + 'helm-grep-last-targets targets + 'helm-grep-include-files (or include-files types) + 'helm-grep-in-recurse recurse + 'helm-grep-use-zgrep (eq backend 'zgrep) + 'helm-grep-default-command com + 'helm-input-idle-delay helm-grep-input-idle-delay + 'default-directory helm-ff-default-directory) ;; [1] + ;; Setup the source. + (set source (helm-make-source src-name 'helm-grep-class + :backend backend + :pcre (string-match-p "\\`ack" com))) + (helm + :sources source + :buffer (format "*helm %s*" (helm-grep-command recurse backend)) + :default default-input + :input input + :keymap helm-grep-map + :history 'helm-grep-history + :truncate-lines helm-grep-truncate-lines))) + + +;;; zgrep +;; +;; +(defun helm-ff-zgrep-1 (flist recursive) + (unwind-protect + (let* ((def-dir (or helm-ff-default-directory + default-directory)) + (only (if recursive + (or (gethash def-dir helm-rzgrep-cache) + (puthash + def-dir + (helm-walk-directory + def-dir + :directories nil + :path 'full + :match helm-zgrep-file-extension-regexp) + helm-rzgrep-cache)) + flist))) + (helm-do-grep-1 only recursive 'zgrep)) + (setq helm-zgrep-recurse-flag nil))) + + +;;; transformers +;; +;; +(defun helm-grep-split-line (line) + "Split a grep output line." + ;; The output of grep may send a truncated line in this chunk, + ;; so don't split until grep line is valid, that is + ;; once the second part of the line comes with next chunk + ;; send by process. + (when (string-match helm-grep-split-line-regexp line) + ;; Don't use split-string because buffer/file name or string + ;; may contain a ":". + (cl-loop for n from 1 to 3 collect (match-string n line)))) + +(defun helm-grep--filter-candidate-1 (candidate &optional dir pcre) + (let* ((root (or dir (and helm-grep-default-directory-fn + (funcall helm-grep-default-directory-fn)))) + (ansi-p (string-match-p helm--ansi-color-regexp candidate)) + (line (if ansi-p (ansi-color-apply candidate) candidate)) + (split (helm-grep-split-line line)) + (fname (if (and root split) + ;; Filename should always be provided as a local + ;; path, if the root directory is remote, the + ;; tramp prefix will be added before executing + ;; action, see `helm-grep-action' and Bug#2032. + (expand-file-name (car split) + (or (file-remote-p root 'localname) + root)) + (car-safe split))) + (lineno (nth 1 split)) + (str (nth 2 split)) + (display-fname (cl-ecase helm-grep-file-path-style + (basename (and fname (file-name-nondirectory fname))) + (absolute fname) + (relative (and fname root + (file-relative-name fname root)))))) + (if (and display-fname lineno str) + (cons (concat (propertize display-fname + 'face 'helm-grep-file + 'help-echo (abbreviate-file-name fname) + 'helm-grep-fname fname) + ":" + (propertize lineno 'face 'helm-grep-lineno) + ":" + (if ansi-p str (helm-grep-highlight-match str pcre))) + line) + ""))) + +(defun helm-grep-filter-one-by-one (candidate &optional pcre) + "`filter-one-by-one' transformer function for `helm-do-grep-1'." + (let ((helm-grep-default-directory-fn + (or helm-grep-default-directory-fn + (lambda () (or helm-ff-default-directory + (and helm-alive-p + (helm-default-directory)) + default-directory))))) + (if (consp candidate) + ;; Already computed do nothing (default as input). + candidate + (and (stringp candidate) + (helm-grep--filter-candidate-1 candidate nil pcre))))) + +(defun helm-grep-fc-transformer (candidates source) + (let ((helm-grep-default-directory-fn + (or helm-grep-default-directory-fn + (lambda () (or helm-ff-default-directory + (and (null (eq major-mode 'helm-grep-mode)) + (helm-default-directory)) + default-directory)))) + (pcre (helm-get-attr 'pcre source))) + (cl-loop for c in candidates + collect (helm-grep--filter-candidate-1 c nil pcre)))) + +(defun helm-grep-highlight-match (str &optional pcre) + "Highlight in string STR all occurences matching `helm-pattern'." + (let (beg end) + (condition-case-unless-debug nil + (with-temp-buffer + (insert (propertize str 'read-only nil)) ; Fix bug#1176 + (goto-char (point-min)) + (cl-loop for reg in + (cl-loop for r in (helm-mm-split-pattern + helm-input) + unless (string-match "\\`!" r) + collect + (helm-aif (and helm-migemo-mode + (assoc r helm-mm--previous-migemo-info)) + (cdr it) r)) + do + (while (and (re-search-forward + (if pcre + (helm--translate-pcre-to-elisp reg) + reg) + nil t) + (> (- (setq end (match-end 0)) + (setq beg (match-beginning 0))) + 0)) + (helm-add-face-text-properties beg end 'helm-grep-match)) + do (goto-char (point-min))) + (buffer-string)) + (error nil)))) + + +;;; Grep from buffer list +;; +;; +(defun helm-grep-buffers-1 (candidate &optional zgrep) + "Run grep on all file buffers or CANDIDATE if it is a file buffer. +If one of selected buffers is not a file buffer, it is ignored +and grep will run on all others file-buffers. +If only one candidate is selected and it is not a file buffer, +switch to this buffer and run `helm-occur'. +If a prefix arg is given run grep on all buffers ignoring +non-file buffers." + (let* ((prefarg (or current-prefix-arg helm-current-prefix-arg)) + (helm-ff-default-directory + (if (and helm-ff-default-directory + (file-remote-p helm-ff-default-directory)) + default-directory + helm-ff-default-directory)) + (cands (if prefarg + (buffer-list) + (helm-marked-candidates))) + (win-conf (current-window-configuration)) + ;; Non--fname and remote buffers are ignored. + (bufs (cl-loop for buf in cands + for fname = (buffer-file-name (get-buffer buf)) + when (and fname (not (file-remote-p fname))) + collect (expand-file-name fname)))) + (if bufs + (if zgrep + (helm-do-grep-1 bufs nil 'zgrep) + (helm-do-grep-1 bufs)) + ;; bufs is empty, thats mean we have only CANDIDATE + ;; and it is not a buffer-filename, fallback to occur. + (switch-to-buffer candidate) + (when (get-buffer helm-action-buffer) + (kill-buffer helm-action-buffer)) + (helm-occur) + (when (eq helm-exit-status 1) + (set-window-configuration win-conf))))) + +(defun helm-grep-buffers (candidate) + "Action to grep buffers." + (helm-grep-buffers-1 candidate)) + +(defun helm-zgrep-buffers (candidate) + "Action to zgrep buffers." + (helm-grep-buffers-1 candidate 'zgrep)) + + +;;; Helm interface for pdfgrep +;; pdfgrep program +;; and a pdf-reader (e.g xpdf) are needed. +;; +(defvar helm-pdfgrep-default-function 'helm-pdfgrep-init) +(defun helm-pdfgrep-init (only-files &optional recurse) + "Start an asynchronous pdfgrep process in ONLY-FILES list." + (let* ((default-directory (or helm-ff-default-directory + default-directory)) + (fnargs (helm-grep-prepare-candidates + (if (file-remote-p default-directory) + (mapcar (lambda (x) + (file-remote-p x 'localname)) + only-files) + only-files) + default-directory)) + (cmd-line (format (if recurse + helm-pdfgrep-default-recurse-command + helm-pdfgrep-default-command) + helm-pattern + fnargs)) + process-connection-type) + ;; Start pdf grep process. + (helm-log "Starting Pdf Grep process in directory `%s'" default-directory) + (helm-log "Command line used was:\n\n%s" + (concat ">>> " (propertize cmd-line 'face 'helm-grep-cmd-line) "\n\n")) + (prog1 + (start-file-process-shell-command + "pdfgrep" helm-buffer cmd-line) + (message nil) + (set-process-sentinel + (get-buffer-process helm-buffer) + (lambda (_process event) + (if (string= event "finished\n") + (with-helm-window + (setq mode-line-format + '(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format "[Pdfgrep Process Finish - %s result(s)] " + (max (1- (count-lines + (point-min) (point-max))) 0)) + 'face 'helm-grep-finish)))) + (force-mode-line-update) + (when helm-allow-mouse + (helm--bind-mouse-for-selection helm-selection-point))) + (helm-log "Error: Pdf grep %s" + (replace-regexp-in-string "\n" "" event)))))))) + +(defun helm-do-pdfgrep-1 (only &optional recurse) + "Launch pdfgrep with a list of ONLY files." + (unless (executable-find "pdfgrep") + (error "Error: No such program `pdfgrep'.")) + (let (helm-grep-in-recurse) ; recursion is implemented differently in *pdfgrep. + ;; When called as action from an other source e.g *-find-files + ;; we have to kill action buffer. + (when (get-buffer helm-action-buffer) + (kill-buffer helm-action-buffer)) + (setq helm-pdfgrep-targets only) + (helm + :sources (helm-build-async-source "PdfGrep" + :init (lambda () + ;; If `helm-find-files' haven't already started, + ;; give a default value to `helm-ff-default-directory'. + (setq helm-ff-default-directory (or helm-ff-default-directory + default-directory))) + :candidates-process (lambda () + (funcall helm-pdfgrep-default-function + helm-pdfgrep-targets recurse)) + :nohighlight t + :nomark t + :filter-one-by-one #'helm-grep-filter-one-by-one + :candidate-number-limit 9999 + :history 'helm-grep-history + :keymap helm-pdfgrep-map + :help-message 'helm-pdfgrep-help-message + :action #'helm-pdfgrep-action + :persistent-help "Jump to PDF Page" + :requires-pattern 2) + :buffer "*helm pdfgrep*" + :history 'helm-grep-history))) + +(defun helm-pdfgrep-action (candidate) + (helm-grep-action candidate 'pdf)) + +(defun helm-pdfgrep-action-1 (_split pageno fname) + (save-selected-window + (start-file-process-shell-command + "pdf-reader" nil + (format-spec helm-pdfgrep-default-read-command + (list (cons ?f fname) (cons ?p pageno)))))) + +;;; AG - PT - RG +;; +;; https://github.com/ggreer/the_silver_searcher +;; https://github.com/monochromegane/the_platinum_searcher +;; https://github.com/BurntSushi/ripgrep + +(defun helm-grep--ag-command () + (car (helm-remove-if-match + "\\`[A-Z]*=" (split-string helm-grep-ag-command)))) + +(defun helm-grep-ag-get-types () + "Returns a list of AG types if available with AG version. +See AG option \"--list-file-types\" +Ripgrep (rg) types are also supported if this backend is used." + (with-temp-buffer + (let* ((com (helm-grep--ag-command)) + (ripgrep (string= com "rg")) + (regex (if ripgrep "^\\(.*\\):" "^ *\\(--[a-z]*\\)")) + (prefix (if ripgrep "-t " ""))) + (when (equal (call-process com + nil t nil + (if ripgrep + "--type-list" "--list-file-types")) 0) + (goto-char (point-min)) + (cl-loop while (re-search-forward regex nil t) + for type = (match-string 1) + collect (cons type (concat prefix type))))))) + +(defun helm-grep-ag-prepare-cmd-line (pattern directory &optional type) + "Prepare AG command line to search PATTERN in DIRECTORY. +When TYPE is specified it is one of what `helm-grep-ag-get-types' +returns if available with current AG version." + (let* ((patterns (helm-mm-split-pattern pattern t)) + (pipe-switches (mapconcat 'identity helm-grep-ag-pipe-cmd-switches " ")) + (pipe-cmd (helm-acase (helm-grep--ag-command) + (("ag" "pt") + (format "%s -S --color%s" it (concat " " pipe-switches))) + ("rg" (format "rg -N -S --color=%s%s" + (when (string-match "--color=\\([a-z]+\\) " + helm-grep-ag-command) + (match-string 1 helm-grep-ag-command)) + (concat " " pipe-switches))))) + (cmd (format helm-grep-ag-command + (mapconcat 'identity type " ") + (shell-quote-argument (car patterns)) + (shell-quote-argument directory)))) + (helm-aif (cdr patterns) + (concat cmd (cl-loop for p in it concat + (format " | %s -- %s" + pipe-cmd (shell-quote-argument p)))) + cmd))) + +(defun helm-grep-ag-init (directory &optional type) + "Start AG process in DIRECTORY maybe searching only files of type TYPE." + (let ((default-directory (or helm-ff-default-directory + (helm-default-directory) + default-directory)) + (cmd-line (helm-grep-ag-prepare-cmd-line + helm-pattern (or (file-remote-p directory 'localname) + directory) + type)) + (start-time (float-time)) + (proc-name (helm-grep--ag-command))) + (set (make-local-variable 'helm-grep-last-cmd-line) cmd-line) + (helm-log "Starting %s process in directory `%s'" + proc-name directory) + (helm-log "Command line used was:\n\n%s" + (concat ">>> " cmd-line "\n\n")) + (prog1 + (start-file-process-shell-command + proc-name helm-buffer cmd-line) + (set-process-sentinel + (get-buffer-process helm-buffer) + (lambda (process event) + (let* ((err (process-exit-status process)) + (noresult (= err 1))) + (cond (noresult + (with-helm-buffer + (insert (concat "* Exit with code 1, no result found," + " command line was:\n\n " + (propertize helm-grep-last-cmd-line + 'face 'helm-grep-cmd-line))) + (setq mode-line-format + `(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format + "[%s process finished - (no results)] " + ,(upcase proc-name)) + 'face 'helm-grep-finish)))))) + ((string= event "finished\n") + (helm-log "%s process finished with %s results in %fs" + proc-name + (helm-get-candidate-number) + (- (float-time) start-time)) + (helm-maybe-show-help-echo) + (with-helm-window + (setq mode-line-format + `(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format + "[%s process finished in %.2fs - (%s results)] " + ,(upcase proc-name) + ,(- (float-time) start-time) + (helm-get-candidate-number)) + 'face 'helm-grep-finish)))) + (force-mode-line-update) + (when helm-allow-mouse + (helm--bind-mouse-for-selection helm-selection-point)))) + (t (helm-log + "Error: %s %s" + proc-name + (replace-regexp-in-string "\n" "" event)))))))))) + +(defclass helm-grep-ag-class (helm-source-async) + ((nohighlight :initform t) + (pcre :initarg :pcre :initform t + :documentation + " Backend is using pcre regexp engine when non--nil.") + (keymap :initform 'helm-grep-map) + (history :initform 'helm-grep-ag-history) + (help-message :initform 'helm-grep-help-message) + (filtered-candidate-transformer :initform #'helm-grep-fc-transformer) + (persistent-action :initform 'helm-grep-persistent-action) + (persistent-help :initform "Jump to line (`C-u' Record in mark ring)") + (candidate-number-limit :initform 99999) + (requires-pattern :initform 2) + (nomark :initform t) + (action :initform 'helm-grep-actions) + (find-file-target :initform #'helm-grep-quit-an-find-file-fn) + (group :initform 'helm-grep))) + +(defvar helm-source-grep-ag nil) + +(cl-defmethod helm--setup-source ((source helm-grep-ag-class)) + (cl-call-next-method) + (helm-aif (and helm-follow-mode-persistent + helm-source-grep-ag + (assoc-default 'follow helm-source-grep-ag)) + (setf (slot-value source 'follow) it))) + +(defun helm-grep-ag-1 (directory &optional type input) + "Start helm ag in DIRECTORY maybe searching in files of type TYPE. +If INPUT is provided, use it as the search string." + (setq helm-source-grep-ag + (helm-make-source (upcase (helm-grep--ag-command)) 'helm-grep-ag-class + :header-name (lambda (name) + (format "%s [%s]" + name (abbreviate-file-name directory))) + :candidates-process + (lambda () (helm-grep-ag-init directory type)))) + (helm-set-local-variable 'helm-input-idle-delay helm-grep-input-idle-delay) + (helm :sources 'helm-source-grep-ag + :keymap helm-grep-map + :history 'helm-grep-ag-history + :input input + :truncate-lines helm-grep-truncate-lines + :buffer (format "*helm %s*" (helm-grep--ag-command)))) + +(defun helm-grep-ag (directory with-types) + "Start grep AG in DIRECTORY. +When WITH-TYPES is non-nil provide completion on AG types." + (require 'helm-adaptive) + (helm-grep-ag-1 directory + (helm-aif (and with-types + (helm-grep-ag-get-types)) + (helm-comp-read + "Ag type: " it + :must-match t + :marked-candidates t + :fc-transformer 'helm-adaptive-sort + :buffer "*helm ag types*")))) + +;;; Git grep +;; +;; +(defvar helm-source-grep-git nil) + +(defun helm-grep-git-1 (directory &optional all default input) + "Run git-grep on DIRECTORY. +If DIRECTORY is not inside or part of a git repo exit with error. +If optional arg ALL is non-nil grep the whole repo otherwise +start at DIRECTORY. +Arg DEFAULT is what you will have with `next-history-element', +arg INPUT is what you will have by default at prompt on startup." + (require 'vc) + (let* (helm-grep-default-recurse-command + ;; Expand filename of each candidate with the git root dir. + ;; The filename will be in the helm-grep-fname prop. + (helm-grep-default-directory-fn (lambda () + (vc-find-root directory ".git"))) + (helm-ff-default-directory (funcall helm-grep-default-directory-fn))) + (cl-assert helm-ff-default-directory nil "Not inside a Git repository") + (helm-do-grep-1 (if all '("") `(,(expand-file-name directory))) + nil 'git nil default input 'helm-source-grep-git))) + + +;;;###autoload +(defun helm-do-grep-ag (arg) + "Preconfigured `helm' for grepping with AG in `default-directory'. +With prefix arg prompt for type if available with your AG +version." + (interactive "P") + (require 'helm-files) + (helm-grep-ag (expand-file-name default-directory) arg)) + +;;;###autoload +(defun helm-grep-do-git-grep (arg) + "Preconfigured `helm' for git-grepping `default-directory'. +With a prefix arg ARG git-grep the whole repository." + (interactive "P") + (require 'helm-files) + (helm-grep-git-1 default-directory arg)) + + +(provide 'helm-grep) + +;;; helm-grep.el ends here diff --git a/code/elpa/helm-20220822.659/helm-help.el b/code/elpa/helm-20220822.659/helm-help.el new file mode 100644 index 0000000..5335318 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-help.el @@ -0,0 +1,2504 @@ +;;; helm-help.el --- Help messages for Helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'helm) + + +(defgroup helm-help nil + "Embedded help for `helm'." + :group 'helm) + +(defface helm-helper + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit helm-header)) + "Face for Helm help string in minibuffer." + :group 'helm-help) + +(defvar helm-help--string-list '(helm-help-message + helm-buffer-help-message + helm-ff-help-message + helm-read-file-name-help-message + helm-generic-file-help-message + helm-fd-help-message + helm-grep-help-message + helm-pdfgrep-help-message + helm-etags-help-message + helm-ucs-help-message + helm-bookmark-help-message + helm-esh-help-message + helm-buffers-ido-virtual-help-message + helm-moccur-help-message + helm-top-help-message + helm-el-package-help-message + helm-M-x-help-message + helm-imenu-help-message + helm-colors-help-message + helm-semantic-help-message + helm-kmacro-help-message + helm-kill-ring-help-message) + "A list of help messages (strings) used by `helm-documentation'.") + +(defvar helm-documentation-buffer-name "*helm documentation*") + +;;;###autoload +(defun helm-documentation () + "Preconfigured `helm' for Helm documentation. +With a prefix arg refresh the documentation. + +Find here the documentation of all documented sources." + (interactive) + (let ((buf (get-buffer-create helm-documentation-buffer-name))) + (switch-to-buffer buf) + (set-buffer buf) + (let ((inhibit-read-only t)) + (erase-buffer) + (save-excursion + (cl-loop for elm in helm-help--string-list + for str = (helm-interpret-value elm) + do (insert (substitute-command-keys str) "\n\n"))) + (org-mode)) + (setq buffer-read-only t) + (view-mode))) + + +;;; Local help messages. + +;;; `helm-buffer-list' help +;; +;; +(defvar helm-buffer-help-message + "* Helm Buffer + +** Tips + +*** Completion + +**** Major-mode + +You can enter a partial major-mode name (e.g. lisp, sh) to narrow down buffers. +To specify the major-mode, prefix it with \"*\" e.g. \"*lisp\". + +If you want to match all buffers but the ones with a specific major-mode +\(negation), prefix the major-mode with \"!\" e.g. \"*!lisp\". + +If you want to specify more than one major-mode, separate them with \",\", +e.g. \"*!lisp,!sh,!fun\" lists all buffers but the ones in lisp-mode, sh-mode +and fundamental-mode. + +Then enter a space followed by a pattern to narrow down to buffers matching this +pattern. + +**** Search inside buffers + +If you enter a space and a pattern prefixed by \"@\", Helm searches for text +matching this pattern *inside* the buffer (i.e. not in the name of the buffer). + +Negation are supported i.e. \"!\". + +When you specify more than one of such patterns, it will match +buffers with contents matching each of these patterns i.e. AND, +not OR. That means that if you specify \"@foo @bar\" the contents +of buffer will have to be matched by foo AND bar. If you specify +\"@foo @!bar\" it means the contents of the buffer have to be +matched by foo but NOT bar. + +If you enter a pattern prefixed with an escaped \"@\", Helm searches for a +buffer matching \"@pattern\" but does not search inside the buffer. + +**** Search by directory name + +If you prefix the pattern with \"/\", Helm matches over the directory names +of the buffers. + +This feature can be used to narrow down the search to one directory while +subsequent strings entered after a space match over the buffer name only. + +Note that negation is not supported for matching on buffer filename. + +Starting from Helm v1.6.8, you can specify more than one directory. + +**** Fuzzy matching + +`helm-buffers-fuzzy-matching' turns on fuzzy matching on buffer +names, but not on directory names or major modes. A pattern +starting with \"^\" disables fuzzy matching and matching is done +litteraly IOW do not use regexps (\"^\" or whatever special +regexp character) when you want to fuzzy match. + +**** Examples + +With the following pattern + + \"*lisp ^helm @moc\" + +Helm narrows down the list by selecting only the buffers that are in lisp mode, +start with \"helm\" and which content matches \"moc\". + +Without the \"@\" + + \"*lisp ^helm moc\" + +Helm looks for lisp mode buffers starting with \"helm\" and containing \"moc\" +in their name. + +With this other pattern + + \"*!lisp !helm\" + +Helm narrows down to buffers that are not in \"lisp\" mode and that do not match +\"helm\". + +With this last pattern + + /helm/ w3 + +Helm narrows down to buffers that are in any \"helm\" subdirectory and +matching \"w3\". + +*** Creating buffers + +When creating a new buffer, use `\\[universal-argument]' to choose a mode from a +list. This list is customizable, see `helm-buffers-favorite-modes'. + +*** Killing buffers + +You can kill buffers either one by one or all the marked buffers at once. + +One kill-buffer command leaves Helm while the other is persistent. Run the +persistent kill-buffer command either with the regular +`helm-execute-persistent-action' called with a prefix argument (`\\[universal-argument] \\\\[helm-execute-persistent-action]') +or with its specific command `helm-buffer-run-kill-persistent'. See the +bindings below. + +*** Switching to buffers + +To switch to a buffer, press RET, to switch to a buffer in another window, select this buffer +and press \\\\[helm-buffer-switch-other-window], when called with a prefix arg +the buffer will be displayed vertically in other window. +If you mark more than one buffer, the marked buffers will be displayed in different windows. + +*** Saving buffers + +If buffer is associated to a file and is modified, it is by default colorized in orange, +see [[Meaning of colors and prefixes for buffers][Meaning of colors and prefixes for buffers]]. +You can save these buffers with \\\\[helm-buffer-save-persistent]. +If you want to save all these buffers, you can mark them with \\[helm-buffers-mark-similar-buffers] +and save them with \\[helm-buffer-save-persistent]. You can also do this in one step with +\\[helm-buffer-run-save-some-buffers]. Note that you will not be asked for confirmation. + +*** Meaning of colors and prefixes for buffers + +Remote buffers are prefixed with '@'. +Red => Buffer's file was modified on disk by an external process. +Indianred2 => Buffer exists but its file has been deleted. +Orange => Buffer is modified and not saved to disk. +Italic => A non-file buffer. +Yellow => Tramp archive buffer. + +** Commands +\\ +|Keys|Description| +|-------------+----------| +|\\[helm-buffer-run-zgrep]|Grep Buffer(s) works as zgrep too (`\\[universal-argument]' to grep all buffers but non-file buffers). +|\\[helm-buffers-run-occur]|Multi-Occur buffer or marked buffers (`\\[universal-argument]' to toggle force-searching current-buffer). +|\\[helm-buffer-switch-other-window]|Switch to other window. +|\\[helm-buffer-switch-other-frame]|Switch to other frame. +|\\[helm-buffers-run-browse-project]|Browse project from buffer. +|\\[helm-buffer-run-query-replace-regexp]|Query-replace-regexp in marked buffers. +|\\[helm-buffer-run-query-replace]|Query-replace in marked buffers. +|\\[helm-buffer-run-ediff]|Ediff current buffer with candidate. With two marked buffers, ediff those buffers. +|\\[helm-buffer-run-ediff-merge]|Ediff-merge current buffer with candidate. With two marked buffers, ediff-merge those buffers. +|\\[helm-buffer-diff-persistent]|Toggle Diff-buffer with saved file without leaving Helm. +|\\[helm-buffer-revert-persistent]|Revert buffer without leaving Helm. +|\\[helm-buffer-save-persistent]|Save buffer without leaving Helm. +|\\[helm-buffer-run-save-some-buffers]|Save all unsaved buffers. +|\\[helm-buffer-run-kill-buffers]|Delete marked buffers and leave Helm. +|\\[helm-buffer-run-kill-persistent]|Delete buffer without leaving Helm. +|\\[helm-buffer-run-rename-buffer]|Rename buffer. +|\\[helm-toggle-all-marks]|Toggle all marks. +|\\[helm-mark-all]|Mark all. +|\\[helm-toggle-buffers-details]|Toggle details. +|\\[helm-buffers-toggle-show-hidden-buffers]|Show hidden buffers. +|\\[helm-buffers-mark-similar-buffers]|Mark all buffers of the same type (color) as current buffer.") + +;;; Find files help (`helm-find-files') +;; +;; +(defvar helm-ff-help-message + "* Helm Find Files + +** Tips + +*** Navigation summary + +For a better experience you can enable auto completion by setting +`helm-ff-auto-update-initial-value' to non-nil in your init file. It is not +enabled by default to not confuse new users. + +**** Navigate with arrow keys + +You can use and arrows to go down or up one level, to disable +this customize `helm-ff-lynx-style-map'. +Note that using `setq' will NOT work. + +**** Use `\\\\[helm-execute-persistent-action]' (persistent action) on a directory to go down one level + +On a symlinked directory a prefix argument expands to its true name. + +**** Use `\\\\[helm-find-files-up-one-level]' or `DEL' on a directory to go up one level + +***** `DEL' behavior + +`DEL' by default deletes char backward. + +But when `helm-ff-DEL-up-one-level-maybe' is non nil `DEL' behaves +differently depending on the contents of helm-pattern. It goes up one +level if the pattern is a directory ending with \"/\" or disables HFF +auto update and delete char backward if the pattern is a filename or +refers to a non existing path. Going up one level can be disabled +if necessary by deleting \"/\" at the end of the pattern using +\\\\[backward-char] and \\[helm-delete-minibuffer-contents]. + +Note that when deleting char backward, Helm takes care of +disabling update giving you the opportunity to edit your pattern for +e.g. renaming a file or creating a new file or directory. +When `helm-ff-auto-update-initial-value' is non nil you may want to +disable it temporarily, see [[Toggle auto-completion][Toggle auto-completion]] for this. + +**** Use `\\\\[helm-find-files-down-last-level]' to walk back the resulting tree of all the `\\\\[helm-find-files-up-one-level]' or DEL you did + +The tree is reinitialized each time you browse a new tree with +`\\\\[helm-execute-persistent-action]' or by entering some pattern in the prompt. + +**** `RET' behavior + +It behaves differently depending on `helm-selection' (current candidate in helm-buffer): + +- candidate basename is \".\" => Open it in dired. +- candidate is a directory => Expand it. +- candidate is a file => Open it. + +If you have marked candidates and you press RET on a directory, +Helm will navigate to this directory. If you want to exit with +RET with default action with these marked candidates, press RET a +second time while you are on the root of this directory e.g. +\"/home/you/dir/.\" or press RET on any file which is not a +directory. You can also exit with default action at any moment +with `f1'. + +Note that when copying, renaming, etc. from `helm-find-files' the +destination file is selected with `helm-read-file-name'. + +**** `TAB' behavior + +Normally `TAB' is bound to `helm-select-action' in helm-map which +display the action menu. + +You can change this behavior by setting in `helm-find-files-map' +a new command for `TAB': + + (define-key helm-find-files-map (kbd \"C-i\") 'helm-ff-TAB) + +It will then behave slighly differently depending of +`helm-selection': + +- candidate basename is \".\" => open the action menu. +- candidate is a directory => expand it (behave as \\\\[helm-execute-persistent-action]). +- candidate is a file => open action menu. + +Called with a prefix arg open menu unconditionally. + +*** Filter out files or directories + +You can show files or directories only with respectively +\\\\[helm-ff-toggle-dirs-only] and \\\\[helm-ff-toggle-files-only]. +These are toggle commands i.e. filter/show_all. +Changing directory disable filtering. + +*** Sort directory contents + +When listing a directory without narrowing its contents, i.e. when pattern ends with \"/\", +you can sort alphabetically, by newest or by size by using respectively +\\\\[helm-ff-sort-alpha], \\[helm-ff-sort-by-newest] or \\[helm-ff-sort-by-size]. +NOTE: +When starting back narrowing i.e. entering something in minibuffer after \"/\" sorting is done +again with fuzzy sorting and no more with sorting methods previously selected. + +You can use these sort functions only on files or directory, +see [[Filter out files or directories][Filter out files or directories]]. + +*** Find file at point + +Helm uses `ffap' partially or completely to find file at point depending on the +value of `helm-ff-guess-ffap-filenames': if non-nil, support is complete +\(annoying), if nil, support is partial. + +Note that when the variable +`helm-ff-allow-non-existing-file-at-point' is non nil Helm will +insert the filename at point even if file with this name doesn't +exists. If non existing file at point ends with numbers prefixed +with \":\" the \":\" and numbers are stripped. + +**** Find file at line number + +When text at point is in the form of + + ~/elisp/helm/helm.el:1234 + +Helm finds this file at the indicated line number, here 1234. + +**** Find URL at point + +When a URL is found at point, Helm expands to that URL only. +Pressing `RET' opens that URL using `browse-url-browser-function'. + +**** Find e-mail address at point + +When an e-mail address is found at point, Helm expands to this e-mail address +prefixed with \"mailto:\". Pressing `RET' opens a message buffer with that +e-mail address. + +*** Quick pattern expansion + +**** Enter `~/' at end of pattern to quickly reach home directory + +**** Enter `/' at end of pattern to quickly reach the file system root + +**** Enter `./' at end of pattern to quickly reach `default-directory' + +\(As per its value at the beginning of the session.) + +If you already are in the `default-directory' this will move the cursor to the top. + +**** Enter `../' at end of pattern will reach upper directory, moving cursor to the top + +This is different from using `\\\\[helm-find-files-up-one-level]' in that it moves +the cursor to the top instead of remaining on the previous subdir name. + +**** Enter `..name/' at end of pattern to start a recursive search + +It searches directories matching \"name\" under the current directory, +see the [[Recursive completion on subdirectories][Recursive completion on subdirectories]] section below for more details. + +**** Any environment variable (e.g. `$HOME') at end of pattern gets expanded + +**** Any valid filename yanked after pattern gets expanded + +**** Special case: URL at point + +The quick expansions do not take effect after end a URL, you must kill the +pattern first (`\\[helm-delete-minibuffer-contents]'). + +*** Helm-find-files supports fuzzy matching + +It starts from the third character of the pattern. + +For instance \"fob\" or \"fbr\" will complete \"foobar\" but \"fb\" needs a +third character in order to complete it. + +*** Watch briefly files contents while navigating + +You can use `\\[helm-execute-persistent-action]' on a filename for this, then: + +- First hit expands to that filename in the Helm buffer. +- Second hit displays the buffer filename. +- Third hit kills the buffer filename. + +Note: `\\[universal-argument] \\[helm-execute-persistent-action]' displays the buffer directly. + +*** Browse images directories with `helm-follow-mode' and navigate up/down + +Before Emacs-27 Helm was using image-dired that works with +external ImageMagick tools. From Emacs-27 Helm use native +display of images with image-mode by default for Emacs-27 (see `helm-ff-display-image-native'), +this allows automatic resize when changing window size, zooming with `\\[helm-ff-increase-image-size-persistent]' and `\\[helm-ff-decrease-image-size-persistent]' +and rotate images as before. + +You can also use `helm-follow-action-forward' and `helm-follow-action-backward' with +`\\[helm-follow-action-forward]' and `\\[helm-follow-action-backward]' respectively. +Note that these commands have different behavior when `helm-follow-mode' +is enabled (go to next/previous line only). + +Use `\\[universal-argument] \\[helm-execute-persistent-action]' to display an image or kill its buffer. + +TIP: Use `\\\\[helm-toggle-resplit-and-swap-windows]' and `\\[helm-enlarge-window]' to display Helm window vertically +and to enlarge it while viewing images. +Note this may not work with exotic Helm windows settings such as the ones in Spacemacs. + +**** Show thumbnails + +Helm use image-dired to show thumbnails on image files, you can +toggle the thumbnail view with \\`\\[helm-ff-toggle-thumbnails]'. + +**** Launch a slideshow from marked files + +Helm provides an action from `helm-find-files' that allows running a slideshow on marked files. +Just mark image files and launch slideshow from action menu, bindings are self documented. + +*** Open files externally + +- Open file with external program (`\\\\[helm-ff-run-open-file-externally]',`C-u' to choose). + +Helm is looking what is used by default to open file +externally (mailcap files) but have its own variable +`helm-external-programs-associations' to store external +applications. If you call the action or its binding without +prefix arg Helm will see if there is an application suitable in +`helm-external-programs-associations', otherwise it will look in +mailcap files. If you want to specify which external application +to use (and its options) use a prefix arg. + +If you have to pass arguments after filename use `%s' in your command e.g. \"foo %s -a -b\" +If you want to detach your program from Emacs, you can use e.g. \"(foo %s &)\" (only supported on Linux/Unix). +When using `%s' do not quote it (i.e. \"%s\"), helm is already quoting filename argument. + +Note: What you configure for Helm in `helm-external-programs-associations' +will take precedence on mailcap files. + +- Preview file with external program (`\\[helm-ff-run-preview-file-externally]'). + +Same as above but doesn't quit Helm session, it is apersistent action. + +- Open file externally with default tool (`\\[helm-ff-run-open-file-with-default-tool]'). + +Use `xdg-open' to open files. + +*** Toggle auto-completion + +It is useful when trying to create a new file or directory and you don't want +Helm to complete what you are writing. + +Note: On a terminal, the default binding `C-' may not work. +In this case use `C-c '. + +*** You can create a new directory and a new file at the same time + +Simply write the path in the prompt and press `RET', e.g. +\"~/new/newnew/newnewnew/my_newfile.txt\". + +*** To create a new directory, append a \"/\" to the new name and press `RET' + +*** To create a new file, enter a filename not ending with \"/\" + +Note that when you enter a new name, this one is prefixed with [?]. + +*** Recursive search from Helm-find-files + +**** You can use Helm-browse-project (see binding below) + +- With no prefix argument: +If the current directory is under version control with either git or hg and +helm-ls-git and/or helm-ls-hg are installed, it lists all the files under +version control. Otherwise it falls back to Helm-find-files. See +https://github.com/emacs-helm/helm-ls-git and +https://github.com/emacs-helm/helm-ls-hg. + +- With one prefix argument: +List all the files under this directory and other subdirectories +\(recursion) and this list of files will be cached. + +- With two prefix arguments: +Same but the cache is refreshed. + +**** You can start a recursive search with \"locate\", \"find\" or [[https://github.com/sharkdp/fd][Fd]] + +See \"Note\" in the [[Recursive completion on subdirectories][section on subdirectories]]. + +Using \"locate\", you can enable the local database with a prefix argument. If the +local database doesn't already exists, you will be prompted for its creation. +If it exists and you want to refresh it, give it two prefix args. + +When using locate the Helm buffer remains empty until you type something. +Regardless Helm uses the basename of the pattern entered in the helm-find-files +session by default. Hitting `\\[next-history-element]' should just kick in the +locate search with this pattern. If you want Helm to automatically do this, add +`helm-source-locate' to `helm-sources-using-default-as-input'. + +NOTE: On Windows use Everything with its command line ~es~ as a replacement of locate. +See [[https://github.com/emacs-helm/helm/wiki/Locate#windows][Locate on Windows]] + +**** Recursive completion on subdirectories + +Starting from the directory you are currently browsing, it is possible to have +completion of all directories underneath. Say you are at \"/home/you/foo/\" and +you want to go to \"/home/you/foo/bar/baz/somewhere/else\", simply type +\"/home/you/foo/..else\" and hit `\\[helm-execute-persistent-action]' or enter +the final \"/\". Helm will then list all possible directories under \"foo\" +matching \"else\". + +Note: Completion on subdirectories uses \"locate\" as backend, you can configure +the command with `helm-locate-recursive-dirs-command'. Because this completion +uses an index, the directory tree displayed may be out-of-date and not reflect +the latest change until you update the index (using \"updatedb\" for \"locate\"). + +If for some reason you cannot use an index, the \"find\" command from +\"findutils\" can be used instead. It will be slower though. You need to pass +the basedir as first argument of \"find\" and the subdir as the value for +'-(i)regex' or '-(i)name' with the two format specs that are mandatory in +`helm-locate-recursive-dirs-command'. + +Examples: +- \"find %s -type d -name '*%s*'\" +- \"find %s -type d -regex .*%s.*$\" + +[[https://github.com/sharkdp/fd][Fd]] command is now also +supported which is regexp based and very fast. Here is the command +line to use: + +- \"fd --hidden --type d .*%s.*$ %s\" + +You can use also a glob based search, in this case use the --glob option: + +- \"fd --hidden --type d --glob '*%s*' %s\" + +*** Insert filename at point or complete filename at point + +On insertion (i.e. there is nothing at point): + +- `\\[helm-ff-run-complete-fn-at-point]': insert absolute file name. +- `\\[universal-argument] \\[helm-ff-run-complete-fn-at-point]': insert abbreviated file name. +- `\\[universal-argument] \\[universal-argument] \\[helm-ff-run-complete-fn-at-point]': insert relative file name. +- `\\[universal-argument] \\[universal-argument] \\[universal-argument] \\[helm-ff-run-complete-fn-at-point]': insert basename. + +On completion (\\[helm-ff-run-complete-fn-at-point]): + +- Target starts with \"~/\": insert abbreviate file name. +- target starts with \"/\" or \"[a-z]:/\": insert full path. +- Otherwise: insert relative file name. + +*** Use the wildcard to select multiple files + +Use of wildcard is supported to run an action over a set of files. + +Example: You can copy all the files with \".el\" extension by using \"*.el\" and +then run copy action. + +Similarly, \"**.el\" (note the two stars) will recursively select all \".el\" +files under the current directory. + +Note that when recursively copying files, you may have files with same name +dispatched across different subdirectories, so when copying them in the same +directory they will get overwritten. To avoid this Helm has a special action +called \"backup files\" that has the same behavior as the command line \"cp -f +--backup=numbered\": it allows you to copy many files with the same name from +different subdirectories into one directory. Files with same name are renamed +as follows: \"foo.txt.~1~\". Like with the --force option of cp, it is possible +to backup files in current directory. + +This command is available only when `dired-async-mode' is active. + +When using an action that involves an external backend (e.g. grep), using \"**\" +is not recommended (even thought it works fine) because it will be slower to +select all the files. You are better off leaving the backend to do it, it will +be faster. However, if you know you have not many files it is reasonable to use +this, also using not recursive wildcard (e.g. \"*.el\") is perfectly fine for +this. + +The \"**\" feature is active by default in the option `helm-file-globstar'. It +is different from the Bash \"shopt globstar\" feature in that to list files with +a named extension recursively you would write \"**.el\" whereas in Bash it would +be \"**/*.el\". Directory selection with \"**/\" like Bash \"shopt globstar\" +option is not supported yet. + +Helm supports different styles of wildcards: + +- `sh' style, the ones supported by `file-expand-wildcards'. +e.g. \"*.el\", \"*.[ch]\" which match respectively all \".el\" +files or all \".c\" and \".h\" files. + +- `bash' style (partially) In addition to what allowed in `sh' +style you can specify file extensions that have more than one +character like this: \"*.{sh,py}\" which match \".sh\" and +\".py\" files. + +Of course in both styles you can specify one or two \"*\". + +*** Query replace regexp on filenames + +Replace different parts of a file basename with something else. + +When calling this action you will be prompted twice as with +`query-replace', first for the matching expression of the text to +replace and second for the replacement text. Several facilities, +however, are provided to make the two prompts more powerfull. + +**** Syntax of the first prompt + +In addition to simple regexps, these shortcuts are available: + +- Basename without extension => \"%.\" +- Only extension => \".%\" +- Substring => \"%::\" +- Whole basename => \"%\" + +**** Syntax of the second prompt + +In addition to a simple string to use as replacement, here is what you can use: + +- A placeholder refering to what you have selected in the first prompt: \"\\@\". + +After this placeholder you can use a search-and-replace syntax à-la sed: + + \"\\@/// + +You can select a substring from the string represented by the placeholder: + + \"\\@::\" + +- A special character representing a number which is incremented: \"\\#\". + +- Shortcuts for `upcase', `downcase' and `capitalize' +are available as`%u', `%d' and `%c' respectively. + +**** Examples + +***** Recursively rename all files with \".JPG\" extension to \".jpg\" + +Use the `helm-file-globstar' feature described in [[Use the wildcard to select multiple files][recursive globbing]] +by entering \"**.JPG\" at the end of the Helm-find-files pattern, then hit +\\\\[helm-ff-run-query-replace-fnames-on-marked] and enter \"JPG\" on first prompt, then \"jpg\" on second prompt and hit `RET'. + +Alternatively you can enter \".%\" at the first prompt, then \"jpg\" and hit +`RET'. Note that when using this instead of using \"JPG\" at the first prompt, +all extensions will be renamed to \"jpg\" even if the extension of one of the +files is, say, \"png\". If you want to keep the original extension you can use +\"%d\" at the second prompt (downcase). + +***** Batch-rename files from number 001 to 00x + +Use \"\\#\" inside the second prompt. + +Example 1: To rename the files + + foo.jpg + bar.jpg + baz.jpg + +to + + foo-001.jpg + foo-002.jpg + foo-003.jpg + +use \"%.\" as matching regexp and \"foo-\\#\" as replacement string. + +Example 2: To rename the files + + foo.jpg + bar.jpg + baz.jpg + +to + + foo-001.jpg + bar-002.jpg + baz-003.jpg + +use as matching regexp \"%.\" and as replacement string \"\\@-\\#\". + +***** Replace a substring + +Use \"%::\". + +Example: To rename files + + foo.jpg + bar.jpg + baz.jpg + +to + + fOo.jpg + bAr.jpg + bAz.jpg + +use as matching regexp \"%:1:2\" and as replacement string \"%u\" (upcase). + +Note that you \*cannot* use \"%.\" and \".%\" along with substring replacement. + +***** Modify the string from the placeholder (\\@) + +- By substring, i.e. only using the substring of the placeholder: \"\\@::\". +The length of placeholder is used for when unspecified. + +Example 1: \"\\@:0:2\" replaces from the beginning to the second char of the placeholder. + +Example 2: \\@:2: replaces from the second char of the placeholder to the end. + +- By search-and-replace: \"\\@///\". + +Incremental replacement is also handled in . + +Example 3: \"\\@/foo/bar/\" replaces \"foo\" by \"bar\" in the placeholder. + +Example 4: \"\\@/foo/-\\#/\" replaces \"foo\" in the placeholder by 001, 002, etc. + +***** Clash in replacements (avoid overwriting files) + +When performing any of these replacement operations you may end up with same +names as replacement. In such cases Helm numbers the file that would otherwise +overwritten. For instance, should you remove the \"-m\" part from the files +\"emacs-m1.txt\", \"emacs-m2.txt\" and \"emacs-m3.txt\" you would end up with +three files named \"emacs.txt\", the second renaming overwriting first file, and +the third renaming overwriting second file and so on. Instead Helm will +automatically rename the second and third files as \"emacs(1).txt\" and +\"emacs(2).txt\" respectively. + +***** Query-replace on filenames vs. serial-rename action + +Unlike the [[Serial renaming][serial rename]] actions, the files renamed with +the query-replace action stay in their initial directory and are not moved to +the current directory. As such, using \"\\#\" to serial-rename files only makes +sense for files inside the same directory. It even keeps renaming files +with an incremental number in the next directories. + +*** Serial renaming + +You can use the serial-rename actions to rename, copy or symlink marked files to +a specific directory or in the current directory with all the files numbered +incrementally. + +- Serial-rename by renaming: +Rename all marked files with incremental numbering to a specific directory. + +- Serial-rename by copying: +Copy all marked files with incremental numbering to a specific directory. + +- Serial-rename by symlinking: +Symlink all marked files with incremental numbering to a specific directory. + +*** Edit marked files in a dired buffer + +You can open a dired buffer containing only marked files with `\\\\[helm-ff-run-marked-files-in-dired]'. +With a prefix argument you can open this same dired buffer in wdired mode for +editing. Note that wildcards are supported as well, so you can use e.g. +\"*.txt\" to select all \".txt\" files in the current directory or \"**.txt\" to +select all files recursively from the current directory. +See [[Use the wildcard to select multiple files]] section above. + +*** Defining default target directory for copying, renaming, etc + +You can customize `helm-dwim-target' to behave differently depending on the +windows open in the current frame. Default is to provide completion on all +directories associated to each window. + +*** Copying/Renaming from or to remote directories + +Never use ssh tramp method to copy/rename large files, use +instead its scp method if you want to avoid out of memory +problems and crash Emacs or the whole system. Moreover when using +scp method, you will hit a bug when copying more than 3 files at +the time, see [[https://github.com/emacs-helm/helm/issues/1945][bug#1945]]. +The best way actually is using Rsync to copy files from or to +remote, see [[Use Rsync to copy files][Use Rsync to copy files]]. +Also if you often work on remote you may consider using SSHFS +instead of relying on tramp. + +*** Copying and renaming asynchronously + +If you have the async library installed (if you got Helm from MELPA you do), you +can use it for copying/renaming files by enabling `dired-async-mode'. + +Note that even when async is enabled, running a copy/rename action with a prefix +argument will execute action synchronously. Moreover it will follow the first +file of the marked files in its destination directory. + +When `dired-async-mode' is enabled, an additional action named \"Backup files\" +will be available. (Such command is not natively available in Emacs). +See [[Use the wildcard to select multiple files]] for details. + +*** Use Rsync to copy files + +If Rsync is available, you can use it to copy/sync files or directories +with some restrictions though: + +- Copying from/to tramp sudo method may not work (permissions). +- Copying from remote to remote is not supported (rsync restriction) +however you can mount a remote with sshfs and copy to it (best), otherwise you have to modify +the command line with a prefix arg, see [[https://unix.stackexchange.com/questions/183504/how-to-rsync-files-between-two-remotes][how-to-rsync-files-between-two-remotes]] +for the command line to use. + +This command is mostly useful when copying large files as it is +fast, asynchronous and provide a progress bar in mode-line. Each +rsync process have its own progress bar, so you can run several +rsync jobs, they are independents. If rsync fails you can +consult the \"*helm-rsync*\" buffer to see rsync errors. An +help-echo (move mouse over progress bar) is provided to see which +file is in transfer. Note that when copying directories, no +trailing slashes are added to directory names, which mean that +directory is created on destination if it doesn't already exists, +see rsync documentation for more infos on rsync behavior. To +synchronize a directory, mark all in the directory and rsync all +marked to the destination directory or rsync the directory itself +to its parent, e.g. remote:/home/you/music => /home/you. + +The options are configurable through `helm-rsync-switches', but +you can modify them on the fly when needed by using a prefix arg, +in this case you will be prompted for modifications. + +NOTE: When selecting a remote file, if you use the tramp syntax +for specifying a port, i.e. host#2222, helm will add +automatically \"-e 'ssh -p 2222'\" to the rsync command line +unless you have specified yourself the \"-e\" option by editing +rsync command line with a prefix arg (see above). + +*** Access files on Android phones from Helm + +Since Android doesn't provide anymore mass storage for USB, it is +not simple to access files on Android, the best way to do this +actually seems to use Adb, here some hints to set this up, read +in addition the Tramp documentation. + +1) Install Adb, most distribution provide it. +2) Enable on your phone USB debug in System/dvlpmnt settings. +3) From helm-find-files use adb tramp method: + /adb::/ +From there you can navigate as usual, mark and copy files etc... + +*** Bookmark the `helm-find-files' session + +You can bookmark the `helm-find-files' session with `\\[helm-ff-bookmark-set]'. +You can later retrieve these bookmarks by calling `helm-filtered-bookmarks' +or, from the current `helm-find-files' session, by hitting `\\[helm-find-files-switch-to-bookmark]'. + +*** Grep files from `helm-find-files' + +You can grep individual files from `helm-find-files' by using +\`\\\\[helm-ff-run-grep]'. This same command can also +recursively grep files from the current directory when called with a prefix +argument. In this case you will be prompted for the file extensions to use +\(grep backend) or the types of files to use (ack-grep backend). See the +`helm-grep-default-command' documentation to set this up. For compressed files +or archives, use zgrep with \`\\\\[helm-ff-run-zgrep]'. + +Otherwise you can use recursive commands like \`\\\\[helm-ff-run-grep-ag]' or `\\\\[helm-ff-run-git-grep]' +that are much faster than using `\\\\[helm-ff-run-grep]' with a prefix argument. +See `helm-grep-ag-command' and `helm-grep-git-grep-command' to set this up. + +You can also use \"id-utils\"' GID with \`\\\\[helm-ff-run-gid]' +by creating an ID index file with the \"mkid\" shell command. + +All those grep commands use the symbol at point as the default pattern. +Note that default is different from input (nothing is added to the prompt until +you hit `\\[next-history-element]'). + +**** Grepping on remote files + +On remote files grep is not well supported by TRAMP unless you suspend updates before +entering the pattern and re-enable it once your pattern is ready. +To toggle suspend-update, use `\\\\[helm-toggle-suspend-update]'. + +*** Execute Eshell commands on files + +Setting up aliases in Eshell allows you to set up powerful customized commands. + +Your aliases for using eshell command on file should allow +specifying one or more files, use e.g. \"alias foo $1\" or +\"alias foo $*\", if you want your command to be asynchronous add +at end \"&\", e.g. \"alias foo $* &\". + +Adding Eshell aliases to your `eshell-aliases-file' or using the +`alias' command from Eshell allows you to create personalized +commands not available in `helm-find-files' actions and use them +from `\\\\[helm-ff-run-eshell-command-on-file]'. + +Example: You want a command to uncompress some \"*.tar.gz\" files from `helm-find-files': + +1) Create an Eshell alias named, say, \"untargz\" with the command +\"alias untargz tar zxvf $*\". + +2) Now from `helm-find-files' select the \"*.tar.gz\" file (you can also +mark files if needed) and hit `\\\\[helm-ff-run-eshell-command-on-file]'. + +Note: When using marked files with this, the meaning of the prefix argument is +quite subtle. Say you have \"foo\", \"bar\" and \"baz\" marked; when you run +the alias command `example' on these files with no prefix argument it will run +`example' sequentially on each file: + +$ example foo +$ example bar +$ example baz + +With a prefix argument however it will apply `example' on all files at once: + +$ example foo bar baz + +Of course the alias command should support this. + +NOTE: Helm assume that any alias command ending with '$*' or +'$*&' supports many files as arguments, so no need to give a +prefix arg for such alias, however if your command is not an +alias you have to specify a prefix arg if you want your command +to apply all files at once. + +If you add %s to the command line %s will be replaced with the candidate, this mean you can +add extra argument to your command e.g. command -extra-arg %s or command %s -extra-arg. +If you want to pass many files inside %s, don't forget to use a prefix arg. + +You can also use special placeholders in extra-args, +see the specific info page once you hit `\\\\[helm-ff-run-eshell-command-on-file]'. + +*** Using TRAMP with `helm-find-files' to read remote directories + +`helm-find-files' works fine with TRAMP despite some limitations. + +- Grepping files is not very well supported when used incrementally. +See [[Grepping on remote files]]. + +- Locate does not work on remote directories. + +**** A TRAMP syntax crash course + +Please refer to TRAMP's documentation for more details. + +- Connect to host 192.168.0.4 as user \"foo\": + +/scp:192.168.0.4@foo: + +- Connect to host 192.168.0.4 as user \"foo\" on port 2222: + +/scp:192.168.0.4@foo#2222: + +- Connect to host 192.168.0.4 as root using multihops syntax: + +/ssh:192.168.0.4@foo|sudo:192.168.0.4: + +Note: You can also use `tramp-default-proxies-alist' when connecting often to +the same hosts. + +As a rule of thumb, prefer the scp method unless using multihops (which only +works with the ssh method), especially when copying large files. + +IMPORTANT: +You need to hit `C-j' once on top of a directory on the first connection +to complete the pattern in the minibuffer. + +**** Display color for directories, symlinks etc... with tramp + +Starting at helm version 2.9.7 it is somewhat possible to +colorize fnames by listing files without loosing performances with +external commands (ls and awk) if your system is compatible. +For this you can use `helm-list-dir-external' as value +for `helm-list-directory-function'. + +See `helm-list-directory-function' documentation for more infos. + +**** Completing host + +As soon as you enter the first \":\" after method e.g =/scp:= you will +have some completion about previously used hosts or from your =~/.ssh/config= +file, hitting `\\[helm-execute-persistent-action]' or `right' on a candidate will insert this host in minibuffer +without addind the ending \":\", second hit insert the last \":\". +As soon the last \":\" is entered TRAMP will kick in and you should see the list +of candidates soon after. + +**** Completion on tramp methods + +If you enter \":\" directly after \"/\" or \"|\" you will have completion on tramp methods, +hitting `\\[helm-execute-persistent-action]' or `right' on a method will insert it in minibuffer. + +When connection fails, be sure to delete your TRAMP connection with M-x +`helm-delete-tramp-connection' before retrying. + +**** Editing local files as root + +Use the sudo method: + +\"/sudo:host:\" or simply \"/sudo::\". + +*** Attach files to a mail buffer (message-mode) + +If you are in a `message-mode' or `mail-mode' buffer, that action will appear +in action menu, otherwise it is available at any time with \\\\[helm-ff-run-mail-attach-files]. +It behaves as follows: + +- If you are in a (mail or message) buffer, files are attached there. + +- If you are not in a mail buffer but one or more mail buffers exist, you are +prompted to attach files to one of these mail buffers. + +- If you are not in a mail buffer and no mail buffer exists, +a new mail buffer is created with the attached files in it. + +*** Open files in separate windows + +When [[Marked candidates][marking]] multiple files or using [[Use the wildcard to select multiple files][wildcard]], helm allow opening all +this files in separate windows using an horizontal layout or a +vertical layout if you used a prefix arg, when no more windows can be +displayed in frame, next files are opened in background without being +displayed. When using \\\\[helm-ff-run-switch-other-window] the current +buffer is kept and files are displayed next to it with same behavior as above. +When using two prefix args, files are opened in background without beeing displayed. + +*** Expand archives as directories in a avfs directory + +If you have mounted your filesystem with mountavfs, +you can expand archives in the \"~/.avfs\" directory with \\\\[helm-execute-persistent-action]. + +*** Tramp archive support (emacs-27+ only) + +If your emacs have library tramp-archive.el, you can browse the +content of archives with emacs and BTW helm-find-files. However this beeing +experimental and not very fast, helm doesn't provide an automatic +expansion and detection of archives, you will have to add the final / +manually and may have to force update (\\\\[helm-refresh]) +or remove and add again the final / until tramp finish decompressing archive. + +*** Touch files + +In the completion buffer, you can choose the default which is the current-time, it is +the first candidate or the timestamp of one of the selected files. +If you need to use something else, use \\\\[next-history-element] and edit +the date in minibuffer. +It is also a way to quickly create a new file without opening a buffer, saving it +and killing it. +To touch more than one new file, separate you filenames with a comma (\",\"). +If one wants to create (touch) a new file with comma inside the name use a prefix arg, +this will prevent splitting the name and create multiple files. + +*** Delete files + +You can delete files without quitting helm with +`\\\\[helm-ff-persistent-delete]' or delete files and quit helm with `\\[helm-ff-run-delete-file]'. + +In the second method you can choose to +make this command asynchronous by customizing +\`helm-ff-delete-files-function'. + +_WARNING_: When deleting files asynchronously you will NOT be +WARNED if directories are not empty, that's mean non empty directories will +be deleted in background without asking. + +A good compromise is to trash your files +when using asynchronous method (see [[Trashing files][Trashing files]]). + +When choosing synchronous delete, you can allow recursive +deletion of directories with `helm-ff-allow-recursive-deletes'. +Note that when trashing (synchronous) you are not asked for recursive deletion. + +Note that `helm-ff-allow-recursive-deletes' have no effect when +deleting asynchronously. + +First method (persistent delete) is always synchronous. + +Note that when a prefix arg is given, trashing behavior is inversed. +See [[Trashing files][Trashing files]]. + +**** Trashing files + +If you want to trash your files instead of deleting them you can +set `delete-by-moving-to-trash' to non nil, like this your files +will be moved to trash instead of beeing deleted. + +You can reverse at any time the behavior of `delete-by-moving-to-trash' by using +a prefix arg with any of the delete files command. + +On GNULinux distributions, when navigating to a Trash directory you +can restore any file in ..Trash/files directory with the 'Restore +from trash' action you will find in action menu (needs the +trash-cli package installed for remote files, see [[Trashing remote files with tramp][Here]]). +You can as well delete files from Trash directories with the 'delete files from trash' +action. +If you want to know where a file will be restored, hit `M-i', you will find a trash info. + +Tip: Navigate to your Trash/files directories with `helm-find-files' and set a bookmark +there with \\\\[helm-ff-bookmark-set] for fast access to Trash. + +NOTE: Restoring files from trash is working only on system using +the [[http://freedesktop.org/wiki/Specifications/trash-spec][freedesktop trash specifications]]. + +_WARNING:_ + +If you have an ENV var XDG_DATA_HOME in your .profile or .bash_profile +and this var is set to something like $HOME/.local/share (like preconized) +`move-file-to-trash' may try to create $HOME/.local/share/Trash (literally) +and its subdirs in the directory where you are actually trying to trash files. +because `move-file-to-trash' is interpreting XDG_DATA_HOME literally instead +of evaling its value (with `substitute-in-file-name'). + +***** Trashing remote files with tramp + +Trashing remote files (or local files with sudo method) is disabled by default +because tramp is requiring the 'trash' command to be installed, if you want to +trash your remote files, customize `helm-trash-remote-files'. +The package on most GNU/Linux based distributions is trash-cli, it is available [[https://github.com/andreafrancia/trash-cli][here]]. + +NOTE: +When deleting your files with sudo method, your trashed files will not be listed +with trash-list until you log in as root. + +*** Checksum file + +Checksum is calculated with the md5sum, sha1sum, sha224sum, +sha256sum, sha384sum and sha512sum when available, otherwise the +Emacs function `secure-hash' is used but it is slow and may crash +Emacs and even the whole system as it eats all memory. So if +your system doesn't have the md5 and sha command line tools be +careful when checking sum of larges files e.g. isos. + +*** Ignored or boring files + +Helm-find-files can ignore files matching +`helm-boring-file-regexp-list' or files that are git ignored, you +can set this with `helm-ff-skip-boring-files' or +`helm-ff-skip-git-ignored-files'. +NOTE: This will slow down helm, be warned. + +*** Helm-find-files is using a cache + +Helm is caching each directory files list in a hash table for +faster search, when a directory is modified it is removed from cache +so that further visit in this directory refresh cache. +You may have in some rare cases to refresh directory manually with `\\\\[helm-refresh]' +for example when helm-find-files session is running and a file is modified/deleted +in current visited directory by an external command from outside Emacs. + +NOTE: Helm is using file-notify to watch visited directories, +nowaday most systems come with a notify package but if your +system doesn't support this, you can turn off file notifications +by customizing the variable `helm-ff-use-notify'. In this case +you will have to refresh manually directories when needed with `\\\\[helm-refresh]'. + +*** Prefix file candidates with icons + +If `all-the-icons' package is installed, turning on +`helm-ff-icon-mode' will show icons before files and directories. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-ff-run-locate]|Run `locate' (`\\[universal-argument]' to specify locate database, `M-n' to insert basename of candidate). +|\\[helm-ff-run-browse-project]|Browse project (`\\[universal-argument]' to recurse, `\\[universal-argument] \\[universal-argument]' to recurse and refresh database). +|\\[helm-ff-run-find-sh-command]|Run `find' shell command from this directory. +|\\[helm-ff-run-grep]|Run Grep (`\\[universal-argument]' to recurse). +|\\[helm-ff-run-pdfgrep]|Run Pdfgrep on marked files. +|\\[helm-ff-run-zgrep]|Run zgrep (`\\[universal-argument]' to recurse). +|\\[helm-ff-run-grep-ag]|Run AG grep on current directory. +|\\[helm-ff-run-git-grep]|Run git-grep on current directory. +|\\[helm-ff-run-gid]|Run gid (id-utils). +|\\[helm-ff-run-etags]|Run Etags (`\\[universal-argument]' to use thing-at-point, `\\[universal-argument] \\[universal-argument]' to reload cache). +|\\[helm-ff-run-rename-file]|Rename Files (`\\[universal-argument]' to follow). +|\\[helm-ff-run-query-replace-fnames-on-marked]|Query replace on marked files. +|\\[helm-ff-run-copy-file]|Copy Files (`\\[universal-argument]' to follow). +|\\[helm-ff-run-rsync-file]|Rsync Files (`\\[universal-argument]' to edit command). +|\\[helm-ff-run-byte-compile-file]|Byte Compile Files (`\\[universal-argument]' to load). +|\\[helm-ff-run-load-file]|Load Files. +|\\[helm-ff-run-symlink-file]|Symlink Files. +|\\[helm-ff-run-hardlink-file]|Hardlink files. +|\\[helm-ff-run-relsymlink-file]|Relative symlink Files. +|\\[helm-ff-run-delete-file]|Delete Files. +|\\[helm-ff-run-touch-files]|Touch files. +|\\[helm-ff-run-kill-buffer-persistent]|Kill buffer candidate without leaving Helm. +|\\[helm-ff-persistent-delete]|Delete file without leaving Helm. +|\\[helm-ff-run-switch-to-shell]|Switch to prefered shell. +|\\[helm-ff-run-eshell-command-on-file]|Eshell command on file (`\\[universal-argument]' to apply on marked files, otherwise treat them sequentially). +|\\[helm-ff-run-ediff-file]|Ediff file. +|\\[helm-ff-run-ediff-merge-file]|Ediff merge file. +|\\[helm-ff-run-complete-fn-at-point]|Complete file name at point. +|\\[helm-ff-run-switch-other-window]|Switch to other window. +|\\[helm-ff-run-switch-other-frame]|Switch to other frame. +|\\[helm-ff-run-open-file-externally]|Open file with external program (`\\[universal-argument]' to choose). +|\\[helm-ff-run-preview-file-externally]|Preview file with external program. +|\\[helm-ff-run-open-file-with-default-tool]|Open file externally with default tool. +|\\[helm-ff-rotate-left-persistent]|Rotate image left. +|\\[helm-ff-rotate-right-persistent]|Rotate image right. +|\\[helm-ff-increase-image-size-persistent]|Zoom in image. +|\\[helm-ff-decrease-image-size-persistent]|Zoom out image. +|\\[helm-ff-toggle-thumbnails]|Show thumbnails on image files. +|\\[helm-find-files-up-one-level]|Go to parent directory. +|\\[helm-find-files-history]|Switch to the visited-directory history. +|\\[helm-ff-file-name-history]|Switch to file name history. +|\\[helm-ff-properties-persistent]|Show file properties in a tooltip. +|\\[helm-mark-all]|Mark all visible candidates. +|\\[helm-ff-run-toggle-auto-update]|Toggle auto-expansion of directories. +|\\[helm-unmark-all]|Unmark all candidates, visible and invisible ones. +|\\[helm-ff-run-mail-attach-files]|Attach files to message buffer. +|\\[helm-ff-run-print-file]|Print file, (`\\[universal-argument]' to refresh printer list). +|\\[helm-enlarge-window]|Enlarge Helm window. +|\\[helm-narrow-window]|Narrow Helm window. +|\\[helm-ff-run-toggle-basename]|Toggle basename/fullpath. +|\\[helm-ff-run-find-file-as-root]|Find file as root. +|\\[helm-ff-run-find-alternate-file]|Find alternate file. +|\\[helm-ff-run-insert-org-link]|Insert org link. +|\\[helm-ff-bookmark-set]|Set bookmark to current directory. +|\\[helm-find-files-switch-to-bookmark]|Jump to bookmark list. +|\\[helm-ff-sort-alpha]|Sort alphabetically. +|\\[helm-ff-sort-by-newest]|Sort by newest. +|\\[helm-ff-sort-by-size]|Sort by size. +|\\[helm-ff-toggle-dirs-only]|Show only directories. +|\\[helm-ff-toggle-files-only]|Show only files. +|\\[helm-ff-sort-by-ext]|Sort by extensions.") + +;;; Help for file-name-history +;; +;; +(defvar helm-file-name-history-help-message + "* Helm file name history + +** Tips +You can open directly the selected file and exit helm or preselect the file in helm-find-files, +see actions in action menu. + +You can toggle the view of deleted files, see commands below. + +** Commands +\\ +\\[helm-file-name-history-show-or-hide-deleted]|Toggle deleted files view.") + +;;; Help for `helm-read-file-name' +;; +;; +(defun helm-read-file-name-help-message () + (let ((name (if helm-alive-p + (assoc-default 'name (helm-get-current-source)) + "generic"))) + (format + "* Helm `%s' read file name completion + +This is `%s' read file name completion that have been \"helmized\" +because you have enabled [[Helm mode][helm-mode]]. +Don't confuse this with `helm-find-files' which is a native helm command, +see [[Helm functions vs helmized Emacs functions]]. + +** Tips + +*** Navigation + +**** Enter `~/' at end of pattern to quickly reach home directory + +**** Enter `/' at end of pattern to quickly reach the file system root + +**** Enter `./' at end of pattern to quickly reach `default-directory' + +\(As per its value at the beginning of the session.) + +If you already are in the `default-directory' this will move the cursor to the top. + +**** Enter `../' at end of pattern will reach upper directory, moving cursor on top + +This is different from using `\\[helm-find-files-up-one-level]' in that it moves +the cursor to the top instead of remaining on the previous subdir name. + +**** You can complete with partial basename + +It starts from the third character of the pattern. + +For instance \"fob\" or \"fbr\" will complete \"foobar\" but \"fb\" needs a +third character in order to complete it. + +*** Persistent actions + +By default `helm-read-file-name' uses the persistent actions of `helm-find-files'. + +**** Use `\\[universal-argument] \\\\[helm-execute-persistent-action]' to display an image + +**** `\\\\[helm-execute-persistent-action]' on a filename will expand to this filename in Helm-buffer + +Second hit displays the buffer filename. +Third hit kills the buffer filename. +Note: `\\[universal-argument] \\\\[helm-execute-persistent-action]' displays the buffer directly. + +**** Browse images directories with `helm-follow-mode' and navigate up/down + +*** Delete characters backward + +When you want to delete characters backward, e.g. to create a new file or directory, +auto-update may come in the way when it keeps updating to an existent directory. +In that case, type `C-' and then `'. +This should not be needed when copying/renaming files because autoupdate is disabled +by default in that case. + +Note: On a terminal, the default binding `C-' may not work. +In this case use `C-c '. + +*** Create new directories and files + +**** You can create a new directory and a new file at the same time + +Simply write the path in prompt and press `RET', e.g. +\"~/new/newnew/newnewnew/my_newfile.txt\". + +**** To create a new directory, append a \"/\" at to the new name and press `RET' + +**** To create a new file, enter a filename not ending with \"/\" + +File and directory creation works only with some commands (e.g. `find-file') +and it will not work with others where it is not intended to return a file or +a directory \(e.g `list-directory'). + +*** Exiting minibuffer with empty string + +You can exit minibuffer with empty string with \\\\[helm-cr-empty-string]. +It is useful when some commands are prompting continuously until you enter an empty prompt. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-find-files-up-one-level]|Go to parent directory. +|\\[helm-ff-run-toggle-auto-update]|Toggle auto-expansion of directories. +|\\[helm-ff-run-toggle-basename]|Toggle basename. +|\\[helm-ff-file-name-history]|File name history. +|C/\\[helm-cr-empty-string]|Return empty string unless `must-match' is non-nil. +|\\[helm-next-source]|Go to next source. +|\\[helm-previous-source]|Go to previous source." + name name))) + +;;; FD help +;; +;; +(defvar helm-fd-help-message + "* Helm fd + +** Tips + +\[[https://github.com/sharkdp/fd][The Fd command line tool]] is very fast to search files recursively. +You may have to wait several seconds at first usage when your +hard drive cache is \"cold\", then once the cache is initialized +searchs are very fast. You can pass any [[https://github.com/sharkdp/fd#command-line-options][Fd options]] before pattern, e.g. \"-e py foo\". + +The [[https://github.com/sharkdp/fd][Fd]] command line can be customized with `helm-fd-switches' user variable. +Always use =--color always= as option otherwise you will have no colors. +To customize colors see [[https://github.com/sharkdp/fd#colorized-output][Fd colorized output]]. + +NOTE: +Starting from fd version 8.2.1, you have to provide the env var +LS_COLORS to Emacs to have a colorized output, the easiest way is +to add to your =~/.profile= file =eval $(dircolors)=. +Another way is using =setenv= in your init file. +This is not needed when running Emacs from a terminal either with =emacs -nw= +or =emacs= because emacs inherit the env vars of this terminal. +See [[https://github.com/sharkdp/fd/issues/725][fd bugref#725]] + +Search is (pcre) regexp based (see [[https://docs.rs/regex/1.0.0/regex/#syntax][Regexp syntax]]), multi patterns are _not_ supported. + +** Man page + +NAME + fd - find entries in the filesystem + +SYNOPSIS + fd [-HIEsiaLp0hV] [-d depth] [-t filetype] [-e ext] [-E exclude] [-c + when] [-j num] [-x cmd] [pattern] [path...] + +DESCRIPTION + fd is a simple, fast and user-friendly alternative to find(1). + +OPTIONS + -H, --hidden + Include hidden files and directories in the search results + (default: hidden files and directories are skipped). + + -I, --no-ignore + Show search results from files and directories that would other‐ + wise be ignored by .gitignore, .ignore, .fdignore, or the global + ignore file. + + -u, --unrestricted + Alias for '--no-ignore'. Can be repeated; '-uu' is an alias for + '--no-ignore --hidden'. + + --no-ignore-vcs + Show search results from files and directories that would other‐ + wise be ignored by .gitignore files. + + -s, --case-sensitive + Perform a case-sensitive search. By default, fd uses case-insen‐ + sitive searches, unless the pattern contains an uppercase char‐ + acter (smart case). + + -i, --ignore-case + Perform a case-insensitive search. By default, fd uses case- + insensitive searches, unless the pattern contains an uppercase + character (smart case). + + -g, --glob + Perform a glob-based search instead of a regular expression + search. + + --regex + Perform a regular-expression based seach (default). This can be + used to override --glob. + + -F, --fixed-strings + Treat the pattern as a literal string instead of a regular + expression. + + -a, --absolute-path + Shows the full path starting from the root as opposed to rela‐ + tive paths. + + -l, --list-details + Use a detailed listing format like 'ls -l'. This is basically an + alias for '--exec-batch ls -l' with some additional 'ls' + options. This can be used to see more metadata, to show symlink + targets and to achieve a deterministic sort order. + + -L, --follow + By default, fd does not descend into symlinked directories. + Using this flag, symbolic links are also traversed. + + -p, --full-path + By default, the search pattern is only matched against the file‐ + name (or directory name). Using this flag, the pattern is + matched against the full path. + + -0, --print0 + Separate search results by the null character (instead of new‐ + lines). Useful for piping results to xargs. + + --max-results count + Limit the number of search results to 'count' and quit immedi‐ + ately. + + -1 Limit the search to a single result and quit immediately. This + is an alias for '--max-results=1'. + + --show-errors + Enable the display of filesystem errors for situations such as + insufficient permissions or dead symlinks. + + --one-file-system, --mount, --xdev + By default, fd will traverse the file system tree as far as + other options dictate. With this flag, fd ensures that it does + not descend into a different file system than the one it started + in. Comparable to the -mount or -xdev filters of find(1). + + -h, --help + Print help information. + + -V, --version + Print version information. + + -d, --max-depth d + Limit directory traversal to at most d levels of depth. By + default, there is no limit on the search depth. + + --min-depth d + Only show search results starting at the given depth. See also: + '--max-depth' and '--exact-depth'. + + --exact-depth d + Only show search results at the exact given depth. This is an + alias for '--min-depth --max-depth '. + + -t, --type filetype + Filter search by type: + + f, file + regular files + + d, directory + directories + + l, symlink + symbolic links + + x, executable + executable (files) + + e, empty + empty files or directories + + s, socket + sockets + + p, pipe + named pipes (FIFOs) + + This option can be used repeatedly to allow for multiple file + types. + + -e, --extension ext + Filter search results by file extension ext. This option can be + used repeatedly to allow for multiple possible file extensions. + + -E, --exclude pattern + Exclude files/directories that match the given glob pattern. + This overrides any other ignore logic. Multiple exclude pat‐ + terns can be specified. + + --ignore-file path + Add a custom ignore-file in '.gitignore' format. These files + have a low precedence. + + -c, --color when + Declare when to colorize search results: + + auto Colorize output when standard output is connected to terminal (default). + + never Do not colorize output. + + always Always colorize output. + + -j, --threads num + Set number of threads to use for searching & executing (default: + number of available CPU cores). + + -S, --size size + Limit results based on the size of files using the format + <+-> + + '+' file size must be greater than or equal to this + + '-' file size must be less than or equal to this + + 'NUM' The numeric size (e.g. 500) + + 'UNIT' The units for NUM. They are not case-sensitive. Allowed + unit values: + + 'b' bytes + + 'k' kilobytes (base ten, 10^3 = 1000 bytes) + + 'm' megabytes + + 'g' gigabytes + + 't' terabytes + + 'ki' kibibytes (base two, 2^10 = 1024 bytes) + + 'mi' mebibytes + + 'gi' gibibytes + + 'ti' tebibytes + + --changed-within date|duration + Filter results based on the file modification time. The argument + can be provided as a specific point in time (YYYY-MM-DD + HH:MM:SS) or as a duration (10h, 1d, 35min). --change-newer- + than can be used as an alias. + + Examples: + --changed-within 2weeks + --change-newer-than \"2018-10-27 10:00:00\" + + --changed-before date|duration + Filter results based on the file modification time. The argument + can be provided as a specific point in time (YYYY-MM-DD + HH:MM:SS) or as a duration (10h, 1d, 35min). --change-older- + than can be used as an alias. + + Examples: + --changed-before \"2018-10-27 10:00:00\" + --change-older-than 2weeks + + -o, --owner [user][:group] + Filter files by their user and/or group. Format: + [(user|uid)][:(group|gid)]. Either side is optional. Precede + either side with a '!' to exclude files instead. + + Examples: + --owner john + --owner :students + --owner \"!john:students\" + + -x, --exec command + Execute command for each search result. The following placehold‐ + ers are substituted by a path derived from the current search + result: + + {} path + + {/} basename + + {//} parent directory + + {.} path without file extension + + {/.} basename without file extension + + -X, --exec-batch command + Execute command with all search results at once. A single + occurence of the following placeholders is authorized and + sub stituted by the paths derived from the search results before the + command is executed: + + {} path + + {/} basename + + {//} parent directory + + {.} path without file extension + + {/.} basename without file extension + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-ff-run-grep]|Run grep (`\\[universal-argument]' to recurse). +|\\[helm-ff-run-zgrep]|Run zgrep. +|\\[helm-ff-run-pdfgrep]|Run PDFgrep on marked files. +|\\[helm-ff-run-copy-file]|Copy file(s) +|\\[helm-ff-run-rename-file]|Rename file(s). +|\\[helm-ff-run-symlink-file]|Symlink file(s). +|\\[helm-ff-run-hardlink-file]|Hardlink file(s). +|\\[helm-ff-run-delete-file]|Delete file(s). +|\\[helm-ff-run-byte-compile-file]|Byte compile Elisp file(s) (`\\[universal-argument]' to load). +|\\[helm-ff-run-load-file]|Load Elisp file(s). +|\\[helm-ff-run-ediff-file]|Ediff file. +|\\[helm-ff-run-ediff-merge-file]|Ediff-merge file. +|\\[helm-ff-run-switch-other-window]|Switch to other window. +|\\[helm-ff-properties-persistent]|Show file properties. +|\\[helm-ff-run-open-file-externally]|Open file with external program (`\\[universal-argument]' to choose). +|\\[helm-ff-run-open-file-with-default-tool]|Open file externally with default tool. +|\\[helm-ff-run-insert-org-link]|Insert org link. +|\\[helm-fd-previous-directory]|Move to previous directory. +|\\[helm-fd-next-directory]|Move to next directory.") + +;;; Generic file help - Used by locate. +;; +;; +(defvar helm-generic-file-help-message + "* Helm Generic files + +** Tips + +*** Locate + +You can append to the search pattern any of the locate command line options, +e.g. -b, -e, -n , etc. See the locate(1) man page for more details. + +Some other sources (at the moment \"recentf\" and \"file in current directory\") +support the -b flag for compatibility with locate when they are used with it. + +When you enable fuzzy matching on locate with `helm-locate-fuzzy-match', the +search will be performed on basename only for efficiency (so don't add \"-b\" at +prompt). As soon as you separate the patterns with spaces, fuzzy matching will +be disabled and search will be done on the full filename. Note that in +multi-match, fuzzy is completely disabled, which means that each pattern is a +match regexp (i.e. \"helm\" will match \"helm\" but \"hlm\" will *not* match +\"helm\"). + +NOTE: On Windows use Everything with its command line ~es~ as a replacement of locate. +See [[https://github.com/emacs-helm/helm/wiki/Locate#windows][Locate on Windows]] + +*** Browse project + +When the current directory is not under version control, don't forget to refresh +the cache when files have been added/removed in the directory. + +*** Find command + +Recursively search files using the \"find\" shell command. + +Candidates are all filenames that match all given globbing patterns. This +respects the options `helm-case-fold-search' and +`helm-findutils-search-full-path'. + +You can pass arbitrary \"find\" options directly after a \"*\" separator. +For example, this would find all files matching \"book\" that are larger +than 1 megabyte: + + book * -size +1M + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-ff-run-toggle-basename]|Toggle basename. +|\\[helm-ff-run-grep]|Run grep (`\\[universal-argument]' to recurse). +|\\[helm-ff-run-zgrep]|Run zgrep. +|\\[helm-ff-run-pdfgrep]|Run PDFgrep on marked files. +|\\[helm-ff-run-copy-file]|Copy file(s) +|\\[helm-ff-run-rename-file]|Rename file(s). +|\\[helm-ff-run-symlink-file]|Symlink file(s). +|\\[helm-ff-run-hardlink-file]|Hardlink file(s). +|\\[helm-ff-run-delete-file]|Delete file(s). +|\\[helm-ff-run-byte-compile-file]|Byte compile Elisp file(s) (`\\[universal-argument]' to load). +|\\[helm-ff-run-load-file]|Load Elisp file(s). +|\\[helm-ff-run-ediff-file]|Ediff file. +|\\[helm-ff-run-ediff-merge-file]|Ediff-merge file. +|\\[helm-ff-run-switch-other-window]|Switch to other window. +|\\[helm-ff-properties-persistent]|Show file properties. +|\\[helm-ff-run-open-file-externally]|Open file with external program (`\\[universal-argument]' to choose). +|\\[helm-ff-run-open-file-with-default-tool]|Open file externally with default tool. +|\\[helm-ff-run-insert-org-link]|Insert org link.") + +;;; Grep help +;; +;; +(defvar helm-grep-help-message + "* Helm Grep + +** Tips + +With Helm supporting Git-grep and AG/RG, you are better off using +one of them for recursive searches, keeping grep or ack-grep to +grep individual or marked files. See [[Helm AG][Helm AG]]. + +*** Meaning of the prefix argument +**** With grep or ack-grep + +Grep recursively, in this case you are +prompted for types (ack-grep) or for wild cards (grep). + +**** With AG or RG + +the prefix arg allows you to specify a type of file to search in. + +*** You can use wild cards when selecting files (e.g. \"*.el\") + +Note that a way to grep specific files recursively is to use +e.g. \"**.el\" to select files, the variable `helm-file-globstar' +controls this (it is non nil by default), however it is much +slower than using grep recusively (see helm-find-files +documentation about this feature). + +*** Grep hidden files + +You may want to customize your command line for grepping hidden +files, for AG/RG use \"--hidden\", see man page +of your backend for more infos. + +*** You can grep in different directories by marking files or using wild cards + +*** You can save the result in a `helm-grep-mode' buffer + +See [[Commands][commands]] below. + +Once in that buffer you can use [[https://github.com/mhayashi1120/Emacs-wgrep][emacs-wgrep]] (external package not bundled with Helm) +to edit your changes, for Helm the package name is `wgrep-helm', it is hightly recommended. + +Type `g' to update (revert) the buffer (after saving your buffer's changes to file). + +NOTE: `next-error' is available from this `helm-grep-mode' buffer. + +When you are running `next-error' from elsewhere, you can update +the buffer with `helm-revert-next-error-last-buffer' (up to you +to bind it to a convenient key). + +*** Helm-grep supports multi-matching + +\(Starting from version 1.9.4.) + +Simply add a space between each pattern as for most Helm commands. + +NOTE: Depending the regexp you use it may match as well the +filename, this because we pipe the first grep command which send +the filename in output. + +*** See full path of selected candidate + +Add (helm-popup-tip-mode 1) in your init file or enable it +interactively with M-x helm-popup-tip-mode, however it is +generally enough to just put your mouse cursor over candidate. + +*** Open file in other window + +The command \\\\[helm-grep-run-other-window-action] allow you to open file +in other window horizontally or vertically if a prefix arg is supplied. + +*** Performance over TRAMP + +Grepping works but it is badly supported as TRAMP doesn't support multiple +processes running in a short delay (less than 5s) among other things. + +Helm uses a special hook to suspend the process automatically while you are +typing. Even if Helm handles this automatically by delaying each process by 5s, +you are adviced to this manually by hitting `\\\\[helm-toggle-suspend-update]' (suspend process) before +typing, and hit again `\\\\[helm-toggle-suspend-update]' when the regexp is ready to send to the remote +process. For simple regexps, there should be no need for this. + +Another solution is to not use TRAMP at all and mount your remote file system via +SSHFS. + +* Helm GID + +Still supported, but mostly deprecated, using AG/RG or Git-grep +is much more efficient, also `id-utils' seems no more maintained. + +** Tips + +Helm-GID reads the database created with the `mkid' command from id-utils. +The name of the database file can be customized with `helm-gid-db-file-name', it +is usually \"ID\". + +Helm-GID use the symbol at point as default-input. This command is also +accessible from `helm-find-files' which allow you to navigate to another +directory to consult its database. + +Note: Helm-GID supports multi-matches but only the last pattern entered will be +highlighted since there is no ~--color~-like option in GID itself. + +* Helm AG + +** Tips + +Helm-AG is different from grep or ack-grep in that it works on a +directory recursively and not on a list of files. It is called +helm-AG but it support several backend, namely AG, RG and PT. +Nowaday the best backend is Ripgrep aka RG, it is the fastest and +is actively maintained, see `helm-grep-ag-command' and +`helm-grep-ag-pipe-cmd-switches' to configure it. + +You can ignore files and directories with a \".agignore\" file, local to a +directory or global when placed in the home directory. (See the AG man page for +more details.) That file follows the same syntax as `helm-grep-ignored-files' +and `helm-grep-ignored-directories'. + +As always you can access Helm AG from `helm-find-files'. + +Starting with version 0.30, AG accepts one or more TYPE arguments on its command +line. Helm provides completion on these TYPE arguments when available with your +AG version. Use a prefix argument when starting a Helm-AG session to enable this +completion. See RG and AG man pages on how to add new types. + + +Note: You can mark several types to match in the AG query. The first AG +versions providing this feature allowed only one type, so in this case only the +last mark will be used. + +* Helm git-grep + +Helm-git-grep searches the current directory, i.e. the default directory or the +directory in Helm-find-files. If this current directory is a subdirectory of a +project and you want to also match parent directories (i.e the whole project), +use a prefix argument. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-goto-next-file]|Next File. +|\\[helm-goto-precedent-file]|Previous File. +|\\[helm-yank-text-at-point]|Yank text at point in minibuffer. +|\\[helm-grep-run-other-window-action]|Jump to other window. +|\\[helm-grep-run-other-frame-action]|Jump to other frame. +|\\[helm-grep-run-default-action]|Run default action (same as `RET'). +|\\[helm-grep-run-save-buffer]|Save to a `helm-grep-mode' enabled buffer.") + +;;; PDF grep help +;; +;; +(defvar helm-pdfgrep-help-message + "* Helm PDFgrep Map + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-goto-next-file]|Next file. +|\\[helm-goto-precedent-file]|Previous file. +|\\[helm-yank-text-at-point]|Yank text at point in minibuffer.") + +;;; Etags help +;; +;; +(defvar helm-etags-help-message + "* Helm Etags Map + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-goto-next-file]|Next file. +|\\[helm-goto-precedent-file]|Previous file. +|\\[helm-yank-text-at-point]|Yank text at point in minibuffer.") + +;;; UCS help +;; +;; +(defvar helm-ucs-help-message + "* Helm UCS + +** Tips + +Use commands below to insert unicode characters in current buffer without +leaving Helm. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-ucs-persistent-insert]|Insert character. +|\\[helm-ucs-persistent-forward]|Forward character. +|\\[helm-ucs-persistent-backward]|Backward character. +|\\[helm-ucs-persistent-delete]|Delete character backward. +|\\[helm-ucs-persistent-insert-space]|Insert space.") + +;;; Bookmark help +;; +;; +(defvar helm-bookmark-help-message + "* Helm bookmark name + +When `helm-bookmark-use-icon' is non nil and `all-the-icons' +package is installed icons before candidates will be displayed. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-bookmark-run-jump-other-window]|Jump other window. +|\\[helm-bookmark-run-delete]|Delete bookmark. +|\\[helm-bookmark-run-edit]|Edit bookmark. +|\\[helm-bookmark-toggle-filename]|Toggle bookmark location visibility.") + +;;; Eshell command on file help +;; +;; +(defvar helm-esh-help-message + "* Helm Eshell on file + +** Tips + +*** Pass extra arguments after filename + +Normally the command or alias will be called with file as argument. For instance + + candidate_file + +But you can also pass an argument or more after \"candidate_file\" like this: + + %s [extra_args] + +\"candidate_file\" will be added at \"%s\" and the command will look at this: + + candidate_file [extra_args] + +**** Use placeholders in extra arguments + +placeholder for file without extension: \\@ +placeholder for incremental number: \\# + +\"candidate_file\" will be added at \"%s\" and \\@ but without extension. + + + +\"candidate_file\" will be added at \"%s\" and \\# will be replaced by an incremental number. + + %s \\# + +Here examples: + +Say you want to use the =convert= command to convert all your .png files in a directory to .jpg. + +This will convert all your files to jpg keeping the same basename. + + convert %s \\@.jpg + +This will convert all your files to foo-001.jpg, foo-002.jpg etc... + + convert %s foo-\\#.jpg + +You can of course combine both placeholders if needed. + + convert %s \\@-\\#.jpg + +*** Specify marked files as arguments + +When you have marked files and your command support only one file +as arg, helm will execute command sequencially on each file like +this: + +Example: + + file1 + file2 + ...etc + +When you have marked files and your command accept many files at +once helm will run your command with all files at once like this: + +Example: + + file1 file2 etc... + +The two use case above are applied automatically by Helm +depending if your command is an eshell alias which value ends by +'$1' or '$*'. If your command is not an alias, i.e. you entered +an arbitrary command on prompt with '%s' to specify filenames, +you will have to pass one prefix argument from the command +selection buffer. + +Note: This does not work on remote files. + +With two prefix-args the output is printed to the +`current-buffer', the command being executed in the same +conditions as described above. +NOTE: If your command is not an alias, you can't pass all files at once and print in current buffer at the same time. +Also note that running multiple files at once is not supported with remote files. + +*** Run eshell commands asynchronously + +You can run your commands asynchronously by adding \"&\" at end +of any commands, e.g. \"foo %s &\". You can also directly setup +your alias in the eshell alias file with e.g. \"alias foo $1 &\". + +** Commands +\\") + +;;; Ido virtual buffer help +;; +;; +(defvar helm-buffers-ido-virtual-help-message + "* Helm Ido virtual buffers + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-ff-run-switch-other-window]|Switch to other window. +|\\[helm-ff-run-switch-other-frame]|Switch to other frame. +|\\[helm-ff-run-grep]|Grep file. +|\\[helm-ff-run-zgrep]|Zgrep file. +|\\[helm-ff-run-delete-file]|Delete file. +|\\[helm-ff-run-open-file-externally]|Open file externally.") + +;;; helm-occur help +;; +;; +(defvar helm-moccur-help-message + "* Helm Moccur + +** Tips + +*** Searching in many buffers + +Start from `helm-buffers-list' or `helm-mini', mark some buffers and hit \\\\[helm-execute-persistent-action]' (persistent-action), to do it repeatedly +you can use `\\\\[helm-follow-action-forward]' and `\\\\[helm-follow-action-backward]' or enable `helm-follow-mode' with `\\\\[helm-follow-mode]'. +Follow mode is enabled by default in helm-occur. + +*** Switch to buffer in other window + +The command \\\\[helm-moccur-run-goto-line-ow] allow you to switch to buffer +in other window horizontally or vertically if a prefix arg is supplied. + +*** Save the results + +Similarly to Helm-grep, you can save the results with `\\\\[helm-occur-run-save-buffer]'. +Once in the saved buffer, you can edit it, see [[Edit a saved buffer][below]]. + +Of course if you don't save the results, you can resume the Helm session with +`helm-resume'. + +*** Refresh the resumed session + +When the buffer(s) where you ran helm-(m)occur get(s) modified, the Helm buffer +will flash red as a warning. You can refresh the buffer by running `\\\\[helm-refresh]'. +This can be done automatically by customizing `helm-moccur-auto-update-on-resume'. + +*** Refresh a saved buffer + +Type `g' to update (revert) the buffer. + +When you are running `next-error' from elsewhere, you can update +the buffer with `helm-revert-next-error-last-buffer' (up to you +to bind it to a convenient key). + +*** Edit a saved buffer + +First, install wgrep (https://github.com/mhayashi1120/Emacs-wgrep) and then: + +1) `C-c C-p' (`wgrep-change-to-wgrep-mode') to edit the buffer(s). +2) `C-x C-s' to save your changes. + +Tip: Use the excellent iedit (https://github.com/victorhge/iedit) to modify all +occurences at once in the buffer. + +NOTE: `next-error' is available from this `helm-occur-mode' buffer. + +*** Search in region + +When searching in current-buffer with `helm-occur', if a region +is found helm will search in this region only. If you marked +this region with `mark-defun' the symbol that was at point before +marking defun will be used when `helm-source-occur' is member of +`helm-sources-using-default-as-input'. + +*** Switch to next or previous source + +See [[Moving in `helm-buffer'][Moving in `helm-buffer']]. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-occur-run-goto-line-ow]|Go to line in other window. +|\\[helm-occur-run-goto-line-of]|Go to line in new frame. +|\\[helm-occur-run-save-buffer]|Save results in new buffer.") +;;; Helm Top +;; +;; +(defvar helm-top-help-message + "* Helm Top + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-top-run-sort-by-com]|Sort by commands. +|\\[helm-top-run-sort-by-cpu]|Sort by CPU usage. +|\\[helm-top-run-sort-by-user]|Sort alphabetically by user. +|\\[helm-top-run-sort-by-mem]|Sort by memory.") + +;;; Helm Elisp package +;; +;; +(defvar helm-el-package-help-message + "* Helm Elisp package + +** Tips + +*** Compile all your packages asynchronously + +If you use async (if you have installed Helm from MELPA you do), only \"helm\", +\"helm-core\", and \"magit\" are compiled asynchronously. If you want all your +packages compiled asynchronously, add this to your init file: + + (setq async-bytecomp-allowed-packages '(all)) + +*** Upgrade Elisp packages + +On initialization (when Emacs is fetching packages on remote), if Helm finds +packages to upgrade, it will start in the upgradable packages view showing the packages +available for upgrade. + +On subsequent runs, you will have to refresh the list with `C-c \\[universal-argument]'. If Helm +finds upgrades you can switch to upgrade view (see below) to see what packages +are available for upgrade or simply hit `C-c U' to upgrade them all. + +To see upgradable packages hit `M-U'. + +Then you can install all upgradable packages with the \"upgrade all\" action +\(`C-c \\[universal-argument]'), or upgrade only specific packages by marking them and running the +\"upgrade\" action (visible only when there are upgradable packages). Of course +you can upgrade a single package by just running the \"upgrade\" action without +marking it (`C-c u' or `RET') . + +\*Warning:* You are strongly advised to \*restart* Emacs after \*upgrading* packages. + +*** Meaning of flags prefixing packages + +\(Emacs ≥25) + +- The flag \"S\" that prefixes package names means that the packages belong to `package-selected-packages'. + +- The flag \"U\" that prefix package names mean that this package is no more needed. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-el-package-show-all]|Show all packages. +|\\[helm-el-package-show-installed]|Show installed packages only. +|\\[helm-el-package-show-uninstalled]|Show non-installed packages only. +|\\[helm-el-package-show-upgrade]|Show upgradable packages only. +|\\[helm-el-package-show-built-in]|Show built-in packages only. +|\\[helm-el-run-package-install]|Install package(s). +|\\[helm-el-run-package-reinstall]|Reinstall package(s). +|\\[helm-el-run-package-uninstall]|Uninstall package(s). +|\\[helm-el-run-package-upgrade]|Upgrade package(s). +|\\[helm-el-run-package-upgrade-all]|Upgrade all packages. +|\\[helm-el-run-visit-homepage]|Visit package homepage.") + +;;; Helm M-x +;; +;; +(defvar helm-M-x-help-message + "* Helm M-x + +** Tips + +*** Display docstring without quitting session (persistent action) + +You can get help on any command with persistent action (\\\\[helm-execute-persistent-action]) + +*** Display short docstring in helm buffer + +You can toggle short docstring description with \\\\[helm-M-x-toggle-short-doc]. +if you want this at startup you can configure `helm-M-x-show-short-doc'. + +NOTE: helm-M-x will be slower with this enabled. + +*** History source + +Helm-M-x is displaying two sources, one for the commands +themselves and one for the command history, more exactly +`extended-command-history', by default the history source is +displayed in first position, however you can put it in second +position if you don't like that by customizing +`helm-M-x-reverse-history'. + +**** Duplicate entries in helm-M-x history + +helm-M-x history obey to history variables, if you have +duplicates in your helm-M-x history set `history-delete-duplicates' to non nil. + +**** Number of entries in history + +The number of entries saved is controlled by `history-length' +global value, however if you want a different value for +`extended-command-history' e.g. 50 you can add to your config: + + (put 'extended-command-history 'history-length 50) + +*** Enabled modes are highlighted in helm-M-x + +*** Prefix arguments + +You can pass prefix arguments *after* starting `helm-M-x'. A mode-line +counter will display the number of given prefix arguments. + +If you pass prefix arguments before running `helm-M-x', it will +be displayed in the prompt. +The first `\\\\[universal-argument]' after `helm-M-x' clears those prefix arguments. + +NOTE: When you specify prefix arguments once `helm-M-x' is +started, the prefix argument apply on the next command, so if you +hit RET, it will apply on the selected command, but if you type a +new character at prompt to narrow down further candidates, the +prefix arg will apply to `self-insert-command' (e.g. if you type +`C-u e' \"eeee\" will be inserted in prompt) so select the +command you want to execute before specifying prefix arg. + +** Commands +\\ +|Keys|Description +|-----------+------------| +|\\[helm-M-x-universal-argument]|Universal argument for selected command +|\\[helm-M-x-toggle-short-doc]|Toggle details on commands") + + +;;; Helm imenu +;; +;; +(defvar helm-imenu-help-message + "* Helm Imenu + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-imenu-next-section]|Go to next section. +|\\[helm-imenu-previous-section]|Go to previous section.") + +;;; Helm colors +;; +;; +(defvar helm-colors-help-message + "* Helm colors + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-color-run-insert-name]|Insert the entry name. +|\\[helm-color-run-kill-name]|Kill the entry name. +|\\[helm-color-run-insert-rgb]|Insert entry in RGB format. +|\\[helm-color-run-kill-rgb]|Kill entry in RGB format.") + +;;; Helm Semantic +;; +;; +(defvar helm-semantic-help-message + "* Helm Semantic + +** Commands +\\") + +;;; Helm kmacro +;; +;; +(defvar helm-kmacro-help-message + "* Helm kmacro + +** Tips + +- Start recording a kmacro with `f3'. +- End the kmacro recording with `f4'. +- Run `helm-execute-kmacro' to list all your kmacros. + +Use persistent action to run your kmacro as many times as needed. +You can browse the kmacros with `helm-next-line' and `helm-previous-line'. + +Note: You can't record keys running Helm commands except `helm-M-x', under the +condition that you don't choose a command using Helm completion. + +** Commands +\\") + +;;; Kill ring +;; +;; +(defvar helm-kill-ring-help-message + "* Helm kill ring + +** Tips + +Every Helm session lets you save a candidate to the kill-ring / clipboard / +primary-selection with `\\\\[helm-kill-selection-and-quit]'. + +To save space, Helm-kill-ring truncates the candidates longer than +`helm-kill-ring-max-offset'. +`\\\\[helm-kill-ring-kill-selection]' then saves the whole +text and not the truncated value. The view of truncated candidates can be +toggled; see the command list below. + +As opposed to `yank', numeric prefix arguments are ignored with +`helm-show-kill-ring': there is no need for them since selection happens within +Helm. Moreover Helm has [[Shortcuts for executing the default action on the n-th candidate][Shortcuts for executing the default action on the n-th candidate]]. + +It is recommended to globally bind `M-y' to `helm-show-kill-ring'. Once in the +Helm-kill-ring session you can navigate to next/previous line with `M-y' and +`M-u' for convenience. Of course `\\[helm-next-line]' and `\\[helm-previous-line]' are still available. + +It is possible to delete candidates from the kill ring with `\\\\[helm-kill-ring-delete]' +but also persistently with `\\\\[helm-kill-ring-run-persistent-delete]'. + +You can concatenate marked candidates and yank them in the current +buffer, thus creating a new entry in the kill ring. Candidates are +concatenated with `helm-kill-ring-separator' as default but you can +change interactively the separator while yanking by using two prefix +args. When you have something else than \"\\n\" as default value for +`helm-kill-ring-separator' and you want to use \"\\n\" from prompt, use +`C-q C-j' to enter a newline in prompt. + +To not push a new entry in the kill ring, use `\\\\[helm-copy-to-buffer]' instead of RET +\(note that you can't change separator with this). + +When inserting candidates with the default action (`RET'), `point' is placed at +the end of the candidate and `mark' at the beginning. You can revert this behavior +by using a prefix argument, i.e. `C-u RET', like the regular `yank' command does. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-next-line]|Next line. +|\\[helm-previous-line]|Previous line. +|\\[helm-kill-ring-delete]|Delete entry. +|\\[helm-kill-ring-toggle-truncated]|Toggle truncated view of candidate. +|\\[helm-kill-ring-kill-selection]|Kill non-truncated of selection.") + +;;; Completing-read +;; +(defun helm-comp-read-help-message () + (let ((com (assoc-default 'name (helm-get-current-source)))) + (format + "* Helm completing-read completion for `%s' + +Command `%s' is using a `completing-read' for completion on your input, +this completion have been \"helmized\" because you have enabled [[Helm mode][helm-mode]]'. + +** Tips + +*** Disabling or use something else than helm for completion of some commands + +You can disable helm completion or use something else for specific commands of your choice, +for this customize variable `helm-completing-read-handlers-alist'. + +*** Exiting minibuffer with empty string + +You can exit minibuffer with empty string with \\\\[helm-cr-empty-string]. +It is useful when some commands are prompting continuously until you enter an empty prompt. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-cr-empty-string]|Exit minibuffer with empty string." + com com))) + + +;;; Mode line strings +;; +;; +;;;###autoload +(defvar helm-comp-read-mode-line "\ +\\\ +C/\\[helm-cr-empty-string]:Empty \ +\\\ +\\[helm-help]:Help \ +\\[helm-select-action]:Act \ +\\[helm-maybe-exit-minibuffer]/\ +f1/f2/f-n:NthAct \ +\\[helm-toggle-suspend-update]:Tog.suspend \ +\\[helm-customize-group]:Conf") + +;;;###autoload +(defvar helm-read-file-name-mode-line-string "\ +\\\ +\\[helm-help]:Help \ +C/\\[helm-cr-empty-string]:Empty \ +\\\ +\\[helm-select-action]:Act \ +\\[helm-maybe-exit-minibuffer]/\ +f1/f2/f-n:NthAct \ +\\[helm-toggle-suspend-update]:Tog.suspend \ +\\[helm-customize-group]:Conf" + "String displayed in mode-line in `helm-source-find-files'.") + +;;;###autoload +(defvar helm-top-mode-line "\ +\\\ +\\[helm-help]:Help \ +\\\ +\\[helm-select-action]:Act \ +\\[helm-maybe-exit-minibuffer]/\ +f1/f2/f-n:NthAct \ +\\[helm-toggle-suspend-update]:Tog.suspend \ +\\[helm-customize-group]:Conf") + + +(provide 'helm-help) + +;;; helm-help.el ends here diff --git a/code/elpa/helm-20220822.659/helm-id-utils.el b/code/elpa/helm-20220822.659/helm-id-utils.el new file mode 100644 index 0000000..0ff71e5 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-id-utils.el @@ -0,0 +1,125 @@ +;;; helm-id-utils.el --- Helm interface for id-utils. -*- lexical-binding: t -*- + +;; Copyright (C) 2015 ~ 2020 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'helm-grep) +(require 'helm-help) + +(defgroup helm-id-utils nil + "ID-Utils related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-gid-program "gid" + "Name of gid command (usually `gid'). +For Mac OS X users, if you install GNU coreutils, the name `gid' +might be occupied by `id' from GNU coreutils, and you should set +it to correct name (or absolute path). For example, if using +MacPorts to install id-utils, it should be `gid32'." + :group 'helm-id-utils + :type 'file) + +(defcustom helm-gid-db-file-name "ID" + "Name of a database file created by `mkid' command from `ID-utils'." + :group 'helm-id-utils + :type 'string) + +(defun helm-gid-candidates-process () + (let* ((patterns (helm-mm-split-pattern helm-pattern)) + (default-com (format "%s -r %s" helm-gid-program + (shell-quote-argument (car patterns)))) + (cmd (helm-aif (cdr patterns) + (concat default-com + (cl-loop for p in it + concat (format " | grep --color=always %s" + (shell-quote-argument p)))) + default-com)) + (proc (start-process-shell-command + "gid" helm-buffer cmd))) + (set (make-local-variable 'helm-grep-last-cmd-line) cmd) + (prog1 proc + (set-process-sentinel + proc (lambda (_process event) + (when (string= event "finished\n") + (helm-maybe-show-help-echo) + (with-helm-window + (setq mode-line-format + '(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format "[Helm Gid process finished - (%s results)]" + (max (1- (count-lines + (point-min) (point-max))) + 0)) + 'face 'helm-locate-finish)))) + (force-mode-line-update)) + (helm-log "Error: Gid %s" + (replace-regexp-in-string "\n" "" event)))))))) + +(defun helm-gid-filtered-candidate-transformer (candidates _source) + ;; "gid -r" may add dups in some rare cases. + (cl-loop for c in (helm-fast-remove-dups candidates :test 'equal) + collect (helm-grep--filter-candidate-1 c))) + +(defclass helm-gid-source (helm-source-async) + ((header-name + :initform + (lambda (name) + (concat name " [" (helm-get-attr 'db-dir) "]"))) + (db-dir :initarg :db-dir + :initform nil + :custom string + :documentation " Location of ID file.") + (candidates-process :initform #'helm-gid-candidates-process) + (filtered-candidate-transformer + :initform #'helm-gid-filtered-candidate-transformer) + (candidate-number-limit :initform 99999) + (action :initform (helm-make-actions + "Find File" 'helm-grep-action + "Find file other frame" 'helm-grep-other-frame + "Save results in grep buffer" 'helm-grep-save-results + "Find file other window" 'helm-grep-other-window)) + (persistent-action :initform 'helm-grep-persistent-action) + (history :initform 'helm-grep-history) + (nohighlight :initform t) + (help-message :initform 'helm-grep-help-message) + (requires-pattern :initform 2))) + +;;;###autoload +(defun helm-gid () + "Preconfigured `helm' for `gid' command line of `ID-Utils'. +Need A database created with the command `mkid' above +`default-directory'. +Need id-utils as dependency which provide `mkid', `gid' etc.. +See ." + (interactive) + (let* ((db (locate-dominating-file + default-directory + helm-gid-db-file-name)) + (helm-grep-default-directory-fn + (lambda () default-directory)) + (helm-maybe-use-default-as-input t)) + (cl-assert db nil "No DataBase found, create one with `mkid'") + (helm :sources (helm-make-source "Gid" 'helm-gid-source + :db-dir db) + :buffer "*helm gid*" + :keymap helm-grep-map + :truncate-lines helm-grep-truncate-lines))) + +(provide 'helm-id-utils) + +;;; helm-id-utils ends here diff --git a/code/elpa/helm-20220822.659/helm-imenu.el b/code/elpa/helm-20220822.659/helm-imenu.el new file mode 100644 index 0000000..36aa978 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-imenu.el @@ -0,0 +1,534 @@ +;;; helm-imenu.el --- Helm interface for Imenu -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-lib) +(require 'imenu) +(require 'helm-utils) +(require 'helm-help) + +(defvar all-the-icons-default-adjust) +(defvar all-the-icons-scale-factor) + +(declare-function which-function "which-func") +(declare-function all-the-icons-material "ext:all-the-icons.el") +(declare-function all-the-icons-octicon "ext:all-the-icons.el") +(declare-function all-the-icons-faicon "ext:all-the-icons.el") +(declare-function all-the-icons-wicon "ext:all-the-icons.el") + + +(defgroup helm-imenu nil + "Imenu related libraries and applications for Helm." + :group 'helm) + +(defcustom helm-imenu-delimiter " / " + "Delimit types of candidates and their value in `helm-buffer'." + :group 'helm-imenu + :type 'string) + +(defcustom helm-imenu-execute-action-at-once-if-one + #'helm-imenu--execute-action-at-once-p + "Goto the candidate when only one is remaining." + :group 'helm-imenu + :type 'function) + +(defcustom helm-imenu-all-buffer-assoc nil + "Major mode association alist for `helm-imenu-in-all-buffers'. +Allow `helm-imenu-in-all-buffers' searching in these associated +buffers even if they are not derived from each other. The alist +is bidirectional, i.e. no need to add \\='((foo . bar) (bar . foo)), +only \\='((foo . bar)) is needed." + :type '(alist :key-type symbol :value-type symbol) + :group 'helm-imenu) + +(defcustom helm-imenu-in-all-buffers-separate-sources t + "Display imenu index of each buffer in its own source when non-nil. + +When nil all candidates are displayed in a single source. + +NOTE: Each source will have as name \"Imenu \". +`helm-source-imenu-all' will not be set, however it will continue +to be used as a flag for using default as input. If you do not +want this behavior, remove it from +`helm-sources-using-default-as-input' even if not using a single +source to display imenu in all buffers." + :type 'boolean + :group 'helm-imenu) + +(defcustom helm-imenu-type-faces + '(("^Variables$" . font-lock-variable-name-face) + ("^\\(Function\\|Functions\\|Defuns\\)$" . font-lock-function-name-face) + ("^\\(Types\\|Provides\\|Requires\\|Classes\\|Class\\|Includes\\|Imports\\|Misc\\|Code\\)$" . font-lock-type-face)) + "Faces for showing type in helm-imenu. +This is a list of cons cells. The cdr of each cell is a face to +be used, and it can also just be like \\='(:foreground +\"yellow\"). Each car is a regexp match pattern of the imenu type +string." + :group 'helm-faces + :type '(repeat + (cons + (regexp :tag "Imenu type regexp pattern") + (sexp :tag "Face")))) + +(defcustom helm-imenu-extra-modes nil + "Extra modes where `helm-imenu-in-all-buffers' should look into." + :group 'helm-imenu + :type '(repeat symbol)) + +(defcustom helm-imenu-hide-item-type-name nil + "Hide display name of imenu item type along with the icon when non nil. + +This value can be toggled with \\\\[helm-imenu-toggle-type-view]." + :group 'helm-imenu + :type 'boolean) + +(defcustom helm-imenu-use-icon nil + "Display an icon from all-the-icons package when non nil." + :group 'helm-imenu + :type 'boolean) + +(defcustom helm-imenu-icon-type-alist + '(("Array" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Array" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Boolean" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Boolean" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Class" . (all-the-icons-octicon "package" :face font-lock-type-face)) + ("Class" . (all-the-icons-octicon "package" :face font-lock-type-face)) + ("Color" . (all-the-icons-material "color_lens" :face font-lock-builtin-face)) + ("Colors" . (all-the-icons-material "color_lens" :face font-lock-builtin-face)) + ("Constant" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Constants" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Constructor" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Constructors" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Enum Member" . (all-the-icons-octicon "three-bars" :face font-lock-type-face)) + ("Enum Members" . (all-the-icons-octicon "three-bars" :face font-lock-type-face)) + ("Enum" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Enums" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Event" . (all-the-icons-wicon "lightning" :face font-lock-builtin-face)) + ("Events" . (all-the-icons-wicon "lightning" :face font-lock-builtin-face)) + ("Field" . (all-the-icons-octicon "three-bars" :face font-lock-type-face)) + ("Fields" . (all-the-icons-octicon "three-bars" :face font-lock-type-face)) + ("File" . (all-the-icons-faicon "file" :face font-lock-variable-name-face)) + ("Files" . (all-the-icons-faicon "file" :face font-lock-variable-name-face)) + ("Folder" . (all-the-icons-faicon "folder" :face font-lock-variable-name-face)) + ("Folders" . (all-the-icons-faicon "folder" :face font-lock-variable-name-face)) + ("Interface" . (all-the-icons-octicon "package" :face font-lock-builtin-face)) + ("Interfaces" . (all-the-icons-octicon "package" :face font-lock-builtin-face)) + ("Keyword" . (all-the-icons-octicon "key" :face font-lock-builtin-face)) + ("Keywords" . (all-the-icons-octicon "key" :face font-lock-builtin-face)) + ("Method" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Methods" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Defun" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Defuns" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Fn" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Fns" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Function" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Functions" . (all-the-icons-faicon "cube" :face font-lock-function-name-face)) + ("Misc" . (all-the-icons-faicon "globe" :face font-lock-function-name-face)) + ("Miscs" . (all-the-icons-faicon "globe" :face font-lock-function-name-face)) + ("Module" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face)) + ("Modules" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face)) + ("Numeric" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Numeric" . (all-the-icons-material "crop" :face font-lock-builtin-face)) + ("Object" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face)) + ("Objects" . (all-the-icons-faicon "angle-double-right" :face font-lock-builtin-face)) + ("Operator" . (all-the-icons-faicon "calculator" :face font-lock-builtin-face)) + ("Operators" . (all-the-icons-faicon "calculator" :face font-lock-builtin-face)) + ("Property" . (all-the-icons-octicon "book" :face font-lock-variable-name-face)) + ("Properties" . (all-the-icons-octicon "book" :face font-lock-variable-name-face)) + ("Reference" . (all-the-icons-octicon "book" :face font-lock-variable-name-face)) + ("References" . (all-the-icons-octicon "book" :face font-lock-variable-name-face)) + ("Snippet" . (all-the-icons-material "border_style" :face font-lock-variable-name-face)) + ("Snippet" . (all-the-icons-material "border_style" :face font-lock-variable-name-face)) + ("String" . (all-the-icons-material "text_fields" :face font-lock-variable-name-face)) + ("Strings" . (all-the-icons-material "text_fields" :face font-lock-variable-name-face)) + ("Struct" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Structs" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Text" . (all-the-icons-material "text_fields" :face font-lock-variable-name-face)) + ("Texts" . (all-the-icons-material "text_fields" :face font-lock-variable-name-face)) + ("Top level" . (all-the-icons-faicon "globe" :face font-lock-function-name-face)) + ("Trait" . (all-the-icons-octicon "package" :face font-lock-builtin-face)) + ("Traits" . (all-the-icons-octicon "package" :face font-lock-builtin-face)) + ("Type" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Types" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Type Parameter" . (all-the-icons-material "code" :face font-lock-type-face)) + ("Type Parameters" . (all-the-icons-material "code" :face font-lock-type-face)) + ("Unit" . (all-the-icons-faicon "bar-chart" :face font-lock-builtin-face)) + ("Units" . (all-the-icons-faicon "bar-chart" :face font-lock-builtin-face)) + ("Value" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Values" . (all-the-icons-faicon "cog" :face font-lock-type-face)) + ("Variable" . (all-the-icons-octicon "book" :face font-lock-variable-name-face)) + ("Variables" . (all-the-icons-octicon "book":face font-lock-variable-name-face))) + "An alist of types associated with a sexp returning an icon. +The sexp should be an `all-the-icons' function with its args." + :type '(alist :key-type string :value-type sexp) + :group 'helm-imenu) + +(defcustom helm-imenu-default-type-sexp + '(all-the-icons-faicon "globe" :face font-lock-function-name-face) + "Default sexp to use when no type for an object is found." + :type 'sexp + :group 'helm-imenu) + +;;; keymap +(defvar helm-imenu-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-") 'helm-imenu-next-section) + (define-key map (kbd "M-") 'helm-imenu-previous-section) + (define-key map (kbd "C-]") 'helm-imenu-toggle-type-view) + map)) + +(defun helm-imenu-toggle-type-view () + "Toggle candidate type view." + (interactive) + (with-helm-window + (setq helm-imenu-hide-item-type-name (not helm-imenu-hide-item-type-name)) + (let* ((sel (substring (helm-get-selection nil 'withprop) + (if helm-imenu-use-icon 2 0))) + (type (get-text-property 1 'type-name sel))) + (setq sel (substring-no-properties sel)) + (helm-force-update (if helm-imenu-hide-item-type-name + (format "^[ ]*%s$" + (car (last (split-string + sel helm-imenu-delimiter t)))) + (format "^[ ]*%s / %s$" + type sel)))))) +(put 'helm-imenu-toggle-type-view 'no-helm-mx t) + +(defcustom helm-imenu-lynx-style-map nil + "Use Arrow keys to jump to occurences." + :group 'helm-imenu + :type 'boolean + :set (lambda (var val) + (set var val) + (if val + (progn + (define-key helm-imenu-map (kbd "") 'helm-execute-persistent-action) + (define-key helm-imenu-map (kbd "") 'helm-maybe-exit-minibuffer)) + (define-key helm-imenu-map (kbd "") nil) + (define-key helm-imenu-map (kbd "") nil)))) + +(defun helm-imenu-next-or-previous-section (n) + (with-helm-window + (let* ((fn (lambda () + (let ((str (buffer-substring + (point-at-bol) (point-at-eol)))) + (if helm-imenu-hide-item-type-name + (get-text-property 1 'type-name str) + (car (split-string str helm-imenu-delimiter)))))) + (curtype (funcall fn)) + (stop-fn (if (> n 0) + #'helm-end-of-source-p + #'helm-beginning-of-source-p))) + (while (and (not (funcall stop-fn)) + (string= curtype (funcall fn))) + (forward-line n)) + (helm-mark-current-line) + (helm-follow-execute-persistent-action-maybe)))) + +(defun helm-imenu-next-section () + (interactive) + (helm-imenu-next-or-previous-section 1)) + +(defun helm-imenu-previous-section () + (interactive) + (helm-imenu-next-or-previous-section -1)) + + +;;; Internals +(defvar helm-cached-imenu-alist nil) +(make-variable-buffer-local 'helm-cached-imenu-alist) + +(defvar helm-cached-imenu-candidates nil) +(make-variable-buffer-local 'helm-cached-imenu-candidates) + +(defvar helm-cached-imenu-tick nil) +(make-variable-buffer-local 'helm-cached-imenu-tick) + +(defvar helm-imenu--in-all-buffers-cache nil) + + +(defvar helm-source-imenu nil "See (info \"(emacs)Imenu\")") +(defvar helm-source-imenu-all nil) + +(defclass helm-imenu-source (helm-source-sync) + ((candidates :initform 'helm-imenu-candidates) + (candidate-transformer :initform 'helm-imenu-transformer) + (persistent-action :initform 'helm-imenu-persistent-action) + (persistent-help :initform "Show this entry") + (nomark :initform t) + (keymap :initform 'helm-imenu-map) + (help-message :initform 'helm-imenu-help-message) + (action :initform 'helm-imenu-action) + (find-file-target :initform #'helm-imenu-quit-and-find-file-fn) + (group :initform 'helm-imenu))) + +(defcustom helm-imenu-fuzzy-match nil + "Enable fuzzy matching in `helm-source-imenu'." + :group 'helm-imenu + :type 'boolean + :set (lambda (var val) + (set var val) + (setq helm-source-imenu + (helm-make-source "Imenu" 'helm-imenu-source + :fuzzy-match helm-imenu-fuzzy-match)))) + +(defun helm-imenu--maybe-switch-to-buffer (candidate) + (let ((cand (cdr candidate))) + (helm-aif (and (markerp cand) (marker-buffer cand)) + (switch-to-buffer it)))) + +(defun helm-imenu--execute-action-at-once-p () + (let ((cur (helm-get-selection)) + (mb (with-helm-current-buffer + (save-excursion + (goto-char (point-at-bol)) + (point-marker))))) + ;; Happen when cursor is on the line where a definition is. This + ;; prevent jumping to the definition where we are already, instead + ;; display helm with all definitions and preselection to the place + ;; we already are. + (if (equal (cdr cur) mb) + (prog1 nil + (helm-set-pattern "") + (helm-force-update)) + t))) + +(defun helm-imenu-quit-and-find-file-fn (source) + (let ((sel (helm-get-selection nil nil source))) + (when (and (consp sel) (markerp (cdr sel))) + (buffer-file-name (marker-buffer (cdr sel)))))) + +(defun helm-imenu-action (candidate) + "Default action for `helm-source-imenu'." + (helm-log-run-hook 'helm-goto-line-before-hook) + (helm-imenu--maybe-switch-to-buffer candidate) + (imenu candidate) + ;; If semantic is supported in this buffer + ;; imenu used `semantic-imenu-goto-function' + ;; and position have been highlighted, + ;; no need to highlight again. + (unless (eq imenu-default-goto-function + 'semantic-imenu-goto-function) + (helm-highlight-current-line))) + +(defun helm-imenu-persistent-action (candidate) + "Default persistent action for `helm-source-imenu'." + (helm-imenu--maybe-switch-to-buffer candidate) + (imenu candidate) + (helm-highlight-current-line)) + +(defun helm-imenu-candidates (&optional buffer) + (with-current-buffer (or buffer helm-current-buffer) + (let ((tick (buffer-modified-tick))) + (if (eq helm-cached-imenu-tick tick) + helm-cached-imenu-candidates + (setq imenu--index-alist nil) + (prog1 (setq helm-cached-imenu-candidates + (let ((index (imenu--make-index-alist t))) + (helm-imenu--candidates-1 + (delete (assoc "*Rescan*" index) index)))) + (setq helm-cached-imenu-tick tick)))))) + +(defun helm-imenu-candidates-in-all-buffers (&optional build-sources) + (let* ((lst (buffer-list)) + (progress-reporter (make-progress-reporter + "Imenu indexing buffers..." 1 (length lst)))) + (prog1 + (cl-loop with cur-buf = (if build-sources + (current-buffer) helm-current-buffer) + for b in lst + for count from 1 + when (with-current-buffer b + (and (or (member major-mode helm-imenu-extra-modes) + (derived-mode-p 'prog-mode)) + (helm-same-major-mode-p + cur-buf helm-imenu-all-buffer-assoc))) + if build-sources + collect (helm-make-source + (format "Imenu in %s" (buffer-name b)) + 'helm-imenu-source + :candidates (with-current-buffer b + (helm-imenu-candidates b)) + :fuzzy-match helm-imenu-fuzzy-match) + else + append (with-current-buffer b + (helm-imenu-candidates b)) + do (progress-reporter-update progress-reporter count)) + (progress-reporter-done progress-reporter)))) + +(defun helm-imenu--candidates-1 (alist) + (cl-loop for elm in alist + nconc (cond + ((imenu--subalist-p elm) + (helm-imenu--candidates-1 + (cl-loop for (e . v) in (cdr elm) collect + (cons (propertize + e 'helm-imenu-type (car elm)) + ;; If value is an integer, convert it + ;; to a marker, otherwise it is a cons cell + ;; and it will be converted on next recursions. + ;; (Bug#1060) [1]. + (if (integerp v) (copy-marker v) v))))) + ((listp (cdr elm)) + (and elm (list elm))) + (t + ;; bug in imenu, should not be needed. + (and (cdr elm) + ;; Semantic uses overlays whereas imenu uses + ;; markers (Bug#1706). + (setcdr elm (pcase (cdr elm) ; Same as [1]. + ((and ov (pred overlayp)) + (copy-overlay ov)) + ((and mk (or (pred markerp) + (pred integerp))) + (copy-marker mk)))) + (list elm)))))) + +(defun helm-imenu--get-prop (item) + ;; property value of ITEM can have itself + ;; a property value which have itself a property value + ;; ...and so on; Return a list of all these + ;; properties values starting at ITEM. + (let* ((prop (get-text-property 0 'helm-imenu-type item)) + (lst (list prop item))) + (when prop + (while prop + (setq prop (get-text-property 0 'helm-imenu-type prop)) + (and prop (push prop lst))) + lst))) + +(defun helm-imenu-icon-for-type (type) + "Return an icon for type TYPE. +The icon is found in `helm-imenu-icon-type-alist', if not +`helm-imenu-default-type-sexp' is evaled to provide a default icon." + (require 'all-the-icons) + (let ((all-the-icons-scale-factor 1.0) + (all-the-icons-default-adjust 0.0)) + (or (helm-aand (assoc-default + type helm-imenu-icon-type-alist) + (apply (car it) (cdr it))) + (apply (car helm-imenu-default-type-sexp) + (cdr helm-imenu-default-type-sexp))))) + +(defun helm-imenu-transformer (candidates) + (cl-loop for (k . v) in candidates + ;; (k . v) == (symbol-name . marker) + for bufname = (buffer-name + (pcase v + ((pred overlayp) (overlay-buffer v)) + ((or (pred markerp) (pred integerp)) + (marker-buffer v)))) + for types = (or (helm-imenu--get-prop k) + (list (if (with-current-buffer bufname + (derived-mode-p 'prog-mode)) + "Function" + "Top level") + k)) + for type-icon = (and helm-imenu-use-icon + (helm-imenu-icon-for-type (car types))) + for type-name = (propertize + (car types) 'face + (cl-loop for (p . f) in helm-imenu-type-faces + when (string-match p (car types)) + return f + finally return 'default)) + for disp1 = (mapconcat 'identity + (cdr types) + (propertize helm-imenu-delimiter + 'face 'shadow)) + for disp = (concat (if helm-imenu-use-icon + (concat (propertize " " 'display type-icon) " ") + "") + (if helm-imenu-hide-item-type-name + "" + (concat type-name + (propertize helm-imenu-delimiter + 'face 'shadow))) + (propertize disp1 'help-echo bufname 'types types)) + collect + (cons (propertize disp 'type-name type-name) (cons k v)))) + + +;;;###autoload +(defun helm-imenu () + "Preconfigured `helm' for `imenu'." + (interactive) + (require 'which-func) + (unless helm-source-imenu + (setq helm-source-imenu + (helm-make-source "Imenu" 'helm-imenu-source + :fuzzy-match helm-imenu-fuzzy-match))) + (let* ((imenu-auto-rescan t) + (helm-highlight-matches-around-point-max-lines 'never) + (str (thing-at-point 'symbol)) + (init-reg (and str (concat "\\_<" (regexp-quote str) "\\_>"))) + (helm-execute-action-at-once-if-one + helm-imenu-execute-action-at-once-if-one)) + (helm :sources 'helm-source-imenu + :default (and str (list init-reg str)) + :preselect (helm-aif (which-function) + (concat "\\_<" (regexp-quote it) "\\_>") + init-reg) + :buffer "*helm imenu*"))) + +;;;###autoload +(defun helm-imenu-in-all-buffers () + "Fetch Imenu entries in all buffers with similar mode as current. +A mode is similar as current if it is the same, it is derived +i.e. `derived-mode-p' or it have an association in +`helm-imenu-all-buffer-assoc'." + (interactive) + (require 'which-func) + (unless helm-imenu-in-all-buffers-separate-sources + (unless helm-source-imenu-all + (setq helm-source-imenu-all + (helm-make-source "Imenu in all buffers" 'helm-imenu-source + :init (lambda () + ;; Use a cache to avoid repeatedly sending + ;; progress-reporter message when updating + ;; (Bug#1704). + (setq helm-imenu--in-all-buffers-cache + (helm-imenu-candidates-in-all-buffers))) + :candidates 'helm-imenu--in-all-buffers-cache + :fuzzy-match helm-imenu-fuzzy-match)))) + (let* ((imenu-auto-rescan t) + (helm-highlight-matches-around-point-max-lines 'never) + (str (thing-at-point 'symbol)) + (init-reg (and str (concat "\\_<" (regexp-quote str) "\\_>"))) + (helm-execute-action-at-once-if-one + helm-imenu-execute-action-at-once-if-one) + (helm-maybe-use-default-as-input + (not (null (memq 'helm-source-imenu-all + helm-sources-using-default-as-input)))) + (sources (if helm-imenu-in-all-buffers-separate-sources + (helm-imenu-candidates-in-all-buffers 'build-sources) + '(helm-source-imenu-all)))) + (helm :sources sources + :default (and str (list init-reg str)) + :preselect (helm-aif (which-function) + (concat "\\_<" (regexp-quote it) "\\_>") + init-reg) + :buffer "*helm imenu all*"))) + +(provide 'helm-imenu) + +;;; helm-imenu.el ends here diff --git a/code/elpa/helm-20220822.659/helm-info.el b/code/elpa/helm-20220822.659/helm-info.el new file mode 100644 index 0000000..e996c09 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-info.el @@ -0,0 +1,300 @@ +;;; helm-info.el --- Browse info index with helm -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-lib) +(require 'helm-utils) +(require 'info) + +(declare-function Info-index-nodes "info" (&optional file)) +(declare-function Info-goto-node "info" (&optional fork)) +(declare-function Info-find-node "info" (filename nodename &optional no-going-back)) +(declare-function ring-insert "ring") +(declare-function ring-empty-p "ring") +(declare-function ring-ref "ring") +(defvar Info-history) +(defvar Info-directory-list) + +;;; Customize + +(defgroup helm-info nil + "Info-related applications and libraries for Helm." + :group 'helm) + +(defcustom helm-info-default-sources + '(helm-source-info-elisp + helm-source-info-cl + helm-source-info-eieio + helm-source-info-pages) + "Default sources to use for looking up symbols at point in Info +files with `helm-info-at-point'." + :group 'helm-info + :type '(repeat (choice symbol))) + +;;; Build info-index sources with `helm-info-source' class. + +(cl-defun helm-info-init (&optional (file (helm-get-attr 'info-file))) + "Initialize candidates for info FILE. +If FILE have nodes, loop through all nodes and accumulate candidates +found in each node, otherwise scan only the current info buffer." + ;; Allow reinit candidate buffer when using edebug. + (helm-aif (and debug-on-error + (helm-candidate-buffer)) + (kill-buffer it)) + (unless (helm-candidate-buffer) + (save-selected-window + (info file " *helm info temp buffer*") + (let ((tobuf (helm-candidate-buffer 'global)) + Info-history) + (helm-aif (Info-index-nodes) + (cl-dolist (node it) + (Info-goto-node node) + (helm-info-scan-current-buffer tobuf)) + (helm-info-scan-current-buffer tobuf)) + (bury-buffer))))) + +(defun helm-info-scan-current-buffer (tobuf) + "Scan current info buffer and print lines to TOBUF. +Argument TOBUF is the `helm-candidate-buffer'." + (let (start end line) + (goto-char (point-min)) + (while (search-forward "\n* " nil t) + (unless (search-forward "Menu:\n" (1+ (point-at-eol)) t) + (setq start (point-at-bol) + ;; Fix Bug#1503 by getting the invisible + ;; info displayed on next line in long strings. + ;; e.g "* Foo.\n (line 12)" instead of + ;; "* Foo.(line 12)" + end (or (save-excursion + (goto-char (point-at-bol)) + (re-search-forward "(line +[0-9]+)" nil t)) + (point-at-eol)) + ;; Long string have a new line inserted before the + ;; invisible spec, remove it. + line (replace-regexp-in-string + "\n" "" (buffer-substring start end))) + (with-current-buffer tobuf + (insert line) + (insert "\n")))))) + +(defun helm-info-goto (node-line) + "The helm-info action to jump to NODE-LINE." + (Info-goto-node (car node-line)) + (helm-goto-line (cdr node-line))) + +(defvar helm-info--node-regexp + "^\\* +\\(.+\\):[ \\t]+\\(.*\\)\\(?:[ \\t]*\\)(line +\\([0-9]+\\))" + "A regexp that should match file name, node name and line number in +a line like this: + +\* bind: Bash Builtins. (line 21).") + +(defun helm-info-display-to-real (line) + "Transform LINE to an acceptable argument for `info'. +If line have a node use the node, otherwise use directly first name found." + (let (nodename linum) + (when (string-match helm-info--node-regexp line) + (setq nodename (match-string 2 line) + linum (match-string 3 line))) + (if nodename + (cons (format "(%s)%s" + (helm-get-attr 'info-file) + (replace-regexp-in-string ":\\'" "" nodename)) + (string-to-number (or linum "1"))) + (cons (format "(%s)%s" + (helm-get-attr 'info-file) + (helm-aand (replace-regexp-in-string "^* " "" line) + (replace-regexp-in-string "::?.*\\'" "" it))) + 1)))) + +(defclass helm-info-source (helm-source-in-buffer) + ((info-file :initarg :info-file + :initform nil + :custom 'string) + (init :initform #'helm-info-init) + (display-to-real :initform #'helm-info-display-to-real) + (get-line :initform #'buffer-substring) + (action :initform '(("Goto node" . helm-info-goto))))) + +(defmacro helm-build-info-source (fname &rest args) + `(helm-make-source (concat "Info Index: " ,fname) 'helm-info-source + :info-file ,fname ,@args)) + +(defun helm-build-info-index-command (name doc source buffer) + "Define a Helm command NAME with documentation DOC. +Arg SOURCE will be an existing helm source named +`helm-source-info-' and BUFFER a string buffer name." + (defalias (intern (concat "helm-info-" name)) + (lambda () + (interactive) + (helm :sources source + :buffer buffer + :candidate-number-limit 1000)) + doc)) + +(defun helm-define-info-index-sources (var-value &optional commands) + "Define Helm sources named helm-source-info-. +Sources are generated for all entries of +`helm-default-info-index-list'. +If COMMANDS arg is non-nil, also build commands named +`helm-info-'. +Where NAME is an element of `helm-default-info-index-list'." + (cl-loop for str in var-value + for sym = (intern (concat "helm-source-info-" str)) + do (set sym (helm-build-info-source str)) + when commands + do (helm-build-info-index-command + str (format "Predefined helm for %s info." str) + sym (format "*helm info %s*" str)))) + +(defun helm-info-index-set (var value) + (set var value) + (helm-define-info-index-sources value t)) + +;;; Search Info files + +;; `helm-info' is the main entry point here. It prompts the user for an Info +;; file, then a term in the file's index to jump to. + +(defvar helm-info-searched (make-ring 32) + "Ring of previously searched Info files.") + +(defun helm-get-info-files () + "Return list of Info files to use for `helm-info'. + +Elements of the list are strings of Info file names without +extensions (e.g., \"emacs\" for file \"emacs.info.gz\"). Info +files are found by searching directories in +`Info-directory-list'." + (info-initialize) ; Build Info-directory-list from INFOPATH (Bug#2118) + (let ((files (cl-loop for d in (or Info-directory-list + Info-default-directory-list) + when (file-directory-p d) + append (directory-files d nil "\\.info")))) + (helm-fast-remove-dups + (cl-loop for f in files collect + (helm-file-name-sans-extension f)) + :test 'equal))) + +(defcustom helm-default-info-index-list + (helm-get-info-files) + "Info files to search in with `helm-info'." + :group 'helm-info + :type '(repeat (choice string)) + :set 'helm-info-index-set) + +(defun helm-info-search-index (candidate) + "Search the index of CANDIDATE's Info file using the function +helm-info-." + (let ((helm-info-function + (intern-soft (concat "helm-info-" candidate)))) + (when (fboundp helm-info-function) + (funcall helm-info-function) + (ring-insert helm-info-searched candidate)))) + +(defun helm-def-source--info-files () + "Return a Helm source for Info files." + (helm-build-sync-source "Helm Info" + :candidates + (lambda () (copy-sequence helm-default-info-index-list)) + :candidate-number-limit 999 + :candidate-transformer + (lambda (candidates) + (sort candidates #'string-lessp)) + :nomark t + :action '(("Search index" . helm-info-search-index)))) + +;;;###autoload +(defun helm-info (&optional refresh) + "Preconfigured `helm' for searching Info files' indices. + +With a prefix argument \\[universal-argument], set REFRESH to +non-nil. + +Optional parameter REFRESH, when non-nil, re-evaluates +`helm-default-info-index-list'. If the variable has been +customized, set it to its saved value. If not, set it to its +standard value. See `custom-reevaluate-setting' for more. + +REFRESH is useful when new Info files are installed. If +`helm-default-info-index-list' has not been customized, the new +Info files are made available." + (interactive "P") + (let ((default (unless (ring-empty-p helm-info-searched) + (ring-ref helm-info-searched 0)))) + (when refresh + (custom-reevaluate-setting 'helm-default-info-index-list)) + (helm :sources (helm-def-source--info-files) + :buffer "*helm Info*" + :preselect (and default + (concat "\\_<" (regexp-quote default) "\\_>"))))) + +;;;; Info at point + +;; `helm-info-at-point' is the main entry point here. It searches for the +;; symbol at point through the Info sources defined in +;; `helm-info-default-sources' and jumps to it. + +(defvar helm-info--pages-cache nil + "Cache for all Info pages on the system.") + +(defvar helm-source-info-pages + (helm-build-sync-source "Info Pages" + :init #'helm-info-pages-init + :candidates (lambda () helm-info--pages-cache) + :action '(("Show with Info" . + (lambda (node-str) + (info (replace-regexp-in-string + "^[^:]+: " "" node-str))))) + :requires-pattern 2) + "Helm source for Info pages.") + +(defun helm-info-pages-init () + "Collect candidates for initial Info node Top." + (or helm-info--pages-cache + (let ((info-topic-regexp "\\* +\\([^:]+: ([^)]+)[^.]*\\)\\.")) + (save-selected-window + (info "dir" " *helm info temp buffer*") + (Info-find-node "dir" "top") + (goto-char (point-min)) + (while (re-search-forward info-topic-regexp nil t) + (push (match-string-no-properties 1) + helm-info--pages-cache)) + (kill-buffer))))) + +;;;###autoload +(defun helm-info-at-point () + "Preconfigured `helm' for searching info at point." + (interactive) + ;; Symbol at point is used as default as long as one of the sources + ;; in `helm-info-default-sources' is member of + ;; `helm-sources-using-default-as-input'. + (cl-loop for src in helm-info-default-sources + for name = (if (symbolp src) + (assoc 'name (symbol-value src)) + (assoc 'name src)) + unless name + do (warn "Couldn't build source `%S' without its info file" src)) + (helm :sources helm-info-default-sources + :buffer "*helm info*")) + +(provide 'helm-info) + +;;; helm-info.el ends here diff --git a/code/elpa/helm-20220822.659/helm-locate.el b/code/elpa/helm-20220822.659/helm-locate.el new file mode 100644 index 0000000..95b478d --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-locate.el @@ -0,0 +1,482 @@ +;;; helm-locate.el --- helm interface for locate. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;; NOTE for WINDOZE users: +;; You have to install Everything with his command line interface here: +;; http://www.voidtools.com/download.php + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-types) +(require 'helm-help) + +(defvar helm-ff-default-directory) +(declare-function helm-read-file-name "helm-mode") + + +(defgroup helm-locate nil + "Locate related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-locate-db-file-regexp "m?locate\\.db$" + "Default regexp to match locate database. +If nil Search in all files." + :type 'string) + +(defcustom helm-ff-locate-db-filename "locate.db" + "The basename of the locatedb file you use locally in your directories. +When this is set and Helm finds such a file in the directory from +where you launch locate, it will use this file and will not +prompt you for a db file. +Note that this happen only when locate is launched with a prefix +arg." + :type 'string) + +(defcustom helm-locate-command nil + "A list of arguments for locate program. + +Helm will calculate a default value for your system on startup +unless `helm-locate-command' is non-nil. + +Here are the default values it will use according to your system: + +Gnu/linux: \"locate %s -e -A --regex %s\" +berkeley-unix: \"locate %s %s\" +windows-nt: \"es %s %s\" +Others: \"locate %s %s\" + +This string will be passed to format so it should end with `%s'. +The first format spec is used for the \"-i\" value of locate/es, +so don't set it directly but use `helm-locate-case-fold-search' +for this. + +The last option must be the one preceding pattern i.e \"-r\" or +\"--regex\". + +You will be able to pass other options such as \"-b\" or \"l\" +during Helm invocation after entering pattern only when multi +matching, not when fuzzy matching. + +Note that the \"-b\" option is added automatically by Helm when +var `helm-locate-fuzzy-match' is non-nil and switching back from +multimatch to fuzzy matching (this is done automatically when a +space is detected in pattern)." + :type 'string) + +(defcustom helm-locate-create-db-command + "updatedb -l 0 -o '%s' -U '%s'" + "Command used to create a locale locate db file." + :type 'string) + +(defcustom helm-locate-case-fold-search helm-case-fold-search + "It have the same meaning as `helm-case-fold-search'. +The -i option of locate will be used depending of value of +`helm-pattern' when this is set to \\='smart. +When nil \"-i\" will not be used at all and when non-nil it will +always be used. +NOTE: the -i option of the \"es\" command used on windows does +the opposite of \"locate\" command." + :type 'symbol) + +(defcustom helm-locate-fuzzy-match nil + "Enable fuzzy matching in `helm-locate'. +Note that when this is enabled searching is done on basename." + :type 'boolean) + +(defcustom helm-locate-fuzzy-sort-fn + #'helm-locate-default-fuzzy-sort-fn + "Default fuzzy matching sort function for locate." + :type 'boolean) + +(defcustom helm-locate-project-list nil + "A list of directories, your projects. +When set, allow browsing recursively files in all directories of +this list with `helm-projects-find-files'." + :type '(repeat string)) + +(defcustom helm-locate-recursive-dirs-command "locate -i -e -A --regex '^%s' '%s.*$'" + "Command used for recursive directories completion in `helm-find-files'. + +For Windows and `es' use something like \"es -r ^%s.*%s.*$\" + +The two format specs are mandatory. + +If for some reasons you can't use locate because your filesystem +doesn't have a database, you can use find command from findutils +but be aware that it will be much slower. See `helm-find-files' +embedded help for more infos." + :type 'string + :group 'helm-files) + + +(defvar helm-locate-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-generic-files-map) + (define-key map (kbd "DEL") 'helm-delete-backward-no-update) + map)) + +(defface helm-locate-finish + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Green")) + "Face used in mode line when locate process is finish." + :group 'helm-locate) + + +(defun helm-ff-find-locatedb (&optional from-ff) + "Try to find if a local locatedb file is available. +The search is done in `helm-ff-default-directory' or falls back to +`default-directory' if FROM-FF is nil." + (helm-aif (and helm-ff-locate-db-filename + (locate-dominating-file + (or (and from-ff + helm-ff-default-directory) + default-directory) + helm-ff-locate-db-filename)) + (expand-file-name helm-ff-locate-db-filename it))) + +(defun helm-locate-create-db-default-function (db-name directory) + "Default function used to create a locale locate db file. +Argument DB-NAME name of the db file. +Argument DIRECTORY root of file system subtree to scan." + (format helm-locate-create-db-command + db-name (expand-file-name directory))) + +(defvar helm-locate-create-db-function + #'helm-locate-create-db-default-function + "Function used to create a locale locate db file. +It should receive the same arguments as +`helm-locate-create-db-default-function'.") + +(defun helm-locate-1 (&optional localdb init from-ff default) + "Generic function to run Locate. +Prefix arg LOCALDB when (4) search and use a local locate db file +when it exists or create it, when (16) force update of existing +db file even if exists. +It has no effect when locate command is \\='es'. INIT is a string +to use as initial input in prompt. +See `helm-locate-with-db' and `helm-locate'." + (require 'helm-mode) + (helm-locate-set-command) + (let ((pfn (lambda (candidate) + (if (file-directory-p candidate) + (message "Error: The locate Db should be a file") + (if (= (shell-command + (funcall helm-locate-create-db-function + candidate + helm-ff-default-directory)) + 0) + (message "New locatedb file `%s' created" candidate) + (error "Failed to create locatedb file `%s'" candidate))))) + (locdb (and localdb + (not (string-match "^es" helm-locate-command)) + (or (and (equal '(4) localdb) + (helm-ff-find-locatedb from-ff)) + (helm-read-file-name + "Create Locate Db file: " + :initial-input (expand-file-name "locate.db" + (or helm-ff-default-directory + default-directory)) + :preselect helm-locate-db-file-regexp + :test (lambda (x) + (if helm-locate-db-file-regexp + ;; Select only locate db files and directories + ;; to allow navigation. + (or (string-match + helm-locate-db-file-regexp x) + (file-directory-p x)) + x))))))) + (when (and locdb (or (equal localdb '(16)) + (not (file-exists-p locdb)))) + (funcall pfn locdb)) + (helm-locate-with-db (and localdb locdb) init default))) + +(defun helm-locate-set-command () + "Setup `helm-locate-command' if not already defined." + (unless helm-locate-command + (setq helm-locate-command + (cl-case system-type + (gnu/linux "locate %s -e -A --regex %s") + (berkeley-unix "locate %s %s") + (windows-nt "es %s %s") + (t "locate %s %s"))))) + +(defun helm-locate-initial-setup () + (require 'helm-for-files) + (helm-locate-set-command)) + +(defvar helm-file-name-history nil) +(defun helm-locate-with-db (&optional db initial-input default) + "Run locate -d DB. +If DB is not given or nil use locate without -d option. +Argument DB can be given as a string or list of db files. +Argument INITIAL-INPUT is a string to use as initial-input. +See also `helm-locate'." + (require 'helm-files) + (when (and db (stringp db)) (setq db (list db))) + (helm-locate-set-command) + (let ((helm-locate-command + (if db + (replace-regexp-in-string + "locate" + (format (if helm-locate-fuzzy-match + "locate -b -d '%s'" "locate -d '%s'") + (mapconcat 'identity + ;; Remove eventually + ;; marked directories by error. + (cl-loop for i in db + unless (file-directory-p i) + ;; expand-file-name to resolve + ;; abbreviated fnames not + ;; expanding inside single + ;; quotes i.e. '%s'. + collect (expand-file-name i)) + ":")) + helm-locate-command) + (if (and helm-locate-fuzzy-match + (not (string-match-p "\\`locate -b" helm-locate-command))) + (replace-regexp-in-string + "\\`locate" "locate -b" helm-locate-command) + helm-locate-command)))) + (setq helm-file-name-history (mapcar 'helm-basename file-name-history)) + (helm :sources 'helm-source-locate + :buffer "*helm locate*" + :ff-transformer-show-only-basename nil + :input initial-input + :default default + :history 'helm-file-name-history))) + +(defun helm-locate-update-mode-line (process-name) + "Update mode-line with PROCESS-NAME status information." + (with-helm-window + (setq mode-line-format + `(" " mode-line-buffer-identification " " + (:eval (format "L%s" (helm-candidate-number-at-point))) " " + (:eval (propertize + (format "[%s process finished - (%s results)]" + (max (1- (count-lines + (point-min) (point-max))) + 0) + ,process-name) + 'face 'helm-locate-finish)))) + (force-mode-line-update))) + +(defun helm-locate--default-process-coding-system () + "Fix `default-process-coding-system' in locate for Windows systems." + ;; This is an attempt to fix issue #1322. + (if (and (eq system-type 'windows-nt) + (boundp 'w32-ansi-code-page)) + (let ((code-page-eol + (intern (format "cp%s-%s" w32-ansi-code-page "dos")))) + (if (ignore-errors (check-coding-system code-page-eol)) + (cons code-page-eol code-page-eol) + default-process-coding-system)) + default-process-coding-system)) + +(defun helm-locate-init () + "Initialize async locate process for `helm-source-locate'." + (let* ((default-process-coding-system + (helm-locate--default-process-coding-system)) + (locate-is-es (string-match "\\`es" helm-locate-command)) + (real-locate (string-match "\\`locate" helm-locate-command)) + (case-sensitive-flag (if locate-is-es "-i" "")) + (ignore-case-flag (if (or locate-is-es + (not real-locate)) "" "-i")) + (args (helm-mm-split-pattern helm-pattern)) + (cmd (format helm-locate-command + (cl-case helm-locate-case-fold-search + (smart (let ((case-fold-search nil)) + (if (string-match "[[:upper:]]" helm-pattern) + case-sensitive-flag + ignore-case-flag))) + (t (if helm-locate-case-fold-search + ignore-case-flag + case-sensitive-flag))) + (helm-aif (cdr args) + (concat + ;; The pattern itself. + (shell-quote-argument (car args)) " " + ;; Possible locate args added + ;; after pattern, don't quote them. + (mapconcat 'identity it " ")) + (shell-quote-argument (car args))))) + (default-directory (if (file-directory-p default-directory) + default-directory "/"))) + (helm-log "Starting helm-locate process") + (helm-log "Command line used was:\n\n%s" + (concat ">>> " (propertize cmd 'face 'font-lock-comment-face) "\n\n")) + (prog1 + (start-process-shell-command + "locate-process" helm-buffer + cmd) + (set-process-sentinel + (get-buffer-process helm-buffer) + (lambda (process event) + (let* ((err (process-exit-status process)) + (noresult (= err 1))) + (cond (noresult + (with-helm-buffer + (unless (cdr helm-sources) + (insert (concat "* Exit with code 1, no result found," + " command line was:\n\n " + cmd))))) + ((string= event "finished\n") + (when (and helm-locate-fuzzy-match + (not (string-match-p "\\s-" helm-pattern))) + (helm-redisplay-buffer)) + (helm-locate-update-mode-line "Locate")) + (t + (helm-log "Error: Locate %s" + (replace-regexp-in-string "\n" "" event)))))))))) + +(defun helm-locate-default-fuzzy-sort-fn (candidates) + "Default sort function for files in fuzzy matching. +Sort is done on basename of CANDIDATES." + (helm-fuzzy-matching-default-sort-fn-1 candidates nil t)) + +(defclass helm-locate-override-inheritor (helm-type-file) ()) + +(defclass helm-locate-source (helm-source-async helm-locate-override-inheritor) + ((init :initform 'helm-locate-initial-setup) + (candidates-process :initform 'helm-locate-init) + (requires-pattern :initform 3) + (history :initform 'helm-file-name-history) + (persistent-action :initform 'helm-ff-kill-or-find-buffer-fname) + (candidate-number-limit :initform 9999) + (redisplay :initform (progn helm-locate-fuzzy-sort-fn)))) + +;; Override helm-type-file class keymap. +(cl-defmethod helm--setup-source :after ((source helm-locate-override-inheritor)) + (setf (slot-value source 'keymap) helm-locate-map) + (setf (slot-value source 'group) 'helm-locate)) + +(defvar helm-source-locate + (helm-make-source "Locate" 'helm-locate-source + :pattern-transformer 'helm-locate-pattern-transformer + ;; :match-part is only used here to tell helm which part + ;; of candidate to highlight. + :match-part (lambda (candidate) + (if (or (string-match-p " -b\\'" helm-pattern) + (and helm-locate-fuzzy-match + (not (string-match "\\s-" helm-pattern)))) + (helm-basename candidate) + candidate)))) + +(defun helm-locate-pattern-transformer (pattern) + (if helm-locate-fuzzy-match + ;; When fuzzy is enabled helm add "-b" option on startup. + (cond ((string-match-p " " pattern) + (when (string-match "\\`locate -b" helm-locate-command) + (setq helm-locate-command + (replace-match "locate" t t helm-locate-command))) + pattern) + (t + (unless (string-match-p "\\`locate -b" helm-locate-command) + (setq helm-locate-command + (replace-regexp-in-string + "\\`locate" "locate -b" helm-locate-command))) + (helm--mapconcat-pattern pattern))) + pattern)) + +(defun helm-locate-find-dbs-in-projects (&optional update) + (let* ((pfn (lambda (candidate directory) + (unless (= (shell-command + (funcall helm-locate-create-db-function + candidate + directory)) + 0) + (error "Failed to create locatedb file `%s'" candidate))))) + (cl-loop for p in helm-locate-project-list + for db = (expand-file-name + helm-ff-locate-db-filename + (file-name-as-directory p)) + if (and (null update) (file-exists-p db)) + collect db + else do (funcall pfn db p) + and collect db))) + +;;; Directory completion for hff. +;; +(defclass helm-locate-subdirs-source (helm-source-in-buffer) + ((basedir :initarg :basedir + :initform nil + :custom string) + (subdir :initarg :subdir + :initform nil + :custom 'string) + (data :initform #'helm-locate-init-subdirs) + (group :initform 'helm-locate))) + +(defun helm-locate-init-subdirs () + (with-temp-buffer + (call-process-shell-command + (if (string-match-p "\\`fd" helm-locate-recursive-dirs-command) + (format helm-locate-recursive-dirs-command + ;; fd pass path at end. + (helm-get-attr 'subdir) (helm-get-attr 'basedir)) + (format helm-locate-recursive-dirs-command + (if (string-match-p "\\`es" helm-locate-recursive-dirs-command) + ;; Fix W32 paths. + (replace-regexp-in-string + "/" "\\\\\\\\" (helm-get-attr 'basedir)) + (helm-get-attr 'basedir)) + (helm-get-attr 'subdir))) + nil t nil) + (buffer-string))) + +;;;###autoload +(defun helm-projects-find-files (update) + "Find files with locate in `helm-locate-project-list'. +With a prefix arg refresh the database in each project." + (interactive "P") + (helm-locate-set-command) + (cl-assert (and (string-match-p "\\`locate" helm-locate-command) + (executable-find "updatedb")) + nil "Unsupported locate version") + (let ((dbs (helm-locate-find-dbs-in-projects update))) + (if dbs + (helm-locate-with-db dbs) + (user-error "No projects found, please setup `helm-locate-project-list'")))) + +;;;###autoload +(defun helm-locate (arg) + "Preconfigured `helm' for Locate. +Note: you can add locate options after entering pattern. +See \\='man locate' for valid options and also `helm-locate-command'. + +You can specify a local database with prefix argument ARG. +With two prefix arg, refresh the current local db or create it if +it doesn't exists. + +To create a user specific db, use +\"updatedb -l 0 -o db_path -U directory\". +Where db_path is a filename matched by +`helm-locate-db-file-regexp'." + (interactive "P") + (helm-set-local-variable 'helm-async-outer-limit-hook + (list (lambda () + (when (and helm-locate-fuzzy-match + (not (string-match-p + "\\s-" helm-pattern))) + (helm-redisplay-buffer))))) + (setq helm-ff-default-directory default-directory) + (helm-locate-1 arg nil nil (thing-at-point 'filename))) + +(provide 'helm-locate) + +;;; helm-locate.el ends here diff --git a/code/elpa/helm-20220822.659/helm-man.el b/code/elpa/helm-20220822.659/helm-man.el new file mode 100644 index 0000000..010b06d --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-man.el @@ -0,0 +1,114 @@ +;;; helm-man.el --- Man and woman UI -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) + +(defvar woman-topic-all-completions) +(defvar woman-manpath) +(defvar woman-path) +(defvar woman-expanded-directory-path) +(declare-function woman-file-name "woman.el" (topic &optional re-cache)) +(declare-function woman-file-name-all-completions "woman.el" (topic)) +(declare-function Man-getpage-in-background "man.el" (topic)) +(declare-function woman-expand-directory-path "woman.el" (path-dirs path-regexps)) +(declare-function woman-topic-all-completions "woman.el" (path)) +(declare-function helm-generic-sort-fn "helm-utils.el" (S1 S2)) +(declare-function helm-comp-read "helm-mode") + +(defgroup helm-man nil + "Man and Woman applications for Helm." + :group 'helm) + +(defcustom helm-man-or-woman-function 'Man-getpage-in-background + "Default command to display a man page." + :group 'helm-man + :type '(radio :tag "Preferred command to display a man page" + (const :tag "Man" Man-getpage-in-background) + (const :tag "Woman" woman))) + +(defcustom helm-man-format-switches (cl-case system-type + ((darwin macos) "%s") + (t "-l %s")) + "Arguments to pass to the `manual-entry' function. +Arguments are passed to `manual-entry' with `format.'" + :group 'helm-man + :type 'string) + +;; Internal +(defvar helm-man--pages nil + "All man pages on system. +Will be calculated the first time you invoke Helm with this +source.") + +(defun helm-man-default-action (candidate) + "Default action for jumping to a woman or man page from Helm." + (let ((wfiles (mapcar #'car (woman-file-name-all-completions candidate)))) + (condition-case nil + (let ((file (if (cdr wfiles) + (helm-comp-read "ManFile: " wfiles :must-match t) + (car wfiles)))) + (if (eq helm-man-or-woman-function 'Man-getpage-in-background) + (manual-entry (format helm-man-format-switches file)) + (condition-case nil + (woman-find-file file) + ;; If woman is unable to format correctly + ;; try Man instead. + (error (kill-buffer) + (manual-entry (format helm-man-format-switches file)))))) + ;; If even Man failed with file as argument, try again with Man + ;; but using Topic candidate instead of the file calculated by + ;; woman. + (error (kill-buffer) + (Man-getpage-in-background candidate))))) + +(defun helm-man--init () + (require 'woman) + (require 'helm-utils) + (unless helm-man--pages + (setq woman-expanded-directory-path + (woman-expand-directory-path woman-manpath woman-path)) + (setq woman-topic-all-completions + (woman-topic-all-completions woman-expanded-directory-path)) + (setq helm-man--pages (mapcar 'car woman-topic-all-completions))) + (helm-init-candidates-in-buffer 'global helm-man--pages)) + +(defvar helm-source-man-pages + (helm-build-in-buffer-source "Manual Pages" + :init #'helm-man--init + :persistent-action #'ignore + :filtered-candidate-transformer + (lambda (candidates _source) + (sort candidates #'helm-generic-sort-fn)) + :action '(("Display Man page" . helm-man-default-action)) + :group 'helm-man)) + +;;;###autoload +(defun helm-man-woman (arg) + "Preconfigured `helm' for Man and Woman pages. +With a prefix arg reinitialize the cache." + (interactive "P") + (when arg (setq helm-man--pages nil)) + (helm :sources 'helm-source-man-pages + :buffer "*helm man woman*")) + +(provide 'helm-man) + +;;; helm-man.el ends here diff --git a/code/elpa/helm-20220822.659/helm-misc.el b/code/elpa/helm-20220822.659/helm-misc.el new file mode 100644 index 0000000..fee7a44 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-misc.el @@ -0,0 +1,393 @@ +;;; helm-misc.el --- Various functions for helm -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-types) + +(declare-function display-time-world-display "time.el") +(defvar display-time-world-list) +(declare-function LaTeX-math-mode "ext:latex.el") +(declare-function jabber-chat-with "ext:jabber.el") +(declare-function jabber-read-account "ext:jabber.el") +(declare-function helm-comp-read "helm-mode") + + +(defgroup helm-misc nil + "Various Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-time-zone-home-location "Paris" + "The time zone of your home." + :group 'helm-misc + :type 'string) + +(defcustom helm-timezone-actions + '(("Set timezone env (TZ)" . (lambda (candidate) + (setenv "TZ" candidate)))) + "Actions for helm-timezone." + :group 'helm-misc + :type '(alist :key-type string :value-type function)) + +(defface helm-time-zone-current + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "green")) + "Face used to colorize current time in `helm-world-time'." + :group 'helm-misc) + +(defface helm-time-zone-home + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "red")) + "Face used to colorize home time in `helm-world-time'." + :group 'helm-misc) + + + +;;; Latex completion +;; +;; Test +;; (setq LaTeX-math-menu '("Math" +;; ["foo" val0 t] +;; ("bar" +;; ["baz" val1 t]) +;; ("aze" +;; ["zer" val2 t]) +;; ("AMS" +;; ("rec" +;; ["fer" val3 t]) +;; ("rty" +;; ["der" val4 t])) +;; ("ABC" +;; ("xcv" +;; ["sdf" val5 t]) +;; ("dfg" +;; ["fgh" val6 t])))) +;; (helm-latex-math-candidates) +;; => +;; (("foo" . val0) +;; ("baz" . val1) +;; ("zer" . val2) +;; ("fer" . val3) +;; ("der" . val4) +;; ("sdf" . val5) +;; ("fgh" . val6)) + +(defvar LaTeX-math-menu) +(defun helm-latex-math-candidates () + (cl-labels ((helm-latex--math-collect (L) + (cond ((vectorp L) + (list (cons (aref L 0) (aref L 1)))) + ((listp L) + (cl-loop for a in L nconc + (helm-latex--math-collect a)))))) + (helm-latex--math-collect LaTeX-math-menu))) + +(defvar helm-source-latex-math + (helm-build-sync-source "Latex Math Menu" + :init (lambda () + (with-helm-current-buffer + (LaTeX-math-mode 1))) + :candidate-number-limit 9999 + :candidates 'helm-latex-math-candidates + :action (lambda (candidate) + (call-interactively candidate)))) + + +;;; Jabber Contacts (jabber.el) +(defun helm-jabber-online-contacts () + "List online Jabber contacts." + (with-no-warnings + (cl-loop for item in (jabber-concat-rosters) + when (get item 'connected) + collect + (if (get item 'name) + (cons (get item 'name) item) + (cons (symbol-name item) item))))) + +(defvar helm-source-jabber-contacts + (helm-build-sync-source "Jabber Contacts" + :init (lambda () (require 'jabber)) + :candidates (lambda () (mapcar 'car (helm-jabber-online-contacts))) + :action (lambda (x) + (jabber-chat-with + (jabber-read-account) + (symbol-name + (cdr (assoc x (helm-jabber-online-contacts)))))))) + +;;; World time +;; +(defvar zoneinfo-style-world-list) +(defvar legacy-style-world-list) + +(defun helm-time-zone-transformer (candidates _source) + (cl-loop for i in candidates + for (z . p) in display-time-world-list + collect + (cons + (cond ((string-match (format-time-string "%H:%M" (current-time)) i) + (propertize i 'face 'helm-time-zone-current)) + ((string-match helm-time-zone-home-location i) + (propertize i 'face 'helm-time-zone-home)) + (t i)) + z))) + +(defvar helm-source-time-world + (helm-build-in-buffer-source "Time World List" + :init (lambda () + (require 'time) + (unless (and display-time-world-list + (listp display-time-world-list)) + ;; adapted from `time--display-world-list' from + ;; emacs-27 for compatibility as + ;; `display-time-world-list' is set by default to t. + (setq display-time-world-list + ;; Determine if zoneinfo style timezones are + ;; supported by testing that America/New York and + ;; Europe/London return different timezones. + (let ((nyt (format-time-string "%z" nil "America/New_York")) + (gmt (format-time-string "%z" nil "Europe/London"))) + (if (string-equal nyt gmt) + legacy-style-world-list + zoneinfo-style-world-list))))) + :data (lambda () + (with-temp-buffer + (display-time-world-display display-time-world-list) + (buffer-string))) + :action 'helm-timezone-actions + :filtered-candidate-transformer 'helm-time-zone-transformer)) + +;;; Commands +;; +(defun helm-call-interactively (cmd-or-name) + "Execute CMD-OR-NAME as Emacs command. +It is added to `extended-command-history'. +`helm-current-prefix-arg' is used as the command's prefix argument." + (setq extended-command-history + (cons (helm-stringify cmd-or-name) + (delete (helm-stringify cmd-or-name) extended-command-history))) + (let ((current-prefix-arg helm-current-prefix-arg) + (cmd (helm-symbolify cmd-or-name))) + (if (stringp (symbol-function cmd)) + (execute-kbd-macro (symbol-function cmd)) + (setq this-command cmd) + (call-interactively cmd)))) + +;;; Minibuffer History +;; +;; +(defvar helm-minibuffer-history-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map [remap helm-minibuffer-history] 'undefined) + map)) + +(defcustom helm-minibuffer-history-must-match t + "Allow inserting non matching elements when nil or \\='confirm." + :group 'helm-misc + :type '(choice + (const :tag "Must match" t) + (const :tag "Confirm" confirm) + (const :tag "Always allow" nil))) + +(defcustom helm-minibuffer-history-key "C-r" + "The key `helm-minibuffer-history' is bound to in minibuffer local maps." + :type '(choice (string :tag "Key") (const :tag "no binding")) + :group 'helm-mode) + +(defconst helm-minibuffer-history-old-key + (cl-loop for map in '(minibuffer-local-completion-map + minibuffer-local-filename-completion-map + minibuffer-local-filename-must-match-map ; Emacs 23.1.+ + minibuffer-local-isearch-map + minibuffer-local-map + minibuffer-local-must-match-filename-map ; Older Emacsen + minibuffer-local-must-match-map + minibuffer-local-ns-map) + when (and (boundp map) (symbol-value map)) + collect (cons map (lookup-key (symbol-value map) "\C-r")))) + +;;;###autoload +(define-minor-mode helm-minibuffer-history-mode + "Bind `helm-minibuffer-history-key' in al minibuffer maps. +This mode is enabled by `helm-mode', so there is no need to enable it directly." + :group 'helm-misc + :global t + (if helm-minibuffer-history-mode + (let ((key helm-minibuffer-history-key)) + (cl-dolist (map '(minibuffer-local-completion-map + minibuffer-local-filename-completion-map + minibuffer-local-filename-must-match-map ; Emacs 23.1.+ + minibuffer-local-isearch-map + minibuffer-local-map + minibuffer-local-must-match-filename-map ; Older Emacsen + minibuffer-local-must-match-map + minibuffer-local-ns-map)) + (let ((vmap (and (boundp map) (symbol-value map)))) + (when (keymapp vmap) + (let ((val (and (boundp 'helm-minibuffer-history-key) + (symbol-value 'helm-minibuffer-history-key)))) + (when val + (define-key vmap + (if (stringp val) (read-kbd-macro val) val) + nil))) + (when key + (define-key (symbol-value map) + (if (stringp key) (read-kbd-macro key) key) + 'helm-minibuffer-history)))))) + (cl-dolist (map '(minibuffer-local-completion-map + minibuffer-local-filename-completion-map + minibuffer-local-filename-must-match-map + minibuffer-local-isearch-map + minibuffer-local-map + minibuffer-local-must-match-filename-map + minibuffer-local-must-match-map + minibuffer-local-ns-map)) + (let ((vmap (and (boundp map) (symbol-value map)))) + (when (keymapp vmap) + (let ((val (and (boundp 'helm-minibuffer-history-key) + (symbol-value 'helm-minibuffer-history-key)))) + (when val + (define-key vmap + (if (stringp val) (read-kbd-macro val) val) + (assoc-default map helm-minibuffer-history-old-key))))))))) + + +;;; Helm ratpoison UI +;; +;; +(defvar helm-source-ratpoison-commands + (helm-build-in-buffer-source "Ratpoison Commands" + :init 'helm-ratpoison-commands-init + :action (helm-make-actions + "Execute the command" 'helm-ratpoison-commands-execute) + :display-to-real 'helm-ratpoison-commands-display-to-real + :candidate-number-limit 999999)) + +(defun helm-ratpoison-commands-init () + (unless (helm-candidate-buffer) + (with-current-buffer (helm-candidate-buffer 'global) + ;; with ratpoison prefix key + (save-excursion + (call-process "ratpoison" nil (current-buffer) nil "-c" "help")) + (while (re-search-forward "^\\([^ ]+\\) \\(.+\\)$" nil t) + (replace-match " \\1: \\2")) + (goto-char (point-max)) + ;; direct binding + (save-excursion + (call-process "ratpoison" nil (current-buffer) nil "-c" "help top")) + (while (re-search-forward "^\\([^ ]+\\) \\(.+\\)$" nil t) + (replace-match "\\1: \\2"))))) + +(defun helm-ratpoison-commands-display-to-real (display) + (and (string-match ": " display) + (substring display (match-end 0)))) + +(defun helm-ratpoison-commands-execute (candidate) + (call-process "ratpoison" nil nil nil "-ic" candidate)) + +;;; Helm stumpwm UI +;; +;; +(defvar helm-source-stumpwm-commands + (helm-build-in-buffer-source "Stumpwm Commands" + :init 'helm-stumpwm-commands-init + :action (helm-make-actions + "Execute the command" 'helm-stumpwm-commands-execute) + :candidate-number-limit 999999)) + +(defun helm-stumpwm-commands-init () + (with-current-buffer (helm-candidate-buffer 'global) + (save-excursion + (call-process "stumpish" nil (current-buffer) nil "commands")) + (while (re-search-forward "[ ]*\\([^ ]+\\)[ ]*\n?" nil t) + (replace-match "\n\\1\n")) + (delete-blank-lines) + (sort-lines nil (point-min) (point-max)) + (goto-char (point-max)))) + +(defun helm-stumpwm-commands-execute (candidate) + (call-process "stumpish" nil nil nil candidate)) + +;;;###autoload +(defun helm-world-time () + "Preconfigured `helm' to show world time. +Default action change TZ environment variable locally to emacs." + (interactive) + (helm-other-buffer 'helm-source-time-world "*helm world time*")) + +;;;###autoload +(defun helm-insert-latex-math () + "Preconfigured helm for latex math symbols completion." + (interactive) + (helm-other-buffer 'helm-source-latex-math "*helm latex*")) + +;;;###autoload +(defun helm-ratpoison-commands () + "Preconfigured `helm' to execute ratpoison commands." + (interactive) + (helm-other-buffer 'helm-source-ratpoison-commands + "*helm ratpoison commands*")) + +;;;###autoload +(defun helm-stumpwm-commands() + "Preconfigured helm for stumpwm commands." + (interactive) + (helm-other-buffer 'helm-source-stumpwm-commands + "*helm stumpwm commands*")) + +;;;###autoload +(defun helm-minibuffer-history () + "Preconfigured `helm' for `minibuffer-history'." + (interactive) + (cl-assert (minibuffer-window-active-p (selected-window)) nil + "Error: Attempt to use minibuffer history outside a minibuffer") + (let* ((enable-recursive-minibuffers t) + (query-replace-p (or (eq last-command 'query-replace) + (eq last-command 'query-replace-regexp))) + (elm (helm-comp-read "Next element matching (regexp): " + (cl-loop for i in + (symbol-value minibuffer-history-variable) + unless (equal "" i) collect i into history + finally return + (if (consp (car history)) + (mapcar 'prin1-to-string history) + history)) + :header-name + (lambda (name) + (format "%s (%s)" name minibuffer-history-variable)) + :buffer "*helm minibuffer-history*" + :must-match helm-minibuffer-history-must-match + :multiline t + :keymap helm-minibuffer-history-map + :allow-nest t))) + ;; Fix Bug#1667 with emacs-25+ `query-replace-from-to-separator'. + (when (and (boundp 'query-replace-from-to-separator) query-replace-p) + (let ((pos (string-match "\0" elm))) + (and pos + (add-text-properties + pos (1+ pos) + `(display ,query-replace-from-to-separator separator t) + elm)))) + (delete-minibuffer-contents) + (insert elm))) + + +(provide 'helm-misc) + +;;; helm-misc.el ends here diff --git a/code/elpa/helm-20220822.659/helm-mode.el b/code/elpa/helm-20220822.659/helm-mode.el new file mode 100644 index 0000000..9d2e674 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-mode.el @@ -0,0 +1,2249 @@ +;;; helm-mode.el --- Enable helm completion everywhere. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-lib) +(require 'helm-files) +(require 'helm-misc) + +(defvar crm-separator) +(defvar ido-everywhere) +(defvar completion-flex-nospace) +(defvar helm-completion--sorting-done) +(defvar helm-mode) +(defvar password-cache) + +;; No warnings in Emacs built --without-x +(declare-function x-file-dialog "xfns.c") + +(declare-function ido-mode "ido.el") +(declare-function helm-apropos-init "helm-elisp") +(declare-function helm-lisp-completion-persistent-action "helm-elisp") +(declare-function helm-lisp-completion-persistent-help "helm-elisp") +(declare-function help--symbol-class "help-fns.el") +(declare-function helm-get-first-line-documentation "helm-elisp") + +(defgroup helm-mode nil + "Enable helm completion." + :group 'helm) + +(defcustom helm-completing-read-handlers-alist + '((find-tag . helm-completing-read-default-find-tag) + (xref-find-definitions . helm-completing-read-default-find-tag) + (xref-find-references . helm-completing-read-default-find-tag) + (ggtags-find-tag-dwim . helm-completing-read-default-find-tag) + (tmm-menubar . nil) + (find-file . nil) + (execute-extended-command . nil) + (dired-do-rename . helm-read-file-name-handler-1) + (dired-do-copy . helm-read-file-name-handler-1) + (dired-do-symlink . helm-read-file-name-handler-1) + (dired-do-relsymlink . helm-read-file-name-handler-1) + (dired-do-hardlink . helm-read-file-name-handler-1) + (basic-save-buffer . helm-read-file-name-handler-1) + (write-file . (default helm-read-file-name-handler-1)) + (write-region . (default helm-read-file-name-handler-1))) + "Completing read functions for specific Emacs commands. + +By default `helm-mode' use `helm-completing-read-default-handler' to +provide helm completion in each `completing-read' or `read-file-name' +found, but other functions can be specified here for specific +commands. This also allows disabling helm completion for some commands +when needed. + +Each entry is a cons cell like (EMACS_COMMAND . COMPLETING-READ_HANDLER) +where key and value are symbols. +However if a command is using in its definition both a `completing-read' AND +a `read-file-name' we may want to specify a handler for both of them, +this can be done by specifying value as a list of two symbols instead of +a single symbol where the 1st element of the list specify the handler for the +`completing-read' and the second the handler for the `read-file-name'. +Special symbol \\='default' means use the default helm handler for either +`completing-read' or `read-file-name'. +e.g. (write-region . (default helm-read-file-name-handler-1)) +means helm will use `helm-completing-read-default-handler' when +`write-region' calls `completing-read' and +`helm-read-file-name-handler-1' when it calls `read-file-name'. + +Each key is an Emacs command that use originaly `completing-read' +or/and `read-file-name'. + +Each value maybe a helm function that takes same arguments as +`completing-read' plus NAME and BUFFER, where NAME is the name of the new +helm source and BUFFER the name of the buffer we will use, but it can +be also a function not using helm, in this case the function should +take the same args as `completing-read' and not be prefixed by \"helm-\". + +`helm' will use the name of the command calling `completing-read' as +NAME and BUFFER will be computed as well with NAME but prefixed with +\"*helm-mode-\". + +This function prefix name must start by \"helm-\" when it uses helm, +otherwise `helm' assumes the function is not a helm function and +expects the same args as `completing-read', this allows you to define a +handler not using helm completion. + +Example: + + (defun foo/test () + (interactive) + (message \"%S\" (completing-read \"test: \" \\='(a b c d e)))) + + (defun helm-foo/test-completing-read-handler (prompt collection + predicate require-match + initial-input hist def + inherit-input-method + name buffer) + (helm-comp-read prompt collection :marked-candidates t + :name name + :buffer buffer)) + + (add-to-list \\='helm-completing-read-handlers-alist + \\='(foo/test . helm-foo/test-completing-read-handler)) + + +We want here to make the regular `completing-read' in `foo/test' +return a list of candidate(s) instead of a single candidate. + +Note that this function will be reused for ALL the `completing-read' +of this command, so it should handle all cases. E.g., +if first `completing-read' completes against symbols and +second `completing-read' should handle only buffer, +your specialized function should handle both. + +If the value of an entry is nil completion will fall back to +Emacs vanilla behaviour. +Example: + +If you want to disable helm completion for `describe-function', use: + + (describe-function . nil) + +Ido is also supported, you can use `ido-completing-read' and +`ido-read-file-name' as value of an entry or just \\='ido. +Example: +Enable ido completion for `find-file': + + (find-file . ido) + +same as + + (find-file . ido-read-file-name) + +Note that you don't need to enable `ido-mode' for this to work, see +`helm-mode' documentation." + :group 'helm-mode + :type '(alist :key-type symbol :value-type symbol)) + +(defcustom helm-comp-read-case-fold-search helm-case-fold-search + "Default Local setting of `helm-case-fold-search' for `helm-comp-read'. +See `helm-case-fold-search' for more info." + :group 'helm-mode + :type 'symbol) + +(defcustom helm-mode-handle-completion-in-region t + "Whether to replace or not `completion-in-region-function'. +This enables support for `completing-read-multiple' and `completion-at-point' +when non--nil." + :group 'helm-mode + :type 'boolean) + +(defcustom helm-mode-no-completion-in-region-in-modes nil + "A list of modes that do not want helm for `completion-in-region'." + :group 'helm-mode + :type 'boolean) + +(defcustom helm-mode-reverse-history t + "Display history source after current source when non nil. + +Apply only in `helm-mode' handled commands." + :group 'helm-mode + :type 'boolean) + +(defcustom helm-completion-in-region-default-sort-fn + 'helm-completion-in-region-sort-fn + "The default sort function to sort candidates in completion-in-region. + +When nil no sorting is done. +The function is a `filtered-candidate-transformer' function which takes +two args CANDIDATES and SOURCE. +The function must use the flag `helm-completion--sorting-done' and +return CANDIDATES unchanged when the flag is nil. +See default function `helm-completion-in-region-sort-fn' as example. +It will be used only when `helm-completion-style' is either Emacs or +helm, otherwise when helm-fuzzy style is used, the fuzzy sort function +will be used." + :group 'helm-mode + :type 'function) + +(defcustom helm-mode-ignore-diacritics nil + "Ignore diacritics in completing-read." + :group 'helm-mode + :type 'boolean) + +(defcustom helm-completion-mark-suffix t + "Push mark at end of suffix when non nil." + :group 'helm-mode + :type 'boolean) + +(defcustom helm-read-file-name-use-default-arg-behavior nil + "Use emacs vanilla `read-file-name' behavior for default arg. + +The behavior of default arg in `read-file-name' and friends is using +the default arg as default value when initial input is not modified, +even if this initial input is a valid value i.e. an existing file. +We expect generally a default arg to be used if nothing is specified +in the prompt or if what is specified is invalid, but the emacs behavior +here is really weird, so we use this variable to disable this +behavior, letting user specify default if needed with `M-n'. +However we keep the emacs default for `read-file-name' and derived +fns, this variable affecting only `helm-read-file-name'." + :type 'boolean + :group 'helm-mode) + +(defvar helm-mode-minibuffer-setup-hook-black-list '(minibuffer-completion-help) + "Incompatible `minibuffer-setup-hook' functions go here. +A list of symbols. `helm-mode' is rejecting all lambda's, byte-code fns +and all functions belonging in this list from `minibuffer-setup-hook'. +This is mainly needed to prevent \"*Completions*\" buffers to popup.") + +(defface helm-mode-prefix + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + (:background "red" :foreground "black"))) + "Face used for prefix completion." + :group 'helm-mode) + +(defvar helm-comp-read-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "") 'helm-cr-empty-string) + (define-key map (kbd "M-RET") 'helm-cr-empty-string) + map) + "Keymap for `helm-comp-read'.") + +(defun helm-mode-delete-char-backward-1 () + (interactive) + (condition-case err + (call-interactively 'delete-backward-char) + (text-read-only + (if (with-selected-window (minibuffer-window) + (not (string= (minibuffer-contents) ""))) + (message "Trying to delete prefix completion, next hit will quit") + (user-error "%s" (car err)))))) +(put 'helm-mode-delete-char-backward-1 'helm-only t) + +(defun helm-mode-delete-char-backward-2 () + (interactive) + (condition-case _err + (call-interactively 'delete-backward-char) + (text-read-only + (unless (with-selected-window (minibuffer-window) + (string= (minibuffer-contents) "")) + (with-helm-current-buffer + (run-with-timer 0.1 nil (lambda () + (call-interactively 'delete-backward-char)))) + (helm-keyboard-quit))))) +(put 'helm-mode-delete-char-backward-2 'helm-only t) + +(helm-multi-key-defun helm-mode-delete-char-backward-maybe + "Delete char backward when text is not the prefix helm is completing against. +First call warns user about deleting prefix completion. +Second call deletes backward char in current-buffer and quits helm completion, +letting the user start a new completion with a new prefix." + '(helm-mode-delete-char-backward-1 helm-mode-delete-char-backward-2) 1) + +(defcustom helm-completion-style 'helm + "Style of completion to use in `completion-in-region'. + +This affects only `completion-at-point' and friends, and +the `completing-read' using the default handler +i.e. `helm-completing-read-default-handler'. + +NB: This has nothing to do with `completion-styles', it is independent from +helm, but when using \\='emacs as helm-completion-style helm +will use the `completion-styles' for its completions. +Up to the user to configure `completion-styles'. + +There are three possible values to use: + +- helm, use multi match regular helm completion. + +- helm-fuzzy, use fuzzy matching. Note that as usual when + entering a space helm switches to multi matching mode. + +- emacs, use regular Emacs completion according to + `completion-styles'. Note that even in this style, helm allows using + multi match. Emacs-27 provides a style called `flex' that can be used + aside `helm' style (see `completion-styles-alist'). When `flex' style + is not available (Emacs<27) helm provides `helm-flex' style which is + similar to `flex' and helm fuzzy matching. + +For a better experience with emacs style, if you don't know what to use, set +`completion-styles' to \\='(flex) if you are using emacs-27 or to +\\='(helm-flex) if you are using emacs-26 and keep \\='emacs as default +value for `helm-completion-style'. Advanced users can also have a +look to `completion-category-overrides' to set styles according to category. +You can as well use `helm-completion-styles-alist' to override +`helm-completion-style' in specific modes. + +Of course when using `helm' or `helm-fuzzy' as `helm-completion-style' +emacs `completion-styles' have no effect. + +Please use custom interface or `customize-set-variable' to set this, +NOT `setq'." + :group 'helm-mode + :type '(choice (const :tag "Emacs" emacs) + (const :tag "Helm" helm) + (const :tag "Helm-fuzzy" helm-fuzzy)) + :set (lambda (var val) + (set var val) + (if (memq val '(helm helm-fuzzy)) + (define-key helm-comp-read-map (kbd "DEL") 'helm-mode-delete-char-backward-maybe) + (define-key helm-comp-read-map (kbd "DEL") 'delete-backward-char)))) + +(defconst helm-completion--all-styles + (let ((flex (if (assq 'flex completion-styles-alist) + 'flex 'helm-flex))) + (helm-fast-remove-dups + (append (list 'helm flex) + (mapcar 'car completion-styles-alist))))) + +(defconst helm-completion--styles-type + `(repeat :tag "with other completion styles" + (choice ,@(mapcar (lambda (x) (list 'const x)) + helm-completion--all-styles)))) + +(defcustom helm-completion-styles-alist '((gud-mode . helm) + ;; See https://github.com/djcb/mu/issues/2181. + (mu4e-compose-mode . emacs)) + "Allow configuring `helm-completion-style' per mode. + +Each entry is a cons cell like (mode . style) where style must be a +suitable value for `helm-completion-style'. +When specifying emacs as style for a mode, `completion-styles' can be +specified by using a cons cell specifying completion-styles to use +with helm emacs style, e.g. (foo-mode . (emacs helm flex)) will set +`completion-styles' to \\='(helm flex) for foo-mode. This affects only +completions happening in buffers and not minibuffer completions, +i.e. completing-read's." + :group 'helm-mode + :type + `(alist :key-type (symbol :tag "Major Mode") + :value-type + (choice :tag "Use helm style or completion styles" + (radio :tag "Helm Style" + (const helm) + (const helm-fuzzy) + (const emacs)) + (cons :tag "Completion Styles" + (const :tag "Using Helm `emacs' style" emacs) + ,helm-completion--styles-type)))) + +;;; helm-comp-read +;; +;; +(defvar helm-comp-read-use-marked nil + "[INTERNAL] When non nil `helm-comp-read' will return marked candidates. + +Use this ONLY in `let', NOT globally, this allows third party packages +to use a list as return value when `helm-mode' is enabled, e.g. + + (let ((helm-comp-read-use-marked t)) + (completing-read \"test: \" \\='(a b c d e f g))) + +") + +(defun helm-cr-empty-string () + "Return empty string." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action + (lambda (_candidate) + (identity ""))))) +(put 'helm-cr-empty-string 'helm-only t) + +(defun helm-mode--keyboard-quit () + ;; Use this instead of `keyboard-quit' + ;; to avoid deactivating mark in current-buffer. + (let ((debug-on-quit nil)) + (signal 'quit nil))) + +(cl-defun helm-comp-read-get-candidates (collection &optional + test sort-fn alistp + (input helm-pattern)) + "Convert COLLECTION to list removing elements that don't match TEST. +See `helm-comp-read' about supported COLLECTION arguments. + +SORT-FN is a predicate to sort COLLECTION. + +ALISTP when non--nil will not use `all-completions' to collect +candidates because it doesn't handle alists correctly for helm. +i.e In `all-completions' the car of each pair is used as value. +In helm we want to use the cdr instead like (display . real), +so we return the alist as it is with no transformation by +`all-completions'. + +e.g + +\(setq A \\='((a . 1) (b . 2) (c . 3))) +==>((a . 1) (b . 2) (c . 3)) +\(helm-comp-read \"test: \" A :alistp nil + :exec-when-only-one t + :initial-input \"a\") +==>\"a\" Which is not what we expect. + +\(helm-comp-read \"test: \" A :alistp t + :exec-when-only-one t + :initial-input \"1\") +==>\"1\" + +See docstring of `all-completions' for more info. + +INPUT is the string you want to complete against, defaulting to +`helm-pattern' which is the value of what you enter in minibuffer. +Note that when using a function as COLLECTION this value will be +available with the input argument of the function only when using a +sync source from `helm-comp-read', i.e. not using +`:candidates-in-buffer', otherwise the function is called only once +with an empty string as value for `helm-pattern' because +`helm-pattern' is not yet computed, which is what we want otherwise +data would not be fully collected at init time. + +If COLLECTION is an `obarray', a TEST should be needed. See `obarray'." + ;; Ensure COLLECTION is computed from `helm-current-buffer' + ;; because some functions used as COLLECTION work + ;; only in the context of current-buffer (Bug#1030) . + (with-helm-current-buffer + (let ((cands + (cond ((and alistp (hash-table-p collection)) + (cl-loop for k being the hash-keys of collection + using (hash-values v) + collect (cons k v))) + ((vectorp collection) + (all-completions input collection test)) + ((and (symbolp collection) (boundp collection) + ;; Bug#324 history is let-bounded and given + ;; quoted as hist argument of completing-read. + ;; See example in `rcirc-browse-url'. + (symbolp (symbol-value collection))) + nil) + ;; When collection is a symbol, most of the time + ;; it should be a symbol used as a minibuffer-history. + ;; The value of this symbol in this case return a list + ;; of string which maybe are converted later as symbol + ;; in special cases. + ;; we treat here commandp as a special case as it return t + ;; also with a string unless its last arg is provided. + ;; Also, the history collections generally collect their + ;; elements as string, so intern them to call predicate. + ((and (symbolp collection) (boundp collection) test) + (let ((predicate (lambda (elm) + (condition-case _err + (if (eq test 'commandp) + (funcall test (intern elm)) + (funcall test elm)) + (wrong-type-argument + (funcall test (intern elm))))))) + (all-completions input (symbol-value collection) predicate))) + ((and (symbolp collection) (boundp collection)) + (all-completions input (symbol-value collection))) + ;; Normally file completion should not be handled here, + ;; but special cases like `find-file-at-point' do it. + ;; Handle here specially such cases. + ((and (functionp collection) (not (string= input "")) + (or minibuffer-completing-file-name + (eq (completion-metadata-get + (completion-metadata input collection test) + 'category) + 'file))) + (cl-loop for f in (funcall collection input test t) + unless (member f '("./" "../")) + if (string-match helm--url-regexp input) + collect f + else + collect (concat (file-name-as-directory + (helm-basedir input)) + f))) + ((functionp collection) + (funcall collection input test t)) + ((and alistp (null test)) collection) + ;; Next test ensure circular objects are removed + ;; with `all-completions' (Bug#1530). + (t (all-completions input collection test))))) + (if sort-fn (sort cands sort-fn) cands)))) + +(cl-defun helm-cr--pattern-in-candidates-p (candidates &optional (pattern helm-pattern)) + (or (assoc pattern candidates) + (assq (intern pattern) candidates) + (member pattern candidates) + (member (downcase pattern) candidates) + (member (upcase pattern) candidates))) + +(defun helm-cr-default-transformer (candidates source) + "Default filter candidate function for `helm-comp-read'." + (let ((must-match (helm-get-attr 'must-match source))) + (cl-loop for c in candidates + for cand = (let ((elm (if (stringp c) + (replace-regexp-in-string "\\s\\" "" c) + c))) + (cond ((and (stringp elm) + (string-match "\n" elm)) + (cons (replace-regexp-in-string "\n" "->" elm) c)) + (t c))) + collect cand into lst + finally return + ;; Unquote helm-pattern when it is added as candidate + ;; (Bug#2015). + (let ((pat (replace-regexp-in-string "\\s\\" "" helm-pattern))) + (if (or (string= pat "") + (eq must-match t) + (helm-cr--pattern-in-candidates-p lst pat)) + lst + (append (list (cons (concat (propertize + " " 'display + (propertize "[?]" + 'face 'helm-ff-prefix + 'unknown t)) + pat) + pat)) + lst)))))) + +(defun helm-comp-read--move-to-first-real-candidate () + (helm-aif (helm-get-selection nil 'withprop) + ;; Avoid error with candidates with an image as display (Bug#2296). + (when (equal (get-text-property 0 'display it) "[?]") + (helm-next-line)))) + +(defun helm-cr-default (default cands) + (delq nil + (cond ((and (stringp default) + (not (string= default "")) + (string= helm-pattern "")) + (cons default (delete default cands))) + ((and (consp default) (string= helm-pattern "")) + (append (cl-loop for d in default + ;; Don't convert + ;; nil to "nil" (i.e the string) + ;; it will be delq'ed on top. + for str = (if (null d) d (helm-stringify d)) + when (member str cands) + do (setq cands (delete d cands)) + when str collect str) + cands)) + (t cands)))) + +;;;###autoload +(cl-defun helm-comp-read (prompt collection + &key + test + initial-input + default + preselect + (buffer "*Helm Completions*") + must-match + fuzzy + reverse-history + (requires-pattern 0) + (history nil shistory) + raw-history + input-history + (case-fold helm-comp-read-case-fold-search) + (persistent-action nil) + (persistent-help "DoNothing") + (mode-line helm-comp-read-mode-line) + help-message + (keymap helm-comp-read-map) + (name "Helm Completions") + header-name + candidates-in-buffer + diacritics + match-part + match-dynamic + exec-when-only-one + quit-when-no-cand + (volatile t) + sort + fc-transformer + hist-fc-transformer + (marked-candidates helm-comp-read-use-marked) + nomark + (alistp t) + (candidate-number-limit helm-candidate-number-limit) + multiline + allow-nest + coerce + (group 'helm)) + "Read a string in the minibuffer, with helm completion. + +It is helm `completing-read' equivalent. + +- PROMPT is the prompt name to use. + +- COLLECTION can be a list, alist, vector, obarray or hash-table. + For alists and hash-tables their car are use as real value of + candidate unless ALISTP is non-nil. + It can be also a function that receives three arguments: + the values string, predicate and t. See `all-completions' for more details. + +Keys description: + +- TEST: A predicate called with one arg i.e candidate. + +- INITIAL-INPUT: Same as input arg in `helm'. + +- PRESELECT: See preselect arg of `helm'. + +- DEFAULT: This option is used only for compatibility with regular + Emacs `completing-read' (Same as DEFAULT arg of `completing-read'). + +- BUFFER: Name of helm-buffer. + +- MUST-MATCH: Candidate selected must be one of COLLECTION. + +- FUZZY: Enable fuzzy matching. + +- REVERSE-HISTORY: When non--nil display history source after current + source completion. + +- REQUIRES-PATTERN: Same as helm attribute, default is 0. + +- HISTORY: A symbol where each result will be saved. + If not specified as a symbol an error will popup. + When specified, all elements of HISTORY are displayed in + a special source before or after COLLECTION according to REVERSE-HISTORY. + The main difference with INPUT-HISTORY is that the result of the + completion is saved whereas in INPUT-HISTORY it is the minibuffer + contents which is saved when you exit. + Don't use the same symbol for INPUT-HISTORY and HISTORY. + NOTE: As mentionned above this has nothing to do with + `minibuffer-history-variable', therefore if you want to save this + history persistently, you will have to add this variable to the + relevant variable of your favorite tool for persistent emacs session + i.e. psession, desktop etc... + +- RAW-HISTORY: When non-nil do not remove backslashs if some in + HISTORY candidates. + +- INPUT-HISTORY: A symbol. The minibuffer input history will be + stored there, if nil or not provided, `minibuffer-history' + will be used instead. You can navigate in this history with + `M-p' and `M-n'. + Don't use the same symbol for INPUT-HISTORY and HISTORY. + +- CASE-FOLD: Same as `helm-case-fold-search'. + +- PERSISTENT-ACTION: A function called with one arg i.e candidate. + +- PERSISTENT-HELP: A string to document PERSISTENT-ACTION. + +- MODE-LINE: A string or list to display in mode line. + Default is `helm-comp-read-mode-line'. + +- KEYMAP: A keymap to use in this `helm-comp-read'. + (the keymap will be shared with history source) + +- NAME: The name related to this local source. + +- HEADER-NAME: A function to alter NAME, see `helm'. + +- EXEC-WHEN-ONLY-ONE: Bound `helm-execute-action-at-once-if-one' + to non--nil. (possibles values are t or nil). + +- VOLATILE: Use volatile attribute. + +- SORT: A predicate to give to `sort' e.g `string-lessp' + Use this only on small data as it is inefficient. + If you want to sort faster add a sort function to + FC-TRANSFORMER. + Note that FUZZY when enabled is already providing a sort function. + +- FC-TRANSFORMER: A `filtered-candidate-transformer' function + or a list of functions. + +- HIST-FC-TRANSFORMER: A `filtered-candidate-transformer' + function for the history source. + +- MARKED-CANDIDATES: If non-nil return candidate or marked candidates as a list. + +- NOMARK: When non--nil don't allow marking candidates. + +- ALISTP: + When non-nil (default) pass the value of (DISPLAY . REAL) + candidate in COLLECTION to action when COLLECTION is an alist or a + hash-table, otherwise DISPLAY is always returned as result on exit, + which is the default when using `completing-read'. + See `helm-comp-read-get-candidates'. + +- CANDIDATES-IN-BUFFER: when non--nil use a source build with + `helm-source-in-buffer' which is much faster. + Argument VOLATILE have no effect when CANDIDATES-IN-BUFFER is non--nil. + +- MATCH-PART: Allow matching only one part of candidate. + See match-part documentation in `helm-source'. + +- MATCH-DYNAMIC: See match-dynamic in `helm-source-sync' + It has no effect when used with CANDIDATES-IN-BUFFER. + +- ALLOW-NEST: Allow nesting this `helm-comp-read' in a helm session. + See `helm'. + +- MULTILINE: See multiline in `helm-source'. + +- COERCE: See coerce in `helm-source'. + +- GROUP: See group in `helm-source'. + +Any prefix args passed during `helm-comp-read' invocation will be recorded +in `helm-current-prefix-arg', otherwise if prefix args were given before +`helm-comp-read' invocation, the value of `current-prefix-arg' will be used. +That means you can pass prefix args before or after calling a command +that use `helm-comp-read'. See `helm-M-x' for example." + ;; Handle error with HISTORY: + ;; + ;; Should show helm with one source at first run and save result on + ;; exit, should show the history source along candidates source on + ;; next run as soon as `test-hist' value is feeded. + ;; (setq test-hist nil) + ;; (helm-comp-read "test: " '(a b c d e) + ;; :history 'test-hist) + ;; + ;; Should run normally as long as `test-hist' is bound and nil. As + ;; soon `test-hist' becomes non-nil throw an error. + ;; (helm-comp-read "test: " '(a b c d e) + ;; :history test-hist) + ;; + ;; Should run normally. + ;; (completing-read "test: " '(a b c d e)) + (cl-assert (if shistory + (or (null history) + (and history (symbolp history))) + t) + nil "Error: History should be specified as a symbol") + (when (get-buffer helm-action-buffer) + (kill-buffer helm-action-buffer)) + (unless (memq must-match '(confirm confirm-after-completion t nil)) + ;; Fix completing-read's using something else than `t' e.g. 1 or + ;; whatever (bug #2527). + (setq must-match t)) + (let ((action-fn `(("Sole action (Identity)" + . (lambda (candidate) + (if ,marked-candidates + (helm-marked-candidates) + (identity candidate))))))) + (let* ((minibuffer-completion-predicate test) + (minibuffer-completion-table collection) + (helm-read-file-name-mode-line-string + (replace-regexp-in-string "helm-maybe-exit-minibuffer" + "helm-confirm-and-exit-minibuffer" + helm-read-file-name-mode-line-string)) + (get-candidates + (lambda () + (let ((cands (helm-comp-read-get-candidates + ;; If `helm-pattern' is passed as INPUT + ;; and :alistp is nil INPUT is passed to + ;; `all-completions' which defeat helm + ;; matching functions (multi match, fuzzy + ;; etc...) Bug#2134. + collection test sort alistp + (if (and match-dynamic (null candidates-in-buffer)) + helm-pattern "")))) + (helm-cr-default default cands)))) + (history-get-candidates + (lambda () + (let ((cands (helm-comp-read-get-candidates + history test nil alistp))) + (when cands + (delete "" (helm-cr-default default cands)))))) + (src-hist (helm-build-sync-source (format "%s History" name) + :candidates history-get-candidates + :fuzzy-match fuzzy + :multiline multiline + :match-part match-part + :filtered-candidate-transformer + (append `((lambda (candidates _source) + (if ,raw-history + candidates + (cl-loop for i in candidates + ;; Input is added to history in completing-read's + ;; and may be regexp-quoted, so unquote it + ;; but check if cand is a string (it may be at this stage + ;; a symbol or nil) Bug#1553. + when (stringp i) + collect (replace-regexp-in-string "\\s\\" "" i))))) + (and hist-fc-transformer (helm-mklist hist-fc-transformer))) + :persistent-action persistent-action + :persistent-help persistent-help + :keymap keymap + :must-match must-match + :group group + :coerce coerce + :mode-line mode-line + :help-message help-message + :action action-fn)) + (src (helm-build-sync-source name + :candidates get-candidates + :match-part match-part + :multiline multiline + :header-name header-name + :filtered-candidate-transformer + (let ((transformers (helm-mklist fc-transformer))) + (append transformers + (unless (member 'helm-cr-default-transformer transformers) + '(helm-cr-default-transformer)))) + :requires-pattern requires-pattern + :persistent-action persistent-action + :persistent-help persistent-help + :fuzzy-match fuzzy + :diacritics diacritics + :keymap keymap + :must-match must-match + :group group + :coerce coerce + :mode-line mode-line + :match-dynamic match-dynamic + :help-message help-message + :action action-fn + :volatile volatile)) + (src-1 (helm-build-in-buffer-source name + :data get-candidates + :match-part match-part + :multiline multiline + :header-name header-name + :filtered-candidate-transformer + (append (helm-mklist fc-transformer) + '(helm-cr-default-transformer)) + :requires-pattern requires-pattern + :persistent-action persistent-action + :fuzzy-match fuzzy + :diacritics diacritics + :keymap keymap + :must-match must-match + :group group + :coerce coerce + :persistent-help persistent-help + :mode-line mode-line + :help-message help-message + :action action-fn)) + (src-list (list src-hist + (if candidates-in-buffer + src-1 src))) + (helm-execute-action-at-once-if-one exec-when-only-one) + (helm-quit-if-no-candidate quit-when-no-cand) + result) + (when nomark + (setq src-list (cl-loop for src in src-list + collect (cons '(nomark) src)))) + (when reverse-history (setq src-list (nreverse src-list))) + (add-hook 'helm-after-update-hook 'helm-comp-read--move-to-first-real-candidate) + (unwind-protect + (setq result (helm + :sources src-list + :input initial-input + :default default + :preselect preselect + :prompt prompt + :resume 'noresume + :keymap keymap ;; Needed with empty collection. + :allow-nest allow-nest + :candidate-number-limit candidate-number-limit + :case-fold-search case-fold + :history (and (symbolp input-history) input-history) + :buffer buffer)) + (remove-hook 'helm-after-update-hook 'helm-comp-read--move-to-first-real-candidate)) + ;; If `history' is a symbol save it. + (when (and result history (symbolp history)) + (set history + ;; RESULT may be a a string or a list of strings bug #2461. + (delete-dups (append (mapcar #'substring-no-properties (helm-mklist result)) + (symbol-value history))))) + (or result (helm-mode--keyboard-quit))))) + + +;; Generic completing-read +;; +;; Support also function as collection. +;; e.g M-x man is supported. +;; Support hash-table and vectors as collection. +;; NOTE: +;; Some crap emacs functions may not be supported +;; like ffap-alternate-file (bad use of completing-read) +;; and maybe others. +;; Provide a mode `helm-mode' which turn on +;; helm in all `completing-read' and `read-file-name' in Emacs. +;; +(defvar helm-completion-mode-string " Helm") + +(defvar helm-completion-mode-quit-message + "Helm completion disabled") + +(defvar helm-completion-mode-start-message + "Helm completion enabled") + +;;; Specialized handlers +;; +;; +(defun helm-completing-read-symbols + (prompt _collection test _require-match init + hist default _inherit-input-method name buffer) + "Specialized function for fast symbols completion in `helm-mode'." + (require 'helm-elisp) + (or + (helm + :sources (helm-build-in-buffer-source name + :init (lambda () + (helm-apropos-init (lambda (x) + (and (funcall test x) + (not (keywordp x)))) + (or (car-safe default) default))) + :filtered-candidate-transformer 'helm-apropos-default-sort-fn + :help-message #'helm-comp-read-help-message + :fuzzy-match (eq helm-completion-style 'helm-fuzzy) + :persistent-action + (lambda (candidate) + (helm-lisp-completion-persistent-action + candidate name)) + :persistent-help (helm-lisp-completion-persistent-help)) + :prompt prompt + :buffer buffer + :input init + :history hist + :resume 'noresume + :default (or default "")) + (helm-mode--keyboard-quit))) + + +;;; Generic completing read +;; +;; +(defun helm-completing-read-default-1 + (prompt collection test require-match + init hist default _inherit-input-method + name buffer &optional cands-in-buffer exec-when-only-one) + "Call `helm-comp-read' with same args as `completing-read'. +Extra optional arg CANDS-IN-BUFFER means use `candidates-in-buffer' +method which is faster. +It should be used when candidate list doesn't need to be rebuilt dynamically." + (let ((history (or (car-safe hist) hist)) + (initial-input (helm-aif (pcase init + ((pred (stringp)) init) + ;; INIT is a cons cell. + (`(,l . ,_ll) l)) + it))) + (helm-comp-read + prompt collection + :test test + :history history + :reverse-history helm-mode-reverse-history + :input-history history + :must-match require-match + :alistp nil + :diacritics helm-mode-ignore-diacritics + :help-message #'helm-comp-read-help-message + :name name + :requires-pattern (if (and (stringp default) + (string= default "") + (or (eq require-match 'confirm) + (eq require-match + 'confirm-after-completion))) + 1 0) + :quit-when-no-cand (eq require-match t) + :nomark (null helm-comp-read-use-marked) + :candidates-in-buffer cands-in-buffer + :exec-when-only-one exec-when-only-one + :fuzzy (eq helm-completion-style 'helm-fuzzy) + :buffer buffer + ;; If DEF is not provided, fallback to empty string + ;; to avoid `thing-at-point' to be appended on top of list + :default (or default "") + ;; Fail with special characters (e.g in gnus "nnimap+gmail:") + ;; if regexp-quote is not used. + ;; when init is added to history, it will be unquoted by + ;; helm-comp-read. + :initial-input initial-input))) + +(defun helm-completing-read-default-2 + (prompt collection predicate require-match + init hist default _inherit-input-method + name buffer &optional _cands-in-buffer exec-when-only-one) + "Call `helm-comp-read' with same args as `completing-read'. + +This handler uses dynamic matching which allows honouring `completion-styles'." + (let* ((history (or (car-safe hist) hist)) + (input (pcase init + ((pred (stringp)) init) + ;; INIT is a cons cell. + (`(,l . ,_ll) l))) + (completion-flex-nospace t) + (completion-styles + (helm--prepare-completion-styles 'nomode)) + (metadata (or (completion-metadata (or input "") collection predicate) + '(metadata))) + (afun (or (plist-get completion-extra-properties :annotation-function) + (completion-metadata-get metadata 'annotation-function))) + (afix (or (plist-get completion-extra-properties :affixation-function) + (completion-metadata-get metadata 'affixation-function))) + (file-comp-p (eq (completion-metadata-get metadata 'category) 'file)) + (compfn (lambda (str _predicate _action) + (let* ((completion-ignore-case (helm-set-case-fold-search)) + (comps + (completion-all-completions + str ; This is helm-pattern + collection + predicate + (length str) + metadata)) + (last-data (last comps)) + ;; Helm syle sort fn is added to + ;; metadata only in emacs-27, so in + ;; emacs-26 use helm-generic-sort-fn + ;; which handle both helm and + ;; helm-flex styles. When + ;; helm-completion-style is helm or + ;; helm-fuzzy, sorting will be done + ;; later in FCT. + (sort-fn + (and (eq helm-completion-style 'emacs) + (or + ;; Emacs-27 + (completion-metadata-get + metadata 'display-sort-function) + ;; Emacs-26 + (lambda (candidates) + (sort candidates #'helm-generic-sort-fn))))) + all) + (when (cdr last-data) + ;; Remove the last element of + ;; comps by side-effect. + (setcdr last-data nil)) + (setq helm-completion--sorting-done (and sort-fn t)) + (setq all (copy-sequence comps)) + ;; Default is passed here only with helm + ;; h-c-styles, otherwise with emacs style it is + ;; passed with the :default arg of helm-comp-read + ;; and computed in its get-candidates function. + (append (and default + (memq helm-completion-style '(helm helm-fuzzy)) + (list default)) + (helm-completion-in-region--initial-filter + (pcase-let ((lst (if (and sort-fn (> (length str) 0)) + (funcall sort-fn all) + all))) + (if (and default afix) + (prog1 (append (list default) + (delete default lst)) + (setq default nil)) + lst)) + afun afix file-comp-p))))) + (data (if (memq helm-completion-style '(helm helm-fuzzy)) + (funcall compfn (or input "") nil nil) + compfn)) + (helm-completion-in-region-default-sort-fn + (lambda (candidates _source) + (if (or helm-completion--sorting-done + (string= helm-pattern "")) + candidates + (sort candidates 'helm-generic-sort-fn))))) + (unwind-protect + (helm-comp-read + ;; Completion-at-point and friends have no prompt. + prompt + data + :name name + :initial-input input + :buffer buffer + :history history + :nomark (null helm-comp-read-use-marked) + :reverse-history helm-mode-reverse-history + ;; In helm h-c-styles default is passed directly in + ;; candidates. + :default (and (eq helm-completion-style 'emacs) (null afix) default) + :fc-transformer + ;; Ensure sort fn is at the end. + (append '(helm-cr-default-transformer) + (and helm-completion-in-region-default-sort-fn + (list helm-completion-in-region-default-sort-fn))) + :match-dynamic (eq helm-completion-style 'emacs) + :diacritics helm-mode-ignore-diacritics + :fuzzy (eq helm-completion-style 'helm-fuzzy) + :exec-when-only-one exec-when-only-one + :quit-when-no-cand (eq require-match t) + :must-match require-match) + (setq helm-completion--sorting-done nil)))) + +(defun helm-completing-read-default-find-tag + (prompt collection test require-match + init hist default inherit-input-method + name buffer) + "Specialized `helm-mode' handler for `find-tag'." + ;; Some commands like find-tag may use `read-file-name' from inside + ;; the calculation of collection. in this case it clash with + ;; candidates-in-buffer that reuse precedent data (files) which is wrong. + ;; So (re)calculate collection outside of main helm-session. + (let* ((cands (helm-comp-read-get-candidates + collection test nil nil))) + (helm-completing-read-default-1 prompt cands test require-match + init hist default inherit-input-method + name buffer t))) + +(defun helm-completing-read-sync-default-handler + (prompt collection test require-match + init hist default inherit-input-method + name buffer) + "`helm-mode' handler using sync source as backend." + (helm-completing-read-default-1 prompt collection test require-match + init hist default inherit-input-method + name buffer)) + +(defun helm-completing-read-inbuffer-default-handler + (prompt collection test require-match + init hist default inherit-input-method + name buffer) + "`helm-mode' handler using inbuffer source as backend." + (helm-completing-read-default-1 prompt collection test require-match + init hist default inherit-input-method + name buffer t)) + +(defun helm-completing-read-default-handler + (prompt collection test require-match + init hist default inherit-input-method + name buffer) + "Default `helm-mode' handler for all `completing-read'." + (let* (;; Standard will be used as CANDS-IN-BUFFER arg. + (standard (and (memq helm-completion-style '(helm helm-fuzzy)) t)) + (fn (if standard + #'helm-completing-read-default-1 + #'helm-completing-read-default-2))) + (funcall fn + prompt collection test require-match + init hist default inherit-input-method name buffer + ;; CANDS-IN-BUFFER + standard))) + +(defun helm-mode--read-buffer-to-switch (prompt) + "[INTERNAL] This is used to advice `read-buffer-to-switch'. +Don't use it directly." + ;; `read-buffer-to-switch' is passing `minibuffer-completion-table' + ;; to `read-buffer' through `minibuffer-setup-hook' which is too + ;; late to be known by `read-buffer-function', in our case + ;; `helm--generic-read-buffer'. It should let bind it to allow us + ;; using it. + (let ((minibuffer-completion-table (internal-complete-buffer-except))) + (read-buffer prompt (other-buffer (current-buffer)) + (confirm-nonexistent-file-or-buffer)))) + +(defun helm--generic-read-buffer (prompt &optional default require-match predicate) + "The `read-buffer-function' for `helm-mode'. +Affects `switch-to-buffer' `kill-buffer' and related." + ;; `read-buffer' is using internally `Vbuffer_alist' which is an + ;; alist with elements like (BUF-NAME . BUF-OBJ), therefore some + ;; predicates in Emacs are working only on such cons cells. + ;; However, helm is transforming COLLECTION in a list of strings and + ;; such predicates are failing because they expect cons cells (see + ;; bug#2506 with `project-switch-to-buffer'), even if they should + ;; handle strings as well according to `read-buffer' + ;; documentation. + (let ((pred (when predicate + (lambda (buffer) + (let ((buf (cons buffer (get-buffer buffer)))) + (condition-case _err + (funcall predicate buffer) + (wrong-type-argument + (funcall predicate buf)))))))) + (helm--completing-read-default + prompt (or minibuffer-completion-table + (internal-complete-buffer "" nil t)) + pred require-match nil nil default))) + +(defun helm-mode--get-default-handler-for (comp-or-file entry) + ;; Use 'comp for completing-read and 'file for 'read-file-name as + ;; COMP-OR-FILE value. + (let ((val (cdr-safe entry)) + (reading-file (eq comp-or-file 'file))) + (if (consp val) + (helm-acase (if reading-file (cadr val) (car val)) + (default (if reading-file + #'helm-read-file-name + #'helm-completing-read-default-handler)) + (t it)) + val))) + +(defun helm-mode--apply-helm-handler (handler arg-list) + "Ensure `minibuffer-complete' is disabled when running HANDLER. +ARG-LIST is a list of arguments to pass to HANDLER." + ;; Some functions are calling `minibuffer-complete' + ;; within `minibuffer-setup-hook' when calling their + ;; `completing-read', like `woman-file-name' (bug #2527). + ;; This defeat Helm which is already + ;; completing minibuffer, so deactivate + ;; minibuffer-complete one time for all [1]. + (cl-letf (((symbol-function 'minibuffer-complete) #'ignore)) + (apply handler arg-list))) + +(cl-defun helm--completing-read-default + (prompt collection &optional + predicate require-match + initial-input hist def + inherit-input-method) + "An helm replacement of `completing-read'. +This function should be used only as a `completing-read-function'. + +Don't use it directly, use instead `helm-comp-read' in your programs. + +See documentation of `completing-read' and `all-completions' for details." + (let* ((current-command (or (helm-this-command) this-command)) + (str-command (if current-command + (helm-symbol-name current-command) + "completing-read")) + (buf-name (format "*helm-mode-%s*" str-command)) + (entry (assq current-command + helm-completing-read-handlers-alist)) + (def-com (helm-mode--get-default-handler-for 'comp entry)) + (str-defcom (and def-com (helm-symbol-name def-com))) + (def-args (list prompt collection predicate require-match + initial-input hist def inherit-input-method)) + ;; Append the two extra args needed to set the buffer and source name + ;; in helm specialized functions. + (others-args (append def-args (list str-command buf-name))) + helm-completion-mode-start-message ; Be quiet + helm-completion-mode-quit-message + ;; Be sure this pesty *completion* buffer doesn't popup. + ;; Note: `minibuffer-with-setup-hook' may setup a lambda + ;; calling `minibuffer-completion-help' or other minibuffer + ;; functions we DONT WANT here, in these cases removing the hook + ;; (a symbol) have no effect. Bug#448. + ;; Because `minibuffer-completion-table' and + ;; `minibuffer-completion-predicate' are not bound + ;; anymore here, these functions should have no effect now, + ;; except in some rare cases like in `woman-file-name', + ;; so remove all incompatible functions + ;; from `minibuffer-setup-hook' (Bug#1205, Bug#1240). + ;; otherwise helm have not the time to close its initial session. + (minibuffer-setup-hook + (cl-loop for h in minibuffer-setup-hook + unless (or (consp h) ; a lambda. + (byte-code-function-p h) + (helm-subr-native-elisp-p h) + (memq h helm-mode-minibuffer-setup-hook-black-list)) + collect h)) + ;; Disable hack that could be used before `completing-read'. + ;; i.e (push ?\t unread-command-events). + unread-command-events + (default-handler + ;; If nothing is found in + ;; helm-completing-read-handlers-alist use default + ;; handler. + #'helm-completing-read-default-handler)) + (when (eq def-com 'ido) (setq def-com 'ido-completing-read)) + (unless (or (not entry) def-com) + ;; An entry in *read-handlers-alist exists but have + ;; a nil value, so we exit from here, disable `helm-mode' + ;; and run the command again with it original behavior. + ;; `helm-mode' will be restored on exit. + (cl-return-from helm--completing-read-default + (unwind-protect + (progn + (helm-mode -1) + (apply completing-read-function def-args)) + (helm-mode 1)))) + ;; If we use now `completing-read' we MUST turn off `helm-mode' + ;; to avoid infinite recursion and CRASH. It will be reenabled on exit. + (when (or (eq def-com 'completing-read) + ;; All specialized functions are prefixed by "helm" + (and (stringp str-defcom) + (not (string-match "^helm" str-defcom)))) + (helm-mode -1)) + (unwind-protect + (cond (;; An helm specialized function exists, run it. + (and def-com helm-mode) + ;; Disable `minibuffer-complete' for handlers using + ;; helm (bug #2533). + (helm-mode--apply-helm-handler + def-com others-args)) + (;; Try to handle `ido-completing-read' everywhere. + (and def-com (eq def-com 'ido-completing-read)) + (setcar (memq collection def-args) + (all-completions "" collection predicate)) + (apply def-com def-args)) + (;; A non helm function specified in + ;; `helm-completing-read-handlers-alist' use it with + ;; exactly the same args as in `completing-read'. If + ;; we are here `helm-mode' is now disabled. + def-com + (apply def-com def-args)) + (;; Use by default a in-buffer handler unless + ;; COLLECTION is a function. + t + ;; Disable `minibuffer-complete' for handlers using + ;; helm (bug #2533). + (helm-mode--apply-helm-handler + default-handler others-args))) + (helm-mode 1) + ;; When exiting minibuffer, `this-command' is set to + ;; `helm-exit-minibuffer', which is unwanted when starting + ;; on another `completing-read', so restore `this-command' to + ;; initial value when exiting. + (setq this-command current-command)))) + +;;; Generic read-file-name +;; +;; +;;;###autoload +(cl-defun helm-read-file-name + (prompt + &key + (name "Read File Name") + initial-input + (buffer "*Helm file completions*") + test + noret + (case-fold helm-file-name-case-fold-search) + preselect + history + must-match + (fuzzy t) + default + marked-candidates + (candidate-number-limit helm-ff-candidate-number-limit) + nomark + (alistp t) + (persistent-action-if 'helm-find-files-persistent-action-if) + (persistent-help "Hit1 Expand Candidate, Hit2 or (C-u) Find file") + (mode-line helm-read-file-name-mode-line-string)) + "Read a file name with helm completion. + +It is helm `read-file-name' emulation. + +Argument PROMPT is the default prompt to use. + +Keys description: + +- NAME: Source name, default to \"Read File Name\". + +- INITIAL-INPUT: Where to start reading file name, + default to `default-directory' or $HOME. + +- BUFFER: `helm-buffer' name, defaults to \"*Helm Completions*\". + +- TEST: A predicate called with one arg \\='candidate'. + +- NORET: Allow disabling helm-ff-RET (have no effect if helm-ff-RET + isn't bound to RET). + +- CASE-FOLD: Same as `helm-case-fold-search'. + +- PRESELECT: helm preselection. + +- HISTORY: Display HISTORY in a special source. + +- MUST-MATCH: Can be \\='confirm, nil, or t. + +- FUZZY: Enable fuzzy matching when non-nil (Enabled by default). + +- MARKED-CANDIDATES: When non--nil return a list of marked candidates. + +- NOMARK: When non--nil don't allow marking candidates. + +- ALISTP: Don't use `all-completions' in history + (take effect only on history). + +- PERSISTENT-ACTION-IF: a persistent if action function. + +- PERSISTENT-HELP: persistent help message. + +- MODE-LINE: A mode line message, default is + `helm-read-file-name-mode-line-string'." + (require 'tramp) + (unless initial-input + (setq initial-input (or default-directory (getenv "HOME")))) + (when (get-buffer helm-action-buffer) + (kill-buffer helm-action-buffer)) + (mapc (lambda (hook) + (add-hook 'helm-after-update-hook hook)) + '(helm-ff-move-to-first-real-candidate + helm-ff-update-when-only-one-matched + helm-ff-auto-expand-to-home-or-root)) + (let* ((action-fn `(("Sole action (Identity)" + . (lambda (candidate) + (if ,marked-candidates + (helm-marked-candidates :with-wildcard t) + (identity candidate)))))) + ;; Be sure we don't erase the underlying minibuffer if some. + (helm-ff-auto-update-initial-value + (and helm-ff-auto-update-initial-value + (not (minibuffer-window-active-p (minibuffer-window))))) + helm-follow-mode-persistent + (helm-ff-fuzzy-matching + (and fuzzy + (not (memq helm-mm-matching-method '(multi1 multi3p))))) + (hist (and history (helm-comp-read-get-candidates + history nil nil alistp))) + (helm-ff--RET-disabled noret) + (minibuffer-completion-predicate test) + (minibuffer-completing-file-name t) + ;; Ensure not being prompted for password each time we + ;; navigate to a directory. + (password-cache t) + (helm--completing-file-name t) + (helm-read-file-name-mode-line-string + (replace-regexp-in-string "helm-maybe-exit-minibuffer" + "helm-confirm-and-exit-minibuffer" + helm-read-file-name-mode-line-string)) + (src-list + (list + ;; History source. + (helm-build-sync-source (format "%s History" name) + :header-name (lambda (name) + (concat name (substitute-command-keys + helm-find-files-doc-header))) + :mode-line mode-line + :candidates hist + :nohighlight t + :fuzzy-match fuzzy + :persistent-action-if persistent-action-if + :persistent-help persistent-help + :keymap helm-read-file-map + :must-match must-match + :nomark nomark + :action action-fn) + ;; Other source. + (helm-build-sync-source name + :header-name (lambda (name) + (concat name (substitute-command-keys + helm-find-files-doc-header))) + :init (lambda () + (setq helm-ff-auto-update-flag + helm-ff-auto-update-initial-value) + (setq helm-ff--auto-update-state + helm-ff-auto-update-flag)) + :mode-line mode-line + :help-message 'helm-read-file-name-help-message + :nohighlight t + :candidates + (lambda () + (if test + (append (and (not (file-exists-p helm-pattern)) + (not (helm-ff--invalid-tramp-name-p helm-pattern)) + (list (helm-ff-filter-candidate-one-by-one + helm-pattern nil t))) + (cl-loop with hn = (helm-ff--tramp-hostnames) + ;; helm-find-files-get-candidates is + ;; returning a list of cons cells. + for (d . r) in (helm-find-files-get-candidates + must-match) + when (or (member r hn) ; A tramp host + (funcall test r)) ; Test ok + collect (cons d r))) + (helm-find-files-get-candidates must-match))) + :update (lambda () + (remhash helm-ff-default-directory + helm-ff--list-directory-cache)) + :match-on-real t + :filtered-candidate-transformer (delq nil `(helm-ff-fct + helm-ff-sort-candidates + ,(and helm-ff-icon-mode + 'helm-ff-icons-transformer))) + :persistent-action-if persistent-action-if + :persistent-help persistent-help + :volatile t + :keymap helm-read-file-map + :must-match must-match + :cleanup 'helm-find-files-cleanup + :nomark nomark + :action action-fn))) + ;; Helm result. + (result (helm + :sources (if helm-mode-reverse-history + (reverse src-list) src-list) + :input (if (string-match helm-ff-url-regexp initial-input) + initial-input + (expand-file-name initial-input)) + :prompt prompt + :candidate-number-limit candidate-number-limit + :resume 'noresume + :case-fold-search case-fold + :default default + :buffer buffer + :full-frame nil + :preselect preselect))) + (or + (cond ((and result (stringp result) + (string= result "") "")) + ((and result + (stringp result) + (file-equal-p result initial-input) + helm-read-file-name-use-default-arg-behavior + default) + (if (listp default) (car default) default)) + ((and result (listp result)) + (mapcar #'expand-file-name result)) + ((and result (file-directory-p result)) + (file-name-as-directory (expand-file-name result))) + (result (expand-file-name result))) + (helm-mode--keyboard-quit)))) + +(defun helm-mode--default-filename (fname dir initial) + (unless dir (setq dir default-directory)) + (unless (file-name-absolute-p dir) + (setq dir (expand-file-name dir))) + (unless (or fname (consp fname)) + (setq fname (expand-file-name + (or initial buffer-file-name dir) + dir))) + (if (and fname (consp fname)) + (setq fname (cl-loop for f in fname + collect (expand-file-name f dir))) + (if (file-name-absolute-p fname) + fname (expand-file-name fname dir)))) + +(cl-defun helm--generic-read-file-name + (prompt &optional dir default-filename mustmatch initial predicate) + "Generic helm replacement of `read-file-name'. +Don't use it directly, use instead `helm-read-file-name' in your programs." + (let* ((init (or initial dir default-directory)) + (helm-read-file-name-use-default-arg-behavior t) + (current-command (or (helm-this-command) this-command)) + (str-command (if current-command + (helm-symbol-name current-command) + "read-file-name")) + (helm--file-completion-sources + (cons str-command + (remove str-command helm--file-completion-sources))) + (buf-name (format "*helm-mode-%s*" str-command)) + (entry (assq current-command + helm-completing-read-handlers-alist)) + (def-com (helm-mode--get-default-handler-for 'file entry)) + (str-defcom (and def-com (helm-symbol-name def-com))) + ;; Don't modify the original args list for emacs generic functions. + (def-args (list prompt dir default-filename mustmatch initial predicate)) + ;; Append the two extra args needed to set the buffer and source name + ;; in helm specialized functions. + (others-args (append def-args (list str-command buf-name))) + (reading-directory (eq predicate 'file-directory-p)) + (use-dialog (and (next-read-file-uses-dialog-p) + ;; Graphical file dialogs can't handle + ;; remote files. + (not (file-remote-p init)) + use-file-dialog)) + helm-completion-mode-start-message ; Be quiet + helm-completion-mode-quit-message ; Same here + add-to-history fname) + ;; Build `default-filename' with `dir'+`initial' when + ;; `default-filename' is not specified. + ;; See `read-file-name' docstring for more infos. + (setq default-filename (helm-mode--default-filename + default-filename dir initial)) + ;; Some functions that normally call `completing-read' can switch + ;; brutally to `read-file-name' (e.g find-tag), in this case + ;; the helm specialized function will fail because it is build + ;; for `completing-read', so set it to 'incompatible to be sure + ;; we switch to `helm-read-file-name' and don't try to call it + ;; with wrong number of args. + (when (eq def-com 'ido) + (setq def-com 'ido-read-file-name)) + (when (and def-com (> (length (help-function-arglist def-com)) 8)) + (setq def-com 'incompatible)) + (unless (or (not entry) def-com) + (cl-return-from helm--generic-read-file-name + (unwind-protect + (progn + (helm-mode -1) + (apply read-file-name-function def-args)) + (helm-mode 1)))) + ;; If we use now `read-file-name' or dialog we MUST turn off `helm-mode' + ;; to avoid infinite recursion and CRASH. It will be reenabled on exit. + (when (or (memq def-com '(read-file-name ido-read-file-name)) + use-dialog + (and (stringp str-defcom) + (not (string-match "^helm" str-defcom)))) + (helm-mode -1)) + (unwind-protect + (setq fname + (cond (use-dialog + (let ((dialog-mustmatch + (not (memq mustmatch + '(nil confirm confirm-after-completion))))) + ;; Dialogs don't support a list of default fnames. + (when (and default-filename (consp default-filename)) + (setq default-filename + (expand-file-name (car default-filename) init))) + (setq add-to-history t) + (x-file-dialog prompt init default-filename + dialog-mustmatch + reading-directory))) + ;; A specialized function exists, run it + ;; with the two extra args specific to helm. + ;; Note that the helm handler should ensure + ;; :initial-input is not nil i.e. Use init + ;; which fallback to default-directory instead + ;; of INITIAL. + ((and def-com helm-mode + (not (eq def-com 'ido-read-file-name)) + (not (eq def-com 'incompatible)) + ;; The entry in + ;; `helm-completing-read-handlers-alist' is + ;; a cons cell specifying a completing-read + ;; and a read-file-name handler default + ;; e.g. (foo (default default)). + (not (eq def-com 'helm-read-file-name))) + (apply def-com others-args)) + (;; Def-com value is `ido-read-file-name' + ;; run it with default args. + (and def-com (eq def-com 'ido-read-file-name)) + (ido-mode 1) + (apply def-com def-args)) + (;; Def-com value is `read-file-name' + ;; run it with default args. + (eq def-com 'read-file-name) + (apply def-com def-args)) + (t ; Fall back to classic `helm-read-file-name'. + (helm-read-file-name + prompt + :name str-command + :buffer buf-name + :default default-filename + ;; Helm handlers should always have a non nil INITIAL arg. + :initial-input (if (string-match helm-ff-url-regexp init) + init + (expand-file-name init dir)) + :alistp nil + :nomark (null helm-comp-read-use-marked) + :marked-candidates helm-comp-read-use-marked + :must-match mustmatch + :test predicate + :noret reading-directory)))) + (and ido-mode (ido-mode -1)) + (helm-mode 1) + ;; Same comment as in `helm--completing-read-default'. + (setq this-command current-command)) + (when add-to-history + (add-to-history 'file-name-history + (minibuffer-maybe-quote-filename fname))) + (if (and + ;; Using `read-directory-name'. + reading-directory + ;; `file-name-as-directory' return "./" when FNAME is + ;; empty string. + (not (string= fname ""))) + (file-name-as-directory fname) + fname))) + +;; Read file name handler with history (Bug#1652) +(defun helm-read-file-name-handler-1 (prompt dir default-filename + mustmatch initial predicate + name buffer) + "A `read-file-name' handler with history. +Can be added to `helm-completing-read-handlers-alist' for functions +that need a `read-file-name' function with directory history. +The `helm-find-files' history `helm-ff-history' is used here." + (let ((helm-always-two-windows t) + (helm-split-window-default-side + (if (eq helm-split-window-default-side 'same) + 'below helm-split-window-default-side)) + helm-split-window-inside-p + helm-reuse-last-window-split-state + ;; Helm handlers should always have a non nil INITIAL arg. + (init (or initial dir default-directory))) + (helm-read-file-name + prompt + :name name + :history helm-ff-history + :buffer buffer + :default default-filename + :initial-input (expand-file-name init dir) + :alistp nil + :must-match mustmatch + :test predicate))) + + +;;; Completion in region and Helm style +;; +(defun helm-mode--advice-lisp--local-variables (old--fn &rest args) + (ignore-errors + (apply old--fn args))) + +(defvar helm-completion--sorting-done nil + "Flag that notifies the FCT if sorting has been done in completion function.") +(defun helm-completion-in-region-sort-fn (candidates _source) + "Default sort function for completion-in-region." + (if helm-completion--sorting-done + candidates + (sort candidates 'helm-generic-sort-fn))) + +(defun helm-mode--completion-in-region-initial-input (str) + "Highlight prefix in helm and helm-fuzzy `helm-completion-styles'." + (if (memq helm-completion-style '(helm helm-fuzzy)) + (propertize str 'read-only t 'face 'helm-mode-prefix 'rear-nonsticky t) + str)) + +(defun helm--symbol-completion-table-affixation (completions) + "Same as `help--symbol-completion-table-affixation' but for helm. + +Return a list of cons cells of the form (disp . real)." + (mapcar (lambda (c) + (let* ((s (intern c)) + (doc (ignore-errors + (helm-get-first-line-documentation s)))) + (cons (concat (propertize + (format "%-4s" (help--symbol-class s)) + 'face 'completions-annotations) + c + (if doc + (propertize (format " -- %s" doc) + 'face 'completions-annotations) + "")) + c))) + completions)) + +(defun helm-completion-in-region--initial-filter (comps afun afix file-comp-p) + "Compute COMPS with function AFUN or AFIX unless FILE-COMP-P non nil. + +If both AFUN and AFIX are provided only AFIX is used. +When FILE-COMP-P is provided only filter out dot files." + (if file-comp-p + ;; Filter out dot files in file completion. + (cl-loop for f in comps unless + (string-match "\\`\\.\\{1,2\\}/\\'" f) + collect f) + (cond (afix (helm--symbol-completion-table-affixation comps)) + (afun + ;; Add annotation at end of + ;; candidate if needed, e.g. foo, this happen when + ;; completing against a quoted symbol. + (mapcar (lambda (s) + (let ((ann (funcall afun s))) + (if ann + (cons + (concat + s + (propertize + " " 'display + (propertize + ann + 'face 'completions-annotations))) + s) + s))) + comps)) + (t comps)))) + +;; Helm multi matching style + +(defun helm-completion-try-completion (string table pred point) + "The try completion function for `completing-styles-alist'. +Actually does nothing." + ;; AFAIU the try-completions style functions + ;; are here to check if what is at point is suitable for TABLE but + ;; there is no way to pass a multiple pattern from what is at point + ;; apart sending STRING in a minibuffer like helm does. Perhaps + ;; minibuffer-complete should benefit of this but for now just do + ;; nothing as this is used nowhere. It is anyway not clear what the + ;; try-completions functions do in emacs so just do nothing for now. + (ignore string table pred point)) + +(defun helm-completion-all-completions (string table pred point) + "The all completions function for `completing-styles-alist'." + ;; FIXME: No need to bind all these value. + ;; (cl-multiple-value-bind (all _pattern prefix _suffix _carbounds) + (pcase-let ((`(,all ,_pattern ,prefix ,_suffix ,_carbounds) + (helm-completion--multi-all-completions string table pred point))) + (when all (nconc all (length prefix))))) + +(defun helm-completion--multi-all-completions-1 (string collection &optional predicate) + "Allow `all-completions' multi matching on its candidates." + ;; Doing an initial call of all-completions on the first element of + ;; STRING speedup completion and fix file completion when CAPF + ;; returns relative paths to initial pattern (eshell and shell). + (let* ((split (helm-mm-split-pattern string)) + (fpat (or (car split) "")) + (file-comp-p (or minibuffer-completing-file-name + (eq + (completion-metadata-get + (completion-metadata string collection predicate) + 'category) + 'file))) + (all (and file-comp-p + (or (cdr split) + (and (not (cdr split)) + ;; Kickin when STRING is a simple string. + ;; Handle as well "foo " (space at end). + (not (string= fpat ""))) + (string= string "")) + (not (string-match "\\`!" fpat)) + ;; all-completions should return nil if FPAT is a + ;; regexp, it is what we expect. + (all-completions fpat collection + (lambda (x &optional _y) + (let ((elm (if (listp x) (car x) x))) + (funcall (or predicate #'identity) elm)))))) + (pattern (helm-aand all (string-match " " string) + ;; Returns the part of STRING after space + ;; e.g. "foo bar baz" => "bar baz". + (substring string (1+ it))))) + (if (or (and all (not (cdr split))) + (equal pattern "")) ; e.g. STRING == "foo ". + all + (all-completions "" (or all collection) + (lambda (x &optional _y) + ;; Second arg _y is needed when + ;; COLLECTION is a hash-table (Bug#2231) + ;; (C-x 8 RET). + ;; Elements of COLLECTION may be + ;; lists or alists, in this case consider the + ;; car of element (Bug#2219 org-refile). + (let ((elm (if (listp x) (car x) x))) + ;; PREDICATE have been already called in + ;; initial all-completions, no need to call + ;; it a second time, thus ALL is now a list + ;; of strings maybe not supported by + ;; PREDICATE (e.g. symbols vs strings). + (if (and predicate (null all)) + (and (funcall predicate elm) + ;; ALL is nil so use whole STRING + ;; against COLLECTION. + (helm-mm-match (helm-stringify elm) string)) + (helm-mm-match (helm-stringify elm) + (or (and all pattern) string))))))))) + +(defun helm-completion--multi-all-completions (string table pred point) + "Collect completions from TABLE for helm completion style." + (let* ((beforepoint (substring string 0 point)) + (afterpoint (substring string point)) + (bounds (completion-boundaries beforepoint table pred afterpoint)) + (prefix (substring beforepoint 0 (car bounds))) + (suffix (substring afterpoint (cdr bounds))) + (all (helm-completion--multi-all-completions-1 + ;; Using `regexp-quote' on STRING fixes bug#2355 but + ;; breaks regexp matching in multi match, actually with + ;; Helm-3.7.1 and emacs-27+ it seems using plain STRING + ;; works for both so use it. + ;;(regexp-quote string) + string table pred))) + (list all string prefix suffix point))) + +;; The adjust-metadata functions run only in emacs-27, they are NOT +;; used otherwise. +(defun helm-completion--adjust-metadata (metadata) + (if (memq helm-completion-style '(helm helm-fuzzy)) + metadata + (let ((compose-helm-sort-fn + (lambda (candidates) + (sort candidates #'helm-generic-sort-fn)))) + `(metadata + (display-sort-function + . ,compose-helm-sort-fn) + (cycle-sort-function + . ,compose-helm-sort-fn) + ,@(cdr metadata))))) +(put 'helm 'completion--adjust-metadata 'helm-completion--adjust-metadata) + +;; Helm-flex style. +;; This is more or less the same as emacs-27 flex style. +(defun helm-flex-completion-try-completion (string table pred point) + "The try completion function for `completing-styles-alist'." + ;; It is needed here to make minibuffer-complete work in emacs-26, + ;; e.g. with regular M-x. + (unless (string-match-p " " string) + (pcase-let ((`(,all ,pattern ,prefix ,suffix ,_carbounds) + (helm-completion--flex-all-completions string table pred point))) + (when minibuffer-completing-file-name + (setq all (completion-pcm--filename-try-filter all))) + (completion-pcm--merge-try pattern all prefix suffix)))) + +(defun helm-flex-completion-all-completions (string table pred point) + "The all completions function for `completing-styles-alist'." + ;; FIXME: No need to bind all these value. + (unless (string-match-p " " string) + (pcase-let ((`(,all ,pattern ,prefix ,_suffix ,_carbounds) + (helm-completion--flex-all-completions + string table pred point + #'helm-completion--flex-transform-pattern))) + (let ((regexp (completion-pcm--pattern->regex pattern 'group))) + (when all (nconc (helm-flex-add-score-as-prop all regexp) + (length prefix))))))) + +;; Same as emacs-27 completion-substring--all-completions. +(defun helm-completion--flex-all-completions + (string table pred point &optional transform-pattern-fn) + "Match the presumed substring STRING to the entries in TABLE. +Respect PRED and POINT. The pattern used is a PCM-style substring +pattern, but it will be massaged by TRANSFORM-PATTERN-FN, if that +is non-nil." + (let* ((beforepoint (substring string 0 point)) + (afterpoint (substring string point)) + (bounds (completion-boundaries beforepoint table pred afterpoint)) + (suffix (substring afterpoint (cdr bounds))) + (prefix (substring beforepoint 0 (car bounds))) + (basic-pattern (completion-basic--pattern + beforepoint afterpoint bounds)) + (pattern (if (not (stringp (car basic-pattern))) + basic-pattern + (cons 'prefix basic-pattern))) + (pattern (if transform-pattern-fn + (funcall transform-pattern-fn pattern) + pattern)) + (all (completion-pcm--all-completions prefix pattern table pred))) + (list all pattern prefix suffix (car bounds)))) + +(defun helm-completion-in-region--selection () + (with-helm-buffer + (setq helm-saved-selection (helm-get-selection nil 'withprop)))) + +;; Completion-in-region-function + +(defun helm--completion-in-region (origfun start end collection &optional predicate) + "Helm replacement of `completion--in-region'. + +Can be used for `completion-in-region-function' by advicing it with an +:around advice to allow passing the old +`completion-in-region-function' value in ORIGFUN." + (cl-declare (special require-match prompt)) + (if (memq major-mode helm-mode-no-completion-in-region-in-modes) + (funcall origfun start end collection predicate) + (advice-add + 'lisp--local-variables + :around #'helm-mode--advice-lisp--local-variables) + (let ((old--helm-completion-style helm-completion-style) + (exit-fun (plist-get completion-extra-properties :exit-function)) + ;; Always start with prefix to allow completing without + ;; the need of inserting a space after cursor or + ;; relaying on crap old completion-styles emacs22 which + ;; add suffix after prefix. e.g. def|else. + (initial-input (buffer-substring-no-properties start (point))) + string) + (helm-aif (cdr (assq major-mode helm-completion-styles-alist)) + (customize-set-variable 'helm-completion-style + (if (cdr-safe it) (car it) it))) + ;; This hook force usage of the display part of candidate with + ;; its properties, this is needed for lsp-mode in its + ;; :exit-function see Bug#2265. + (add-hook 'helm-before-action-hook 'helm-completion-in-region--selection) + (unwind-protect + (let* ((enable-recursive-minibuffers t) + (completion-flex-nospace t) + (completion-styles (helm--prepare-completion-styles)) + (input (buffer-substring-no-properties start end)) + (prefix (and (eq helm-completion-style 'emacs) initial-input)) + (point (point)) + (current-command (or (helm-this-command) + this-command + ;; Some backends are async and + ;; use a callback, in those + ;; cases, we can't retrieve from + ;; frames the last interactive + ;; command, so fallback to + ;; `last-command' which may be + ;; the one that called the callback. + last-command)) + (crm (eq current-command 'crm-complete)) + (str-command (helm-symbol-name current-command)) + (buf-name (format "*helm-mode-%s*" str-command)) + (require-match (or (and (boundp 'require-match) require-match) + minibuffer-completion-confirm + ;; If prompt have not been propagated here, that's + ;; probably mean we have no prompt and we are in + ;; completion-at-point or friend, so use a non--nil + ;; value for require-match. + (not (boundp 'prompt)))) + (metadata (completion-metadata input collection predicate)) + ;; `completion-extra-properties' is let-bounded in `completion-at-point'. + ;; `afun' is a closure to call against each string in `data'. + ;; it provide the annotation info for each string. + ;; e.g "foo" => "foo " where foo is a function. + ;; See Bug#407. + (afun (or (plist-get completion-extra-properties :annotation-function) + (completion-metadata-get metadata 'annotation-function))) + ;; Not sure if affixations are provided in + ;; completion-in-region, try anyway never know. + (afix (or (plist-get completion-extra-properties :affixation-function) + (completion-metadata-get metadata 'affixation-function))) + (init-space-suffix (unless (or (memq helm-completion-style '(helm-fuzzy emacs)) + (string-suffix-p " " input) + (string= input "")) + " ")) + (file-comp-p (or (eq (completion-metadata-get metadata 'category) 'file) + (helm-mode--in-file-completion-p) + ;; Assume that when `afun' and `predicate' are null + ;; we are in filename completion. + (and (null afun) (null predicate)))) + ;; `completion-all-completions' store the base-size in the last `cdr', + ;; so data looks like this: '(a b c d . 0) and (last data) == (d . 0). + base-size + (compfn (lambda (str _predicate _action) + (let* ((completion-ignore-case (helm-set-case-fold-search)) + (comps + (completion-all-completions + str ; This is helm-pattern + collection + predicate + ;; Use prefix length at first call to + ;; allow styles matching + ;; "prefix*suffix" to kick in. + (length (or prefix str)) + metadata)) + (last-data (last comps)) + (bs (helm-aif (cdr last-data) + (prog1 it + ;; Remove the last element of + ;; comps by side-effect. + (setcdr last-data nil)) + 0)) + ;; Helm syle sort fn is added to + ;; metadata only in emacs-27, so in + ;; emacs-26 use helm-generic-sort-fn + ;; which handle both helm and + ;; helm-flex styles. When + ;; helm-completion-style is helm or + ;; helm-fuzzy, sorting will be done + ;; later in FCT. + (sort-fn + (and (eq helm-completion-style 'emacs) + (or + ;; Emacs-27 + (completion-metadata-get + metadata 'display-sort-function) + ;; Emacs-26 + (lambda (candidates) + (sort candidates #'helm-generic-sort-fn))))) + all) + ;; Reset prefix to allow using length of + ;; helm-pattern on next calls (this avoid + ;; args-out-of-range error). + (and prefix (setq prefix nil)) + ;; base-size needs to be set only once at + ;; first call. + (unless base-size (setq base-size bs)) + (setq helm-completion--sorting-done (and sort-fn t)) + (setq all (copy-sequence comps)) + (helm-completion-in-region--initial-filter + (if (and sort-fn (> (length str) 0)) + (funcall sort-fn all) + all) + afun afix file-comp-p)))) + (data (if (memq helm-completion-style '(helm helm-fuzzy)) + (funcall compfn input nil nil) + compfn)) + (result (if (stringp data) + data + (helm-comp-read + ;; Completion-at-point and friends have no prompt. + (or (and (boundp 'prompt) prompt) "Pattern: ") + data + :name str-command + :nomark (null crm) + :marked-candidates crm + :initial-input + (cond ((and file-comp-p + (not (string-match "/\\'" initial-input))) + (concat (helm-mode--completion-in-region-initial-input + (if (memq helm-completion-style '(helm helm-fuzzy)) + (helm-basename initial-input) + initial-input)) + init-space-suffix)) + ((string-match "/\\'" initial-input) + (and (eq helm-completion-style 'emacs) initial-input)) + ((or (null require-match) + (stringp require-match)) + (helm-mode--completion-in-region-initial-input initial-input)) + (t (concat (helm-mode--completion-in-region-initial-input initial-input) + init-space-suffix))) + :buffer buf-name + :fc-transformer + ;; Ensure sort fn is at the end. + (append '(helm-cr-default-transformer) + (and helm-completion-in-region-default-sort-fn + (list helm-completion-in-region-default-sort-fn))) + :match-dynamic (eq helm-completion-style 'emacs) + :fuzzy (eq helm-completion-style 'helm-fuzzy) + :exec-when-only-one t + :quit-when-no-cand + (lambda () + ;; Delay message to overwrite "Quit". + (run-with-timer + 0.01 nil + (lambda () + (message "[No matches]"))) + t) ; exit minibuffer immediately. + :must-match require-match)))) + ;; `helm-completion-in-region--insert-result' is stripping + ;; out properties on RESULT by side-effect (perhaps + ;; `choose-completion-string'?) so make a copy of STRING + ;; to not loose props. + (setq string (copy-sequence result)) + (helm-completion-in-region--insert-result + result start point end base-size)) + ;; Allow running extra property `:exit-function' (Bug#2265, + ;; Bug#2356). Function is called with 'exact if for a unique + ;; match which is exact, the return value of `try-completion' + ;; is t or a string ending with "/" i.e. possibly a directory + ;; (Bug#2274), + ;; otherwise it is called with 'finished. + (when (and (stringp string) exit-fun) + (let ((tcomp (try-completion initial-input collection))) + (funcall exit-fun string + (if (or (eq tcomp t) ; Unique. + (and (stringp tcomp) + (string-match "/\\'" tcomp))) ; A directory. + 'exact 'finished)))) + (remove-hook 'helm-before-action-hook 'helm-completion-in-region--selection) + (customize-set-variable 'helm-completion-style old--helm-completion-style) + (setq helm-completion--sorting-done nil) + (advice-remove 'lisp--local-variables + #'helm-mode--advice-lisp--local-variables))))) + +(defvar helm-crm-default-separator "," + "Default separator for `completing-read-multiple'. + +`crm-separator' will take precedence on this when it is a string composed +of a single character. +If used globally, it is a string composed of a single character, +if let-bounded, it can be also nil or a symbol which mean no +separator. Don't set this to a string composed of more than one +character. +Be sure to know what you are doing when modifying this.") +(defun helm-completion-in-region--insert-result (result start point end base-size) + (cond ((stringp result) + ;; When RESULT have annotation, annotation is displayed + ;; in it with a display property attached to a space + ;; added at end of string, take care of removing this + ;; space (Bug#2360). However keep RESULT intact to + ;; pass it to `:exit-function' i.e. Don't store the + ;; modified string in STRING. + (choose-completion-string + (replace-regexp-in-string " \\'" "" result) + (current-buffer) + (list (+ start base-size) point) + completion-list-insert-choice-function) + (when helm-completion-mark-suffix + (run-with-idle-timer 0.01 nil + (lambda () + (helm-aand + (+ (- (point) point) end) + (and (> it (point)) it) + (push-mark it t t)))))) + ((consp result) ; crm. + (let ((beg (+ start base-size)) + (sep (or (and + ;; If `crm-separator' is a string of length 1 + ;; assume it can be used as separator (Bug#2298), + ;; otherwise it is a regexp and use the value + ;; it matches or default to "," if no match. + (eq (length crm-separator) 1) + crm-separator) + helm-crm-default-separator))) + ;; Try to find a default separator. If `crm-separator' is a + ;; regexp use the string the regexp is matching. + ;; If SEP is not a string, it have been probably bound to a + ;; symbol or nil through `helm-crm-default-separator' that serve + ;; as a flag to say "Please no separator" (Bug#2353 with + ;; `magit-completing-read-multiple'). + (if (stringp sep) + (save-excursion + (goto-char beg) + (when (looking-back crm-separator (1- (point))) + (setq sep (match-string 0)))) + (setq sep nil)) + (funcall completion-list-insert-choice-function + beg end (mapconcat 'identity (append result '("")) sep)))) + (t nil))) + +(defun helm-mode--in-file-completion-p () + (with-helm-current-buffer + (run-hook-with-args-until-success 'file-name-at-point-functions))) + +(defun helm-mode--disable-ido-maybe (&optional from-hook) + (when (and (boundp 'ido-everywhere) ido-everywhere) + (remove-function read-file-name-function #'ido-read-file-name) + (remove-function read-buffer-function #'ido-read-buffer) + (setq ido-everywhere nil) + (if from-hook + (user-error "Unable to turn on Ido-everywhere while Helm-mode is enabled") + (user-error "Helm-mode enabled (Ido-everywhere is incompatible with Helm-mode, disabling it)")))) + +(defun helm-mode--ido-everywhere-hook () + ;; Called only when user calls directly ido-everywhere + ;; and helm-mode is enabled. + (when helm-mode + (helm-mode--disable-ido-maybe t))) + +;;;###autoload +(define-minor-mode helm-mode + "Toggle generic helm completion. + +All functions in Emacs that use `completing-read', +`read-file-name', `completion-in-region' and friends will use helm +interface when this mode is turned on. + +However you can modify this behavior for functions of your choice +with `helm-completing-read-handlers-alist'. + +Called with a positive arg, turn on unconditionally, with a +negative arg turn off. +You can toggle it with M-x `helm-mode'. + +About `ido-mode': +DO NOT enable `ido-everywhere' when using `helm-mode'. Instead of +using `ido-mode', add the commands where you want to use ido to +`helm-completing-read-handlers-alist' with `ido' as value. + +Note: This mode is incompatible with Emacs23." + :group 'helm-mode + :global t + :lighter helm-completion-mode-string + (cl-assert (boundp 'completing-read-function) nil + "`helm-mode' not available, upgrade to Emacs-24") + (if helm-mode + (progn + (add-function :override completing-read-function + #'helm--completing-read-default) + (add-function :override read-file-name-function + #'helm--generic-read-file-name) + (add-function :override read-buffer-function + #'helm--generic-read-buffer) + (when helm-mode-handle-completion-in-region + (add-function :around completion-in-region-function + #'helm--completion-in-region)) + ;; If user have enabled ido-everywhere BEFORE enabling + ;; helm-mode disable it and warn user about its + ;; incompatibility with helm-mode (Bug#2085). + (helm-mode--disable-ido-maybe) + ;; If ido-everywhere is not enabled yet anticipate and + ;; disable it if user attempt to enable it while helm-mode + ;; is running (Bug#2085). + (add-hook 'ido-everywhere-hook #'helm-mode--ido-everywhere-hook) + (when (fboundp 'ffap-read-file-or-url-internal) + ;; `ffap-read-file-or-url-internal' have been removed in + ;; emacs-27 and `ffap-read-file-or-url' is fixed, so no need + ;; to advice it. + (advice-add 'ffap-read-file-or-url :override #'helm-advice--ffap-read-file-or-url)) + (advice-add 'read-buffer-to-switch :override #'helm-mode--read-buffer-to-switch) + (helm-minibuffer-history-mode 1)) + (progn + (remove-function completing-read-function #'helm--completing-read-default) + (remove-function read-file-name-function #'helm--generic-read-file-name) + (remove-function read-buffer-function #'helm--generic-read-buffer) + (remove-function completion-in-region-function #'helm--completion-in-region) + (remove-hook 'ido-everywhere-hook #'helm-mode--ido-everywhere-hook) + (when (fboundp 'ffap-read-file-or-url-internal) + (advice-remove 'ffap-read-file-or-url #'helm-advice--ffap-read-file-or-url)) + (advice-remove 'read-buffer-to-switch #'helm-mode--read-buffer-to-switch) + (helm-minibuffer-history-mode -1)))) + +(provide 'helm-mode) + +;;; helm-mode.el ends here diff --git a/code/elpa/helm-20220822.659/helm-net.el b/code/elpa/helm-20220822.659/helm-net.el new file mode 100644 index 0000000..e60e9ca --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-net.el @@ -0,0 +1,436 @@ +;;; helm-net.el --- helm browse url and search web. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'url) +(require 'xml) +(require 'browse-url) + +(declare-function helm-comp-read "helm-mode") + + +(defgroup helm-net nil + "Net related applications and libraries for Helm." + :group 'helm) + +(defcustom helm-google-suggest-default-browser-function nil + "The browse url function you prefer to use with Google suggest. +When nil, use the first browser function available +See `helm-browse-url-default-browser-alist'." + :group 'helm-net + :type 'symbol) + +(defcustom helm-home-url "https://www.google.com" + "Default url to use as home url." + :group 'helm-net + :type 'string) + +(defcustom helm-surfraw-default-browser-function nil + "The browse url function you prefer to use with surfraw. +When nil, fallback to `browse-url-browser-function'." + :group 'helm-net + :type 'symbol) + +(defcustom helm-google-suggest-url + "https://encrypted.google.com/complete/search?output=toolbar&q=%s" + "URL used for looking up Google suggestions. +This is a format string, don't forget the `%s'." + :type 'string + :group 'helm-net) + +(defcustom helm-google-suggest-search-url + "https://encrypted.google.com/search?ie=utf-8&oe=utf-8&q=%s" + "URL used for Google searching. +This is a format string, don't forget the `%s'." + :type 'string + :group 'helm-net) + +(defvaralias 'helm-google-suggest-use-curl-p 'helm-net-prefer-curl) +(make-obsolete-variable 'helm-google-suggest-use-curl-p 'helm-net-prefer-curl "1.7.7") + +(defcustom helm-net-prefer-curl nil + "When non--nil use CURL external program to fetch data. +Otherwise `url-retrieve-synchronously' is used." + :type 'boolean + :group 'helm-net) + +(defcustom helm-surfraw-duckduckgo-url + "https://duckduckgo.com/lite/?q=%s&kp=1" + "The Duckduckgo url. +This is a format string, don't forget the `%s'. +If you have personal settings saved on duckduckgo you should have +a personal url, see your settings on duckduckgo." + :type 'string + :group 'helm-net) + +(defcustom helm-search-suggest-action-wikipedia-url + "https://en.wikipedia.org/wiki/Special:Search?search=%s" + "The Wikipedia search url. +This is a format string, don't forget the `%s'." + :type 'string + :group 'helm-net) + +(defcustom helm-search-suggest-action-youtube-url + "https://www.youtube.com/results?aq=f&search_query=%s" + "The Youtube search url. +This is a format string, don't forget the `%s'." + :type 'string + :group 'helm-net) + +(defcustom helm-search-suggest-action-imdb-url + "http://www.imdb.com/find?s=all&q=%s" + "The IMDb search url. +This is a format string, don't forget the `%s'." + :type 'string + :group 'helm-net) + +(defcustom helm-search-suggest-action-google-maps-url + "https://maps.google.com/maps?f=q&source=s_q&q=%s" + "The Google Maps search url. +This is a format string, don't forget the `%s'." + :type 'string + :group 'helm-net) + +(defcustom helm-search-suggest-action-google-news-url + "https://www.google.com/search?safe=off&prmd=nvlifd&source=lnms&tbs=nws:1&q=%s" + "The Google News search url. +This is a format string, don't forget the `%s'." + :type 'string + :group 'helm-net) + +(defcustom helm-google-suggest-actions + '(("Google Search" . helm-google-suggest-action) + ("Wikipedia" . (lambda (candidate) + (helm-search-suggest-perform-additional-action + helm-search-suggest-action-wikipedia-url + candidate))) + ("Youtube" . (lambda (candidate) + (helm-search-suggest-perform-additional-action + helm-search-suggest-action-youtube-url + candidate))) + ("IMDb" . (lambda (candidate) + (helm-search-suggest-perform-additional-action + helm-search-suggest-action-imdb-url + candidate))) + ("Google Maps" . (lambda (candidate) + (helm-search-suggest-perform-additional-action + helm-search-suggest-action-google-maps-url + candidate))) + ("Google News" . (lambda (candidate) + (helm-search-suggest-perform-additional-action + helm-search-suggest-action-google-news-url + candidate)))) + "List of actions for google suggest sources." + :group 'helm-net + :type '(alist :key-type string :value-type function)) + +(defcustom helm-browse-url-firefox-new-window "--new-tab" + "Allow choosing to browse url in new window or new tab. +Can be \"--new-tab\" (default), \"--new-window\" or \"--private-window\"." + :group 'helm-net + :type '(radio + (const :tag "New tab" "--new-tab") + (const :tag "New window" "--new-window") + (const :tag "New private window" "--private-window"))) + +(defcustom helm-net-curl-switches '("-s" "-L") + "Arguments list passed to curl when using `helm-net-prefer-curl'." + :group 'helm-net + :type '(repeat string)) + +;;; Additional actions for search suggestions +;; +;; +;; Internal +(defvar helm-net-curl-log-file (expand-file-name "helm-curl.log" user-emacs-directory)) +(defun helm-search-suggest-perform-additional-action (url query) + "Perform the search via URL using QUERY as input." + (browse-url (format url (url-hexify-string query)))) + +(defun helm-net--url-retrieve-sync (request parser) + (if helm-net-prefer-curl + (with-temp-buffer + (apply #'call-process "curl" + nil `(t ,helm-net-curl-log-file) nil request helm-net-curl-switches) + (funcall parser)) + (with-current-buffer (url-retrieve-synchronously request) + (funcall parser)))) + + +;;; Google Suggestions +;; +;; +(defun helm-google-suggest-parser () + (cl-loop + with result-alist = (xml-get-children + (car (xml-parse-region + (point-min) (point-max))) + 'CompleteSuggestion) + for i in result-alist collect + (cdr (cl-caadr (assq 'suggestion i))))) + +(defun helm-google-suggest-fetch (input) + "Fetch suggestions for INPUT from XML buffer." + (let ((request (format helm-google-suggest-url + (url-hexify-string input)))) + (helm-net--url-retrieve-sync + request #'helm-google-suggest-parser))) + +(defun helm-google-suggest-set-candidates (&optional request-prefix) + "Set candidates with result and number of Google results found." + (let ((suggestions (helm-google-suggest-fetch + (or (and request-prefix + (concat request-prefix + " " helm-pattern)) + helm-pattern)))) + (if (member helm-pattern suggestions) + suggestions + ;; if there is no suggestion exactly matching the input then + ;; prepend a Search on Google item to the list + (append + suggestions + (list (cons (format "Search for '%s' on Google" helm-input) + helm-input)))))) + +(defun helm-ggs-set-number-result (num) + (if num + (progn + (and (numberp num) (setq num (number-to-string num))) + (cl-loop for i in (reverse (split-string num "" t)) + for count from 1 + append (list i) into C + when (= count 3) + append (list ",") into C + and do (setq count 0) + finally return + (replace-regexp-in-string + "^," "" (mapconcat 'identity (reverse C) "")))) + "?")) + +(defun helm-google-suggest-action (candidate) + "Default action to jump to a Google suggested candidate." + (let ((arg (format helm-google-suggest-search-url + (url-hexify-string candidate)))) + (helm-aif helm-google-suggest-default-browser-function + (funcall it arg) + (helm-browse-url arg)))) + +(defvar helm-google-suggest-default-function + 'helm-google-suggest-set-candidates + "Default function to use in `helm-google-suggest'.") + +(defvar helm-source-google-suggest + (helm-build-sync-source "Google Suggest" + :candidates (lambda () + (funcall helm-google-suggest-default-function)) + :action 'helm-google-suggest-actions + :match-dynamic t + :keymap helm-map + :requires-pattern 3)) + +(defun helm-google-suggest-emacs-lisp () + "Try to emacs lisp complete with Google suggestions." + (helm-google-suggest-set-candidates "emacs lisp")) + + +;;; Web browser functions. +;; +;; +;; If default setting of `w3m-command' is not +;; what you want and you modify it, you will have to reeval +;; also `helm-browse-url-default-browser-alist'. + +(defvar helm-browse-url-chromium-program "chromium-browser") +(defvar helm-browse-url-uzbl-program "uzbl-browser") +(defvar helm-browse-url-nyxt-program "nyxt") +(defvar helm-browse-url-conkeror-program "conkeror") +(defvar helm-browse-url-opera-program "opera") +(defvar helm-browse-url-w3m-program (or (and (boundp 'w3m-command) w3m-command) + (executable-find "w3m"))) +(defvar helm-browse-url-default-browser-alist + '((helm-browse-url-w3m-program . w3m-browse-url) + (browse-url-firefox-program . browse-url-firefox) + (helm-browse-url-chromium-program . helm-browse-url-chromium) + (helm-browse-url-conkeror-program . helm-browse-url-conkeror) + (helm-browse-url-opera-program . helm-browse-url-opera) + (helm-browse-url-uzbl-program . helm-browse-url-uzbl) + (helm-browse-url-nyxt-program . helm-browse-url-nyxt) + (browse-url-kde-program . browse-url-kde) + (browse-url-gnome-moz-program . browse-url-gnome-moz) + (browse-url-mozilla-program . browse-url-mozilla) + (browse-url-galeon-program . browse-url-galeon) + (browse-url-netscape-program . browse-url-netscape) + (browse-url-xterm-program . browse-url-text-xterm) + ("emacs" . eww-browse-url)) + "Alist of (browse_url_variable . function) to try to find a suitable url browser.") + +(cl-defun helm-generic-browser (url cmd-name &rest args) + "Browse URL with NAME browser." + (let ((proc (concat cmd-name " " url))) + (message "Starting %s..." cmd-name) + (apply 'start-process proc nil cmd-name + (append args (list url))) + (set-process-sentinel + (get-process proc) + (lambda (process event) + (when (string= event "finished\n") + (message "%s process %s" process event)))))) + +;;;###autoload +(defun helm-browse-url-firefox (url &optional _ignore) + "Same as `browse-url-firefox' but detach from Emacs. + +So when you quit Emacs you can keep your Firefox session open and +not be prompted to kill the Firefox process. + +NOTE: Probably not supported on some systems (e.g., Windows)." + (interactive (list (read-string "URL: " (browse-url-url-at-point)) + nil)) + (setq url (browse-url-encode-url url)) + (let ((process-environment (browse-url-process-environment))) + (call-process-shell-command + (format "(%s %s %s &)" + browse-url-firefox-program + helm-browse-url-firefox-new-window + (shell-quote-argument url))))) + +;;;###autoload +(defun helm-browse-url-opera (url &optional _ignore) + "Browse URL with Opera browser and detach from Emacs. + +So when you quit Emacs you can keep your Opera session open and +not be prompted to kill the Opera process. + +NOTE: Probably not supported on some systems (e.g., Windows)." + (interactive (list (read-string "URL: " (browse-url-url-at-point)) + nil)) + (setq url (browse-url-encode-url url)) + (let ((process-environment (browse-url-process-environment))) + (call-process-shell-command + (format "(%s %s &)" + helm-browse-url-opera-program (shell-quote-argument url))))) + +;;;###autoload +(defun helm-browse-url-chromium (url &optional _ignore) + "Browse URL with Google Chrome browser." + (interactive "sURL: ") + (helm-generic-browser + url helm-browse-url-chromium-program)) + +;;;###autoload +(defun helm-browse-url-uzbl (url &optional _ignore) + "Browse URL with uzbl browser." + (interactive "sURL: ") + (helm-generic-browser url helm-browse-url-uzbl-program "-u")) + +;;;###autoload +(defun helm-browse-url-conkeror (url &optional _ignore) + "Browse URL with conkeror browser." + (interactive "sURL: ") + (helm-generic-browser url helm-browse-url-conkeror-program)) + +;;;###autoload +(defun helm-browse-url-nyxt (url &optional _ignore) + "Browse URL with nyxt browser." + (interactive "sURL: ") + (helm-generic-browser url helm-browse-url-nyxt-program)) + +(defun helm-browse-url-default-browser (url &rest args) + "Find the first available browser and ask it to load URL." + (let ((default-browser-fn + (cl-loop for (var . fn) in helm-browse-url-default-browser-alist + for exe = (if (stringp var) + var + (and (boundp var) (symbol-value var))) + thereis (and exe (executable-find exe) (fboundp fn) fn)))) + (if default-browser-fn + (apply default-browser-fn url args) + (error "No usable browser found")))) + +(defun helm-browse-url (url &rest args) + "Default command to browse URL." + (if browse-url-browser-function + (browse-url url args) + (helm-browse-url-default-browser url args))) + + +;;; Surfraw +;; +;; Need external program surfraw. +;; + +;; Internal +(defvar helm-surfraw-engines-history nil) +(defvar helm-surfraw-input-history nil) +(defvar helm-surfraw--elvi-cache nil) + +(defun helm-build-elvi-list () + "Return list of all engines and descriptions handled by surfraw." + (or helm-surfraw--elvi-cache + (setq helm-surfraw--elvi-cache + (cdr (with-temp-buffer + (call-process "surfraw" nil t nil "-elvi") + (split-string (buffer-string) "\n")))))) + +;;;###autoload +(defun helm-surfraw (pattern engine) + "Preconfigured `helm' to search PATTERN with search ENGINE." + (interactive + (list + (let* ((default (if (use-region-p) + (buffer-substring-no-properties + (region-beginning) (region-end)) + (thing-at-point 'symbol))) + (prompt (if default + (format "SearchFor (default %s): " default) + "SearchFor: "))) + (read-string prompt nil 'helm-surfraw-input-history default)) + (helm-comp-read + "Engine: " + (helm-build-elvi-list) + :must-match t + :name "Surfraw Search Engines" + :history 'helm-surfraw-engines-history))) + (let* ((engine-nodesc (car (split-string engine))) + (url (if (string= engine-nodesc "duckduckgo") + ;; "sr duckduckgo -p foo" is broken, workaround. + (format helm-surfraw-duckduckgo-url + (url-hexify-string pattern)) + (with-temp-buffer + (apply 'call-process "surfraw" nil t nil + (append (list engine-nodesc "-p") (split-string pattern))) + (replace-regexp-in-string + "\n" "" (buffer-string))))) + (browse-url-browser-function (or helm-surfraw-default-browser-function + browse-url-browser-function))) + (if (string= engine-nodesc "W") + (helm-browse-url helm-home-url) + (helm-browse-url url)))) + +;;;###autoload +(defun helm-google-suggest () + "Preconfigured `helm' for Google search with Google suggest." + (interactive) + (helm-other-buffer 'helm-source-google-suggest "*helm google*")) + +(provide 'helm-net) + +;;; helm-net.el ends here diff --git a/code/elpa/helm-20220822.659/helm-occur.el b/code/elpa/helm-20220822.659/helm-occur.el new file mode 100644 index 0000000..ad269de --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-occur.el @@ -0,0 +1,833 @@ +;;; helm-occur.el --- Incremental Occur for Helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-utils) + +(declare-function helm-buffers-get-visible-buffers "helm-buffers") +(declare-function helm-buffer-list "helm-buffers") +(declare-function helm-grep-split-line "helm-grep") +(declare-function helm-grep-highlight-match "helm-grep") +(declare-function helm-comp-read "helm-mode") + +(defvar helm-current-error) + +;;; Internals +;; +(defvar helm-source-occur nil + "This will be the name of the source related to `current-buffer'. +Don't use it as it value changes always.") +(defvar helm-source-moccur nil + "This is just a flag to add to `helm-sources-using-default-as-input'. +Don't set it to any value, it will have no effect.") +(defvar helm-occur--buffer-list nil) +(defvar helm-occur--buffer-tick nil) +(defvar helm-occur-history nil) +(defvar helm-occur--search-buffer-regexp "\\`\\([0-9]*\\)\\s-\\{1\\}\\(.*\\)\\'" + "The regexp matching candidates in helm-occur candidate buffer.") +(defvar helm-occur-mode--last-pattern nil) +(defvar helm-occur--initial-pos 0) + +(defvar helm-occur-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c o") 'helm-occur-run-goto-line-ow) + (define-key map (kbd "C-c C-o") 'helm-occur-run-goto-line-of) + (define-key map (kbd "C-x C-s") 'helm-occur-run-save-buffer) + map) + "Keymap used in occur source.") + +(defgroup helm-occur nil + "Regexp related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-occur-actions + '(("Go to Line" . helm-occur-goto-line) + ("Goto line other window (C-u vertically)" . helm-occur-goto-line-ow) + ("Goto line new frame" . helm-occur-goto-line-of) + ("Save buffer" . helm-occur-save-results) + ) + "Actions for helm-occur." + :type '(alist :key-type string :value-type function)) + +(defcustom helm-occur-use-ioccur-style-keys nil + "Similar to `helm-grep-use-ioccur-style-keys' but for multi occur. + +Note that if you define this variable with `setq' your change will +have no effect, use customize instead." + :type 'boolean + :set (lambda (var val) + (set var val) + (if val + (progn + (define-key helm-occur-map (kbd "") 'helm-occur-right) + (define-key helm-occur-map (kbd "") 'helm-occur-run-default-action)) + (define-key helm-occur-map (kbd "") nil) + (define-key helm-occur-map (kbd "") nil)))) + +(defcustom helm-occur-always-search-in-current nil + "Helm multi occur always search in current buffer when non--nil." + :type 'boolean) + +(defcustom helm-occur-truncate-lines t + "Truncate lines in occur buffer when non nil." + :type 'boolean) + +(defcustom helm-occur-auto-update-on-resume nil + "Allow auto updating helm-occur buffer when outdated. +noask => Always update without asking +nil => Don't update but signal buffer needs update +never => Never update and do not signal buffer needs update +Any other non--nil value update after confirmation." + :type '(radio :tag "Allow auto updating helm-occur buffer when outdated." + (const :tag "Always update without asking" noask) + (const :tag "Never update and do not signal buffer needs update" never) + (const :tag "Don't update but signal buffer needs update" nil) + (const :tag "Update after confirmation" t))) + +(defcustom helm-occur-candidate-number-limit 99999 + "Value of `helm-candidate-number-limit' for helm-occur." + :type 'integer) + +(defcustom helm-occur-buffer-substring-fn-for-modes + '((mu4e-headers-mode . buffer-substring)) + "Function to use to display buffer contents for major-mode. + +Can be one of `buffer-substring' or `buffer-substring-no-properties'. + +Note that when using `buffer-substring' initialization will be slower." + :type '(alist :key-type (symbol :tag "Mode") + :value-type (radio (const :tag "With text properties" buffer-substring) + (const :tag "Without text properties" buffer-substring-no-properties)))) + +(defcustom helm-occur-keep-closest-position t + "When non nil select closest candidate from point after update. +This happen only in `helm-source-occur' which is always related to +`current-buffer'." + :type 'boolean) + +(defcustom helm-occur-ignore-diacritics nil + "When non nil helm-occur will ignore diacritics in patterns." + :type 'boolean) + +(defface helm-moccur-buffer + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "DarkTurquoise" :underline t)) + "Face used to highlight occur buffer names.") + +(defface helm-resume-need-update + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "red")) + "Face used to flash occur buffer when it needs update.") + + +(defun helm-occur--select-closest-candidate () + ;; Prevent error with `with-helm-window' when switching to help. + (unless (or (not (get-buffer-window helm-buffer 'visible)) + (string-equal helm-pattern "")) + (with-helm-window + (let ((lst '()) + (name (helm-get-attr 'name helm-source-occur)) + closest beg end) + (while-no-input + (goto-char (point-min)) + (if (string= name "Helm occur") + (setq beg (point) + end (point-max)) + (helm-awhile (helm-get-next-header-pos) + (when (string= name (buffer-substring-no-properties + (point-at-bol) (point-at-eol))) + (forward-line 1) + (setq beg (point) + end (or (helm-get-next-header-pos) (point-max))) + (cl-return)))) + (save-excursion + (when (and beg end) + (goto-char beg) + (while (re-search-forward "^[0-9]+" end t) + (push (string-to-number (match-string 0)) lst)) + (setq closest (helm-closest-number-in-list + helm-occur--initial-pos lst)))) + (when (and closest (re-search-forward (format "^%s" closest) end t)) + (helm-mark-current-line) + (goto-char (overlay-start + helm-selection-overlay)))))))) + +;;;###autoload +(defun helm-occur () + "Preconfigured helm for searching lines matching pattern in `current-buffer'. + +When `helm-source-occur' is member of +`helm-sources-using-default-as-input' which is the default, +symbol at point is searched at startup. + +When a region is marked search only in this region by narrowing. + +To search in multiples buffers start from one of the commands listing +buffers (i.e. a helm command using `helm-source-buffers-list' like +`helm-mini') and use the multi occur buffers action. + +This is the helm implementation that collect lines matching pattern +like vanilla Emacs `occur' but have nothing to do with it, the search +engine beeing completely different and also much faster." + (interactive) + (setq helm-source-occur + (car (helm-occur-build-sources (list (current-buffer)) "Helm occur"))) + (helm-set-local-variable 'helm-occur--buffer-list (list (current-buffer)) + 'helm-occur--buffer-tick + (list (buffer-chars-modified-tick (current-buffer)))) + (helm-set-attr 'header-name (lambda (_name) + (format "HO [%s]" + (buffer-name helm-current-buffer))) + helm-source-occur) + (when helm-occur-keep-closest-position + (setq helm-occur--initial-pos (line-number-at-pos)) + (add-hook 'helm-after-update-hook 'helm-occur--select-closest-candidate)) + (save-restriction + (let ((helm-sources-using-default-as-input + (unless (> (buffer-size) 2000000) + helm-sources-using-default-as-input)) + def pos) + (when (use-region-p) + ;; When user mark defun with `mark-defun' with intention of + ;; using helm-occur on this region, it is relevant to use the + ;; thing-at-point located at previous position which have been + ;; pushed to `mark-ring', if it's within the active region. + (let ((beg (region-beginning)) + (end (region-end)) + (prev-pos (car mark-ring))) + (when (and prev-pos (>= prev-pos beg) (< prev-pos end)) + (setq def (save-excursion + (goto-char (setq pos prev-pos)) + (helm-aif (thing-at-point 'symbol) (regexp-quote it))))) + (narrow-to-region beg end))) + (unwind-protect + (helm :sources 'helm-source-occur + :buffer "*helm occur*" + :history 'helm-occur-history + :default (or def (helm-aif (thing-at-point 'symbol) + (regexp-quote it))) + :preselect (and (memq 'helm-source-occur + helm-sources-using-default-as-input) + (format "^%d:" (line-number-at-pos + (or pos (point))))) + :truncate-lines helm-occur-truncate-lines) + (deactivate-mark t) + (remove-hook 'helm-after-update-hook 'helm-occur--select-closest-candidate))))) + +;;;###autoload +(defun helm-occur-visible-buffers () + "Run helm-occur on all visible buffers in frame." + (interactive) + (require 'helm-buffers) + (if (or (one-window-p) (region-active-p)) + (call-interactively #'helm-occur) + (let ((buffers (helm-buffers-get-visible-buffers))) + (helm-multi-occur-1 (mapcar 'get-buffer buffers))))) + +(defun helm-occur-transformer (candidates source) + "Return CANDIDATES prefixed with line number." + (cl-loop with buf = (helm-get-attr 'buffer-name source) + for c in candidates + for disp-linum = (when (string-match helm-occur--search-buffer-regexp c) + (let ((linum (match-string 1 c)) + (disp (match-string 2 c))) + (list + linum + (format "%s:%s" + (propertize + linum 'face 'helm-grep-lineno + 'help-echo (buffer-file-name + (get-buffer buf))) + disp)))) + for linum = (car disp-linum) + for disp = (cadr disp-linum) + when (and disp (not (string= disp ""))) + collect (cons disp (string-to-number linum)))) + +(defclass helm-moccur-class (helm-source-in-buffer) + ((buffer-name :initarg :buffer-name + :initform nil) + (moccur-buffers :initarg :moccur-buffers + :initform nil) + (find-file-target :initform #'helm-occur-quit-an-find-file-fn))) + +(defun helm-occur-build-sources (buffers &optional source-name) + "Build sources for `helm-occur' for each buffer in BUFFERS list." + (cl-loop for buf in buffers + for bname = (buffer-name buf) + collect + (helm-make-source (or source-name bname) + 'helm-moccur-class + :header-name (lambda (name) + (format "HO [%s]" (if (string= name "Helm occur") + bname name))) + :buffer-name bname + :match-part + (lambda (candidate) + ;; The regexp should match what is in candidate buffer, + ;; not what is displayed in helm-buffer e.g. "12 foo" + ;; and not "12:foo". + (when (string-match helm-occur--search-buffer-regexp + candidate) + (match-string 2 candidate))) + :diacritics helm-occur-ignore-diacritics + :search (lambda (pattern) + (when (string-match "\\`\\^\\([^ ]*\\)" pattern) + (setq pattern (concat "^[0-9]* \\{1\\}" (match-string 1 pattern)))) + (condition-case _err + (re-search-forward pattern nil t) + (invalid-regexp nil))) + :init `(lambda () + (with-current-buffer ,buf + (let* ((bsfn (or (cdr (assq + major-mode + helm-occur-buffer-substring-fn-for-modes)) + #'buffer-substring-no-properties)) + (contents (funcall bsfn (point-min) (point-max)))) + (helm-set-attr 'get-line bsfn) + (with-current-buffer (helm-candidate-buffer 'global) + (insert contents) + (goto-char (point-min)) + (let ((linum 1)) + (insert (format "%s " linum)) + (while (re-search-forward "\n" nil t) + (cl-incf linum) + (insert (format "%s " linum)))))))) + :filtered-candidate-transformer 'helm-occur-transformer + :help-message 'helm-moccur-help-message + :nomark t + :migemo t + ;; Needed for resume. + :history 'helm-occur-history + :candidate-number-limit helm-occur-candidate-number-limit + :action 'helm-occur-actions + :requires-pattern 2 + :follow 1 + :group 'helm-occur + :keymap helm-occur-map + :resume 'helm-occur-resume-fn + :moccur-buffers buffers))) + +(defun helm-multi-occur-1 (buffers &optional input) + "Run `helm-occur' on a list of buffers. +Each buffer's result is displayed in a separated source." + (let* ((curbuf (current-buffer)) + (bufs (if helm-occur-always-search-in-current + (cons curbuf (remove curbuf buffers)) + buffers)) + (helm-sources-using-default-as-input + (unless (cl-loop with total_size = 0 + for b in bufs + do (setq total_size (buffer-size b)) + finally return (> total_size 2000000)) + helm-sources-using-default-as-input)) + (sources (helm-occur-build-sources bufs (and (eql curbuf (car bufs)) + (not (cdr bufs)) + "Helm occur"))) + (helm-maybe-use-default-as-input + (not (null (memq 'helm-source-moccur + helm-sources-using-default-as-input))))) + (helm-set-local-variable 'helm-occur--buffer-list bufs + 'helm-occur--buffer-tick + (cl-loop for b in bufs collect + (buffer-chars-modified-tick + (get-buffer b)))) + (when (and helm-occur-always-search-in-current + helm-occur-keep-closest-position) + (setq helm-source-occur + (cl-loop for s in sources + when (eql helm-current-buffer + (get-buffer (helm-get-attr 'buffer-name s))) + return s)) + (setq helm-occur--initial-pos (line-number-at-pos)) + (add-hook 'helm-after-update-hook 'helm-occur--select-closest-candidate)) + (unwind-protect + (helm :sources sources + :buffer "*helm moccur*" + :history 'helm-occur-history + :default (helm-aif (thing-at-point 'symbol) (regexp-quote it)) + :input input + :truncate-lines helm-occur-truncate-lines) + (remove-hook 'helm-after-update-hook 'helm-occur--select-closest-candidate)))) + +;;; Actions +;; +(cl-defun helm-occur-action (lineno + &optional (method (quote buffer))) + "Jump to line number LINENO with METHOD. +METHOD can be one of buffer, buffer-other-window, buffer-other-frame." + (require 'helm-grep) + (let ((buf (if (eq major-mode 'helm-occur-mode) + (get-text-property (point) 'buffer-name) + (helm-get-attr 'buffer-name))) + (split-pat (helm-mm-split-pattern helm-input))) + (cl-case method + (buffer (switch-to-buffer buf)) + (buffer-other-window (helm-window-show-buffers (list buf) t)) + (buffer-other-frame (switch-to-buffer-other-frame buf))) + (with-current-buffer buf + (helm-goto-line lineno) + ;; Move point to the nearest matching regexp from bol. + (cl-loop for reg in split-pat + when (save-excursion + (condition-case _err + (if helm-migemo-mode + (helm-mm-migemo-forward reg (point-at-eol) t) + (re-search-forward reg (point-at-eol) t)) + (invalid-regexp nil))) + collect (match-beginning 0) into pos-ls + finally (when pos-ls (goto-char (apply #'min pos-ls))))))) + +(defun helm-occur-goto-line (candidate) + "From multi occur, switch to buffer and CANDIDATE line." + (helm-occur-action + candidate 'buffer)) + +(defun helm-occur-goto-line-ow (candidate) + "Go to CANDIDATE line in other window. +Same as `helm-occur-goto-line' but go in other window." + (helm-occur-action + candidate 'buffer-other-window)) + +(defun helm-occur-goto-line-of (candidate) + "Go to CANDIDATE line in new frame. +Same as `helm-occur-goto-line' but go in new frame." + (helm-occur-action + candidate 'buffer-other-frame)) + +(defun helm-occur-run-goto-line-ow () + "Run goto line other window action from `helm-occur'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-occur-goto-line-ow))) +(put 'helm-occur-run-goto-line-ow 'helm-only t) + +(defun helm-occur-run-goto-line-of () + "Run goto line new frame action from `helm-occur'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-occur-goto-line-of))) +(put 'helm-occur-run-goto-line-of 'helm-only t) + +(defun helm-occur-run-default-action () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-occur-goto-line))) +(put 'helm-occur-run-default-action 'helm-only t) + +(defun helm-occur-run-save-buffer () + "Run moccur save results action from `helm-moccur'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-occur-save-results))) +(put 'helm-moccur-run-save-buffer 'helm-only t) + +(defun helm-occur-right () + "`helm-occur' action for right arrow. +This is used when `helm-occur-use-ioccur-style-keys' is enabled. +If follow is enabled (default) go to next source, otherwise execute +persistent action." + (interactive) + (if (helm-aand (helm-get-attr 'follow) (> it 0)) + (helm-next-source) + (helm-execute-persistent-action))) +(put 'helm-occur-right 'helm-only t) + +(defun helm-occur-quit-an-find-file-fn (source) + (let* ((sel (helm-get-selection nil nil source)) + (occur-fname (helm-aand (numberp sel) + (helm-get-attr 'buffer-name) + (buffer-file-name (get-buffer it))))) + (when (and occur-fname (file-exists-p occur-fname)) + (expand-file-name occur-fname)))) + +;;; helm-occur-mode +;; +;; +(defvar helm-occur-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "RET") 'helm-occur-mode-goto-line) + (define-key map (kbd "C-o") 'helm-occur-mode-goto-line-ow) + (define-key map (kbd "") 'helm-occur-mode-goto-line-ow-forward) + (define-key map (kbd "") 'helm-occur-mode-goto-line-ow-backward) + (define-key map (kbd "") 'helm-gm-next-file) + (define-key map (kbd "") 'helm-gm-precedent-file) + (define-key map (kbd "M-n") 'helm-occur-mode-goto-line-ow-forward) + (define-key map (kbd "M-p") 'helm-occur-mode-goto-line-ow-backward) + (define-key map (kbd "M-N") 'helm-gm-next-file) + (define-key map (kbd "M-P") 'helm-gm-precedent-file) + (define-key map (kbd "C-c b") 'helm-occur-mode-resume-session) + map)) + +(defun helm-occur-mode-goto-line () + (interactive) + (setq next-error-last-buffer (current-buffer)) + (setq-local helm-current-error (point-marker)) + (helm-aif (get-text-property (point) 'helm-realvalue) + (progn (helm-occur-goto-line it) (helm-match-line-cleanup-pulse)))) + +(defun helm-occur-mode-goto-line-ow () + (interactive) + (setq next-error-last-buffer (current-buffer)) + (setq-local helm-current-error (point-marker)) + (helm-aif (get-text-property (point) 'helm-realvalue) + (progn (helm-occur-goto-line-ow it) (helm-match-line-cleanup-pulse)))) + +(defun helm-occur-mode-goto-line-ow-forward-1 (arg) + (condition-case nil + (progn + (when (or (eq last-command 'helm-occur-mode-goto-line-ow-forward) + (eq last-command 'helm-occur-mode-goto-line-ow-backward)) + (forward-line arg)) + (save-selected-window + (helm-occur-mode-goto-line-ow) + (recenter))) + (error nil))) + +(defun helm-occur-mode-goto-line-ow-forward (arg) + (interactive "p") + (helm-occur-mode-goto-line-ow-forward-1 arg)) + +(defun helm-occur-mode-goto-line-ow-backward (arg) + (interactive "p") + (helm-occur-mode-goto-line-ow-forward-1 (- arg))) + +(defun helm-occur-save-results (_candidate) + "Save helm moccur results in a `helm-moccur-mode' buffer." + (let ((buf "*hmoccur*") + new-buf) + (when (get-buffer buf) + (setq new-buf (helm-read-string "OccurBufferName: " buf)) + (cl-loop for b in (helm-buffer-list) + when (and (string= new-buf b) + (not (y-or-n-p + (format "Buffer `%s' already exists overwrite? " + new-buf)))) + do (setq new-buf (helm-read-string + "OccurBufferName: " "*hmoccur "))) + (setq buf new-buf)) + (with-current-buffer (get-buffer-create buf) + (kill-all-local-variables) + (setq buffer-read-only t) + (buffer-disable-undo) + (let ((inhibit-read-only t) + (map (make-sparse-keymap)) + buf-name) + (erase-buffer) + (insert "-*- mode: helm-occur -*-\n\n" + (format "Occur Results for `%s':\n\n" helm-input)) + (save-excursion + (insert (with-current-buffer helm-buffer + (goto-char (point-min)) + (forward-line 1) + (buffer-substring (point) (point-max))))) + (save-excursion + (forward-line -2) + (while (not (eobp)) + (if (helm-pos-header-line-p) + (let ((beg (point-at-bol)) + (end (point-at-eol))) + (set-text-properties beg (1+ end) nil) + (delete-region (1- beg) end)) + (helm-aif (setq buf-name (assoc-default + 'buffer-name + (get-text-property (point) 'helm-cur-source))) + (progn + (insert (propertize (concat it ":") + 'face 'helm-moccur-buffer + 'helm-realvalue (get-text-property (point) 'helm-realvalue))) + (add-text-properties + (point-at-bol) (point-at-eol) + `(buffer-name ,buf-name)) + (add-text-properties + (point-at-bol) (point-at-eol) + `(keymap ,map + help-echo ,(concat + (buffer-file-name + (get-buffer buf-name)) + "\nmouse-1: set point\nmouse-2: jump to selection") + mouse-face highlight + invisible nil)) + (define-key map [mouse-1] 'mouse-set-point) + (define-key map [mouse-2] 'helm-occur-mode-mouse-goto-line) + (define-key map [mouse-3] 'ignore)))) + (forward-line 1)))) + (buffer-enable-undo) + (helm-occur-mode)) + (pop-to-buffer buf) + (setq next-error-last-buffer (get-buffer buf)) + (message "Helm occur Results saved in `%s' buffer" buf))) + +(defun helm-occur-mode-mouse-goto-line (event) + (interactive "e") + (let* ((window (posn-window (event-end event))) + (pos (posn-point (event-end event)))) + (with-selected-window window + (when (eq major-mode 'helm-occur-mode) + (goto-char pos) + (helm-occur-mode-goto-line))))) +(put 'helm-moccur-mode-mouse-goto-line 'helm-only t) + +(defun helm-occur-mode-resume-session () + (interactive) + (cl-assert (eq major-mode 'helm-occur-mode) nil "Helm command called in wrong context") + (helm-multi-occur-1 helm-occur--buffer-list helm-occur-mode--last-pattern)) + +(defun helm-occur-buffer-substring-with-linums () + "Return current-buffer contents as a string with all lines +numbered. The property \\='buffer-name is added to the whole string." + (let ((bufstr (buffer-substring-no-properties (point-min) (point-max))) + (bufname (buffer-name))) + (with-temp-buffer + (save-excursion + (insert bufstr)) + (let ((linum 1)) + (insert (format "%s " linum)) + (while (re-search-forward "\n" nil t) + (cl-incf linum) + (insert (format "%s " linum))) + (add-text-properties (point-min) (point-max) `(buffer-name ,bufname))) + (buffer-string)))) + +(defun helm-occur-mode--revert-buffer-function (&optional _ignore-auto _noconfirm) + "The `revert-buffer-function' for `helm-occur-mode'." + (goto-char (point-min)) + (let (pattern) + (when (re-search-forward "^Occur Results for `\\(.*\\)'" nil t) + (setq pattern (match-string 1)) + (forward-line 0) + (when (re-search-forward "^$" nil t) + (forward-line 1)) + (let ((inhibit-read-only t) + (buffer (current-buffer)) + (buflst helm-occur--buffer-list)) + (delete-region (point) (point-max)) + (message "Reverting buffer...") + (save-excursion + (with-temp-buffer + (insert + "\n" + (cl-loop for buf in buflst + for bufstr = (or (and (buffer-live-p (get-buffer buf)) + (with-current-buffer buf + (helm-occur-buffer-substring-with-linums))) + "") + concat bufstr) + "\n") + (goto-char (point-min)) + (cl-loop with linum + with mpart + ;; Bind helm-pattern used by `helm-grep-split-line'. + with helm-pattern = pattern + while (helm-mm-search pattern) ; point is at eol. + ;; Calculate line number (linum) and extract real + ;; part of line (mpart). + do (when (save-excursion + ;; `helm-mm-search' puts point at eol. + (forward-line 0) + (re-search-forward "^\\([0-9]*\\)\\s-\\{1\\}\\(.*\\)$" + (point-at-eol) t)) + (setq linum (string-to-number (match-string 1)) + mpart (match-string 2))) + ;; Match part after line number. + when (and mpart (helm-mm-match mpart pattern)) + for line = (format "%s:%d:%s" + (get-text-property (point) 'buffer-name) + linum + mpart) + when line + do (with-current-buffer buffer + (insert + (propertize + (car (helm-occur-filter-one-by-one line)) + 'helm-realvalue linum) + "\n")))) + (when (fboundp 'wgrep-cleanup-overlays) + (wgrep-cleanup-overlays (point-min) (point-max))) + (message "Reverting buffer done") + (when executing-kbd-macro (sit-for 1))))))) + +(defun helm-occur-filter-one-by-one (candidate) + "`filter-one-by-one' function for `helm-source-moccur'." + (require 'helm-grep) + (let* ((split (helm-grep-split-line candidate)) + (buf (car split)) + (lineno (nth 1 split)) + (str (nth 2 split))) + (cons (concat (propertize + buf + 'face 'helm-moccur-buffer + 'help-echo (buffer-file-name + (get-buffer buf)) + 'buffer-name buf) + ":" + (propertize lineno 'face 'helm-grep-lineno) + ":" + (helm-grep-highlight-match str)) + candidate))) + +(define-derived-mode helm-occur-mode + special-mode "helm-moccur" + "Major mode to provide actions in helm moccur saved buffer. + +Special commands: +\\{helm-occur-mode-map}" + (set (make-local-variable 'helm-occur--buffer-list) + (with-helm-buffer helm-occur--buffer-list)) + (set (make-local-variable 'revert-buffer-function) + #'helm-occur-mode--revert-buffer-function) + (set (make-local-variable 'helm-occur-mode--last-pattern) + helm-input) + (set (make-local-variable 'next-error-function) + #'helm-occur-next-error) + (set (make-local-variable 'helm-current-error) nil)) +(put 'helm-moccur-mode 'helm-only t) + +(defun helm-occur-next-error (&optional argp reset) + "Goto ARGP position from a `helm-occur-mode' buffer. +RESET non-nil means rewind to the first match. +This is the `next-error-function' for `helm-occur-mode'." + (interactive "p") + (goto-char (cond (reset (point-min)) + ((and (< argp 0) helm-current-error) + (line-beginning-position)) + ((and (> argp 0) helm-current-error) + (line-end-position)) + ((point)))) + (let ((fun (if (> argp 0) + #'next-single-property-change + #'previous-single-property-change))) + (helm-aif (funcall fun (point) 'buffer-name) + (progn + (goto-char it) + (forward-line 0) + ;; `helm-current-error' is set in + ;; `helm-occur-mode-goto-line'. + (helm-occur-mode-goto-line)) + (user-error "No more matches")))) + +;;; Resume +;; +(defun helm-occur-resume-fn () + (with-helm-buffer + (let (new-tick-ls buffer-is-modified) + (set (make-local-variable 'helm-occur--buffer-list) + (cl-loop for b in helm-occur--buffer-list + when (buffer-live-p (get-buffer b)) + collect b)) + (setq buffer-is-modified (/= (length helm-occur--buffer-list) + (length (helm-get-attr 'moccur-buffers)))) + (helm-set-attr 'moccur-buffers helm-occur--buffer-list) + (setq new-tick-ls (cl-loop for b in helm-occur--buffer-list + collect (buffer-chars-modified-tick + (get-buffer b)))) + (when buffer-is-modified + (setq helm-occur--buffer-tick new-tick-ls)) + (cl-assert (> (length helm-occur--buffer-list) 0) nil + "helm-resume error: helm-(m)occur buffer list is empty") + (unless (eq helm-occur-auto-update-on-resume 'never) + (when (or buffer-is-modified + (cl-loop for b in helm-occur--buffer-list + for new-tick = (buffer-chars-modified-tick + (get-buffer b)) + for tick in helm-occur--buffer-tick + thereis (/= tick new-tick))) + (helm-aif helm-occur-auto-update-on-resume + (when (or (eq it 'noask) + (y-or-n-p "Helm (m)occur Buffer outdated, update? ")) + (run-with-idle-timer + 0.1 nil (lambda () + (with-helm-buffer + (helm-force-update) + (message "Helm (m)occur Buffer have been udated") + (sit-for 1) (message nil)))) + (unless buffer-is-modified (setq helm-occur--buffer-tick + new-tick-ls))) + (run-with-idle-timer + 0.1 nil + (lambda () + (with-helm-buffer + (let ((ov (make-overlay (save-excursion + (goto-char (point-min)) + (forward-line 1) + (point)) + (point-max)))) + (overlay-put ov 'face 'helm-resume-need-update) + (sit-for 0) + (delete-overlay ov) + (message "[Helm occur Buffer outdated (C-c C-u to update)]"))))) + (unless buffer-is-modified + (with-helm-after-update-hook + (setq helm-occur--buffer-tick new-tick-ls) + (message "Helm (m)occur Buffer have been udated"))))))))) + +;;; Helm occur from isearch +;; +;;;###autoload +(defun helm-occur-from-isearch () + "Invoke `helm-occur' from isearch. + +To use this bind it to a key in `isearch-mode-map'." + (interactive) + (let ((input (if isearch-regexp + isearch-string + (regexp-quote isearch-string))) + (bufs (list (current-buffer))) + ;; Use `helm-occur-always-search-in-current' as a flag for + ;; `helm-occur--select-closest-candidate'. + (helm-occur-always-search-in-current t)) + (isearch-exit) + (helm-multi-occur-1 bufs input))) + +;;;###autoload +(defun helm-multi-occur-from-isearch () + "Invoke `helm-multi-occur' from isearch. + +With a prefix arg, reverse the behavior of +`helm-moccur-always-search-in-current'. +The prefix arg can be set before calling +`helm-multi-occur-from-isearch' or during the buffer selection. + +To use this bind it to a key in `isearch-mode-map'." + (interactive) + (let (buf-list + helm-moccur-always-search-in-current + (input (if isearch-regexp + isearch-string + (regexp-quote isearch-string)))) + (isearch-exit) + (setq buf-list (mapcar 'get-buffer + (helm-comp-read "Buffers: " + (helm-buffer-list) + :name "Occur in buffer(s)" + :marked-candidates t))) + (setq helm-moccur-always-search-in-current + (if (or current-prefix-arg + helm-current-prefix-arg) + (not helm-moccur-always-search-in-current) + helm-moccur-always-search-in-current)) + (helm-multi-occur-1 buf-list input))) + +(provide 'helm-occur) + +;;; helm-occur.el ends here diff --git a/code/elpa/helm-20220822.659/helm-pkg.el b/code/elpa/helm-20220822.659/helm-pkg.el new file mode 100644 index 0000000..da183e2 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-pkg.el @@ -0,0 +1,11 @@ +(define-package "helm" "20220822.659" "Helm is an Emacs incremental and narrowing framework" + '((helm-core "3.8.7") + (popup "0.5.3")) + :commit "4e99cc8ef66aac2d824c456f58abe833be26c99d" :authors + '(("Thierry Volpiatto" . "thievol@posteo.net")) + :maintainer + '("Thierry Volpiatto" . "thievol@posteo.net") + :url "https://emacs-helm.github.io/helm/") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/helm-20220822.659/helm-regexp.el b/code/elpa/helm-20220822.659/helm-regexp.el new file mode 100644 index 0000000..eea6e67 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-regexp.el @@ -0,0 +1,132 @@ +;;; helm-regexp.el --- In buffer regexp searching and replacement for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-utils) + +(declare-function helm-mm-split-pattern "helm-multi-match") + + +(defgroup helm-regexp nil + "Regexp related Applications and libraries for Helm." + :group 'helm) + + + +;; History vars +(defvar helm-build-regexp-history nil) + +(defun helm-query-replace-regexp (_candidate) + "Query replace regexp from `helm-regexp'. +With a prefix arg replace only matches surrounded by word boundaries, +i.e. don't replace inside a word, regexp is surrounded with \\bregexp\\b." + (let ((regexp helm-input)) + (apply 'query-replace-regexp + (helm-query-replace-args regexp)))) + +(defun helm-kill-regexp-as-sexp (_candidate) + "Kill regexp in a format usable in lisp code." + (helm-regexp-kill-new + (prin1-to-string helm-input))) + +(defun helm-kill-regexp (_candidate) + "Kill regexp as it is in `helm-pattern'." + (helm-regexp-kill-new helm-input)) + +(defun helm-query-replace-args (regexp) + "Create arguments of `query-replace-regexp' action in `helm-regexp'." + (let ((region-only (helm-region-active-p))) + (list + regexp + (query-replace-read-to regexp + (format "Query replace %sregexp %s" + (if helm-current-prefix-arg "word " "") + (if region-only "in region " "")) + t) + helm-current-prefix-arg + (when region-only (region-beginning)) + (when region-only (region-end))))) + +(defvar helm-source-regexp + (helm-build-in-buffer-source "Regexp Builder" + :init (lambda () + (helm-init-candidates-in-buffer + 'global (with-temp-buffer + (insert-buffer-substring helm-current-buffer) + (buffer-string)))) + :get-line #'helm-regexp-get-line + :persistent-action #'helm-regexp-persistent-action + :persistent-help "Show this line" + :multiline t + :multimatch nil + :requires-pattern 2 + :group 'helm-regexp + :mode-line "Press TAB to select action." + :action '(("Kill Regexp as sexp" . helm-kill-regexp-as-sexp) + ("Query Replace Regexp (C-u Not inside word.)" + . helm-query-replace-regexp) + ("Kill Regexp" . helm-kill-regexp)))) + +(defun helm-regexp-get-line (s e) + (let ((matches (match-data)) + (line (buffer-substring s e))) + (propertize + (cl-loop with ln = (format "%5d: %s" (1- (line-number-at-pos s)) line) + for i from 0 to (1- (/ (length matches) 2)) + if (match-string i) + concat (format "\n%s%s'%s'" + (make-string 10 ? ) (format "Group %d: " i) it) + into ln1 + finally return (concat ln ln1)) + 'helm-realvalue s))) + +(defun helm-regexp-persistent-action (pt) + (helm-goto-char pt) + (helm-highlight-current-line)) + +(defun helm-regexp-kill-new (input) + (kill-new (substring-no-properties input)) + (message "Killed: %s" input)) + + +;;; Predefined commands +;; +;; + +;;;###autoload +(defun helm-regexp () + "Preconfigured helm to build regexps. +`query-replace-regexp' can be run from there against found regexp." + (interactive) + (save-restriction + (when (and (helm-region-active-p) + ;; Don't narrow to region if buffer is already narrowed. + (not (helm-current-buffer-narrowed-p (current-buffer)))) + (narrow-to-region (region-beginning) (region-end))) + (helm :sources helm-source-regexp + :buffer "*helm regexp*" + :prompt "Regexp: " + :history 'helm-build-regexp-history))) + + +(provide 'helm-regexp) + +;;; helm-regexp.el ends here diff --git a/code/elpa/helm-20220822.659/helm-ring.el b/code/elpa/helm-20220822.659/helm-ring.el new file mode 100644 index 0000000..6459ba3 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-ring.el @@ -0,0 +1,604 @@ +;;; helm-ring.el --- kill-ring, mark-ring, and register browsers for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-utils) +(require 'helm-help) +(require 'helm-elisp) + +(declare-function undo-tree-restore-state-from-register "ext:undo-tree.el" (register)) + + +(defgroup helm-ring nil + "Ring related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-kill-ring-threshold 3 + "Minimum length of a candidate to be listed by `helm-source-kill-ring'." + :type 'integer + :group 'helm-ring) + +(defcustom helm-kill-ring-max-offset 400 + "Max number of chars displayed per candidate in kill-ring browser. +When `t', don't truncate candidate, show all. +By default it is approximatively the number of bits contained in five lines +of 80 chars each, i.e. 80*5. +Note that if you set this to nil multiline will be disabled, i.e. you +will not have separators between candidates any more." + :type '(choice (const :tag "Disabled" t) + (integer :tag "Max candidate offset")) + :group 'helm-ring) + +(defcustom helm-kill-ring-actions + '(("Yank marked" . helm-kill-ring-action-yank) + ("Delete marked" . helm-kill-ring-action-delete) + ("Search from candidate" . helm-kill-ring-search-from-string)) + "List of actions for kill ring source." + :group 'helm-ring + :type '(alist :key-type string :value-type function)) + +(defcustom helm-kill-ring-separator "\n" + "The separator used to separate marked candidates when yanking." + :group 'helm-ring + :type 'string) + +(defcustom helm-register-max-offset 160 + "Max size of string register entries before truncating." + :group 'helm-ring + :type 'integer) + +;;; Kill ring +;; +;; +(defvar helm-kill-ring-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-y") 'helm-next-line) + (define-key map (kbd "M-u") 'helm-previous-line) + (define-key map (kbd "M-D") 'helm-kill-ring-delete) + (define-key map (kbd "C-s") 'helm-kill-ring-run-search-from-string) + (define-key map (kbd "C-]") 'helm-kill-ring-toggle-truncated) + (define-key map (kbd "C-c C-k") 'helm-kill-ring-kill-selection) + (define-key map (kbd "C-c d") 'helm-kill-ring-run-persistent-delete) + map) + "Keymap for `helm-show-kill-ring'.") + +(defvar helm-source-kill-ring + (helm-build-sync-source "Kill Ring" + :init (lambda () + (helm-set-attr 'last-command last-command) + (helm-set-attr 'multiline helm-kill-ring-max-offset)) + :candidates #'helm-kill-ring-candidates + :filtered-candidate-transformer #'helm-kill-ring-transformer + :action 'helm-kill-ring-actions + :persistent-action 'ignore + :help-message 'helm-kill-ring-help-message + :persistent-help "DoNothing" + :keymap helm-kill-ring-map + :migemo t + :multiline 'helm-kill-ring-max-offset + :group 'helm-ring) + "Source for browse and insert contents of kill-ring.") + +(defun helm-kill-ring-candidates () + (cl-loop with cands = (helm-fast-remove-dups kill-ring :test 'equal) + for kill in (if (eq (helm-get-attr 'last-command) 'yank) + (cdr cands) + cands) + unless (or (< (length kill) helm-kill-ring-threshold) + (string-match "\\`[\n[:blank:]]+\\'" kill)) + collect kill)) + +(defun helm-kill-ring-transformer (candidates _source) + "Ensure CANDIDATES are not read-only." + (cl-loop for i in candidates + when (get-text-property 0 'read-only i) + do (set-text-properties 0 (length i) '(read-only nil) i) + collect i)) + +(defvar helm-kill-ring--truncated-flag nil) +(defun helm-kill-ring-toggle-truncated () + "Toggle truncated view of candidates in helm kill-ring browser." + (interactive) + (with-helm-alive-p + (setq helm-kill-ring--truncated-flag (not helm-kill-ring--truncated-flag)) + (let* ((cur-cand (helm-get-selection)) + (presel-fn (lambda () + (helm-kill-ring--preselect-fn cur-cand)))) + (helm-set-attr 'multiline + (if helm-kill-ring--truncated-flag + 15000000 + helm-kill-ring-max-offset)) + (helm-update presel-fn)))) +(put 'helm-kill-ring-toggle-truncated 'helm-only t) + +(defun helm-kill-ring-kill-selection () + "Store the real value of candidate in kill-ring. +Same as `helm-kill-selection-and-quit' called with a prefix arg." + (interactive) + (helm-kill-selection-and-quit t)) +(put 'helm-kill-ring-kill-selection 'helm-only t) + +(defun helm-kill-ring--preselect-fn (candidate) + "Internal, used to preselect CANDIDATE when toggling truncated view." + ;; Preselection by regexp may not work if candidate is huge, so walk + ;; the helm buffer until selection is on CANDIDATE. + (helm-awhile (condition-case-unless-debug nil + (and (not (helm-pos-header-line-p)) + (helm-get-selection)) + (error nil)) + (if (string= it candidate) + (cl-return) + (helm-next-line)))) + +(defun helm-kill-ring-action-yank (_str) + "Insert concatenated marked candidates in current-buffer. + +When two prefix args are given prompt to choose separator, otherwise +use `helm-kill-ring-separator' as default." + (let ((marked (helm-marked-candidates)) + (sep (if (equal helm-current-prefix-arg '(16)) + (read-string "Separator: ") + helm-kill-ring-separator))) + (helm-kill-ring-action-yank-1 + (cl-loop for c in (butlast marked) + concat (concat c sep) into str + finally return (concat str (car (last marked))))))) + +(defun helm-kill-ring-action-yank-1 (str) + "Insert STR in `kill-ring' and set STR to the head. + +When called with a prefix arg, point and mark are exchanged +without activating region. +If this action is executed just after `yank', replace with STR as +yanked string." + (let ((yank-fn (lambda (&optional before yank-pop) + (insert-for-yank str) + ;; Set the window start back where it was in + ;; the yank command, if possible. + (when yank-pop + (set-window-start (selected-window) yank-window-start t)) + (when (or (equal helm-current-prefix-arg '(4)) before) + ;; Same as exchange-point-and-mark but without + ;; activating region. + (goto-char (prog1 (mark t) + (set-marker (mark-marker) + (point) + helm-current-buffer))))))) + ;; Prevent inserting and saving highlighted items. + (set-text-properties 0 (length str) nil str) + (with-helm-current-buffer + (unwind-protect + (progn + (setq kill-ring (delete str kill-ring)) + ;; Adding a `delete-selection' property + ;; to `helm-kill-ring-action' is not working + ;; because `this-command' will be `helm-maybe-exit-minibuffer', + ;; so use this workaround (Bug#1520). + (when (and (region-active-p) delete-selection-mode) + (delete-region (region-beginning) (region-end))) + (if (not (eq (helm-get-attr 'last-command helm-source-kill-ring) 'yank)) + (progn + ;; Ensure mark is at beginning of inserted text. + (push-mark) + ;; When yanking in a helm minibuffer we need a small + ;; delay to detect the mark in previous minibuffer. [1] + (run-with-timer 0.01 nil yank-fn)) + ;; from `yank-pop' + (let ((inhibit-read-only t) + (before (< (point) (mark t)))) + (if before + (funcall (or yank-undo-function 'delete-region) (point) (mark t)) + (funcall (or yank-undo-function 'delete-region) (mark t) (point))) + (setq yank-undo-function nil) + (set-marker (mark-marker) (point) helm-current-buffer) + ;; Same as [1] but use the same mark and point as in + ;; the initial yank according to BEFORE even if no + ;; prefix arg is given. + (run-with-timer 0.01 nil yank-fn before 'pop)))) + (kill-new str))))) +(define-obsolete-function-alias 'helm-kill-ring-action 'helm-kill-ring-action-yank "2.4.0") + +(defun helm-kill-ring-search-from-string (candidate) + (let ((str (car (split-string candidate "\n")))) + (helm-multi-occur-1 + (list (current-buffer)) + (regexp-quote (substring-no-properties str))))) + +(defun helm-kill-ring-run-search-from-string () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-kill-ring-search-from-string))) +(put 'helm-kill-ring-run-search-from-string 'helm-only t) + +(defun helm-kill-ring-action-delete (_candidate) + "Delete marked candidates from `kill-ring'." + (cl-loop for c in (helm-marked-candidates) + do (setq kill-ring + (delete c kill-ring)))) + +(defun helm-kill-ring-persistent-delete (_candidate) + (unwind-protect + (cl-loop for c in (helm-marked-candidates) + do (progn + (helm-preselect (format "^%s" (regexp-quote c))) + (setq kill-ring (delete c kill-ring)) + (helm-delete-current-selection) + (helm--remove-marked-and-update-mode-line c))) + (with-helm-buffer + (setq helm-marked-candidates nil + helm-visible-mark-overlays nil)) + (helm-force-update (helm-aif (helm-get-selection nil t) (regexp-quote it))))) + +(defun helm-kill-ring-run-persistent-delete () + "Delete current candidate without quitting." + (interactive) + (with-helm-alive-p + (helm-set-attr 'quick-delete '(helm-kill-ring-persistent-delete . never-split)) + (helm-execute-persistent-action 'quick-delete))) +(put 'helm-kill-ring-run-persistent-delete 'helm-only t) + +(defun helm-kill-ring-delete () + "Delete marked candidates from `kill-ring'. + +This is a command for `helm-kill-ring-map'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-kill-ring-action-delete))) +(put 'helm-kill-ring-delete 'helm-only t) + + +;;;; +;; DO NOT use these sources with other sources use +;; the commands `helm-mark-ring', `helm-global-mark-ring' or +;; `helm-all-mark-rings' instead. + +(defun helm-mark-ring-line-string-at-pos (pos) + "Return line string at position POS." + (save-excursion + (goto-char pos) + (forward-line 0) + (let ((line (car (split-string (thing-at-point 'line) "[\n\r]")))) + (remove-text-properties 0 (length line) '(read-only) line) + (if (string= "" line) + "" + line)))) + +(defun helm-mark-ring-get-candidates () + (with-helm-current-buffer + (cl-loop with marks = (if (mark t) + (cons (mark-marker) mark-ring) + mark-ring) + for marker in marks + with max-line-number = (line-number-at-pos (point-max)) + with width = (length (number-to-string max-line-number)) + for m = (format (concat "%" (number-to-string width) "d: %s") + (line-number-at-pos marker) + (helm-mark-ring-line-string-at-pos marker)) + unless (and recip (assoc m recip)) + collect (cons m marker) into recip + finally return recip))) + +(defun helm-mark-ring-default-action (candidate) + (let ((target (copy-marker candidate))) + (helm-aif (marker-buffer candidate) + (progn + (switch-to-buffer it) + (helm-log-run-hook 'helm-goto-line-before-hook) + (helm-match-line-cleanup) + (with-helm-current-buffer + (unless helm-yank-point (setq helm-yank-point (point)))) + (helm-goto-char target) + (helm-highlight-current-line)) + ;; marker points to no buffer, no need to dereference it, just + ;; delete it. + (setq mark-ring (delete target mark-ring)) + (error "Marker points to no buffer")))) + +(defvar helm-source-mark-ring + (helm-build-sync-source "mark-ring" + :candidates #'helm-mark-ring-get-candidates + :action '(("Goto line" . helm-mark-ring-default-action)) + :persistent-help "Show this line" + :group 'helm-ring)) + +;;; Global-mark-ring +(defvar helm-source-global-mark-ring + (helm-build-sync-source "global-mark-ring" + :candidates #'helm-global-mark-ring-get-candidates + :action '(("Goto line" . helm-mark-ring-default-action)) + :persistent-help "Show this line" + :group 'helm-ring)) + +(defun helm-global-mark-ring-format-buffer (marker) + (with-current-buffer (marker-buffer marker) + (goto-char marker) + (forward-line 0) + (let ((line (pcase (thing-at-point 'line) + ((and line (pred stringp) + (guard (not (string-match-p "\\`\n?\\'" line)))) + (car (split-string line "[\n\r]"))) + (_ "")))) + (remove-text-properties 0 (length line) '(read-only) line) + (format "%7d:%s: %s" + (line-number-at-pos) (marker-buffer marker) line)))) + +(defun helm-global-mark-ring-get-candidates () + (let ((marks global-mark-ring)) + (when marks + (cl-loop for marker in marks + for mb = (marker-buffer marker) + for gm = (unless (or (string-match "^ " (format "%s" mb)) + (null mb)) + (helm-global-mark-ring-format-buffer marker)) + when (and gm (not (assoc gm recip))) + collect (cons gm marker) into recip + finally return recip)))) + +;;;; +;;; Insert from register +(defvar helm-source-register + (helm-build-sync-source "Registers" + :candidates #'helm-register-candidates + :action-transformer #'helm-register-action-transformer + :persistent-help "" + :multiline t + :action '(("Delete Register(s)" . + (lambda (_candidate) + (cl-loop for candidate in (helm-marked-candidates) + for register = (car candidate) + do (setq register-alist + (delq (assoc register register-alist) + register-alist)))))) + :group 'helm-ring) + "See (info \"(emacs)Registers\")") + +(defun helm-register-candidates () + "Collecting register contents and appropriate commands." + (cl-loop for (char . rval) in register-alist + for key = (single-key-description char) + for e27 = (registerv-p rval) + for val = (if e27 ; emacs-27 + (registerv-data rval) + rval) + for string-actions = + (cond + ((numberp val) + (list (int-to-string val) + 'insert-register + 'increment-register)) + ((markerp val) + (let ((buf (marker-buffer val))) + (if (null buf) + (list "a marker in no buffer") + (list (concat + "a buffer position:" + (buffer-name buf) + ", position " + (int-to-string (marker-position val))) + 'jump-to-register + 'insert-register)))) + ((and (consp val) (window-configuration-p (car val))) + (list "window configuration." + 'jump-to-register)) + ((and (vectorp val) + (fboundp 'undo-tree-register-data-p) + (undo-tree-register-data-p (if e27 val (elt val 1)))) + (list + "Undo-tree entry." + 'undo-tree-restore-state-from-register)) + ((or (and (vectorp val) (eq 'registerv (aref val 0))) + (and (consp val) (frame-configuration-p (car val)))) + (list "frame configuration." + 'jump-to-register)) + ((and (consp val) (eq (car val) 'file)) + (list (concat "file:" + (prin1-to-string (cdr val)) + ".") + 'jump-to-register)) + ((and (consp val) (eq (car val) 'file-query)) + (list (concat "file:a file-query reference: file " + (car (cdr val)) + ", position " + (int-to-string (car (cdr (cdr val)))) + ".") + 'jump-to-register)) + ((consp val) + (let ((lines (format "%4d" (length val)))) + (list (format "%s: %s\n" lines + (truncate-string-to-width + (mapconcat 'identity (list (car val)) + "^J") + (- (window-width) 15))) + 'insert-register))) + ((stringp val) + (list + (concat (substring-no-properties + val 0 (min (length val) helm-register-max-offset)) + (if (> (length val) helm-register-max-offset) + "[...]" "")) + 'insert-register + 'kill-new + 'append-to-register + 'prepend-to-register))) + unless (null string-actions) ; Fix Bug#1107. + collect (cons (format "Register %3s:\n %s" key (car string-actions)) + (cons char (cdr string-actions))))) + +(defun helm-register-action-transformer (actions register-and-functions) + "Decide actions by the contents of register." + (cl-loop with func-actions = + '((insert-register + "Insert Register" . + (lambda (c) (insert-register (car c)))) + (kill-new + "Kill Register" . + (lambda (c) (with-temp-buffer + (insert-register (car c)) + (kill-new (buffer-string))))) + (jump-to-register + "Jump to Register" . + (lambda (c) (jump-to-register (car c)))) + (append-to-register + "Append Region to Register" . + (lambda (c) (append-to-register + (car c) (region-beginning) (region-end)))) + (prepend-to-register + "Prepend Region to Register" . + (lambda (c) (prepend-to-register + (car c) (region-beginning) (region-end)))) + (increment-register + "Increment Prefix Arg to Register" . + (lambda (c) (increment-register + helm-current-prefix-arg (car c)))) + (undo-tree-restore-state-from-register + "Restore Undo-tree register" . + (lambda (c) (and (fboundp 'undo-tree-restore-state-from-register) + (undo-tree-restore-state-from-register (car c)))))) + for func in (cdr register-and-functions) + when (assq func func-actions) + collect (cdr it) into transformer-actions + finally return (append transformer-actions actions))) + +;;;###autoload +(defun helm-mark-ring () + "Preconfigured `helm' for `helm-source-mark-ring'." + (interactive) + (helm :sources 'helm-source-mark-ring + :resume 'noresume + :buffer "*helm mark*")) + +;;;###autoload +(defun helm-global-mark-ring () + "Preconfigured `helm' for `helm-source-global-mark-ring'." + (interactive) + (helm :sources 'helm-source-global-mark-ring + :resume 'noresume + :buffer "*helm global mark*")) + +;;;###autoload +(defun helm-all-mark-rings () + "Preconfigured `helm' for mark rings. +Source used are `helm-source-global-mark-ring' and +`helm-source-mark-ring'." + (interactive) + (helm :sources '(helm-source-mark-ring + helm-source-global-mark-ring) + :resume 'noresume + :buffer "*helm mark ring*")) + +;;;###autoload +(defun helm-register () + "Preconfigured `helm' for Emacs registers." + (interactive) + (helm :sources 'helm-source-register + :resume 'noresume + :buffer "*helm register*")) + +;;;###autoload +(defun helm-show-kill-ring () + "Preconfigured `helm' for `kill-ring'. +It is drop-in replacement of `yank-pop'. + +First call open the kill-ring browser, next calls move to next line." + (interactive) + (setq helm-kill-ring--truncated-flag nil) + (let ((enable-recursive-minibuffers t)) + (helm :sources helm-source-kill-ring + :buffer "*helm kill ring*" + :resume 'noresume + :allow-nest t))) + +;;;###autoload +(defun helm-execute-kmacro () + "Preconfigured helm for keyboard macros. +Define your macros with `f3' and `f4'. +See (info \"(emacs) Keyboard Macros\") for detailed infos. +This command is useful when used with persistent action." + (interactive) + (let ((helm-quit-if-no-candidate + (lambda () (message "No kbd macro has been defined")))) + (helm :sources + (helm-build-sync-source "Kmacro" + :candidates (lambda () + (helm-fast-remove-dups + (cons (kmacro-ring-head) + kmacro-ring) + :test 'equal)) + :multiline t + :candidate-transformer + (lambda (candidates) + (cl-loop for c in candidates collect + (propertize (help-key-description (car c) nil) + 'helm-realvalue c))) + :persistent-help "Execute kmacro" + :help-message 'helm-kmacro-help-message + :action + (helm-make-actions + "Execute kmacro (`C-u ' to execute times)" + 'helm-kbd-macro-execute + "Concat marked macros" + 'helm-kbd-macro-concat-macros + "Delete marked macros" + 'helm-kbd-macro-delete-macro + "Edit marked macro" + 'helm-kbd-macro-edit-macro) + :group 'helm-ring) + :buffer "*helm kmacro*"))) + +(defun helm-kbd-macro-execute (candidate) + ;; Move candidate on top of list for next use. + (setq kmacro-ring (delete candidate kmacro-ring)) + (kmacro-push-ring) + (kmacro-split-ring-element candidate) + (kmacro-exec-ring-item + candidate helm-current-prefix-arg)) + +(defun helm-kbd-macro-concat-macros (_candidate) + (let ((mkd (helm-marked-candidates))) + (when (cdr mkd) + (kmacro-push-ring) + (setq last-kbd-macro + (mapconcat 'identity + (cl-loop for km in mkd + if (vectorp km) + append (cl-loop for k across km collect + (key-description (vector k))) + into result + else collect (car km) into result + finally return result) + ""))))) + +(defun helm-kbd-macro-delete-macro (_candidate) + (let ((mkd (helm-marked-candidates))) + (kmacro-push-ring) + (cl-loop for km in mkd + do (setq kmacro-ring (delete km kmacro-ring))) + (kmacro-pop-ring1))) + +(defun helm-kbd-macro-edit-macro (candidate) + (kmacro-push-ring) + (setq kmacro-ring (delete candidate kmacro-ring)) + (kmacro-split-ring-element candidate) + (kmacro-edit-macro)) + +(provide 'helm-ring) + +;;; helm-ring.el ends here diff --git a/code/elpa/helm-20220822.659/helm-semantic.el b/code/elpa/helm-20220822.659/helm-semantic.el new file mode 100644 index 0000000..4687029 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-semantic.el @@ -0,0 +1,232 @@ +;;; helm-semantic.el --- Helm interface for Semantic -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2017 Daniel Hackney +;; 2012 ~ 2021 Thierry Volpiatto + +;; Author: Daniel Hackney + +;; 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 . + +;;; Commentary: + +;; Uses `candidates-in-buffer' for speed. + +;;; Code: + +(require 'cl-lib) +(require 'semantic) +(require 'helm-help) +(require 'helm-imenu) + +(declare-function pulse-momentary-highlight-one-line "pulse.el" (point &optional face)) + +(defgroup helm-semantic nil + "Semantic tags related libraries and applications for helm." + :group 'helm) + +(defcustom helm-semantic-display-style + '((python-mode . semantic-format-tag-summarize) + (c-mode . semantic-format-tag-concise-prototype-c-mode) + (emacs-lisp-mode . semantic-format-tag-abbreviate-emacs-lisp-mode)) + "Function to present a semantic tag according to `major-mode'. + +It is an alist where the `car' of each element is a `major-mode' and +the `cdr' a `semantic-format-tag-*' function. + +If no function is found for current `major-mode', fall back to +`semantic-format-tag-summarize' default function. + +You can have more or less informations depending of the `semantic-format-tag-*' +function you choose. + +All the supported functions are prefixed with \"semantic-format-tag-\", +you have completion on these functions with `C-M i' in the customize interface." + :group 'helm-semantic + :type '(alist :key-type symbol :value-type symbol)) + +;;; keymap +(defvar helm-semantic-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + map)) + +(defcustom helm-semantic-lynx-style-map nil + "Use Arrow keys to jump to occurences." + :group 'helm-semantic + :type 'boolean + :set (lambda (var val) + (set var val) + (if val + (progn + (define-key helm-semantic-map (kbd "") 'helm-execute-persistent-action) + (define-key helm-semantic-map (kbd "") 'helm-maybe-exit-minibuffer)) + (define-key helm-semantic-map (kbd "") nil) + (define-key helm-semantic-map (kbd "") nil)))) + +;; Internals vars +(defvar helm-semantic--tags-cache nil) + +(defun helm-semantic--fetch-candidates (tags depth &optional class) + "Write the contents of TAGS to the current buffer." + (let ((class class) cur-type + (stylefn (or (with-helm-current-buffer + (assoc-default major-mode helm-semantic-display-style)) + #'semantic-format-tag-summarize))) + (cl-dolist (tag tags) + (when (listp tag) + (cl-case (setq cur-type (semantic-tag-class tag)) + ((function variable type) + (let ((spaces (make-string (* depth 2) ?\s)) + (type-p (eq cur-type 'type))) + (unless (and (> depth 0) (not type-p)) + (setq class nil)) + (insert + (if (and class (not type-p)) + (format "%s%s(%s) " + spaces (if (< depth 2) "" "├►") class) + spaces) + ;; Save the tag for later + (propertize (funcall stylefn tag nil t) + 'semantic-tag tag) + "\n") + (and type-p (setq class (car tag))) + ;; Recurse to children + (unless (eq cur-type 'function) + (helm-semantic--fetch-candidates + (semantic-tag-components tag) (1+ depth) class)))) + + ;; Don't do anything with packages or includes for now + ((package include) + (insert + (propertize (funcall stylefn tag nil t) + 'semantic-tag tag) + "\n") + ) + ;; Catch-all + (t)))))) + +(defun helm-semantic-default-action (_candidate &optional persistent) + ;; By default, helm doesn't pass on the text properties of the selection. + ;; Fix this. + (helm-log-run-hook 'helm-goto-line-before-hook) + (with-current-buffer helm-buffer + (when (looking-at " ") + (goto-char (next-single-property-change + (point-at-bol) 'semantic-tag nil (point-at-eol)))) + (let ((tag (get-text-property (point) 'semantic-tag))) + (semantic-go-to-tag tag) + (unless persistent + (pulse-momentary-highlight-one-line (point)))))) + +(defun helm-semantic--maybe-set-needs-update () + (with-helm-current-buffer + (when (semantic-parse-tree-needs-update-p) + (semantic-parse-tree-set-needs-update)))) + +(defvar helm-source-semantic nil) + +(defclass helm-semantic-source (helm-source-in-buffer) + ((init :initform (lambda () + (helm-semantic--maybe-set-needs-update) + (setq helm-semantic--tags-cache (semantic-fetch-tags)) + (with-current-buffer (helm-candidate-buffer 'global) + (let ((major-mode (with-helm-current-buffer major-mode))) + (helm-semantic--fetch-candidates helm-semantic--tags-cache 0))))) + (get-line :initform 'buffer-substring) + (persistent-help :initform "Show this entry") + (keymap :initform 'helm-semantic-map) + (help-message :initform 'helm-semantic-help-message) + (persistent-action :initform (lambda (elm) + (helm-semantic-default-action elm t) + (helm-highlight-current-line))) + (action :initform 'helm-semantic-default-action))) + +(defcustom helm-semantic-fuzzy-match nil + "Enable fuzzy matching in `helm-source-semantic'." + :group 'helm-semantic + :type 'boolean + :set (lambda (var val) + (set var val) + (setq helm-source-semantic + (helm-make-source "Semantic Tags" 'helm-semantic-source + :fuzzy-match helm-semantic-fuzzy-match)))) + +;;;###autoload +(defun helm-semantic (arg) + "Preconfigured `helm' for `semantic'. +If ARG is supplied, pre-select symbol at point instead of current." + (interactive "P") + (let ((tag (helm-aif (car (semantic-current-tag-parent)) + (let ((curtag (car (semantic-current-tag)))) + (if (string= it curtag) + (format "\\_<%s\\_>" curtag) + (cons (format "\\_<%s\\_>" it) + (format "\\_<%s\\_>" curtag)))) + (format "\\_<%s\\_>" (car (semantic-current-tag))))) + (helm-highlight-matches-around-point-max-lines 'never)) + (unless helm-source-semantic + (setq helm-source-semantic + (helm-make-source "Semantic Tags" 'helm-semantic-source + :fuzzy-match helm-semantic-fuzzy-match))) + (helm :sources 'helm-source-semantic + :candidate-number-limit 9999 + :preselect (if arg + (thing-at-point 'symbol) + tag) + :buffer "*helm semantic*"))) + +;;;###autoload +(defun helm-semantic-or-imenu (arg) + "Preconfigured helm for `semantic' or `imenu'. +If ARG is supplied, pre-select symbol at point instead of current +semantic tag in scope. + +If `semantic-mode' is active in the current buffer, then use +semantic for generating tags, otherwise fall back to `imenu'. +Fill in the symbol at point by default." + (interactive "P") + (unless helm-source-semantic + (setq helm-source-semantic + (helm-make-source "Semantic Tags" 'helm-semantic-source + :fuzzy-match helm-semantic-fuzzy-match))) + (unless helm-source-imenu + (setq helm-source-imenu + (helm-make-source "Imenu" 'helm-imenu-source + :fuzzy-match helm-imenu-fuzzy-match))) + (let* ((source (if (semantic-active-p) + 'helm-source-semantic + 'helm-source-imenu)) + (helm-highlight-matches-around-point-max-lines 'never) + (imenu-p (eq source 'helm-source-imenu)) + (imenu-auto-rescan imenu-p) + (str (thing-at-point 'symbol)) + (helm-execute-action-at-once-if-one + (and imenu-p + helm-imenu-execute-action-at-once-if-one)) + (tag (helm-aif (car (semantic-current-tag-parent)) + (let ((curtag (car (semantic-current-tag)))) + (if (string= it curtag) + (format "\\_<%s\\_>" curtag) + (cons (format "\\_<%s\\_>" it) + (format "\\_<%s\\_>" curtag)))) + (format "\\_<%s\\_>" (car (semantic-current-tag)))))) + (helm :sources source + :candidate-number-limit 9999 + :default (and imenu-p (list (concat "\\_<" (and str (regexp-quote str)) "\\_>") str)) + :preselect (if (or arg imenu-p) str tag) + :buffer "*helm semantic/imenu*"))) + +(provide 'helm-semantic) + +;;; helm-semantic.el ends here diff --git a/code/elpa/helm-20220822.659/helm-shell.el b/code/elpa/helm-20220822.659/helm-shell.el new file mode 100644 index 0000000..ce70751 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-shell.el @@ -0,0 +1,38 @@ +;;; helm-shell.el --- Shell prompt navigation for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2020 Pierre Neidhardt + +;; 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 . + +;;; Commentary: +;; +;; This is superseded by helm-comint.el. + +;;; Code: +(require 'cl-lib) +(require 'helm) +(require 'helm-lib) +(require 'helm-help) +(require 'helm-elisp) +(require 'helm-comint) + +;;;###autoload +(defalias 'helm-shell-prompts 'helm-comint-prompts) + +;;;###autoload +(defalias 'helm-shell-prompts-all 'helm-comint-prompts-all) + +(provide 'helm-shell) + +;;; helm-shell ends here diff --git a/code/elpa/helm-20220822.659/helm-sys.el b/code/elpa/helm-20220822.659/helm-sys.el new file mode 100644 index 0000000..57b1c8d --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-sys.el @@ -0,0 +1,472 @@ +;;; helm-sys.el --- System related functions for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-utils) + + +(defgroup helm-sys nil + "System related helm library." + :group 'helm) + +(defface helm-top-columns + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit helm-header)) + "Face for helm help string in minibuffer." + :group 'helm-sys) + + +(defcustom helm-top-command + (cl-case system-type + (darwin "env COLUMNS=%s ps -axo pid,user,pri,nice,ucomm,tty,start_time,vsz,%%cpu,%%mem,etime,command") + (t "env COLUMNS=%s top -b -n 1")) + "Top command used to display output of top. +A format string where %s will be replaced with `frame-width'. + +To use 'top' command, a version supporting batch mode (-b option) +is needed. On Mac OSX 'top' command doesn't support this, so the +'ps' command is used instead by default. + +Normally 'top' command output have 12 columns, but in some +versions you may have less than this, so you can either customize +'top' to use 12 columns with the interactives 'f' and 'W' commands +of 'top', or modify `helm-top-sort-columns-alist' to fit with the +number of columns your 'top' command is using. + +If you modify 'ps' command be sure that 'pid' comes in first and +\"env COLUMNS=%s\" is specified at beginning of command. Ensure +also that no elements contain spaces (e.g., use start_time and +not start). Same as for 'top': you can customize +`helm-top-sort-columns-alist' to make sort commands working +properly according to your settings." + :group 'helm-sys + :type 'string) + +(defcustom helm-top-sort-columns-alist '((com . 11) + (mem . 9) + (cpu . 8) + (user . 1)) + "Allow defining which column to use when sorting output of top/ps command. +Only com, mem, cpu and user are sorted, so no need to put something +else there,it will have no effect. +Note that column numbers are counted from zero, i.e. column 1 is the +nth 0 column." + :group 'helm-sys + :type '(alist :key-type symbol :value-type (integer :tag "Column number"))) + +(defcustom helm-top-poll-delay 1.5 + "Helm top poll after this delay when `helm-top-poll-mode' is enabled. +The minimal delay allowed is 1.5, if less than this helm-top will use 1.5." + :group 'helm-sys + :type 'float) + +(defcustom helm-top-poll-delay-post-command 1.0 + "Helm top stop polling during this delay. +This delay is added to `helm-top-poll-delay' after Emacs stops +being idle." + :group 'helm-sys + :type 'float) + +(defcustom helm-top-poll-preselection 'linum + "Stay on same line or follow candidate when `helm-top-poll' updates display. +Possible values are \\='candidate or \\='linum. +This affects also sorting functions in the same way." + :group'helm-sys + :type '(radio :tag "Preferred preselection action for helm-top" + (const :tag "Follow candidate" candidate) + (const :tag "Stay on same line" linum))) + +;;; Top (process) +;; +;; +(defvar helm-top-sort-fn nil) +(defvar helm-top-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-P") 'helm-top-run-sort-by-cpu) + (define-key map (kbd "M-C") 'helm-top-run-sort-by-com) + (define-key map (kbd "M-M") 'helm-top-run-sort-by-mem) + (define-key map (kbd "M-U") 'helm-top-run-sort-by-user) + map)) + +(defvar helm-top-after-init-hook nil + "Local hook for helm-top.") + +(defvar helm-top--poll-timer nil) + +(defun helm-top-poll (&optional no-update delay) + (when helm-top--poll-timer + (cancel-timer helm-top--poll-timer)) + (condition-case nil + (progn + (when (and (helm--alive-p) (null no-update)) + ;; Fix quitting while process is running + ;; by binding `with-local-quit' in init function + ;; Bug#1521. + (helm-force-update + (cl-ecase helm-top-poll-preselection + (candidate (replace-regexp-in-string + "[0-9]+" "[0-9]+" + (regexp-quote (helm-get-selection nil t)))) + (linum `(lambda () + (goto-char (point-min)) + (forward-line ,(helm-candidate-number-at-point))))))) + (setq helm-top--poll-timer + (run-with-idle-timer + (helm-aif (current-idle-time) + (time-add it (seconds-to-time + (or delay (helm-top--poll-delay)))) + (or delay (helm-top--poll-delay))) + nil + 'helm-top-poll))) + (quit (cancel-timer helm-top--poll-timer)))) + +(defun helm-top--poll-delay () + (max 1.5 helm-top-poll-delay)) + +(defun helm-top-poll-no-update () + (helm-top-poll t (+ (helm-top--poll-delay) + helm-top-poll-delay-post-command))) + +(defun helm-top-initialize-poll-hooks () + ;; When Emacs is idle during say 20s + ;; the idle timer will run in 20+1.5 s. + ;; This is fine when Emacs stays idle, because the next timer + ;; will run at 21.5+1.5 etc... so the display will be updated + ;; at every 1.5 seconds. + ;; But as soon as emacs looses its idleness, the next update + ;; will occur at say 21+1.5 s, so we have to reinitialize + ;; the timer at 0+1.5. + (add-hook 'post-command-hook 'helm-top-poll-no-update) + (add-hook 'focus-in-hook 'helm-top-poll-no-update)) + +;;;###autoload +(define-minor-mode helm-top-poll-mode + "Refresh automatically helm top buffer once enabled." + :group 'helm-top + :global t + (if helm-top-poll-mode + (progn + (add-hook 'helm-top-after-init-hook 'helm-top-poll-no-update) + (add-hook 'helm-top-after-init-hook 'helm-top-initialize-poll-hooks)) + (remove-hook 'helm-top-after-init-hook 'helm-top-poll-no-update) + (remove-hook 'helm-top-after-init-hook 'helm-top-initialize-poll-hooks))) + +(defvar helm-source-top + (helm-build-in-buffer-source "Top" + :header-name (lambda (name) + (concat name (if helm-top-poll-mode + " (auto updating)" + " (Press C-c C-u to refresh)"))) + :init #'helm-top-init + :after-init-hook 'helm-top-after-init-hook + :cleanup (lambda () + (when helm-top--poll-timer + (cancel-timer helm-top--poll-timer)) + (remove-hook 'post-command-hook 'helm-top-poll-no-update) + (remove-hook 'focus-in-hook 'helm-top-poll-no-update)) + :display-to-real #'helm-top-display-to-real + :persistent-action '(helm-top-sh-persistent-action . never-split) + :persistent-help "SIGTERM" + :help-message 'helm-top-help-message + :mode-line 'helm-top-mode-line + :follow 'never + :keymap helm-top-map + :filtered-candidate-transformer #'helm-top-sort-transformer + :action-transformer #'helm-top-action-transformer + :group 'helm-sys)) + +(defvar helm-top--line nil) +(defun helm-top-transformer (candidates _source) + "Transformer for `helm-top'. +Return empty string for non--valid candidates." + (cl-loop for disp in candidates collect + (cond ((string-match "^ *[0-9]+" disp) disp) + ((string-match "^ *PID" disp) + (setq helm-top--line (cons (propertize disp 'face 'helm-top-columns) ""))) + (t (cons disp ""))) + into lst + finally return (or (member helm-top--line lst) + (cons helm-top--line lst)))) + +(defun helm-top--skip-top-line () + (let* ((src (helm-get-current-source)) + (src-name (assoc-default 'name src))) + (helm-aif (and (stringp src-name) + (string= src-name "Top") + (helm-get-selection nil t src)) + (when (string-match-p "^ *PID" it) + (helm-next-line))))) + +(defun helm-top-action-transformer (actions _candidate) + "Action transformer for `top'. +Show actions only on line starting by a PID." + (let ((disp (helm-get-selection nil t))) + (cond ((string-match "\\` *[0-9]+" disp) + (list '("kill (SIGTERM)" . (lambda (_pid) + (helm-top-sh "TERM" (helm-top--marked-pids)))) + '("kill (SIGKILL)" . (lambda (_pid) + (helm-top-sh "KILL" (helm-top--marked-pids)))) + '("kill (SIGINT)" . (lambda (_pid) + (helm-top-sh "INT" (helm-top--marked-pids)))) + '("kill (Choose signal)" + . (lambda (_pid) + (let ((pids (helm-top--marked-pids))) + (helm-top-sh + (helm-comp-read (format "Kill %d pids with signal: " + (length pids)) + '("ALRM" "HUP" "INT" "KILL" "PIPE" "POLL" + "PROF" "TERM" "USR1" "USR2" "VTALRM" + "STKFLT" "PWR" "WINCH" "CHLD" "URG" + "TSTP" "TTIN" "TTOU" "STOP" "CONT" + "ABRT" "FPE" "ILL" "QUIT" "SEGV" + "TRAP" "SYS" "EMT" "BUS" "XCPU" "XFSZ") + :must-match t) + pids)))))) + (t actions)))) + +(defun helm-top--marked-pids () + (helm-remove-if-not-match "\\`[0-9]+\\'" (helm-marked-candidates))) + +(defun helm-top-sh (sig pids) + "Run kill shell command with signal SIG on PIDS for `helm-top'." + (message "kill -%s %s exited with status %s" + sig (mapconcat 'identity pids " ") + (apply #'call-process + "kill" nil nil nil (format "-%s" sig) pids))) + +(defun helm-top-sh-persistent-action (pid) + (helm-top-sh "TERM" (list pid)) + (helm-delete-current-selection)) + +(defun helm-top-init () + "Insert output of top command in candidate buffer." + (with-local-quit + (unless helm-top-sort-fn (helm-top-set-mode-line "CPU")) + (with-current-buffer (helm-candidate-buffer 'global) + (call-process-shell-command + (format helm-top-command (frame-width)) + nil (current-buffer))))) + +(defun helm-top-display-to-real (line) + "Return pid only from LINE." + (car (split-string line))) + +;; Sort top command + +(defun helm-top-set-mode-line (str) + (if (string-match "Sort:\\[\\(.*\\)\\] " helm-top-mode-line) + (setq helm-top-mode-line (replace-match str nil nil helm-top-mode-line 1)) + (setq helm-top-mode-line (concat (format "Sort:[%s] " str) helm-top-mode-line)))) + +(defun helm-top-sort-transformer (candidates source) + (helm-top-transformer + (if helm-top-sort-fn + (cl-loop for c in candidates + if (string-match "^ *[0-9]+" c) + collect c into pid-cands + else collect c into header-cands + finally return (append + header-cands + (sort pid-cands helm-top-sort-fn))) + candidates) + source)) + +(defun helm-top-sort-by-com (s1 s2) + (let* ((split-1 (split-string s1)) + (split-2 (split-string s2)) + (col (cdr (assq 'com helm-top-sort-columns-alist))) + (com-1 (nth col split-1)) + (com-2 (nth col split-2))) + (string< com-1 com-2))) + +(defun helm-top-sort-by-mem (s1 s2) + (let* ((split-1 (split-string s1)) + (split-2 (split-string s2)) + (col (cdr (assq 'mem helm-top-sort-columns-alist))) + (mem-1 (string-to-number (nth col split-1))) + (mem-2 (string-to-number (nth col split-2)))) + (> mem-1 mem-2))) + +(defun helm-top-sort-by-cpu (s1 s2) + (let* ((split-1 (split-string s1)) + (split-2 (split-string s2)) + (col (cdr (assq 'cpu helm-top-sort-columns-alist))) + (cpu-1 (string-to-number (nth col split-1))) + (cpu-2 (string-to-number (nth col split-2)))) + (> cpu-1 cpu-2))) + +(defun helm-top-sort-by-user (s1 s2) + (let* ((split-1 (split-string s1)) + (split-2 (split-string s2)) + (col (cdr (assq 'user helm-top-sort-columns-alist))) + (user-1 (nth col split-1)) + (user-2 (nth col split-2))) + (string< user-1 user-2))) + +(defun helm-top--preselect-fn () + (if (eq helm-top-poll-preselection 'linum) + `(lambda () + (goto-char (point-min)) + (forward-line ,(helm-candidate-number-at-point))) + (replace-regexp-in-string + "[0-9]+" "[0-9]+" + (regexp-quote (helm-get-selection nil t))))) + +(defun helm-top-run-sort-by-com () + (interactive) + (helm-top-set-mode-line "COM") + (setq helm-top-sort-fn 'helm-top-sort-by-com) + (helm-update (helm-top--preselect-fn))) + +(defun helm-top-run-sort-by-cpu () + (interactive) + (helm-top-set-mode-line "CPU") + ;; Force sorting by CPU even if some versions of top are using by + ;; default CPU sorting (Bug#1908). + (setq helm-top-sort-fn 'helm-top-sort-by-cpu) + (helm-update (helm-top--preselect-fn))) + +(defun helm-top-run-sort-by-mem () + (interactive) + (helm-top-set-mode-line "MEM") + (setq helm-top-sort-fn 'helm-top-sort-by-mem) + (helm-update (helm-top--preselect-fn))) + +(defun helm-top-run-sort-by-user () + (interactive) + (helm-top-set-mode-line "USER") + (setq helm-top-sort-fn 'helm-top-sort-by-user) + (helm-update (helm-top--preselect-fn))) + + +;;; X RandR resolution change +;; +;; +;;; FIXME I do not care multi-display. + +(defun helm-xrandr-info () + "Return a pair with current X screen number and current X display name." + (with-temp-buffer + (call-process "xrandr" nil (current-buffer) nil + "--current") + (let (screen output) + (goto-char (point-min)) + (save-excursion + (when (re-search-forward "\\(^Screen \\)\\([0-9]\\):" nil t) + (setq screen (match-string 2)))) + (when (re-search-forward "^\\(.*\\) connected" nil t) + (setq output (match-string 1))) + (list screen output)))) + +(defun helm-xrandr-screen () + "Return current X screen number." + (car (helm-xrandr-info))) + +(defun helm-xrandr-output () + "Return current X display name." + (cadr (helm-xrandr-info))) + +(defvar helm-source-xrandr-change-resolution + (helm-build-sync-source "Change Resolution" + :candidates + (lambda () + (with-temp-buffer + (call-process "xrandr" nil (current-buffer) nil + "--screen" (helm-xrandr-screen) "-q") + (goto-char 1) + (cl-loop while (re-search-forward " \\([0-9]+x[0-9]+\\)" nil t) + for mode = (match-string 1) + unless (member mode modes) + collect mode into modes + finally return modes))) + :action + (helm-make-actions "Change Resolution" + (lambda (mode) + (call-process "xrandr" nil nil nil + "--screen" (helm-xrandr-screen) + "--output" (helm-xrandr-output) + "--mode" mode))))) + + +;;; Emacs process +;; +;; +(defvar helm-source-emacs-process + (helm-build-sync-source "Emacs Process" + :init (lambda () + (let (tabulated-list-use-header-line) + (list-processes--refresh))) + :candidates (lambda () (mapcar #'process-name (process-list))) + :candidate-transformer + (lambda (candidates) + (cl-loop for c in candidates + for command = (mapconcat + 'identity + (process-command (get-process c)) " ") + if (and command (not (string= command ""))) collect + (cons (concat c " --> " + (mapconcat 'identity + (process-command (get-process c)) " ")) + c) + else collect c)) + :multiline t + :persistent-action (lambda (elm) + (delete-process (get-process elm)) + (helm-delete-current-selection)) + :persistent-help "Kill Process" + :action (helm-make-actions "Kill Process" + (lambda (_elm) + (cl-loop for p in (helm-marked-candidates) + do (delete-process (get-process p))))))) + + +;;;###autoload +(defun helm-top () + "Preconfigured `helm' for top command." + (interactive) + (add-hook 'helm-after-update-hook 'helm-top--skip-top-line) + (unwind-protect + (helm :sources 'helm-source-top + :buffer "*helm top*" :full-frame t + :candidate-number-limit 9999 + :preselect "^\\s-*[0-9]+" + :truncate-lines helm-show-action-window-other-window) + (remove-hook 'helm-after-update-hook 'helm-top--skip-top-line))) + +;;;###autoload +(defun helm-list-emacs-process () + "Preconfigured `helm' for Emacs process." + (interactive) + (helm :sources 'helm-source-emacs-process + :truncate-lines t + :buffer "*helm process*")) + +;;;###autoload +(defun helm-xrandr-set () + "Preconfigured helm for xrandr." + (interactive) + (helm :sources 'helm-source-xrandr-change-resolution + :buffer "*helm xrandr*")) + +(provide 'helm-sys) + +;;; helm-sys.el ends here diff --git a/code/elpa/helm-20220822.659/helm-tags.el b/code/elpa/helm-20220822.659/helm-tags.el new file mode 100644 index 0000000..6b29f83 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-tags.el @@ -0,0 +1,342 @@ +;;; helm-tags.el --- Helm for Etags. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) +(require 'helm-utils) +(require 'helm-grep) + +(defvar helm-etags-fuzzy-match) +(declare-function xref-push-marker-stack "xref") + + +(defgroup helm-tags nil + "Tags related Applications and libraries for Helm." + :group 'helm) + +(defcustom helm-etags-tag-file-name "TAGS" + "Etags tag file name." + :type 'string) + +(defcustom helm-etags-tag-file-search-limit 10 + "The limit level of directory to search tag file. +Don't search tag file deeply if outside this value." + :type 'number) + +(defcustom helm-etags-match-part-only 'tag + "Allow choosing the tag part of CANDIDATE in `helm-source-etags-select'. +A tag looks like this: + filename: (defun foo +You can choose matching against the tag part (i.e \"(defun foo\"), +or against the whole candidate (i.e \"(filename:5:(defun foo\")." + :type '(choice + (const :tag "Match only tag" tag) + (const :tag "Match all file+tag" all))) + +(defcustom helm-etags-execute-action-at-once-if-one t + "Whether to jump straight to the selected tag if there's only +one match." + :type 'boolean) + + +(defgroup helm-tags-faces nil + "Customize the appearance of helm-tags faces." + :prefix "helm-" + :group 'helm-tags + :group 'helm-faces) + +(defface helm-etags-file + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "Lightgoldenrod4" + :underline t)) + "Face used to highlight etags filenames." + :group 'helm-tags-faces) + + +;;; Etags +;; +;; +(defun helm-etags-run-switch-other-window () + "Run switch to other window action from `helm-source-etags-select'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action + (lambda (c) + (helm-etags-action-goto 'find-file-other-window c))))) +(put 'helm-etags-run-switch-other-window 'helm-only t) + +(defun helm-etags-run-switch-other-frame () + "Run switch to other frame action from `helm-source-etags-select'." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action + (lambda (c) + (helm-etags-action-goto 'find-file-other-frame c))))) +(put 'helm-etags-run-switch-other-frame 'helm-only t) + +(defvar helm-etags-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "M-") 'helm-goto-next-file) + (define-key map (kbd "M-") 'helm-goto-precedent-file) + (define-key map (kbd "C-c o") 'helm-etags-run-switch-other-window) + (define-key map (kbd "C-c C-o") 'helm-etags-run-switch-other-frame) + map) + "Keymap used in Etags.") + +(defvar helm-etags-mtime-alist nil + "Store the last modification time of etags files here.") +(defvar helm-etags-cache (make-hash-table :test 'equal) + "Cache content of etags files used here for faster access.") + +(defun helm-etags-get-tag-file (&optional directory) + "Return the path of etags file if found in DIRECTORY. +Look recursively in parents directorys for a +`helm-etags-tag-file-name' file." + ;; Get tag file from `default-directory' or upper directory. + (let ((current-dir (helm-etags-find-tag-file-directory + (or directory default-directory)))) + ;; Return nil if not find tag file. + (when current-dir + (expand-file-name helm-etags-tag-file-name current-dir)))) + +(defun helm-etags-all-tag-files () + "Find Etags files. +Return files from the following sources: + 1) An automatically located file in the parent directories, + by `helm-etags-get-tag-file'. + 2) `tags-file-name', which is commonly set by `find-tag' command. + 3) `tags-table-list' which is commonly set by `visit-tags-table' command." + (helm-fast-remove-dups + (delq nil + (append (list (helm-etags-get-tag-file) + tags-file-name) + tags-table-list)) + :test 'equal)) + +(defun helm-etags-find-tag-file-directory (current-dir) + "Try to find the directory containing tag file. +If not found in CURRENT-DIR search in upper directory." + (let ((file-exists? (lambda (dir) + (let ((tag-path (expand-file-name + helm-etags-tag-file-name dir))) + (and (stringp tag-path) + (file-regular-p tag-path) + (file-readable-p tag-path)))))) + (cl-loop with count = 0 + until (funcall file-exists? current-dir) + ;; Return nil if outside the value of + ;; `helm-etags-tag-file-search-limit'. + if (= count helm-etags-tag-file-search-limit) + do (cl-return nil) + ;; Or search upper directories. + else + do (cl-incf count) + (setq current-dir (expand-file-name (concat current-dir "../"))) + finally return current-dir))) + +(defun helm-etags-get-header-name (_x) + "Create header name for this helm etags session." + (concat "Etags in " + (with-helm-current-buffer + (helm-etags-get-tag-file)))) + +(defun helm-etags-create-buffer (file) + "Create the `helm-buffer' based on contents of etags tag FILE." + (let* (max + (split (with-temp-buffer + (insert-file-contents file) + (prog1 + (split-string (buffer-string) "\n" 'omit-nulls) + (setq max (line-number-at-pos (point-max)))))) + (progress-reporter (make-progress-reporter "Loading tag file..." 0 max))) + (cl-loop + with fname + with cand + for i in split for count from 0 + for elm = (unless (string-match "^\x0c" i) ;; "^L" + (helm-aif (string-match "\177" i) ;; "^?" + (substring i 0 it) + i)) + for linum = (when (string-match "[0-9]+,?[0-9]*$" i) + (car (split-string (match-string 0 i) ","))) + do (cond ((and elm (string-match "^\\([^,]+\\),[0-9]+$" elm)) + (setq fname (propertize (match-string 1 elm) + 'face 'helm-etags-file))) + (elm (setq cand (format "%s:%s:%s" fname linum elm))) + (t (setq cand nil))) + when cand do (progn + (insert (propertize (concat cand "\n") 'linum linum)) + (progress-reporter-update progress-reporter count))))) + +(defun helm-etags-init () + "Feed `helm-buffer' using `helm-etags-cache' or tag file. +If there is no entry in cache, create one." + (let ((tagfiles (helm-etags-all-tag-files))) + (when tagfiles + (with-current-buffer (helm-candidate-buffer 'global) + (dolist (f tagfiles) + (helm-aif (gethash f helm-etags-cache) + ;; An entry is present in cache, insert it. + (insert it) + ;; No entry, create a new buffer using content of tag file (slower). + (helm-etags-create-buffer f) + ;; Store content of buffer in cache. + (puthash f (buffer-string) helm-etags-cache) + ;; Store or set the last modification of tag file. + (helm-aif (assoc f helm-etags-mtime-alist) + ;; If an entry exists modify it. + (setcdr it (helm-etags-mtime f)) + ;; No entry create a new one. + (cl-pushnew (cons f (helm-etags-mtime f)) + helm-etags-mtime-alist + :test 'equal)))))))) + +(defvar helm-source-etags-select nil + "Helm source for Etags.") + +(defun helm-etags-build-source () + (helm-build-in-buffer-source "Etags" + :header-name 'helm-etags-get-header-name + :init 'helm-etags-init + :get-line 'buffer-substring + :match-part (lambda (candidate) + ;; Match only the tag part of CANDIDATE + ;; and not the filename. + (cl-case helm-etags-match-part-only + (tag (cl-caddr (helm-grep-split-line candidate))) + (t candidate))) + :fuzzy-match helm-etags-fuzzy-match + :help-message 'helm-etags-help-message + :keymap helm-etags-map + :action '(("Go to tag" . (lambda (c) + (helm-etags-action-goto 'find-file c))) + ("Go to tag in other window" . (lambda (c) + (helm-etags-action-goto + 'find-file-other-window + c))) + ("Go to tag in other frame" . (lambda (c) + (helm-etags-action-goto + 'find-file-other-frame + c)))) + :group 'helm-tags + :persistent-help "Go to line" + :persistent-action (lambda (candidate) + (helm-etags-action-goto 'find-file candidate) + (helm-highlight-current-line)))) + +(defcustom helm-etags-fuzzy-match nil + "Use fuzzy matching in `helm-etags-select'." + :group 'helm-tags + :type 'boolean + :set (lambda (var val) + (set var val) + (setq helm-source-etags-select + (helm-etags-build-source)))) + +(defsubst helm-etags--file-from-tag (fname) + (cl-loop for ext in + (cons "" (remove "" tags-compression-info-list)) + for file = (concat fname ext) + when (file-exists-p file) + return file)) + +(defun helm-etags-action-goto (switcher candidate) + "Helm default action to jump to an etags entry in other window." + (require 'etags) + (deactivate-mark t) + (helm-log-run-hook 'helm-goto-line-before-hook) + (let* ((split (helm-grep-split-line candidate)) + (fname (cl-loop for tagf being the hash-keys of helm-etags-cache + for f = (expand-file-name + (car split) (file-name-directory tagf)) + ;; Try to find an existing file, possibly compressed. + when (helm-etags--file-from-tag f) + return it)) + (elm (cl-caddr split)) + (linum (string-to-number (cadr split)))) + (if (null fname) + (error "file %s not found" fname) + (xref-push-marker-stack) + (funcall switcher fname) + (helm-goto-line linum t) + (when (search-forward elm nil t) + (goto-char (match-beginning 0)))))) + +(defun helm-etags-mtime (file) + "Last modification time of etags tag FILE." + (cadr (nth 5 (file-attributes file)))) + +(defun helm-etags-file-modified-p (file) + "Check if tag FILE have been modified in this session. +If FILE is nil return nil." + (let ((last-modif (and file + (assoc-default file helm-etags-mtime-alist)))) + (and last-modif + (/= last-modif (helm-etags-mtime file))))) + +;;;###autoload +(defun helm-etags-select (reinit) + "Preconfigured helm for etags. +If called with a prefix argument REINIT +or if any of the tag files have been modified, reinitialize cache. + +This function aggregates three sources of tag files: + + 1) An automatically located file in the parent directories, + by `helm-etags-get-tag-file'. + 2) `tags-file-name', which is commonly set by `find-tag' command. + 3) `tags-table-list' which is commonly set by `visit-tags-table' command." + (interactive "P") + (let ((tag-files (helm-etags-all-tag-files)) + (helm-execute-action-at-once-if-one + helm-etags-execute-action-at-once-if-one) + (str (if (region-active-p) + (buffer-substring-no-properties + (region-beginning) (region-end)) + (thing-at-point 'symbol)))) + (if (cl-notany 'file-exists-p tag-files) + (message "Error: No tag file found.\ +Create with etags shell command, or visit with `find-tag' or `visit-tags-table'.") + (cl-loop for k being the hash-keys of helm-etags-cache + unless (member k tag-files) + do (remhash k helm-etags-cache)) + (mapc (lambda (f) + (when (or (equal reinit '(4)) + (and helm-etags-mtime-alist + (helm-etags-file-modified-p f))) + (remhash f helm-etags-cache))) + tag-files) + (unless helm-source-etags-select + (setq helm-source-etags-select + (helm-etags-build-source))) + (helm :sources 'helm-source-etags-select + :keymap helm-etags-map + :default (and (stringp str) + (if (or helm-etags-fuzzy-match + (and (eq major-mode 'haskell-mode) + (string-match "[']\\'" str))) + str + (list (concat "\\_<" str "\\_>") str))) + :buffer "*helm etags*")))) + +(provide 'helm-tags) + +;;; helm-tags.el ends here diff --git a/code/elpa/helm-20220822.659/helm-types.el b/code/elpa/helm-20220822.659/helm-types.el new file mode 100644 index 0000000..d59e031 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-types.el @@ -0,0 +1,336 @@ +;;; helm-types.el --- Helm types classes and methods. -*- lexical-binding: t -*- + +;; Copyright (C) 2015 ~ 2020 Thierry Volpiatto + +;; Author: Thierry Volpiatto +;; URL: http://github.com/emacs-helm/helm + +;; 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 . + + +;;; Code: + +(require 'cl-lib) +(require 'eieio) +(eval-when-compile (require 'helm-source)) + +(defvar helm-map) +(defvar helm-mode-line-string) +(defvar helm-bookmark-map) +(declare-function helm-make-actions "helm-lib") +(declare-function helm-ediff-marked-buffers "helm-buffers") +(declare-function helm-make-type "helm-source") + + +;; Files +(defclass helm-type-file (helm-source) () + "A class to define helm type file.") + +(cl-defmethod helm-source-get-action-from-type ((object helm-type-file)) + (slot-value object 'action)) + +(defun helm-actions-from-type-file () + (let ((source (make-instance 'helm-type-file))) + (helm--setup-source source) + (helm-source-get-action-from-type source))) + +(defvar helm-generic-files-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-]") 'helm-ff-run-toggle-basename) + (define-key map (kbd "C-s") 'helm-ff-run-grep) + (define-key map (kbd "M-g s") 'helm-ff-run-grep) + (define-key map (kbd "M-g z") 'helm-ff-run-zgrep) + (define-key map (kbd "M-g p") 'helm-ff-run-pdfgrep) + (define-key map (kbd "M-R") 'helm-ff-run-rename-file) + (define-key map (kbd "M-C") 'helm-ff-run-copy-file) + (define-key map (kbd "M-B") 'helm-ff-run-byte-compile-file) + (define-key map (kbd "M-L") 'helm-ff-run-load-file) + (define-key map (kbd "M-S") 'helm-ff-run-symlink-file) + (define-key map (kbd "M-H") 'helm-ff-run-hardlink-file) + (define-key map (kbd "M-D") 'helm-ff-run-delete-file) + (define-key map (kbd "C-=") 'helm-ff-run-ediff-file) + (define-key map (kbd "C-c =") 'helm-ff-run-ediff-merge-file) + (define-key map (kbd "C-c o") 'helm-ff-run-switch-other-window) + (define-key map (kbd "C-c r") 'helm-ff-run-find-file-as-root) + (define-key map (kbd "C-c C-o") 'helm-ff-run-switch-other-frame) + (define-key map (kbd "M-i") 'helm-ff-properties-persistent) + (define-key map (kbd "C-c C-x") 'helm-ff-run-open-file-externally) + (define-key map (kbd "C-c X") 'helm-ff-run-open-file-with-default-tool) + (define-key map (kbd "C-c @") 'helm-ff-run-insert-org-link) + (define-key map (kbd "C-x C-q") 'helm-ff-run-marked-files-in-dired) + (define-key map (kbd "C-c C-a") 'helm-ff-run-mail-attach-files) + map) + "Generic Keymap for files.") + +(defcustom helm-type-file-actions + (helm-make-actions + "Find file" 'helm-find-file-or-marked + "Find file as root" 'helm-find-file-as-root + "Find file other window" 'helm-find-files-other-window + "Find file other frame" 'find-file-other-frame + "Open dired in file's directory" 'helm-open-dired + "Attach file(s) to mail buffer `C-c C-a'" 'helm-ff-mail-attach-files + "Marked files in dired" 'helm-marked-files-in-dired + "Grep File(s) `C-u recurse'" 'helm-find-files-grep + "Zgrep File(s) `C-u Recurse'" 'helm-ff-zgrep + "Pdfgrep File(s)" 'helm-ff-pdfgrep + "Insert as org link" 'helm-files-insert-as-org-link + "Checksum File" 'helm-ff-checksum + "Ediff File" 'helm-find-files-ediff-files + "Ediff Merge File" 'helm-find-files-ediff-merge-files + "View file" 'view-file + "Insert file" 'insert-file + "Add marked files to file-cache" 'helm-ff-cache-add-file + "Delete file(s)" 'helm-ff-delete-files + "Copy file(s) `M-C, C-u to follow'" 'helm-find-files-copy + "Rename file(s) `M-R, C-u to follow'" 'helm-find-files-rename + "Symlink files(s) `M-S, C-u to follow'" 'helm-find-files-symlink + "Relsymlink file(s) `C-u to follow'" 'helm-find-files-relsymlink + "Hardlink file(s) `M-H, C-u to follow'" 'helm-find-files-hardlink + "Open file externally (C-u to choose)" 'helm-open-file-externally + "Open file with default tool" 'helm-open-file-with-default-tool + "Find file in hex dump" 'hexl-find-file) + "Default actions for type files." + :group 'helm-files + :type '(alist :key-type string :value-type function)) + +(cl-defmethod helm--setup-source ((_source helm-type-file))) + +(cl-defmethod helm--setup-source :before ((source helm-type-file)) + (setf (slot-value source 'action) 'helm-type-file-actions) + (setf (slot-value source 'persistent-help) "Show this file") + (setf (slot-value source 'action-transformer) + '(helm-transform-file-load-el + helm-transform-file-browse-url + helm-transform-file-cache)) + (setf (slot-value source 'candidate-transformer) + '(helm-skip-boring-files + helm-w32-pathname-transformer)) + (setf (slot-value source 'filtered-candidate-transformer) + 'helm-highlight-files) + (setf (slot-value source 'help-message) 'helm-generic-file-help-message) + (setf (slot-value source 'mode-line) (list "File(s)" helm-mode-line-string)) + (setf (slot-value source 'keymap) helm-generic-files-map) + (setf (slot-value source 'group) 'helm-files)) + + +;; Bookmarks +(defclass helm-type-bookmark (helm-source) () + "A class to define type bookmarks.") + +(defcustom helm-type-bookmark-actions + (helm-make-actions + "Jump to bookmark" 'helm-bookmark-jump + "Jump to BM other window" 'helm-bookmark-jump-other-window + "Jump to BM other frame" 'helm-bookmark-jump-other-frame + "Bookmark edit annotation" 'bookmark-edit-annotation + "Bookmark show annotation" 'bookmark-show-annotation + "Delete bookmark(s)" 'helm-delete-marked-bookmarks + "Edit Bookmark" 'helm-bookmark-edit-bookmark + "Rename bookmark" 'helm-bookmark-rename + "Relocate bookmark" 'bookmark-relocate) + "Default actions for type bookmarks." + :group 'helm-bookmark + :type '(alist :key-type string + :value-type function)) + +(cl-defmethod helm-source-get-action-from-type ((object helm-type-bookmark)) + (slot-value object 'action)) + +(cl-defmethod helm--setup-source ((_source helm-type-bookmark))) + +(cl-defmethod helm--setup-source :before ((source helm-type-bookmark)) + (setf (slot-value source 'action) 'helm-type-bookmark-actions) + (setf (slot-value source 'keymap) helm-bookmark-map) + (setf (slot-value source 'mode-line) (list "Bookmark(s)" helm-mode-line-string)) + (setf (slot-value source 'help-message) 'helm-bookmark-help-message) + (setf (slot-value source 'migemo) t) + (setf (slot-value source 'follow) 'never) + (setf (slot-value source 'group) 'helm-bookmark)) + + +;; Buffers +(defclass helm-type-buffer (helm-source) () + "A class to define type buffer.") + +(defcustom helm-type-buffer-actions + (helm-make-actions + "Switch to buffer(s)" 'helm-buffer-switch-buffers + "Switch to buffer(s) other window `C-c o'" + 'helm-buffer-switch-buffers-other-window + "Switch to buffer(s) other frame `C-c C-o'" + 'helm-buffer-switch-to-buffer-other-frame + "Raise buffer frame maybe" + 'helm-buffers-maybe-raise-buffer-frame + (lambda () (and (fboundp 'tab-bar-mode) + "Switch to buffer(s) other tab `C-c C-t'")) + 'helm-buffers-switch-to-buffer-other-tab + "Switch to buffer at line number" + 'helm-switch-to-buffer-at-linum + "Browse project `C-x C-d'" + 'helm-buffers-browse-project + "Switch to shell" + 'helm-buffer-switch-to-shell + "Query replace regexp `C-M-%'" + 'helm-buffer-query-replace-regexp + "Query replace `M-%'" 'helm-buffer-query-replace + "View buffer" 'view-buffer + "Display buffer" 'display-buffer + "Rename buffer `M-R'" 'helm-buffers-rename-buffer + "Grep buffer(s) `M-g s' (C-u grep all buffers)" + 'helm-zgrep-buffers + "Multi occur buffer(s) `C-s (C-u search also in current)'" + 'helm-multi-occur-as-action + "Revert buffer(s) `M-G'" 'helm-revert-marked-buffers + "Insert buffer" 'insert-buffer + "Kill buffer(s) `M-D'" 'helm-kill-marked-buffers + "Diff with file `C-='" 'diff-buffer-with-file + "Ediff Marked buffers `C-c ='" 'helm-ediff-marked-buffers + "Ediff Merge marked buffers `M-='" + (lambda (candidate) + (helm-ediff-marked-buffers candidate t))) + "Default actions for type buffers." + :group 'helm-buffers + :type '(alist :key-type string :value-type function)) + +(cl-defmethod helm-source-get-action-from-type ((object helm-type-buffer)) + (slot-value object 'action)) + +(cl-defmethod helm--setup-source ((_source helm-type-buffer))) + +(cl-defmethod helm--setup-source :before ((source helm-type-buffer)) + (setf (slot-value source 'action) 'helm-type-buffer-actions) + (setf (slot-value source 'persistent-help) "Show this buffer") + (setf (slot-value source 'mode-line) + ;; Use default-value of `helm-mode-line-string' in case user + ;; starts with a helm buffer as current-buffer otherwise the + ;; local value of this helm buffer is used (bug#1517, bug#2377). + (list "Buffer(s)" (default-value 'helm-mode-line-string))) + (setf (slot-value source 'filtered-candidate-transformer) + '(helm-skip-boring-buffers + helm-buffers-sort-transformer + helm-highlight-buffers)) + (setf (slot-value source 'group) 'helm-buffers)) + +;; Functions +(defclass helm-type-function (helm-source) () + "A class to define helm type function.") + +(defcustom helm-type-function-actions + (helm-make-actions + "Describe function" 'helm-describe-function + "Find function" 'helm-find-function + "Info lookup" 'helm-info-lookup-symbol + "Debug on entry" 'debug-on-entry + "Cancel debug on entry" 'cancel-debug-on-entry + "Trace function" 'trace-function + "Trace function (background)" 'trace-function-background + "Untrace function" 'untrace-function) + "Default actions for type functions." + :group 'helm-elisp + ;; Use symbol as value type because some functions may not be + ;; autoloaded (like untrace-function). + :type '(alist :key-type string :value-type symbol)) + +(cl-defmethod helm-source-get-action-from-type ((object helm-type-function)) + (slot-value object 'action)) + +(defun helm-actions-from-type-function () + (let ((source (make-instance 'helm-type-function))) + (helm--setup-source source) + (helm-source-get-action-from-type source))) + +(cl-defmethod helm--setup-source ((_source helm-type-function))) + +(cl-defmethod helm--setup-source :before ((source helm-type-function)) + (setf (slot-value source 'action) 'helm-type-function-actions) + (setf (slot-value source 'action-transformer) + 'helm-transform-function-call-interactively) + (setf (slot-value source 'candidate-transformer) + 'helm-mark-interactive-functions) + (setf (slot-value source 'coerce) 'helm-symbolify)) + + +;; Commands +(defclass helm-type-command (helm-source) () + "A class to define helm type command.") + +(defun helm-actions-from-type-command () + (let ((source (make-instance 'helm-type-command))) + (helm--setup-source source) + (helm-source-get-action-from-type source))) + +(defcustom helm-type-command-actions + (append (helm-make-actions + "Execute command" 'helm-M-x-execute-command) + (symbol-value + (helm-actions-from-type-function))) + "Default actions for type command." + :group 'helm-command + :type '(alist :key-type string :value-type symbol)) + +(cl-defmethod helm--setup-source ((_source helm-type-command))) + +(cl-defmethod helm--setup-source :before ((source helm-type-command)) + (setf (slot-value source 'action) 'helm-type-command-actions) + (setf (slot-value source 'coerce) 'helm-symbolify) + (setf (slot-value source 'persistent-action) 'helm-M-x-persistent-action) + (setf (slot-value source 'persistent-help) "Describe this command") + (setf (slot-value source 'group) 'helm-command)) + +;; Timers +(defclass helm-type-timers (helm-source) () + "A class to define helm type timers.") + +(defcustom helm-type-timers-actions + '(("Cancel Timer" . (lambda (_timer) + (let ((mkd (helm-marked-candidates))) + (cl-loop for timer in mkd + do (cancel-timer timer))))) + ("Describe Function" . (lambda (tm) + (describe-function (timer--function tm)))) + ("Find Function" . (lambda (tm) + (helm-aif (timer--function tm) + (if (or (byte-code-function-p it) + (helm-subr-native-elisp-p it)) + (message "Can't find anonymous function `%s'" it) + (find-function it)))))) + "Default actions for type timers." + :group 'helm-elisp + :type '(alist :key-type string :value-type function)) + +(cl-defmethod helm--setup-source ((_source helm-type-timers))) + +(cl-defmethod helm--setup-source :before ((source helm-type-timers)) + (setf (slot-value source 'action) 'helm-type-timers-actions) + (setf (slot-value source 'persistent-action) + (lambda (tm) + (describe-function (timer--function tm)))) + (setf (slot-value source 'persistent-help) "Describe Function") + (setf (slot-value source 'group) 'helm-elisp)) + +;; Builders. +(defun helm-build-type-file () + (helm-make-type 'helm-type-file)) + +(defun helm-build-type-function () + (helm-make-type 'helm-type-function)) + +(defun helm-build-type-command () + (helm-make-type 'helm-type-command)) + +(provide 'helm-types) + +;;; helm-types.el ends here diff --git a/code/elpa/helm-20220822.659/helm-utils.el b/code/elpa/helm-20220822.659/helm-utils.el new file mode 100644 index 0000000..bac0d39 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm-utils.el @@ -0,0 +1,1094 @@ +;;; helm-utils.el --- Utilities Functions for helm. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm) +(require 'helm-help) + +(declare-function helm-find-files-1 "helm-files" (fname &optional preselect)) +(declare-function helm-grep-split-line "helm-grep" (line)) +(declare-function popup-tip "ext:popup") +(declare-function markdown-show-entry "ext:markdown-mode.el") +(declare-function outline-show-subtree "outline") +(declare-function org-reveal "org") +(declare-function hs-show-block "hideshow.el") +(declare-function tab-bar-tabs "tab-bar") +(declare-function tab-bar-select-tab "tab-bar") +(declare-function dired-goto-file "dired") +(declare-function bookmark-get-filename "bookmark") +(declare-function package-installed-p "package") +(declare-function package-desc-dir "package") + +(defvar hs-minor-mode) +(defvar hs-show-hook) +(defvar org-directory) +(defvar winner-boring-buffers) +(defvar bookmark-alist) +(defvar dired-buffers) +(defvar helm-show-completion-overlay) +(defvar helm-buffers-maybe-switch-to-tab) +(defvar helm-ff-transformer-show-only-basename) +(defvar helm-popup-tip-mode) +(defvar helm-ff-last-expanded-candidate-regexp) + + +(defgroup helm-utils nil + "Utilities routines for Helm." + :group 'helm) + +(defcustom helm-su-or-sudo "sudo" + "What command to use for root access." + :type 'string + :group 'helm-utils) + +(defcustom helm-default-kbsize 1024.0 + "Default Kbsize to use for showing files size. +It is a float, usually 1024.0 but could be 1000.0 on some systems." + :group 'helm-utils + :type 'float) + +(define-obsolete-variable-alias + 'helm-highlight-number-lines-around-point + 'helm-highlight-matches-around-point-max-lines + "20160119") + +(defcustom helm-highlight-matches-around-point-max-lines '(15 . 15) + "Number of lines around point where matched items are highlighted. + +Possible value are: +- A cons cell (x . y) + Match x lines before point and y lines after point. +- An integer + Positive means this number lines after point. + Negative means this number lines before point. + A zero value means highlight only inside matched lines. +- The symbol never + Means do not highlight matched items. " + :group 'helm-utils + :type '(choice (cons (integer :tag "Match before") + (integer :tag "Match after")) + (const :tag "Match in line only" 0) + (integer :tag "Match after or before (+/-)") + (const :tag "Never match" never))) + +(defcustom helm-buffers-to-resize-on-pa nil + "A list of helm buffers where the helm-window should be reduced on PA. +Where PA means persistent action." + :group 'helm-utils + :type '(repeat (choice string))) + +(defcustom helm-resize-on-pa-text-height 12 + "The size of the helm-window when resizing on persistent action." + :group 'helm-utils + :type 'integer) + +(defcustom helm-sources-using-help-echo-popup '("Ack-Grep" "AG" "RG" "Gid" "Git-Grep") + "Show the buffer name or the filename in a popup at selection." + :group 'helm-utils + :type '(repeat (choice string))) + +(defcustom helm-html-decode-entities-function #'helm-html-decode-entities-string + "Function used to decode HTML entities in HTML bookmarks. +Helm comes by default with `helm-html-decode-entities-string', if +you need something more sophisticated you can use +`w3m-decode-entities-string' if available. + +In Emacs itself org-entities seem broken and `xml-substitute-numeric-entities' +supports only numeric entities." + :group 'helm-utils + :type 'function) + + +(defvar helm-goto-line-before-hook '(helm-save-current-pos-to-mark-ring) + "Run before jumping to line. +This hook runs when jumping from `helm-goto-line', `helm-etags-default-action', +and `helm-imenu-default-action'. +This allows you to retrieve a previous position after using the different helm +tools for searching (etags, grep, gid, (m)occur etc...). +By default positions are added to `mark-ring'. +You can also add to register by using (or adding) +`helm-save-pos-to-register-before-jump' instead. In this case +last position is added to the register `helm-save-pos-before-jump-register'.") + +(defvar helm-save-pos-before-jump-register ?_ + "The register where `helm-save-pos-to-register-before-jump' saves position.") + +(defconst helm-html-entities-alist + '((""" . 34) ;; " + (">" . 62) ;; > + ("<" . 60) ;; < + ("&" . 38) ;; & + ("€" . 8364) ;; € + ("Ÿ" . 89) ;; Y + ("¡" . 161) ;; ¡ + ("¢" . 162) ;; ¢ + ("£" . 163) ;; £ + ("¤" . 164) ;; ¤ + ("¥" . 165) ;; ¥ + ("¦" . 166) ;; ¦ + ("§" . 167) ;; § + ("¨" . 32) ;; SPC + (" " . 160) ;;   (non breaking space) + ("©" . 169) ;; © + ("ª" . 97) ;; a + ("«" . 171) ;; « + ("¬" . 172) ;; ¬ + ("&masr;" . 174) ;; ® + ("°" . 176) ;; ° + ("±" . 177) ;; ± + ("²" . 50) ;; 2 + ("³" . 51) ;; 3 + ("´" . 39) ;; ' + ("µ" . 956) ;; μ + ("¶" . 182) ;; ¶ + ("·" . 183) ;; · + ("¸" . 32) ;; SPC + ("¹" . 49) ;; 1 + ("º" . 111) ;; o + ("»" . 187) ;; » + ("¼" . 49) ;; 1 + ("½" . 49) ;; 1 + ("¾" . 51) ;; 3 + ("¿" . 191) ;; ¿ + ("À" . 192) ;; À + ("Á" . 193) ;; Á + ("Â" . 194) ;; Â + ("Ã" . 195) ;; Ã + ("Ä" . 196) ;; Ä + ("Å" . 197) ;; Å + ("&Aelig" . 198) ;; Æ + ("Ç" . 199) ;; Ç + ("È" . 200) ;; È + ("É" . 201) ;; É + ("Ê" . 202) ;; Ê + ("Ë" . 203) ;; Ë + ("Ì" . 204) ;; Ì + ("Í" . 205) ;; Í + ("Î" . 206) ;; Î + ("Ï" . 207) ;; Ï + ("ð" . 208) ;; Ð + ("Ñ" . 209) ;; Ñ + ("Ò" . 210) ;; Ò + ("Ó" . 211) ;; Ó + ("Ô" . 212) ;; Ô + ("Õ" . 213) ;; Õ + ("Ö" . 214) ;; Ö + ("×" . 215) ;; × + ("Ø" . 216) ;; Ø + ("Ù" . 217) ;; Ù + ("Ú" . 218) ;; Ú + ("Û" . 219) ;; Û + ("Ü" . 220) ;; Ü + ("Ý" . 221) ;; Ý + ("þ" . 222) ;; Þ + ("ß" . 223) ;; ß + ("à" . 224) ;; à + ("á" . 225) ;; á + ("â" . 226) ;; â + ("ã" . 227) ;; ã + ("ä" . 228) ;; ä + ("å" . 229) ;; å + ("æ" . 230) ;; æ + ("ç" . 231) ;; ç + ("è" . 232) ;; è + ("é" . 233) ;; é + ("ê" . 234) ;; ê + ("ë" . 235) ;; ë + ("ì" . 236) ;; ì + ("í" . 237) ;; í + ("î" . 238) ;; î + ("ï" . 239) ;; ï + ("ð" . 240) ;; ð + ("ñ" . 241) ;; ñ + ("ò" . 242) ;; ò + ("ó" . 243) ;; ó + ("ô" . 244) ;; ô + ("õ" . 245) ;; õ + ("ö" . 246) ;; ö + ("÷" . 247) ;; ÷ + ("ø" . 248) ;; ø + ("ù" . 249) ;; ù + ("ú" . 250) ;; ú + ("û" . 251) ;; û + ("ü" . 252) ;; ü + ("ý" . 253) ;; ý + ("þ" . 254) ;; þ + ("ÿ" . 255) ;; ÿ + ("®" . 174) ;; ® + ("­" . 173)) ;; ­ + + "Table of html character entities and values. +See https://www.freeformatter.com/html-entities.html") + +(defvar helm-find-many-files-after-hook nil + "Hook that runs at end of `helm-find-many-files'.") + +;;; Faces. +;; +(defface helm-selection-line + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit highlight :distant-foreground "black")) + "Face used in the `helm-current-buffer' when jumping to a candidate." + :group 'helm-faces) + +(defface helm-match-item + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit isearch)) + "Face used to highlight the item matched in a selected line." + :group 'helm-faces) + + +;;; Utils functions +;; +;; +(defcustom helm-window-prefer-horizontal-split nil + "Maybe switch to other window vertically when non nil. + +Possible values are t, nil and `decide'. + +When t switch vertically. +When nil switch horizontally. +When `decide' try to guess if it is possible to switch vertically +according to the setting of `split-width-threshold' and the size of +the window from where splitting is done. + +Note that when using `decide' and `split-width-threshold' is nil, the +behavior is the same as with a nil value." + :group 'helm-utils + :type '(choice + (const :tag "Split window vertically" t) + (const :tag "Split window horizontally" nil) + (symbol :tag "Guess how to split window" 'decide))) + +(defcustom helm-window-show-buffers-function #'helm-window-default-split-fn + "The default function to use when opening several buffers at once. +It is typically used to rearrange windows." + :group 'helm-utils + :type '(choice + (function :tag "Split windows vertically or horizontally" + helm-window-default-split-fn) + (function :tag "Split in alternate windows" + helm-window-alternate-split-fn) + (function :tag "Split windows in mosaic" + helm-window-mosaic-fn))) + +(defun helm-window-show-buffers (buffers &optional other-window) + "Show BUFFERS. + +With more than one buffer marked switch to these buffers in separate windows. +If OTHER-WINDOW is non-nil, keep current buffer and switch to other buffers +in separate windows. +If a prefix arg is given split windows vertically." + (let ((initial-ow-fn (if (cdr (window-list)) + #'switch-to-buffer-other-window + #'helm-window-other-window))) + (if (cdr buffers) + (funcall helm-window-show-buffers-function buffers + (and other-window initial-ow-fn)) + (if other-window + (funcall initial-ow-fn (car buffers)) + (helm-buffers-switch-to-buffer-or-tab (car buffers)))))) + +(defvar tab-bar-tab-name-function) +(declare-function tab-bar-switch-to-tab "tab-bar.el") +(declare-function tab-bar-tab-name-all "tab-bar.el") + +(defun helm-buffers-switch-to-buffer-or-tab (buffer) + "Switch to BUFFER in its tab if some." + (if (and (fboundp 'tab-bar-mode) + helm-buffers-maybe-switch-to-tab) + (let* ((tab-bar-tab-name-function #'tab-bar-tab-name-all) + (tabs (tab-bar-tabs)) + (tab-names (mapcar (lambda (tab) + (cdr (assq 'name tab))) + tabs)) + (bname (buffer-name (get-buffer buffer))) + (tab (helm-buffers--get-tab-from-name bname tabs))) + (if (helm-buffers--buffer-in-tab-p bname tab-names) + (progn + (tab-bar-switch-to-tab (alist-get 'name tab)) + (select-window (get-buffer-window bname))) + (switch-to-buffer buffer))) + (switch-to-buffer buffer))) + +(defun helm-buffers--get-tab-from-name (tab-name tabs) + "Return tab from TABS when it contains TAB-NAME." + (cl-loop for tab in tabs + when (member tab-name (split-string (cdr (assq 'name tab)) ", " t)) + return tab)) + +(defun helm-buffers--buffer-in-tab-p (buffer-name tab-names) + "Check if BUFFER-NAME is in TAB-NAMES list." + (cl-loop for name in tab-names + ;; Buf names are separated with "," in TAB-NAMES + ;; e.g. '("tab-bar.el" "*scratch*, helm-buffers.el"). + thereis (member buffer-name (split-string name ", " t)))) + +(defun helm-window-default-split-fn (candidates &optional other-window-fn) + "Split windows in one direction and balance them. + +Direction can be controlled via `helm-window-prefer-horizontal-split'. +If a prefix arg is given split windows the other direction. +This function is suitable for `helm-window-show-buffers-function'." + (if other-window-fn + (funcall other-window-fn (car candidates)) + (switch-to-buffer (car candidates))) + (save-selected-window + (cl-loop with nosplit + for b in (cdr candidates) + when nosplit return + (message "Too many buffers to visit simultaneously") + do (condition-case _err + (helm-window-other-window b 'balance) + (error (setq nosplit t) nil))))) + +(defun helm-window-alternate-split-fn (candidates &optional other-window-fn) + "Split windows horizontally and vertically in alternate fashion. + +Direction can be controlled via `helm-window-prefer-horizontal-split'. +If a prefix arg is given split windows the other direction. +This function is suitable for `helm-window-show-buffers-function'." + (if other-window-fn + (funcall other-window-fn (car candidates)) + (switch-to-buffer (car candidates))) + (let (right-side) + (save-selected-window + (cl-loop with nosplit + for b in (cdr candidates) + when nosplit return + (message "Too many buffers to visit simultaneously") + do (condition-case _err + (let ((helm-current-prefix-arg right-side)) + (helm-window-other-window b) + (setq right-side (not right-side))) + (error (setq nosplit t) nil)))))) + +(defun helm-window-mosaic-fn (candidates &optional other-window-fn) + "Make an as-square-as-possible window mosaic of the CANDIDATES buffers. + +If rectangular, the long side is in the direction given by +`helm-window-prefer-horizontal-split': if non-nil, it is horizontal, vertical +otherwise. +If OTHER-WINDOW-FN is non-nil, current windows are included in the mosaic. +This function is suitable for `helm-window-show-buffers-function'." + (when other-window-fn + (setq candidates (append (mapcar 'window-buffer (window-list)) candidates))) + (delete-other-windows) + (let* ((helm-window-prefer-horizontal-split + (if (eq helm-window-prefer-horizontal-split 'decide) + (and (numberp split-width-threshold) + (>= (window-width (selected-window)) + split-width-threshold)) + helm-window-prefer-horizontal-split)) + mosaic-length-tile-count + mosaic-width-tile-count + mosaic-length-tile-size + mosaic-width-tile-size + next-window) + ;; If 4 tiles, make 2x2 mosaic. + ;; If 5-6 tiles, make 2x3 mosaic with direction depending on `helm-window-prefer-horizontal-split'. + ;; If 7-9 tiles, make 3x3 mosaic. And so on. + (setq mosaic-length-tile-count (ceiling (sqrt (length candidates)))) + (setq mosaic-width-tile-count + (if (<= (length candidates) (* mosaic-length-tile-count (1- mosaic-length-tile-count))) + (1- mosaic-length-tile-count) + mosaic-length-tile-count)) + ;; We lower-bound the tile size, otherwise the function would + ;; fail during the first inner split. + ;; There is consequently no need to check for errors when + ;; splitting. + (let ((frame-mosaic-length-direction-size (frame-height)) + (frame-mosaic-width-direction-size (frame-width)) + (window-mosaic-length-direction-min-size window-min-height) + (window-mosaic-width-direction-min-size window-min-width)) + (if helm-window-prefer-horizontal-split + (setq frame-mosaic-length-direction-size (frame-width) + frame-mosaic-width-direction-size (frame-height) + window-mosaic-length-direction-min-size window-min-width + window-mosaic-width-direction-min-size window-min-height)) + (setq mosaic-length-tile-size (max + (/ frame-mosaic-length-direction-size mosaic-length-tile-count) + window-mosaic-length-direction-min-size) + mosaic-width-tile-size (max + (/ frame-mosaic-width-direction-size mosaic-width-tile-count) + window-mosaic-width-direction-min-size)) + ;; Shorten `candidates' to `max-tiles' elements. + (let ((max-tiles (* (/ frame-mosaic-length-direction-size mosaic-length-tile-size) + (/ frame-mosaic-width-direction-size mosaic-width-tile-size)))) + (when (> (length candidates) max-tiles) + (message "Too many buffers to visit simultaneously") + (setcdr (nthcdr (- max-tiles 1) candidates) nil)))) + ;; Make the mosaic. + (while candidates + (when (> (length candidates) mosaic-length-tile-count) + (setq next-window (split-window nil + mosaic-width-tile-size + (not helm-window-prefer-horizontal-split)))) + (switch-to-buffer (pop candidates)) + (dotimes (_ (min (1- mosaic-length-tile-count) (length candidates))) + (select-window (split-window nil + mosaic-length-tile-size + helm-window-prefer-horizontal-split)) + (switch-to-buffer (pop candidates))) + (when next-window + (select-window next-window))))) + +(defun helm-window-other-window (buffer-or-name &optional balance) + "Switch to BUFFER-OR-NAME in other window. +Direction can be controlled via `helm-window-prefer-horizontal-split'. +If a prefix arg is given split windows the other direction. +When argument BALANCE is provided `balance-windows'." + (let* ((helm-window-prefer-horizontal-split + (if (eq helm-window-prefer-horizontal-split 'decide) + (and (numberp split-width-threshold) + (>= (window-width (selected-window)) + split-width-threshold)) + helm-window-prefer-horizontal-split)) + (right-side (if helm-window-prefer-horizontal-split + (not helm-current-prefix-arg) + helm-current-prefix-arg))) + (select-window (split-window nil nil right-side)) + (and balance (balance-windows)) + (switch-to-buffer buffer-or-name))) + +(cl-defun helm-current-buffer-narrowed-p (&optional + (buffer helm-current-buffer)) + "Check if BUFFER is narrowed. +Default is `helm-current-buffer'." + (with-current-buffer buffer + (let ((beg (point-min)) + (end (point-max)) + (total (buffer-size))) + (or (/= beg 1) (/= end (1+ total)))))) + +(defun helm-goto-char (loc) + "Go to char, revealing if necessary." + (goto-char loc) + (let ((fn (cond ((eq major-mode 'org-mode) + ;; On some old Emacs versions org may not be loaded. + (require 'org) + #'org-reveal) + ((and (boundp 'outline-minor-mode) + outline-minor-mode) + #'outline-show-subtree) + ((and (boundp 'hs-minor-mode) + hs-minor-mode) + #'hs-show-block) + ((and (boundp 'markdown-mode-map) + (derived-mode-p 'markdown-mode)) + #'markdown-show-entry))) + (hs-show-hook (list (lambda () (goto-char loc))))) + ;; outline may fail in some conditions e.g. with markdown enabled + ;; (Bug#1919). + (condition-case-unless-debug nil + (and fn (funcall fn)) + (error nil)))) + +(defun helm-goto-line (lineno &optional noanim) + "Goto LINENO opening only outline headline if needed. +Animation is used unless NOANIM is non--nil." + (helm-log-run-hook 'helm-goto-line-before-hook) + (helm-match-line-cleanup) + (unless helm-alive-p + (with-helm-current-buffer + (unless helm-yank-point (setq helm-yank-point (point))))) + (goto-char (point-min)) + (helm-goto-char (point-at-bol lineno)) + (unless noanim + (helm-highlight-current-line))) + +(defun helm-save-pos-to-register-before-jump () + "Save current buffer position to `helm-save-pos-before-jump-register'. +To use this add it to `helm-goto-line-before-hook'." + (with-helm-current-buffer + (unless helm-in-persistent-action + (point-to-register helm-save-pos-before-jump-register)))) + +(defun helm-save-current-pos-to-mark-ring () + "Save current buffer position to mark ring. +To use this add it to `helm-goto-line-before-hook'." + (with-helm-current-buffer + (unless helm-in-persistent-action + (set-marker (mark-marker) (point)) + (push-mark (point) 'nomsg)))) + +(defun helm-displaying-source-names () + "Return the list of sources name for this helm session." + (with-current-buffer helm-buffer + (goto-char (point-min)) + (cl-loop with pos + while (setq pos (next-single-property-change (point) 'helm-header)) + do (goto-char pos) + collect (buffer-substring-no-properties (point-at-bol)(point-at-eol)) + do (forward-line 1)))) + +(defun helm-handle-winner-boring-buffers () + "Add `helm-buffer' to `winner-boring-buffers' when quitting/exiting helm. +Add this function to `helm-cleanup-hook' when you don't want to see helm buffers +after running winner-undo/redo." + (require 'winner) + (cl-pushnew helm-buffer winner-boring-buffers :test 'equal)) +(add-hook 'helm-cleanup-hook #'helm-handle-winner-boring-buffers) + +(defun helm-quit-and-find-file () + "Drop into `helm-find-files' from `helm'. +If current selection is a buffer or a file, `helm-find-files' +from its directory." + (interactive) + (with-helm-alive-p + (require 'helm-grep) + (require 'helm-elisp) + (require 'bookmark) ; For bookmark-alist + (let ((src (helm-get-current-source))) + (helm-run-after-exit + (lambda (f) + ;; Ensure specifics `helm-execute-action-at-once-if-one' + ;; fns don't run here. + (let (helm-execute-action-at-once-if-one + helm-actions-inherit-frame-settings) ; use this-command + (if (file-exists-p f) + (helm-find-files-1 (file-name-directory f) + (format + helm-ff-last-expanded-candidate-regexp + (regexp-quote + (if helm-ff-transformer-show-only-basename + (helm-basename f) f)))) + (helm-find-files-1 f)))) + (helm--quit-and-find-file-default-file src))))) +(put 'helm-quit-and-find-file 'helm-only t) + +(defun helm--quit-and-find-file-default-file (source) + (let ((target-fn (helm-get-attr 'find-file-target))) + ;; target-fn function may return nil, in this case fallback to default. + (helm-aif (and target-fn (funcall target-fn source)) + it + (let* ((sel (helm-get-selection nil nil source)) + (default-preselection (or (helm-default-directory) + (buffer-file-name helm-current-buffer) + default-directory))) + (cond + ((and (stringp sel) (or (file-remote-p sel) + (file-exists-p sel))) + (expand-file-name sel)) + ;; Url. + ((and (stringp sel) + helm--url-regexp + (string-match helm--url-regexp sel)) + sel) + ;; Exit brutally from a `with-helm-show-completion' + ((and helm-show-completion-overlay + (overlayp helm-show-completion-overlay)) + (delete-overlay helm-show-completion-overlay) + (remove-hook 'helm-move-selection-after-hook 'helm-show-completion) + (expand-file-name default-preselection)) + ;; Default. + (t (expand-file-name default-preselection))))))) + +(defun helm-generic-sort-fn (s1 s2) + "Sort predicate function for helm candidates. +Args S1 and S2 can be single or (display . real) candidates, +that is sorting is done against real value of candidate." + (let* ((qpattern (regexp-quote helm-pattern)) + (reg1 (concat "\\_<" qpattern "\\_>")) + (reg2 (concat "\\_<" qpattern)) + (reg3 helm-pattern) + (split (helm-remove-if-match + "\\`!" (helm-mm-split-pattern helm-pattern))) + (str1 (if (consp s1) (cdr s1) s1)) + (str2 (if (consp s2) (cdr s2) s2)) + (score (lambda (str r1 r2 r3 lst) + (+ (if (string-match (concat "\\`" qpattern) str) 1 0) + (cond ((string-match r1 str) 5) + ((and (string-match " " qpattern) + (string-match + (concat "\\_<" (regexp-quote (car lst))) str) + (cl-loop for r in (cdr lst) + always (string-match r str))) + 4) + ((and (string-match " " qpattern) + (cl-loop for r in lst + always (string-match r str))) + 3) + ((string-match r2 str) 2) + ((string-match r3 str) 1) + (t 0))))) + (sc1 (get-text-property 0 'completion-score str1)) + (sc2 (get-text-property 0 'completion-score str2)) + (sc3 (if sc1 0 (funcall score str1 reg1 reg2 reg3 split))) + (sc4 (if sc2 0 (funcall score str2 reg1 reg2 reg3 split)))) + (cond ((and sc1 sc2) ; helm-flex style. + (> sc1 sc2)) + ((or (zerop (string-width qpattern)) + (and (zerop sc3) (zerop sc4))) + (string-lessp str1 str2)) + ((= sc3 sc4) + (< (length str1) (length str2))) + (t (> sc3 sc4))))) + +(cl-defun helm-file-human-size (size &optional (kbsize helm-default-kbsize)) + "Return a string showing SIZE of a file in human readable form. +SIZE can be an integer or a float depending on it's value. +`file-attributes' will take care of that to avoid overflow error. +KBSIZE is a floating point number, defaulting to `helm-default-kbsize'." + (cl-loop with result = (cons "B" size) + for i in '("k" "M" "G" "T" "P" "E" "Z" "Y") + while (>= (cdr result) kbsize) + do (setq result (cons i (/ (cdr result) kbsize))) + finally return + (helm-acase (car result) + ("B" (format "%s" size)) + (t (format "%.1f%s" (cdr result) it))))) + +(defun helm-directory-size (directory &optional recursive human) + "Return the resulting size of the sum of all files in DIRECTORY. + +If RECURSIVE is non nil return the size of all files in DIRECTORY and +its subdirectories. With arg HUMAN format the size in a human +readable format,see `helm-file-human-size'." + (cl-loop with files = (if recursive + (helm-walk-directory + directory + :path 'full + :directories t) + (directory-files directory t)) + for file in files + sum (nth 7 (file-attributes file)) into total + finally return (if human + (helm-file-human-size total) + total))) + +(cl-defun helm-file-attributes + (file &key type links uid gid access-time modif-time + status size mode gid-change inode device-num dired human-size + mode-type mode-owner mode-group mode-other (string t)) + "Return `file-attributes' elements of FILE separately according to key value. +Availables keys are: +- TYPE: Same as nth 0 `files-attributes' if STRING is nil + otherwise return either symlink, directory or file (default). +- LINKS: See nth 1 `files-attributes'. +- UID: See nth 2 `files-attributes'. +- GID: See nth 3 `files-attributes'. +- ACCESS-TIME: See nth 4 `files-attributes', however format time + when STRING is non--nil (the default). +- MODIF-TIME: See nth 5 `files-attributes', same as above. +- STATUS: See nth 6 `files-attributes', same as above. +- SIZE: See nth 7 `files-attributes'. +- MODE: See nth 8 `files-attributes'. +- GID-CHANGE: See nth 9 `files-attributes'. +- INODE: See nth 10 `files-attributes'. +- DEVICE-NUM: See nth 11 `files-attributes'. +- DIRED: A line similar to what \\='ls -l' return. +- HUMAN-SIZE: The size in human form, see `helm-file-human-size'. +- MODE-TYPE, mode-owner,mode-group, mode-other: Split what + nth 7 `files-attributes' return in four categories. +- STRING: When non--nil (default) `helm-file-attributes' return + more friendly values. +If you want the same behavior as `files-attributes' , +\(but with return values in proplist) use a nil value for STRING. +However when STRING is non--nil, time and type value are different from what +you have in `file-attributes'." + (helm-aif (file-attributes file string) + (let* ((all (cl-destructuring-bind + (type links uid gid access-time modif-time + status size mode gid-change inode device-num) + it + (list :type (if string + (cond ((stringp type) "symlink") ; fname + (type "directory") ; t + (t "file")) ; nil + type) + :links links + :uid uid + :gid gid + :access-time (if string + (format-time-string + "%Y-%m-%d %R" access-time) + access-time) + :modif-time (if string + (format-time-string + "%Y-%m-%d %R" modif-time) + modif-time) + :status (if string + (format-time-string + "%Y-%m-%d %R" status) + status) + :size size + :mode mode + :gid-change gid-change + :inode inode + :device-num device-num))) + (modes (helm-split-mode-file-attributes (cl-getf all :mode)))) + (cond (type (cl-getf all :type)) + (links (cl-getf all :links)) + (uid (cl-getf all :uid)) + (gid (cl-getf all :gid)) + (access-time (cl-getf all :access-time)) + (modif-time (cl-getf all :modif-time)) + (status (cl-getf all :status)) + (size (cl-getf all :size)) + (mode (cl-getf all :mode)) + (gid-change (cl-getf all :gid-change)) + (inode (cl-getf all :inode)) + (device-num (cl-getf all :device-num)) + (dired (concat + (helm-split-mode-file-attributes + (cl-getf all :mode) t) " " + (number-to-string (cl-getf all :links)) " " + (cl-getf all :uid) ":" + (cl-getf all :gid) " " + (if human-size + (helm-file-human-size (cl-getf all :size)) + (int-to-string (cl-getf all :size))) " " + (cl-getf all :modif-time))) + (human-size (helm-file-human-size (cl-getf all :size))) + (mode-type (cl-getf modes :mode-type)) + (mode-owner (cl-getf modes :user)) + (mode-group (cl-getf modes :group)) + (mode-other (cl-getf modes :other)) + (t (append all modes)))))) + +(defun helm-split-mode-file-attributes (str &optional string) + "Split mode file attributes STR into a proplist. +If STRING is non--nil return instead a space separated string." + (cl-loop with type = (substring str 0 1) + with cdr = (substring str 1) + for i across cdr + for count from 1 + if (<= count 3) + concat (string i) into user + if (and (> count 3) (<= count 6)) + concat (string i) into group + if (and (> count 6) (<= count 9)) + concat (string i) into other + finally return + (if string + (mapconcat 'identity (list type user group other) " ") + (list :mode-type type :user user :group group :other other)))) + +(defun helm-format-columns-of-files (files) + "Same as `dired-format-columns-of-files'. +Inlined here for compatibility." + (let ((beg (point))) + (completion--insert-strings files) + (put-text-property beg (point) 'mouse-face nil))) + +(defmacro with-helm-display-marked-candidates (buffer-or-name candidates &rest body) + (declare (indent 0) (debug t)) + (helm-with-gensyms (buffer window winconf) + `(let* ((,buffer (temp-buffer-window-setup ,buffer-or-name)) + (,winconf helm-last-frame-or-window-configuration) + (helm-always-two-windows t) + (helm-split-window-default-side + (if (eq helm-split-window-default-side 'same) + 'below helm-split-window-default-side)) + helm-split-window-inside-p + helm-reuse-last-window-split-state + ,window) + (with-current-buffer ,buffer + (helm-format-columns-of-files ,candidates)) + (unwind-protect + (with-selected-window + (setq ,window (temp-buffer-window-show + ,buffer + '(display-buffer-below-selected + (window-height . fit-window-to-buffer)))) + (progn ,@body)) + (quit-window 'kill ,window) + (and ,winconf (set-window-configuration ,winconf)))))) + +;;; Persistent Action Helpers +;; +;; +;; Internal +(defvar helm-match-line-overlay nil) +(defvar helm--match-item-overlays nil) + +(cl-defun helm-highlight-current-line (&optional start end buf face) + "Highlight and underline current position" + (let* ((start (or start (line-beginning-position))) + (end (or end (1+ (line-end-position)))) + start-match end-match + (args (list start end buf)) + (case-fold-search (if helm-alive-p + (helm-set-case-fold-search) + case-fold-search))) + ;; Highlight the current line. + (if (not helm-match-line-overlay) + (setq helm-match-line-overlay (apply 'make-overlay args)) + (apply 'move-overlay helm-match-line-overlay args)) + (overlay-put helm-match-line-overlay + 'face (or face 'helm-selection-line)) + ;; Now highlight matches only if we are in helm session, we are + ;; maybe coming from helm-grep-mode or helm-moccur-mode buffers. + (when helm-alive-p + (cond (;; These 2 clauses have to be the first otherwise + ;; `helm-highlight-matches-around-point-max-lines' is + ;; compared as a number by other clauses and return an error. + (eq helm-highlight-matches-around-point-max-lines 'never) + (cl-return-from helm-highlight-current-line)) + ((consp helm-highlight-matches-around-point-max-lines) + (setq start-match + (save-excursion + (forward-line + (- (car helm-highlight-matches-around-point-max-lines))) + (point-at-bol)) + end-match + (save-excursion + (forward-line + (cdr helm-highlight-matches-around-point-max-lines)) + (point-at-bol)))) + ((or (null helm-highlight-matches-around-point-max-lines) + (zerop helm-highlight-matches-around-point-max-lines)) + (setq start-match start + end-match end)) + ((< helm-highlight-matches-around-point-max-lines 0) + (setq start-match + (save-excursion + (forward-line + helm-highlight-matches-around-point-max-lines) + (point-at-bol)) + end-match start)) + ((> helm-highlight-matches-around-point-max-lines 0) + (setq start-match start + end-match + (save-excursion + (forward-line + helm-highlight-matches-around-point-max-lines) + (point-at-bol))))) + (catch 'empty-line + (cl-loop with ov + for r in (helm-remove-if-match + "\\`!" (helm-mm-split-pattern + (if (with-helm-buffer + ;; Needed for highlighting AG matches. + (assq 'pcre (helm-get-current-source))) + (helm--translate-pcre-to-elisp helm-input) + helm-input))) + do (save-excursion + (goto-char start-match) + (while (condition-case _err + (and (not (= start-match end-match)) + (if helm-migemo-mode + (helm-mm-migemo-forward r end-match t) + (re-search-forward r end-match t))) + (invalid-regexp nil)) + (let ((s (match-beginning 0)) + (e (match-end 0))) + (if (= s e) + (throw 'empty-line nil) + (push (setq ov (make-overlay s e)) + helm--match-item-overlays) + (overlay-put ov 'face 'helm-match-item) + (overlay-put ov 'priority 1)))))))) + (recenter))) + +(defun helm--translate-pcre-to-elisp (regexp) + "Should translate pcre REGEXP to elisp regexp. +Assume regexp is a pcre based regexp." + (with-temp-buffer + (insert " " regexp " ") + (goto-char (point-min)) + (save-excursion + ;; match (){}| unquoted + (helm-awhile (and (re-search-forward "\\([(){}|]\\)" nil t) + (match-string 1)) + (let ((pos (match-beginning 1))) + (if (eql (char-before pos) ?\\) + (delete-region pos (1- pos)) + (replace-match (concat "\\" it) t t nil 1))))) + ;; match \s or \S + (helm-awhile (and (re-search-forward "\\S\\?\\(\\s\\[sS]\\)[^-]" nil t) + (match-string 1)) + (replace-match (concat it "-") t t nil 1)) + (buffer-substring (1+ (point-min)) (1- (point-max))))) + +(defun helm-match-line-cleanup () + (when helm-match-line-overlay + (delete-overlay helm-match-line-overlay) + (setq helm-match-line-overlay nil)) + (when helm--match-item-overlays + (mapc 'delete-overlay helm--match-item-overlays))) + +(defun helm-match-line-cleanup-maybe () + (when (helm-empty-buffer-p) + (helm-match-line-cleanup))) + +(defun helm-match-line-update () + (when helm--match-item-overlays + (mapc 'delete-overlay helm--match-item-overlays)) + (when helm-match-line-overlay + (delete-overlay helm-match-line-overlay) + (helm-highlight-current-line))) + +(defun helm-persistent-autoresize-hook () + (when (and helm-buffers-to-resize-on-pa + (member helm-buffer helm-buffers-to-resize-on-pa) + (eq helm-split-window-state 'vertical)) + (set-window-text-height (helm-window) helm-resize-on-pa-text-height))) + +(defun helm-match-line-cleanup-pulse () + (run-with-timer 0.3 nil #'helm-match-line-cleanup)) + +(add-hook 'helm-after-update-hook 'helm-match-line-cleanup-maybe) +(add-hook 'helm-after-persistent-action-hook 'helm-persistent-autoresize-hook) +(add-hook 'helm-cleanup-hook 'helm-match-line-cleanup) +(add-hook 'helm-after-action-hook 'helm-match-line-cleanup-pulse) +(add-hook 'helm-after-persistent-action-hook 'helm-match-line-update) + +;;; Popup buffer-name or filename in grep/moccur/imenu-all. +;; +(defvar helm--show-help-echo-timer nil) + +(defun helm-cancel-help-echo-timer () + (when helm--show-help-echo-timer + (cancel-timer helm--show-help-echo-timer) + (setq helm--show-help-echo-timer nil))) + +(defun helm-maybe-show-help-echo () + (when helm--show-help-echo-timer + (cancel-timer helm--show-help-echo-timer) + (setq helm--show-help-echo-timer nil)) + (when (and helm-alive-p + helm-popup-tip-mode + (member (assoc-default 'name (helm-get-current-source)) + helm-sources-using-help-echo-popup)) + (setq helm--show-help-echo-timer + (run-with-timer + 1 nil + (lambda () + (save-selected-window + (with-helm-window + (helm-aif (get-text-property (point-at-bol) 'help-echo) + (popup-tip (concat " " (abbreviate-file-name + (replace-regexp-in-string "\n.*" "" it))) + :around nil + :point (save-excursion + (end-of-visual-line) (point))))))))))) + +;;;###autoload +(define-minor-mode helm-popup-tip-mode + "Show help-echo informations in a popup tip at end of line." + :global t + (require 'popup) + (if helm-popup-tip-mode + (progn + (add-hook 'helm-move-selection-after-hook 'helm-maybe-show-help-echo) + (add-hook 'helm-cleanup-hook 'helm-cancel-help-echo-timer)) + (remove-hook 'helm-move-selection-after-hook 'helm-maybe-show-help-echo) + (remove-hook 'helm-cleanup-hook 'helm-cancel-help-echo-timer))) + +(defun helm-open-file-with-default-tool (file) + "Open FILE with the default tool on this platform." + (let (process-connection-type) + (if (eq system-type 'windows-nt) + (helm-w32-shell-execute-open-file file) + (start-process "helm-open-file-with-default-tool" + nil + (cond ((eq system-type 'gnu/linux) + "xdg-open") + ((or (eq system-type 'darwin) ;; Mac OS X + (eq system-type 'macos)) ;; Mac OS 9 + "open")) + file)))) + +(defun helm-open-dired (file) + "Open a dired buffer in FILE's directory. +If FILE is a directory, open this directory." + (require 'dired) + (if (file-directory-p file) + (dired file) + (dired (file-name-directory file)) + (dired-goto-file file))) + +(defun helm-find-file-as-root (candidate) + (let* ((buf (helm-basename candidate)) + (host (file-remote-p candidate 'host)) + (remote-path (format "/%s:%s:%s" + helm-su-or-sudo + (or host "") + (expand-file-name + (if host + (file-remote-p candidate 'localname) + candidate)))) + non-essential) + (if (buffer-live-p (get-buffer buf)) + (progn + (set-buffer buf) + (find-alternate-file remote-path)) + (find-file remote-path)))) + +(defun helm-find-many-files (_ignore) + "Simple action that run `find-file' on marked candidates. +Run `helm-find-many-files-after-hook' at end." + (let ((helm--reading-passwd-or-string t)) + (mapc 'find-file (helm-marked-candidates)) + (helm-log-run-hook 'helm-find-many-files-after-hook))) + +(defun helm-read-repeat-string (prompt &optional count) + "Prompt as many time PROMPT is not empty. +If COUNT is non--nil add a number after each prompt." + (cl-loop with elm + while (not (string= elm "")) + for n from 1 + do (when count + (setq prompt (concat prompt (int-to-string n) ": "))) + collect (setq elm (helm-read-string prompt)) into lis + finally return (remove "" lis))) + +(defun helm-html-bookmarks-to-alist (file url-regexp bmk-regexp) + "Parse HTML bookmark FILE and return an alist with (title . url) as elements." + (let (bookmarks-alist url title) + (with-temp-buffer + (insert-file-contents file) + (goto-char (point-min)) + (while (re-search-forward "href=\\|^ *
. + +;;; Commentary: + +;;; Code: + +(require 'helm-for-files) + + +;;; List of files gleaned from every dired buffer +;; +;; +(defvar dired-buffers) +(defvar directory-files-no-dot-files-regexp) +(defun helm-files-in-all-dired-candidates () + "Return a list of files from live `dired' buffers." + (save-excursion + (cl-loop for (f . b) in dired-buffers + when (buffer-live-p b) + append (let ((dir (with-current-buffer b dired-directory))) + (if (listp dir) (cdr dir) + (directory-files f t directory-files-no-dot-files-regexp)))))) + +;; (dired '("~/" "~/.emacs.d/.emacs-custom.el" "~/.emacs.d/.emacs.bmk")) + +(defclass helm-files-dired-source (helm-source-sync helm-type-file) + ((candidates :initform #'helm-files-in-all-dired-candidates))) + +(defvar helm-source-files-in-all-dired + (helm-make-source "Files in all dired buffer." 'helm-files-dired-source)) + +;;; session.el files +;; +;; session (http://emacs-session.sourceforge.net/) is an alternative to +;; recentf that saves recent file history and much more. +(defvar session-file-alist) +(defclass helm-source-session-class (helm-source-sync) + ((candidates :initform (lambda () + (cl-delete-if-not + (lambda (f) + (or (string-match helm-tramp-file-name-regexp f) + (file-exists-p f))) + (mapcar 'car session-file-alist)))) + (keymap :initform 'helm-generic-files-map) + (help-message :initform 'helm-generic-file-help-message) + (action :initform 'helm-type-file-actions))) + +(defvar helm-source-session nil + "File list from emacs-session.") + +(defcustom helm-session-fuzzy-match nil + "Enable fuzzy matching in `helm-source-session' when non--nil." + :group 'helm-files + :type 'boolean + :set (lambda (var val) + (set var val) + (setq helm-source-session + (helm-make-source "Session" 'helm-source-session-class + :fuzzy-match val)))) + + +;;; External searching file tools. +;; +;; Tracker desktop search + +(defun helm-source-tracker-transformer (candidates _source) + "Return file names from tracker CANDIDATES." + ;; loop through tracker candidates selecting out file:// lines + ;; then select part after file:// and url decode to get straight filenames + (cl-loop for cand in candidates + when (and (stringp cand) + (string-match "\\`[[:space:]]*file://\\(.*\\)" cand)) + collect (url-unhex-string (match-string 1 cand)))) + +(defvar helm-source-tracker-search + (helm-build-async-source "Tracker Search" + :candidates-process + (lambda () + ;; the tracker-search command has been deprecated, now invoke via tracker + ;; also, disable the contextual snippets which we don't currently use + (start-process "tracker-search-process" nil + "tracker" "search" + "--disable-snippets" + "--disable-color" + "--limit=512" + helm-pattern)) + ;; new simplified transformer of tracker search results + :filtered-candidate-transformer #'helm-source-tracker-transformer + ;;(multiline) ; https://github.com/emacs-helm/helm/issues/529 + :keymap helm-generic-files-map + :action 'helm-type-file-actions + :action-transformer '(helm-transform-file-load-el + helm-transform-file-browse-url) + :requires-pattern 3) + "Source for the Tracker desktop search engine.") + +;; Spotlight (MacOS X desktop search) +(defclass helm-mac-spotlight-source (helm-source-async helm-type-file) + ((candidates-process :initform + (lambda () + (start-process + "mdfind-process" nil "mdfind" helm-pattern))) + (requires-pattern :initform 3))) + +(defvar helm-source-mac-spotlight + (helm-make-source "mdfind" 'helm-mac-spotlight-source) + "Source for retrieving files via Spotlight's command line utility mdfind.") + +(provide 'helm-x-files) + +;;; helm-x-files.el ends here diff --git a/code/elpa/helm-20220822.659/helm.el b/code/elpa/helm-20220822.659/helm.el new file mode 100644 index 0000000..5226560 --- /dev/null +++ b/code/elpa/helm-20220822.659/helm.el @@ -0,0 +1,43 @@ +;;; helm.el --- Helm is an Emacs incremental and narrowing framework -*- lexical-binding: t -*- + +;; Copyright (C) 2007 Tamas Patrovics +;; 2008 ~ 2011 rubikitch +;; 2011 ~ 2021 Thierry Volpiatto + +;; This is a fork of anything.el wrote by Tamas Patrovics. + +;; Authors of anything.el: Tamas Patrovics +;; rubikitch +;; Thierry Volpiatto + +;; Author: Thierry Volpiatto +;; Version: 3.8.7 +;; URL: https://emacs-helm.github.io/helm/ +;; Package-Requires: ((helm-core "3.8.7") (popup "0.5.3")) + +;; 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 . + +;;; Commentary: + +;; This is just a wrapper for helm-core.el and a place holder we +;; currently use only to hold the package's metadata in the header. + +;;; Code: + +(require 'helm-core) +(require 'helm-global-bindings) + +(provide 'helm) + +;;; helm.el ends here diff --git a/code/elpa/helm-core-20220824.1925/helm-core-autoloads.el b/code/elpa/helm-core-20220824.1925/helm-core-autoloads.el new file mode 100644 index 0000000..7a2f97c --- /dev/null +++ b/code/elpa/helm-core-20220824.1925/helm-core-autoloads.el @@ -0,0 +1,264 @@ +;;; helm-core-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "helm-core" "helm-core.el" (0 0 0 0)) +;;; Generated autoloads from helm-core.el + +(autoload 'helm-define-multi-key "helm-core" "\ +In KEYMAP, define key sequence KEY for function list FUNCTIONS. +Each function runs sequentially for each KEY press. +If DELAY is specified, switch back to initial function of FUNCTIONS list +after DELAY seconds. +The functions in FUNCTIONS list take no args. +E.g. + (defun foo () + (interactive) + (message \"Run foo\")) + (defun bar () + (interactive) + (message \"Run bar\")) + (defun baz () + (interactive) + (message \"Run baz\")) + +\(helm-define-multi-key global-map (kbd \" q\") \\='(foo bar baz) 2) + +Each time \" q\" is pressed, the next function is executed. +Waiting more than 2 seconds between key presses switches back to +executing the first function on the next hit. + +\(fn KEYMAP KEY FUNCTIONS &optional DELAY)" nil nil) + +(autoload 'helm-multi-key-defun "helm-core" "\ +Define NAME as a multi-key command running FUNS. +After DELAY seconds, the FUNS list is reinitialized. +See `helm-define-multi-key'. + +\(fn NAME DOCSTRING FUNS &optional DELAY)" nil t) + +(function-put 'helm-multi-key-defun 'lisp-indent-function '2) + +(autoload 'helm-define-key-with-subkeys "helm-core" "\ +Define in MAP a KEY and SUBKEY to COMMAND. + +This allows typing KEY to call COMMAND the first time and +type only SUBKEY on subsequent calls. + +Arg MAP is the keymap to use, SUBKEY is the initial short +key binding to call COMMAND. + +Arg OTHER-SUBKEYS is an alist specifying other short key bindings +to use once started, e.g.: + + (helm-define-key-with-subkeys global-map + (kbd \"C-x v n\") ?n \\='git-gutter:next-hunk + \\='((?p . git-gutter:previous-hunk))) + +In this example, `C-x v n' will run `git-gutter:next-hunk' +subsequent \"n\" will run this command again and subsequent \"p\" +will run `git-gutter:previous-hunk'. + +If specified PROMPT can be displayed in minibuffer to describe +SUBKEY and OTHER-SUBKEYS. Arg EXIT-FN specifies a function to run +on exit. + +For any other key pressed, run their assigned command as defined +in MAP and then exit the loop running EXIT-FN, if specified. + +If DELAY an integer is specified exit after DELAY seconds. + +NOTE: SUBKEY and OTHER-SUBKEYS bindings support only char syntax +and vectors, so don't use strings to define them. While defining +or executing a kbd macro no SUBKEY or OTHER-SUBKEYS are provided, +i.e. the loop is not entered after running COMMAND. + +\(fn MAP KEY SUBKEY COMMAND &optional OTHER-SUBKEYS PROMPT EXIT-FN DELAY DOCSTRING)" nil nil) + +(function-put 'helm-define-key-with-subkeys 'lisp-indent-function '1) + +(autoload 'helm-configuration "helm-core" "\ +Customize Helm." t nil) + +(autoload 'helm-debug-open-last-log "helm-core" "\ +Open Helm log file or buffer of last Helm session." t nil) + +(autoload 'helm "helm-core" "\ +Main function to execute helm sources. + +PLIST is a list like + +\(:key1 val1 :key2 val2 ...) + + or + +\(&optional sources input prompt resume preselect + buffer keymap default history allow-nest). + +** Keywords + +Keywords supported: + +- :sources +- :input +- :prompt +- :resume +- :preselect +- :buffer +- :keymap +- :default +- :history +- :allow-nest + +Extra LOCAL-VARS keywords are supported, see the \"** Other +keywords\" section below. + +Basic keywords are the following: + +*** :sources + +One of the following: + +- List of sources +- Symbol whose value is a list of sources +- Alist representing a Helm source. + - In this case the source has no name and is referenced in + `helm-sources' as a whole alist. + +*** :input + +Initial input of minibuffer (temporary value of `helm-pattern') + +*** :prompt + +Minibuffer prompt. Default value is `helm--prompt'. + +*** :resume + +If t, allow resumption of the previous session of this Helm +command, skipping initialization. + +If \\='noresume, this instance of `helm' cannot be resumed. + +*** :preselect + +Initially selected candidate (string or regexp). + +*** :buffer + +Buffer name for this Helm session. `helm-buffer' will take this value. + +*** :keymap + +\[Obsolete] + +Keymap used at the start of this Helm session. + +It is overridden by keymaps specified in sources, and is kept +only for backward compatibility. + +Keymaps should be specified in sources using the :keymap slot +instead. See `helm-source'. + +This keymap is not restored by `helm-resume'. + +*** :default + +Default value inserted into the minibuffer with +\\\\[next-history-element]. + +It can be a string or a list of strings, in this case +\\\\[next-history-element] cycles through +the list items, starting with the first. + +If nil, `thing-at-point' is used. + +If `helm-maybe-use-default-as-input' is non-nil, display is +updated using this value if this value matches, otherwise it is +ignored. If :input is specified, it takes precedence on :default. + +*** :history + +Minibuffer input, by default, is pushed to `minibuffer-history'. + +When an argument HISTORY is provided, input is pushed to +HISTORY. HISTORY should be a valid symbol. + +*** :allow-nest + +Allow running this Helm command in a running Helm session. + +** Other keywords + +Other keywords are interpreted as local variables of this Helm +session. The `helm-' prefix can be omitted. For example, + +\(helm :sources \\='helm-source-buffers-list + :buffer \"*helm buffers*\" + :candidate-number-limit 10) + +Starts a Helm session with the variable +`helm-candidate-number-limit' set to 10. + +** Backward compatibility + +For backward compatibility, positional parameters are +supported: + +\(helm sources input prompt resume preselect + buffer keymap default history allow-nest) + +However, the use of non-keyword args is deprecated. + +\(fn &key SOURCES INPUT PROMPT RESUME PRESELECT BUFFER KEYMAP DEFAULT HISTORY ALLOW-NEST OTHER-LOCAL-VARS)" nil nil) + +(autoload 'helm-cycle-resume "helm-core" "\ +Cycle in `helm-buffers' list and resume when waiting more than 1.2s." t nil) + +(autoload 'helm-other-buffer "helm-core" "\ +Simplified Helm interface with other `helm-buffer'. +Call `helm' only with SOURCES and BUFFER as args. + +\(fn SOURCES BUFFER)" nil nil) + +(register-definition-prefixes "helm-core" '("helm-" "with-helm-")) + +;;;*** + +;;;### (autoloads nil "helm-lib" "helm-lib.el" (0 0 0 0)) +;;; Generated autoloads from helm-lib.el + +(register-definition-prefixes "helm-lib" '("helm-" "with-helm-")) + +;;;*** + +;;;### (autoloads nil "helm-multi-match" "helm-multi-match.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from helm-multi-match.el + +(register-definition-prefixes "helm-multi-match" '("helm-m")) + +;;;*** + +;;;### (autoloads nil "helm-source" "helm-source.el" (0 0 0 0)) +;;; Generated autoloads from helm-source.el + +(register-definition-prefixes "helm-source" '("helm-")) + +;;;*** + +;;;### (autoloads nil nil ("helm-core-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; helm-core-autoloads.el ends here diff --git a/code/elpa/helm-core-20220824.1925/helm-core-pkg.el b/code/elpa/helm-core-20220824.1925/helm-core-pkg.el new file mode 100644 index 0000000..bc1a725 --- /dev/null +++ b/code/elpa/helm-core-20220824.1925/helm-core-pkg.el @@ -0,0 +1,11 @@ +(define-package "helm-core" "20220824.1925" "Development files for Helm" + '((emacs "25.1") + (async "1.9.4")) + :commit "4e99cc8ef66aac2d824c456f58abe833be26c99d" :authors + '(("Thierry Volpiatto" . "thievol@posteo.net")) + :maintainer + '("Thierry Volpiatto" . "thievol@posteo.net") + :url "https://emacs-helm.github.io/helm/") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/helm-core-20220824.1925/helm-core.el b/code/elpa/helm-core-20220824.1925/helm-core.el new file mode 100644 index 0000000..52c1cd6 --- /dev/null +++ b/code/elpa/helm-core-20220824.1925/helm-core.el @@ -0,0 +1,7773 @@ +;;; helm-core.el --- Development files for Helm -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Thierry Volpiatto + +;; Author: Thierry Volpiatto +;; URL: https://emacs-helm.github.io/helm/ +;; Version: 3.8.7 +;; Package-Requires: ((emacs "25.1") (async "1.9.4")) + +;; 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 . + +;;; Commentary: + +;; Contains the main code for Helm. + +;;; Code: + +(require 'cl-lib) +(require 'async) +(require 'helm-lib) +(require 'helm-multi-match) +(require 'helm-source) + +;; Ensure async-bytecomp is used even with helm-core package. +(declare-function async-bytecomp-package-mode "ext:async-bytecomp.el") +(when (require 'async-bytecomp nil t) + (and (fboundp 'async-bytecomp-package-mode) + (async-bytecomp-package-mode 1))) + +;; Setup completion styles for helm-mode +(helm--setup-completion-styles-alist) + +(declare-function helm-comp-read "helm-mode.el") +(declare-function custom-unlispify-tag-name "cus-edit.el") +(declare-function helm-quit-and-find-file "helm-utils.el") + +(defvar helm-marked-buffer-name) + + +;;; Internal Variables +;; +;; +(defvar helm-source-filter nil + "A list of source names to be displayed. +Other sources won't appear in the search results. +If nil, no filtering is done. +Don't set this directly, use `helm-set-source-filter' during a +Helm session to modify it.") +(defvar helm-saved-action nil + "Saved value of the currently selected action by key.") +(defvar helm-saved-current-source nil + "Value of the current source when the action list is shown.") +(defvar helm-in-persistent-action nil + "Flag whether in persistent-action or not.") +(defvar helm-last-buffer nil + "`helm-buffer' of a previous Helm session.") +(defvar helm-saved-selection nil + "Value of the currently selected object when the action list is shown.") +(defvar helm-sources nil + "[INTERNAL] Value of current sources in use, a list of alists. +The list of sources (symbols or alists) is normalized to alists +in `helm-initialize'.") +(defvar helm-buffer-file-name nil + "Variable `buffer-file-name' when Helm is invoked.") +(defvar helm-candidate-cache (make-hash-table :test 'equal) + "Holds the available candidate within a single Helm invocation.") +(defvar helm--candidate-buffer-alist nil) +(defvar helm-input "" + "The input typed in the candidates panel.") +(defvar helm-input-local nil + "Internal, store locally `helm-pattern' value for later use in `helm-resume'.") +(defvar helm--source-name nil) +(defvar helm-current-source nil) +(defvar helm-issued-errors nil) +(defvar helm--last-log-file nil + "The name of the log file of the last Helm session.") +(defvar helm--local-variables nil) +(defvar helm-split-window-state nil) +(defvar helm--window-side-state nil) +(defvar helm-selection-point nil + "The value of point at selection.") +(defvar helm-alive-p nil) +(defvar helm-visible-mark-overlays nil) +(defvar helm-update-blacklist-regexps '("^" "^ *" "$" "!" " " "\\b" + "\\<" "\\>" "\\_<" "\\_>" ".*" + "??" "?*" "*?" "?")) +(defvar helm--force-updating-p nil + "[INTERNAL] Don't use this in your programs.") +(defvar helm-exit-status 0 + "Flag to inform if Helm did exit or quit. +0 means Helm did exit when executing an action. +1 means Helm did quit with \\[keyboard-quit] +Knowing this exit-status could help restore a window config when +Helm aborts in some special circumstances. See +`helm-exit-minibuffer' and `helm-keyboard-quit'.") +(defvar helm-minibuffer-confirm-state nil) +(defvar helm--quit nil) +(defvar helm-buffers nil + "Helm buffers listed in order of most recently used.") +(defvar helm-current-position nil + "Cons of (point . window-start) when Helm is invoked. +`helm-current-buffer' uses this to restore position after +`helm-keyboard-quit'") +(defvar helm-last-frame-or-window-configuration nil + "Used to store window or frame configuration at Helm start.") +(defvar helm-onewindow-p nil) +(defvar helm-types nil) +(defvar helm--mode-line-string-real nil) ; The string to display in mode-line. +(defvar helm-persistent-action-display-window nil) +(defvar helm-marked-candidates nil + "Marked candidates. List of (source . real) pair.") +(defvar helm--mode-line-display-prefarg nil) +(defvar helm--temp-follow-flag nil + "[INTERNAL] A simple flag to notify persistent action we are following.") +(defvar helm--reading-passwd-or-string nil) +(defvar helm--in-update nil) +(defvar helm--in-fuzzy nil) +(defvar helm-maybe-use-default-as-input nil + "Flag to notify the use of use-default-as-input. +Use only in let-bindings. +Use :default arg of `helm' as input to update display. +Note that if also :input is specified as `helm' arg, it will take +precedence on :default.") +(defvar helm--temp-hooks nil + "Store temporary hooks added by `with-helm-temp-hook'.") +(defvar helm--prompt nil) +(defvar helm--file-completion-sources + '("Find Files" "Read File Name") + "Sources that use the *find-files mechanism can be added here. +Sources generated by `helm-mode' don't need to be added here +because they are automatically added. + +You should not modify this yourself unless you know what you are +doing.") +(defvar helm--completing-file-name nil + "Non nil when `helm-read-file-name' is running. +Used for `helm-file-completion-source-p'.") +;; Same as `ffap-url-regexp' but keep it here to ensure `ffap-url-regexp' is not nil. +(defvar helm--url-regexp "\\`\\(news\\(post\\)?:\\|mailto:\\|file:\\|\\(ftp\\|https?\\|telnet\\|gopher\\|www\\|wais\\)://\\)") +(defvar helm--ignore-errors nil + "Flag to prevent Helm popping up errors in candidates functions. +Should be set in candidates functions if needed, and will be +restored at end of session.") +(defvar helm--action-prompt "Select action: ") +(defvar helm--cycle-resume-iterator nil) +(defvar helm--buffer-in-new-frame-p nil) +(defvar helm-initial-frame nil + "[INTERNAL] The selected frame before starting Helm. +Helm use this internally to know in which frame it started, don't +modify this yourself.") +(defvar helm-popup-frame nil + "The frame where Helm is displayed. + +This is only used when Helm is using +`helm-display-buffer-in-own-frame' as `helm-display-function' and +`helm-display-buffer-reuse-frame' is non nil.") +(defvar helm--nested nil) +(defconst helm--frame-default-attributes + '(width height tool-bar-lines left top + title undecorated vertical-scroll-bars + visibility fullscreen menu-bar-lines undecorated + alpha foreground-color background-color) + "Frame parameters to save in `helm--last-frame-parameters'.") +(defvar helm--last-frame-parameters nil + "Frame parameters to save for later resuming. +Local to `helm-buffer'.") +(defvar helm--executing-helm-action nil + "Non nil when action is triggering a new helm-session. +This may be let bounded in other places to notify the display +function to reuse the same frame parameters as the previous Helm +session just like resume would do.") +(defvar helm--current-buffer-narrowed nil) +(defvar helm--suspend-update-interactive-flag nil) +(defvar helm-persistent-action-window-buffer nil + "[INTERNAL] Store the buffer where helm is started. +It is generally `helm-current-buffer', but when this one is displayed +in a dedicated buffer, helm can't start in this window and use another +window handling a buffer, it is this one we store.") +(defvar helm--tramp-archive-maybe-loaded nil) +(defvar helm--original-dedicated-windows-alist nil + "[INTERNAL] Store all dedicated windows with their dedicated state on startup") + +;;; Multi keys +;; +;; +;;;###autoload +(defun helm-define-multi-key (keymap key functions &optional delay) + "In KEYMAP, define key sequence KEY for function list FUNCTIONS. +Each function runs sequentially for each KEY press. +If DELAY is specified, switch back to initial function of FUNCTIONS list +after DELAY seconds. +The functions in FUNCTIONS list take no args. +E.g. + (defun foo () + (interactive) + (message \"Run foo\")) + (defun bar () + (interactive) + (message \"Run bar\")) + (defun baz () + (interactive) + (message \"Run baz\")) + +\(helm-define-multi-key global-map (kbd \" q\") \\='(foo bar baz) 2) + +Each time \" q\" is pressed, the next function is executed. +Waiting more than 2 seconds between key presses switches back to +executing the first function on the next hit." + (define-key keymap key (helm-make-multi-command functions delay))) + +;;;###autoload +(defmacro helm-multi-key-defun (name docstring funs &optional delay) + "Define NAME as a multi-key command running FUNS. +After DELAY seconds, the FUNS list is reinitialized. +See `helm-define-multi-key'." + (declare (indent 2)) + (setq docstring (if docstring (concat docstring "\n\n") + "This is a helm-ish multi-key command.")) + `(defalias (quote ,name) (helm-make-multi-command ,funs ,delay) ,docstring)) + +(defun helm-make-multi-command (functions &optional delay) + "Return an anonymous multi-key command running FUNCTIONS. +Run each function in the FUNCTIONS list in turn when called within +DELAY seconds." + (declare (indent 1)) + (let ((funs functions) + (iter (list nil)) + (timeout delay)) + (lambda () + (interactive) + (helm-run-multi-key-command funs iter timeout)))) + +(defun helm-run-multi-key-command (functions iterator delay) + (let ((fn (lambda () + (cl-loop for count from 1 to (length functions) + collect count))) + next) + (unless (and (car iterator) + ;; Reset iterator when another key is pressed. + (eq this-command real-last-command)) + (setcar iterator (helm-iter-circular (funcall fn)))) + (setq next (helm-iter-next (car iterator))) + (and next (car iterator) + (call-interactively (nth (1- next) functions))) + (when delay (run-with-idle-timer + delay nil (lambda () + (setcar iterator nil)))))) + +(helm-multi-key-defun helm-toggle-resplit-and-swap-windows + "Multi key command to re-split and swap Helm window. +First call runs `helm-toggle-resplit-window', +and second call within 1s runs `helm-swap-windows'." + '(helm-toggle-resplit-window helm-swap-windows) 1) +(put 'helm-toggle-resplit-and-swap-windows 'helm-only t) + +(defun helm-command-with-subkeys (map subkey command + &optional other-subkeys prompt exit-fn delay) + "Return an anonymous interactive command to use with `helm-define-key-with-subkeys'." + (lambda () + (interactive) + (let (timer) + (call-interactively command) + (unless (or defining-kbd-macro executing-kbd-macro) + (unwind-protect + (progn + (when delay + (setq timer (run-with-idle-timer + delay nil (lambda () (keyboard-quit))))) + (while (let ((input (read-key prompt)) other kb com) + (setq last-command-event input) + (cond + ((eq input subkey) + (call-interactively command) + (setq last-command command) + t) + ((setq other (assoc input other-subkeys)) + (call-interactively (cdr other)) + (setq last-command (cdr other)) + t) + (t + (setq kb (vector last-command-event)) + (setq com (lookup-key map kb)) + (if (commandp com) + (call-interactively com) + (setq unread-command-events + (nconc (mapcar #'identity kb) + unread-command-events))) + nil))))) + (when timer (cancel-timer timer)) + (and exit-fn (funcall exit-fn))))))) + +;;;###autoload +(defun helm-define-key-with-subkeys (map key subkey command + &optional other-subkeys + prompt exit-fn delay + docstring) + "Define in MAP a KEY and SUBKEY to COMMAND. + +This allows typing KEY to call COMMAND the first time and +type only SUBKEY on subsequent calls. + +Arg MAP is the keymap to use, SUBKEY is the initial short +key binding to call COMMAND. + +Arg OTHER-SUBKEYS is an alist specifying other short key bindings +to use once started, e.g.: + + (helm-define-key-with-subkeys global-map + (kbd \"C-x v n\") ?n \\='git-gutter:next-hunk + \\='((?p . git-gutter:previous-hunk))) + +In this example, `C-x v n' will run `git-gutter:next-hunk' +subsequent \"n\" will run this command again and subsequent \"p\" +will run `git-gutter:previous-hunk'. + +If specified PROMPT can be displayed in minibuffer to describe +SUBKEY and OTHER-SUBKEYS. Arg EXIT-FN specifies a function to run +on exit. + +For any other key pressed, run their assigned command as defined +in MAP and then exit the loop running EXIT-FN, if specified. + +If DELAY an integer is specified exit after DELAY seconds. + +NOTE: SUBKEY and OTHER-SUBKEYS bindings support only char syntax +and vectors, so don't use strings to define them. While defining +or executing a kbd macro no SUBKEY or OTHER-SUBKEYS are provided, +i.e. the loop is not entered after running COMMAND." + (declare (indent 1)) + (let ((fn (helm-command-with-subkeys + map subkey command other-subkeys prompt exit-fn delay)) + (com (intern (format "helm-%s-with-subkeys" + (symbol-name command))))) + (defalias com fn + (or docstring + ;; When no DOCSTRING, generate a basic one specifying + ;; COMMAND, SUBKEY and OTHER-SUBKEYS. + (concat + (format "Run `%s' and bound it to `%s' for subsequent calls." + command (if (numberp subkey) (char-to-string subkey) subkey)) + (if other-subkeys + (helm-basic-docstring-from-alist other-subkeys) + "")))) + (define-key map key com))) + +(defun helm-basic-docstring-from-alist (alist) + (let* ((len (length alist)) + (osk (cl-loop for (k . v) in alist + for count from 1 + for sep = (cond ((and (= count len) (> len 1)) + " and ") + ((> count 1) ",") + (t "")) + for key = (if (numberp k) (char-to-string k) k) + concat (format "%s`%s'" sep key) into ks + concat (format "%s`%s'" sep v) into kv + finally return (list ks kv)))) + (format "\nBound as well %s to %s%s." + (car osk) (if (> len 1) "respectively " "") (cadr osk)))) + +;;; Keymap +;; +;; +(defvar helm-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map minibuffer-local-map) + (define-key map (kbd "") #'helm-next-line) + (define-key map (kbd "") #'helm-previous-line) + (define-key map (kbd "C-n") #'helm-next-line) + (define-key map (kbd "C-p") #'helm-previous-line) + (define-key map (kbd "") #'helm-follow-action-forward) + (define-key map (kbd "") #'helm-follow-action-backward) + (define-key map (kbd "") #'helm-previous-page) + (define-key map (kbd "") #'helm-next-page) + (define-key map (kbd "M-v") #'helm-scroll-up) + (define-key map (kbd "C-v") #'helm-scroll-down) + (define-key map (kbd "M-<") #'helm-beginning-of-buffer) + (define-key map (kbd "M->") #'helm-end-of-buffer) + (define-key map (kbd "C-g") #'helm-keyboard-quit) + (define-key map (kbd "") #'helm-maybe-exit-minibuffer) + (define-key map (kbd "C-i") #'helm-select-action) + (define-key map (kbd "C-j") #'helm-execute-persistent-action) + (define-key map (kbd "C-o") #'helm-next-source) + (define-key map (kbd "M-o") #'helm-previous-source) + (define-key map (kbd "") #'helm-next-source) + (define-key map (kbd "") #'helm-previous-source) + (define-key map (kbd "C-l") #'helm-recenter-top-bottom-other-window) + (define-key map (kbd "M-C-l") #'helm-reposition-window-other-window) + (define-key map (kbd "C-M-v") #'helm-scroll-other-window) + (define-key map (kbd "M-") #'helm-scroll-other-window) + (define-key map (kbd "C-M-y") #'helm-scroll-other-window-down) + (define-key map (kbd "C-M-S-v") #'helm-scroll-other-window-down) + (define-key map (kbd "M-") #'helm-scroll-other-window-down) + (define-key map (kbd "") #'helm-scroll-other-window) + (define-key map (kbd "") #'helm-scroll-other-window-down) + (define-key map (kbd "C-@") #'helm-toggle-visible-mark) + (define-key map (kbd "C-SPC") #'helm-toggle-visible-mark-forward) + (define-key map (kbd "M-SPC") #'helm-toggle-visible-mark-backward) + (define-key map (kbd "M-[") nil) + (define-key map (kbd "M-(") #'helm-prev-visible-mark) + (define-key map (kbd "M-)") #'helm-next-visible-mark) + (define-key map (kbd "C-k") #'helm-delete-minibuffer-contents) + (define-key map (kbd "C-x C-f") #'helm-quit-and-find-file) + (define-key map (kbd "M-m") #'helm-toggle-all-marks) + (define-key map (kbd "M-a") #'helm-mark-all) + (define-key map (kbd "M-U") #'helm-unmark-all) + (define-key map (kbd "C-M-a") #'helm-show-all-candidates-in-source) + (define-key map (kbd "C-M-e") #'helm-display-all-sources) + (define-key map (kbd "C-s") #'undefined) + (define-key map (kbd "M-s") #'undefined) + (define-key map (kbd "C-}") #'helm-narrow-window) + (define-key map (kbd "C-{") #'helm-enlarge-window) + (define-key map (kbd "C-c -") #'helm-swap-windows) + (define-key map (kbd "C-c _") #'helm-toggle-full-frame) + (define-key map (kbd "C-c %") #'helm-exchange-minibuffer-and-header-line) + (define-key map (kbd "C-c C-y") #'helm-yank-selection) + (define-key map (kbd "C-c C-k") #'helm-kill-selection-and-quit) + (define-key map (kbd "C-c C-i") #'helm-insert-or-copy) + (define-key map (kbd "C-c C-f") #'helm-follow-mode) + (define-key map (kbd "C-c C-u") #'helm-refresh) + (define-key map (kbd "C-c >") #'helm-toggle-truncate-line) + (define-key map (kbd "C-c l") #'helm-display-line-numbers-mode) + (define-key map (kbd "M-p") #'previous-history-element) + (define-key map (kbd "M-n") #'next-history-element) + (define-key map (kbd "C-!") #'helm-toggle-suspend-update) + (define-key map (kbd "C-x b") #'helm-resume-previous-session-after-quit) + (define-key map (kbd "C-x C-b") #'helm-resume-list-buffers-after-quit) + (helm-define-key-with-subkeys map (kbd "C-c n") ?n #'helm-run-cycle-resume) + ;; Disable `file-cache-minibuffer-complete'. + (define-key map (kbd "") #'undefined) + ;; Multi keys + (define-key map (kbd "C-t") #'helm-toggle-resplit-and-swap-windows) + ;; Debugging command + (define-key map (kbd "C-h C-d") #'helm-enable-or-switch-to-debug) + (define-key map (kbd "C-h c") #'helm-customize-group) + ;; Allow to eval keymap without errors. + (define-key map [f1] nil) + (define-key map (kbd "C-h C-h") #'undefined) + (define-key map (kbd "C-h h") #'undefined) + (helm-define-key-with-subkeys map + (kbd "C-w") ?\C-w #'helm-yank-text-at-point + '((?\C-_ . helm-undo-yank-text-at-point))) + ;; Use `describe-mode' key in `global-map'. + (cl-dolist (k (where-is-internal #'describe-mode global-map)) + (define-key map k #'helm-help)) + ;; Bind all actions from f1 to f12, `helm-select-nth-action' + ;; counts from 0, i.e. (helm-select-nth-action 0) = action 1. + (dotimes (n 12) + (define-key map (kbd (format "" (1+ n))) + (lambda () + (interactive) + (helm-select-nth-action n)))) + map) + "Keymap for helm.") + +(defun helm-customize-group-1 (group) + (require 'cus-edit) + (let ((name (format "*Customize Group: %s*" + (custom-unlispify-tag-name group)))) + (if (buffer-live-p (get-buffer name)) + (switch-to-buffer name) + (custom-buffer-create + (list (list group 'custom-group)) + name + (concat " for group " + (custom-unlispify-tag-name group)))))) + +(defun helm-customize-group () + "Jump to customization group of current source. + +Default to Helm group when group is not defined in source." + (interactive) + (let ((source (or (helm-get-current-source) + (helm-comp-read + "Customize variables for: " + (cl-loop for src in (with-helm-buffer helm-sources) + collect `(,(assoc-default 'name src) . + ,src)) + :allow-nest t + :exec-when-only-one t)))) + (helm-run-after-exit 'helm-customize-group-1 (helm-get-attr 'group source)))) +(put 'helm-customize-group 'helm-only t) + +(defun helm--action-at-nth-set-fn-1 (value &optional negative) + (dotimes (n 9) + (let ((key (format value (1+ n))) + (fn (lambda () + (interactive) + (helm-execute-selection-action-at-nth + (if negative (- (1+ n)) (1+ n)))))) + (define-key helm-map (kbd key) nil) + (define-key helm-map (kbd key) fn)))) + +(defun helm--action-at-nth-set-fn- (var val) + (set var val) + (helm--action-at-nth-set-fn-1 val 'negative)) + +(defun helm--action-at-nth-set-fn+ (var val) + (set var val) + (helm--action-at-nth-set-fn-1 val)) + +(defcustom helm-action-at-nth-negative-prefix-key "C-x %d" + "The prefix key to execute default action on nth <-n> candidate. + +This is a format spec where %d will be replaced by the candidate +number. + +NOTE: `setq' have no effect until you restart Emacs, use +customize for immediate effect." + :group 'helm + :type 'string + :set #'helm--action-at-nth-set-fn-) + +(defcustom helm-action-at-nth-positive-prefix-key "C-c %d" + "The prefix key to execute default action on nth <+n> candidate. + +This is a format spec where %d will be replaced by the candidate +number. + +NOTE: `setq' have no effect until you restart Emacs, use +customize for immediate effect." + :group 'helm + :type 'string + :set #'helm--action-at-nth-set-fn+) + + +(defgroup helm nil + "Open Helm." + :prefix "helm-" :group 'convenience) + +;; Easy access to customize +;;;###autoload +(defun helm-configuration () + "Customize Helm." + (interactive) + (customize-group "helm")) + +(defcustom helm-completion-window-scroll-margin 5 + "`scroll-margin' to use for Helm completion window. +Set to 0 to disable. +NOTE: This has no effect when `helm-display-source-at-screen-top' +id is non-nil." + :group 'helm + :type 'integer) + +(defcustom helm-left-margin-width 0 + "`left-margin-width' value for the `helm-buffer'." + :group 'helm + :type 'integer) + +(defcustom helm-display-source-at-screen-top t + "Display candidates at the top of screen. +This happens with `helm-next-source' and `helm-previous-source'. +NOTE: When non-nil (default), disable +`helm-completion-window-scroll-margin'." + :group 'helm + :type 'boolean) + +(defcustom helm-candidate-number-limit 100 + "Global limit for number of candidates displayed. +When the pattern is empty, the number of candidates shown will be +as set here instead of the entire list, which may be hundreds or +thousands. Since narrowing and filtering rapidly reduces +available candidates, having a small list will keep the interface +responsive. + +Set this value to nil for no limit." + :group 'helm + :type '(choice (const :tag "Disabled" nil) integer)) + +(defcustom helm-input-idle-delay 0.01 + "Idle time before updating, specified in seconds." + :group 'helm + :type 'float) + +(defcustom helm-exit-idle-delay 0 + "Idle time before exiting minibuffer while Helm is updating. +Has no affect when helm-buffer is up to date (i.e. exit without +delay in this condition)." + :group 'helm + :type 'float) + +(defvaralias 'helm-samewindow 'helm-full-frame) +(make-obsolete-variable 'helm-samewindow 'helm-full-frame "1.4.8.1") +(defcustom helm-full-frame nil + "Use current window for showing candidates. +If t, then Helm does not pop-up a new window." + :group 'helm + :type 'boolean) + +(defcustom helm-candidate-separator + (if (fontp (char-displayable-p (read "#x2015"))) + "――――――――――――――――――――――――――――――――――――――" + "--------------------------------------") + "Candidates separator of `multiline' source." + :group 'helm + :type 'string) + +(defcustom helm-save-configuration-functions + '(set-window-configuration . current-window-configuration) + "Functions used to restore or save configurations for frames and windows. +Specified as a pair of functions, where car is the restore +function and cdr is the save function. + +To save and restore frame configuration, set this variable to +\\='(set-frame-configuration . current-frame-configuration) + +NOTE: This may not work properly with own-frame minibuffer +settings. Older versions saves/restores frame configuration, but +the default has changed now to avoid flickering." + :group 'helm + :type 'sexp) + +(defcustom helm-display-function 'helm-default-display-buffer + "Function used to display `helm-buffer'. + +Local value in `helm-buffer' will take precedence on this default +value. Commands that are in `helm-commands-using-frame' will have +`helm-buffer' displayed in frame, `helm-display-function' being +ignored. +If no local value is found and current command is not one of +`helm-commands-using-frame' use this default value. +The function in charge of deciding which value use is +`helm-resolve-display-function'. + +To set it locally to `helm-buffer' in Helm sources use +`helm-set-local-variable' in init function or use +:display-function slot in `helm' call." + :group 'helm + :type 'symbol) + +(defcustom helm-case-fold-search 'smart + "Adds \\='smart' option to `case-fold-search'. +Smart option ignores case for searches as long as there are no +upper case characters in the pattern. + +Use nil or t to turn off smart behavior and use +`case-fold-search' behavior. + +Default is smart. + +NOTE: Case fold search has no effect when searching asynchronous +sources, which relies on customized features implemented directly +into their execution process. See helm-grep.el for an example." + :group 'helm + :type '(choice (const :tag "Ignore case" t) + (const :tag "Respect case" nil) + (other :tag "Smart" smart))) + +(defcustom helm-file-name-case-fold-search + (if (memq system-type + '(cygwin windows-nt ms-dos darwin)) + t + helm-case-fold-search) + "Local setting of `helm-case-fold-search' for reading filenames. + +See `helm-case-fold-search' for more info." + :group 'helm + :type 'symbol) + +(defcustom helm-reuse-last-window-split-state nil + "Use the same state of window split, vertical or horizontal. +`helm-toggle-resplit-window' for the next helm session will use +the same window scheme as the previous session unless +`helm-split-window-default-side' is \\='same or \\='other." + :group 'helm + :type 'boolean) + +(defcustom helm-split-window-preferred-function 'helm-split-window-default-fn + "Default function used for splitting window." + :group 'helm + :type 'function) + +(defcustom helm-split-window-default-side 'below + "The default side to display `helm-buffer'. +Must be one acceptable arg for `split-window' SIDE, +that is `below', `above', `left' or `right'. + +Other acceptable values are `same' which always displays +`helm-buffer' in current window and `other' that displays +`helm-buffer' below if only one window or in +`other-window-for-scrolling' when available. + +A nil value has same effect as `below'. If `helm-full-frame' is +non-nil, it take precedence over this setting. + +See also `helm-split-window-inside-p' and +`helm-always-two-windows' that take precedence over this. + +NOTE: this has no effect if +`helm-split-window-preferred-function' is not +`helm-split-window-default-fn' unless this new function can +handle this." + :group 'helm + :type 'symbol) + +(defcustom helm-split-window-other-side-when-one-window 'below + "The default side to display `helm-buffer' when (1) +`helm-split-window-default-side' is \\='other and (2) +the current frame only has one window. Possible values +are acceptable args for `split-window' SIDE, that is `below', +`above', `left' or `right'. + +If `helm-full-frame' is non-nil, it takes precedence over this +setting. + +See also `helm-split-window-inside-p' and `helm-always-two-windows' that +takes precedence over this. + +NOTE: this has no effect if +`helm-split-window-preferred-function' is not +`helm-split-window-default-fn' unless this new function can +handle this." + :group 'helm + :type 'symbol) + +(defcustom helm-display-buffer-default-height nil + "Initial height of `helm-buffer', specified as an integer or a function. + +The function should take one arg and be responsible for re-sizing +the window; function's return value is ignored. Note that this +has no effect when the split is vertical. See `display-buffer' +for more info." + :group 'helm + :type '(choice integer function)) + +(defcustom helm-display-buffer-default-width nil + "Initial width of `helm-buffer', specified as an integer or a function. + +The function should take one arg and be responsible for re-sizing +the window; function's return value is ignored. Note that this +have no effect when the split is horizontal. See `display-buffer' +for more info." + :group 'helm + :type '(choice integer function)) + +(defvaralias 'helm-split-window-in-side-p 'helm-split-window-inside-p) +(make-obsolete-variable 'helm-split-window-in-side-p 'helm-split-window-inside-p "2.8.6") +(defcustom helm-split-window-inside-p nil + "Force split inside selected window when non-nil. +See also `helm-split-window-default-side'. + +NOTE: this has no effect if +`helm-split-window-preferred-function' is not +`helm-split-window-default-fn' unless this new function can +handle this." + :group 'helm + :type 'boolean) + +(defcustom helm-always-two-windows nil + "When non-nil Helm uses two windows in this frame. + +I.e. `helm-buffer' in one window and `helm-current-buffer' +in the other. + +Note: this has no effect when `helm-split-window-inside-p' is +non-nil, or when `helm-split-window-default-side' is set to +\\='same. + +When `helm-autoresize-mode' is enabled, setting this to nil +will have no effect. + +Also when non-nil it overrides the effect of +`helm-split-window-default-side' set to `other'." + :group 'helm + :type 'boolean) + +(defcustom helm-display-buffer-width 72 + "Frame width when displaying helm-buffer in own frame." + :group 'helm + :type 'integer) + +(defcustom helm-display-buffer-height 20 + "Frame height when displaying helm-buffer in own frame." + :group 'helm + :type 'integer) + +(defcustom helm-default-display-buffer-functions nil + "Action functions to pass to `display-buffer'. +See (info \"(elisp) Buffer Display Action Functions\"). + +It has no effect when `helm-always-two-windows' is non-nil and +may override other settings like `helm-split-window-inside-p'." + :group 'helm + :type '(repeat symbol)) + +(defcustom helm-default-display-buffer-alist nil + "Additional alist to pass to `display-buffer' action. +See (info \"(elisp) Action Alists for Buffer Display\"). + +It has no effect when `helm-always-two-windows' is non-nil and +may override other settings like `helm-split-window-inside-p'. +Note that window-height and window-width have to be configured in +`helm-display-buffer-height' and `helm-display-buffer-width'." + :group 'helm + :type '(alist :key-type symbol :value-type sexp)) + +(defcustom helm-sources-using-default-as-input '(helm-source-imenu + helm-source-imenu-all + helm-source-info-elisp + helm-source-etags-select + helm-source-man-pages + helm-source-occur + helm-source-moccur + helm-source-grep-ag + helm-source-grep-git + helm-source-grep) + "List of Helm sources that need to use `helm-maybe-use-default-as-input'. +When a source is a member of this list, default `thing-at-point' +will be used as input." + :group 'helm + :type '(repeat (choice symbol))) + +(defcustom helm-delete-minibuffer-contents-from-point t + "When non-nil, `helm-delete-minibuffer-contents' deletes region from `point'. +Otherwise it deletes `minibuffer-contents'. +See documentation for `helm-delete-minibuffer-contents'." + :group 'helm + :type 'boolean) + +(defcustom helm-follow-mode-persistent nil + "When non-nil, save last state of `helm-follow-mode' for the next Emacs sessions. + +Each time you turn on or off `helm-follow-mode', the current +source name will be stored or removed from +`helm-source-names-using-follow'. + +Note that this may be disabled in some places where it is unsafe +to use because persistent action is changing according to +context." + :group 'helm + :type 'boolean) + +(defcustom helm-source-names-using-follow nil + "A list of source names to have follow enabled. +This list of source names will be used only +when `helm-follow-mode-persistent' is non-nil. + +You don't have to customize this yourself unless you really want +and know what you are doing, instead just set +`helm-follow-mode-persistent' to non-nil and as soon as you turn +on or off `helm-follow-mode' (C-c C-f) in a source, Helm will +save or remove source name in this variable." + :group 'helm + :type '(repeat (choice string))) + +(defcustom helm-prevent-escaping-from-minibuffer t + "Prevent escaping from minibuffer with `other-window' during the Helm session." + :group 'helm + :type 'boolean) + +(defcustom helm-allow-mouse nil + "Allow mouse usage during the Helm session when non-nil. + +Note that this also allows moving out of minibuffer when clicking +outside of `helm-buffer', so it is up to you to get back to Helm +by clicking back in `helm-buffer' or minibuffer." + :group 'helm + :type 'boolean) + +(defcustom helm-move-to-line-cycle-in-source nil + "Cycle to the beginning or end of the list after reaching the bottom or top. +This applies when using `helm-next/previous-line'." + :group 'helm + :type 'boolean) + +(defcustom helm-fuzzy-match-fn 'helm-fuzzy-match + "The function for fuzzy matching in `helm-source-sync' based sources." + :group 'helm + :type 'function) + +(defcustom helm-fuzzy-search-fn 'helm-fuzzy-search + "The function for fuzzy matching in `helm-source-in-buffer' based sources." + :group 'helm + :type 'function) + +(defcustom helm-fuzzy-sort-fn 'helm-fuzzy-matching-default-sort-fn + "The sort transformer function used in fuzzy matching." + :group 'helm + :type 'function) + +(defcustom helm-fuzzy-matching-highlight-fn 'helm-fuzzy-default-highlight-match + "The function to highlight fuzzy matches." + :group 'helm + :type 'function) + +(defcustom helm-autoresize-max-height 40 + "Specify maximum height and defaults to percent of Helm window's frame height. + +See `fit-window-to-buffer' for more infos." + :group 'helm + :type 'integer) + +(defcustom helm-autoresize-min-height 10 + "Specify minimum height and defaults to percent of Helm window's frame height. + +If nil, `window-min-height' is used. +See `fit-window-to-buffer' for details." + :group 'helm + :type 'integer) + +(defcustom helm-input-method-verbose-flag nil + "The default value for `input-method-verbose-flag' used in Helm minibuffer. +It is nil by default, which does not turn off input method. Helm +updates and exits without interruption -- necessary for complex +methods. + +If set to any other value as per `input-method-verbose-flag', +then use `C-\\' to disable the `current-input-method' to exit or +update Helm." + :group 'helm + :type '(radio :tag "A flag to control extra guidance for input methods in helm." + (const :tag "Never provide guidance" nil) + (const :tag "Always provide guidance" t) + (const :tag "Provide guidance only for complex methods" complex-only))) + +(defcustom helm-display-header-line t + "Display header-line when non nil." + :group 'helm + :type 'boolean) + +(defcustom helm-inherit-input-method t + "Inherit `current-input-method' from `current-buffer' when non-nil. +The default is to enable this by default and then toggle +`toggle-input-method'." + :group 'helm + :type 'boolean) + +(defcustom helm-echo-input-in-header-line nil + "Send current input to header-line when non-nil." + :group 'helm + :type 'boolean) + +(defcustom helm-header-line-space-before-prompt 'left-fringe + "Specify the space before prompt in header-line. + +This will be used when `helm-echo-input-in-header-line' is +non-nil. + +Value can be one of the symbols \\='left-fringe or \\='left-margin or +an integer specifying the number of spaces before prompt. Note +that on input longer that `window-width' the continuation string +will be shown on left side of window without taking care of +this." + :group 'helm + :type '(choice + (symbol + (const :tag "Fringe" left-fringe) + (const :tag "Margin" left-margin)) + integer)) + +(defcustom helm-tramp-connection-min-time-diff 5 + "Value of `tramp-connection-min-time-diff' for Helm remote processes. +If set to zero Helm remote processes are not delayed. + +Setting this to a value less than 5 or disabling it with a zero +value is risky, however on Emacs versions starting at 24.5 it +seems it is now possible to disable it. + +Anyway at any time in Helm you can suspend your processes while +typing by hitting \\ `\\[helm-toggle-suspend-update]'. + +Only async sources than use a sentinel calling +`helm-process-deferred-sentinel-hook' are affected by this." + :type 'integer + :group 'helm) + +(defcustom helm-debug-root-directory nil + "When non-nil, save Helm log messages to a file in this directory. +When nil log messages are saved to a buffer instead. Log message +are saved only when `helm-debug' is non-nil, so setting this +doesn't enable debugging by itself. + +See `helm-log-save-maybe' for more info." + :type 'string + :group 'helm) + +(defcustom helm-show-action-window-other-window nil + "Show action buffer beside `helm-buffer' when non-nil. + +If nil don't split and replace helm-buffer by the action buffer +in same window. +If left display the action buffer at the left of helm-buffer. +If right or any other value, split at right. + +Note that this may not fit well with some Helm window +configurations, so it have only effect when +`helm-always-two-windows' is non-nil." + :group 'helm + :type '(choice + (const :tag "Split at left" left) + (const :tag "Don't split" nil) + (other :tag "Split at right" right))) + +(defcustom helm-cycle-resume-delay 1.0 + "Delay used before resuming in `helm-run-cycle-resume'." + :type 'float + :group 'helm) + +(defcustom helm-display-buffer-reuse-frame nil + "When non nil Helm frame is not deleted and reused in next sessions. + +This was used to workaround a bug in Emacs where frames where +popping up slowly, now that the bug have been fixed upstream +\(emacs-27) probably you don't want to use this any more. On +emacs-26 set `x-wait-for-event-timeout' to nil to have your +frames popping up fast." + :group 'helm + :type 'boolean) + +(defcustom helm-commands-using-frame nil + "A list of commands where `helm-buffer' is displayed in a frame." + :group 'helm + :type '(repeat symbol)) + +(defcustom helm-actions-inherit-frame-settings t + "Actions inherit Helm frame settings of initial command when non nil." + :group 'helm + :type 'boolean) + +(defcustom helm-use-undecorated-frame-option t + "Display Helm frame undecorated when non nil. + +This option has no effect with Emacs versions lower than 26." + :group 'helm + :type 'boolean) + +(defcustom helm-frame-background-color nil + "Background color for Helm frames, a string. +Fallback to default face background when nil." + :group 'helm + :type 'string) + +(defcustom helm-frame-foreground-color nil + "Foreground color for Helm frames, a string. +Fallback to default face foreground when nil" + :group 'helm + :type 'string) + +(defcustom helm-frame-alpha nil + "Alpha parameter for Helm frames, an integer. +Fallback to 100 when nil." + :group 'helm + :type 'integer) + +(defcustom helm-use-frame-when-more-than-two-windows nil + "Display Helm buffer in frame when more than two windows." + :group 'helm + :type 'boolean) + +(defvaralias 'helm-use-frame-when-dedicated-window + 'helm-use-frame-when-no-suitable-window) + +(defcustom helm-use-frame-when-no-suitable-window nil + "Display Helm buffer in frame when Helm is started from a dedicated window." + :group 'helm + :type 'boolean) +(make-obsolete-variable 'helm-use-frame-when-dedicated-window + 'helm-use-frame-when-no-suitable-window + "3.8.1") + +(defcustom helm-default-prompt-display-function + #'helm-set-default-prompt-display + "The function to use to set face of fake cursor in header-line." + :group 'helm + :type 'function) + +(defcustom helm-truncate-lines nil + "The value of `truncate-lines' when Helm starts. +You can toggle later `truncate-lines' with +\\\\[helm-toggle-truncate-line]." + :group 'helm + :type 'boolean) + +(defcustom helm-visible-mark-prefix "*" + "Prefix used in margin for marked candidates. +Set this to an empty string if you don't want prefix in margin when marking." + :group 'helm + :type 'string) + +;;; Faces +;; +;; +(defgroup helm-faces nil + "Customize the appearance of Helm." + :prefix "helm-" + :group 'faces + :group 'helm) + +(defface helm-source-header + `((((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#22083397778B" + :foreground "white" + :weight bold :height 1.3 :family "Sans Serif") + (((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#abd7f0" + :foreground "black" + :weight bold :height 1.3 :family "Sans Serif")) + "Face for source header in the Helm buffer." + :group 'helm-faces) + +(defface helm-visible-mark + `((((min-colors 88) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "green1" + :foreground "black") + (((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "green" + :foreground "black") + (((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#d1f5ea") + (((min-colors 88)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "green1") + (t ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "green")) + "Face for visible mark." + :group 'helm-faces) + +(defface helm-header + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit header-line)) + "Face for header lines in the Helm buffer." + :group 'helm-faces) + +(defface helm-candidate-number + `((((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "Yellow" :foreground "black") + (((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#faffb5" :foreground "black")) + "Face for candidate number in mode-line." + :group 'helm-faces) + +(defface helm-candidate-number-suspended + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit helm-candidate-number :inverse-video t)) + "Face for candidate number in mode-line when Helm is suspended." + :group 'helm-faces) + +(defface helm-selection + `((((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "ForestGreen" + :distant-foreground "black") + (((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#b5ffd1" + :distant-foreground "black")) + "Face for currently selected item in the Helm buffer." + :group 'helm-faces) + +(defface helm-separator + `((((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "red") + (((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "#ffbfb5")) + "Face for multiline source separator." + :group 'helm-faces) + +(defface helm-action + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :underline t)) + "Face for action lines in the Helm action buffer." + :group 'helm-faces) + +(defface helm-prefarg + `((((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "green") + (((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "red")) + "Face for showing prefix arg in mode-line." + :group 'helm-faces) + +(defface helm-match + `((((background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "#b00000") + (((background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "gold1")) + "Face used to highlight matches." + :group 'helm-faces) + +(defface helm-header-line-left-margin + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "black" :background "yellow")) + "Face used to highlight helm-header sign in left-margin." + :group 'helm-faces) + +(defface helm-minibuffer-prompt + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit minibuffer-prompt)) + "Face used for the minibuffer/headline prompt (such as Pattern:) in Helm." + :group 'helm-faces) + +(defface helm-eob-line + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit default)) + "Face for empty line at end of sources in the Helm buffer. +Allow specifying the height of this line." + :group 'helm-faces) + +(defface helm-mark-prefix + `((t :inherit default)) + "Face for string `helm-visible-mark-prefix'." + :group 'helm-faces) + +;;; Variables. +;; +;; +(defvar helm-selection-overlay nil + "Overlay used to highlight the currently selected item.") + +(defvar helm-async-processes nil + "List of information about asynchronous processes managed by Helm.") + +(defvar helm-before-initialize-hook nil + "Runs before Helm initialization. +This hook runs before init functions in `helm-sources', which is +before creation of `helm-buffer'. Set local variables for +`helm-buffer' that need a value from `current-buffer' with +`helm-set-local-variable'.") + +(defvar helm-after-initialize-hook nil + "Runs after Helm initialization. +This hook runs after `helm-buffer' is created but not from +`helm-buffer'. The hook needs to specify in which buffer to +run.") + +(defvaralias 'helm-update-hook 'helm-after-update-hook) +(make-obsolete-variable 'helm-update-hook 'helm-after-update-hook "1.9.9") + +(defvar helm-after-update-hook nil + "Runs after updating the Helm buffer with the new input pattern.") + +(defvar helm-before-update-hook nil + "Runs before updating the Helm buffer with the new input pattern.") + +(defvar helm-cleanup-hook nil + "Runs after exiting the minibuffer and before performing an +action. + +This hook runs even if Helm exits the minibuffer abnormally (e.g. +via `helm-keyboard-quit').") + +(defvar helm-select-action-hook nil + "Runs when opening the action buffer.") + +(defvar helm-before-action-hook nil + "Runs before executing action. +Unlike `helm-cleanup-hook', this hook runs before Helm closes the +minibuffer and also before performing an action.") + +(defvar helm-after-action-hook nil + "Runs after executing action.") + +(defvar helm-exit-minibuffer-hook nil + "Runs just before exiting the minibuffer. + +This hook runs when Helm exits the minibuffer normally (e.g., via +candidate selection), but does NOT run if Helm exits the +minibuffer abnormally (e.g. via `helm-keyboard-quit').") + +(defvar helm-after-persistent-action-hook nil + "Runs after executing persistent action.") + +(defvar helm-move-selection-before-hook nil + "Runs before moving selection in `helm-buffer'.") + +(defvar helm-move-selection-after-hook nil + "Runs after moving selection in `helm-buffer'.") + +(defvar helm-after-preselection-hook nil + "Runs after pre-selection in `helm-buffer'.") + +(defvar helm-window-configuration-hook nil + "Runs when switching to and from the action buffer. +Should run also at end of `helm-display-function'.") + +(defvar helm-execute-action-at-once-if-one nil + "When non-nil execute the default action and then exit if only one candidate. +If symbol \\='current-source is given as value exit if only one +candidate in current source. This variable accepts a function +with no args that should returns a boolean value or \\='current-source.") + +(defvar helm-quit-if-no-candidate nil + "When non-nil, quit if there are no candidates. +This variable accepts a function.") + +(defvar helm-debug-variables nil + "A list of Helm variables that `helm-debug-output' displays. +If nil, `helm-debug-output' includes only variables with `helm-' +prefixes.") + +(defvar helm-debug-buffer "*Debug Helm Log*") + +(defvar helm-debug nil + "If non-nil, write log message to `helm-debug-buffer'. +Default is nil, which disables writing log messages because the +size of `helm-debug-buffer' grows quickly.") + +(defvar helm-mode-line-string "\ +\\\ +\\[helm-help]:Help \ +\\[helm-select-action]:Act \ +\\[helm-maybe-exit-minibuffer]/\ +f1..f12:NthAct \ +\\[helm-toggle-suspend-update]:Tog.suspend \ +\\[helm-customize-group]:Conf" + "Help string displayed by Helm in the mode-line. +It is either a string or a list of two string arguments where the +first string is the name and the second string is displayed in +the mode-line. When nil, it defaults to `mode-line-format'.") + +(defvar helm-minibuffer-set-up-hook nil + "Hook that runs at minibuffer initialization. +A hook useful for modifying minibuffer settings in Helm. + +An example that hides the minibuffer when using +`helm-echo-input-in-header-line': + + (add-hook \\='helm-minibuffer-set-up-hook #'helm-hide-minibuffer-maybe) + +Note that we check `helm-echo-input-in-header-line' value +from `helm-buffer' which allows detecting possible local +value of this var.") + +(defvar helm-help-message + "* Helm Generic Help +** Basics + +To navigate in this Help buffer see [[Helm help][here]]. + +Helm narrows down the list of candidates as you type a filter +pattern. See [[Matching in Helm][Matching in Helm]]. + +Helm accepts multiple space-separated patterns, each pattern can +be negated with \"!\". + +Helm also supports fuzzy matching in some places when specified, +you will find several variables to enable fuzzy matching in +diverse [[Helm sources][sources]], see [[https://github.com/emacs-helm/helm/wiki/Fuzzy-matching][fuzzy-matching]] in helm-wiki for more infos. + +Helm generally uses familiar Emacs keys to navigate the list. +Here follow some of the less obvious bindings: + +- `\\\\[helm-maybe-exit-minibuffer]' selects the +candidate from the list, executes the default action upon exiting +the Helm session. + +- `\\\\[helm-execute-persistent-action]' executes the +default action but without exiting the Helm session. Not all +sources support this. + +- `\\\\[helm-select-action]' displays a list of actions +available on current candidate or all marked candidates. The +default binding is ordinarily used for completion, but that +would be redundant since Helm completes upon every character +entered in the prompt. See [[https://github.com/emacs-helm/helm/wiki#helm-completion-vs-emacs-completion][Helm wiki]]. + +Note: In addition to the default actions list, additional actions +appear depending on the type of the selected candidate(s). They +are called filtered actions. + +** Helm sources + +Helm uses what's called sources to provide different kinds of +completions. Each Helm session can handle one or more source. A +source is an alist object which is build from various classes, +see [[Writing your own Helm sources][here]] and [[https://github.com/emacs-helm/helm/wiki/Developing#creating-a-source][Helm wiki]] for more infos. + +*** Configure sources + +You will find in Helm sources already built and bound to a +variable called generally `helm-source-'. In this case +it is an alist and you can change the attributes (keys) values +using `helm-set-attr' function in your configuration. Of course +you have to ensure before calling `helm-set-attr' that the file +containing source is loaded, e.g. with `with-eval-after-load'. Of +course you can also completely redefine the source but this is +generally not elegant as it duplicate for its most part code +already defined in Helm. + +You will find also sources that are not built and even not bound +to any variables because they are rebuilded at each start of a +Helm session. In this case you can add a defmethod called +`helm-setup-user-source' to your config: + +#+begin_src elisp + + (cl-defmethod helm-setup-user-source ((source helm-moccur-class)) + (setf (slot-value source 'follow) -1)) + +#+end_src + +See +[[https://github.com/emacs-helm/helm/wiki/FAQ#why-is-a-customizable-helm-source-nil][here]] +for more infos, and for more complex examples of configuration +[[https://github.com/thierryvolpiatto/emacs-tv-config/blob/master/init-helm.el#L340][here]]. + +** Modify keybindings in Helm + +Helm main keymap is `helm-map', all keys bound in this map apply +to all Helm sources. However, most sources have their own keymap, +with each binding overriding its counterpart in `helm-map', you +can see all bindings in effect in the [[Commands][Commands]] +section (available only if the source has its own keymap and +documentation of course). + +** Matching in Helm + +All that you write in minibuffer is interpreted as a regexp or +multiple regexps if separated by a space. This is true for most +sources unless the developer of the source has disabled it or +have choosen to use fuzzy matching. Even if a source has fuzzy +matching enabled, Helm will switch to multi match as soon as it +detects a space in the pattern. It may also switch to multi match +as well if pattern starts with a \"^\" beginning of line sign. In +those cases each pattern separated with space should be a regexp +and not a fuzzy pattern. When using multi match patterns, each +pattern starting with \"!\" is interpreted as a negation i.e. +match everything but this. + +*** Completion-styles + +UPDATE: At version 3.8.0 Helm default is now to NOT use +`completion-styles' i.e. now `helm-completion-style' default to +'helm and no more to 'emacs. + +If you want to use `completion-styles' in Helm customize +`helm-completion-style' to 'emacs. + +Helm generally fetches its candidates with the :candidates +function up to `helm-candidate-number-limit' and then applies +match functions to these candidates according to `helm-pattern'. +But Helm allows matching candidates directly from the :candidates +function using its own `completion-styles'. +Helm provides 'helm completion style but also 'helm-flex +completion style for Emacs<27 that don't have 'flex completion +style, otherwise (emacs-27) 'flex completion style is used to +provide fuzzy aka flex completion. + +When using helm-fuzzy as `helm-completion-style' helm use its own +fuzzy implementation which have nothing to do with emacs `flex' +style. + +Any Helm sources can use `completion-styles' +by using :match-dynamic slot and building their :candidates +function with `helm-dynamic-completion'. + +Example: + +#+begin_src elisp + + (helm :sources (helm-build-sync-source \"test\" + :candidates (helm-dynamic-completion + '(foo bar baz foab) + 'symbolp) + :match-dynamic t) + :buffer \"*helm test*\") + +#+end_src + +By default Helm sets up `completion-styles' and always adds 'helm +to it. However the flex completion styles are not added. This is +up to the user if she wants to have such completion to enable +this. +As specified above use 'flex for emacs-27 and 'helm-flex for +emacs-26. Anyway, 'helm-flex is not provided in +`completion-styles-alist' if 'flex is present. + +Finally Helm provides two user variables to control +`completion-styles' usage: `helm-completion-style' and +`helm-completion-styles-alist'. Both variables are customizable. +The former allows retrieving previous Helm behavior if needed, by +setting it to `helm' or `helm-fuzzy', default being `emacs' which +allows dynamic completion and usage of `completion-styles'. The +second allows setting `helm-completion-style' per mode and also +specifying `completion-styles' per mode (see its docstring). Note +that these two variables take effect only in helm-mode i.e. in +all that uses `completion-read' or `completion-in-region', IOW all +helmized commands. File completion in `read-file-name' family +doesn't obey completion-styles and has its own file completion +implementation. Native Helm commands using `completion-styles' +doesn't obey `helm-completion-style' and +`helm-completion-styles-alist' (e.g., helm-M-x). + +Also for a better control of styles in native Helm sources (not +helmized by helm-mode) using :match-dynamic, +`helm-dynamic-completion' provides a STYLES argument that allows +specifying explicitely styles for this source. + +NOTE: Some old completion styles are not working fine with Helm +and are disabled by default in +`helm-blacklist-completion-styles'. They are anyway not useful in +Helm because 'helm style supersedes these styles. + +** Helm mode + +`helm-mode' toggles Helm completion in native Emacs functions, so +when you turn `helm-mode' on, commands like `switch-to-buffer' +will use Helm completion instead of the usual Emacs completion +buffer. + +*** What gets or does not get \"helmized\" when `helm-mode' is enabled? + +Helm provides generic completion on all Emacs functions using +`completing-read', `completion-in-region' and their derivatives, +e.g. `read-file-name'. Helm exposes a user variable to control +which function to use for a specific Emacs command: +`helm-completing-read-handlers-alist'. If the function for a +specific command is nil, it turns off Helm completion. See the +variable documentation for more infos. + +*** Helm functions vs helmized Emacs functions + +While there are Helm functions that perform the same completion +as other helmized Emacs functions, e.g. `switch-to-buffer' and +`helm-buffers-list', the native Helm functions like +`helm-buffers-list' can receive new features that allow marking +candidates, have several actions, etc. Whereas the helmized Emacs +functions only have Helm completion, Emacs can provide no more +than one action for this function. This is the intended behavior. + +Generally you are better off using the native Helm command than +the helmized Emacs equivalent. + +*** Completion behavior with Helm and completion-at-point + +Helm is NOT completing dynamically. That means that when you are +completing some text at point, completion is done against this +text and subsequent characters you add AFTER this text. This +allows you to use matching methods provided by Helm, that is multi +matching or fuzzy matching (see [[Matching in Helm][Matching in +Helm]]). + +Completion is not done dynamically (against `helm-pattern') +because backend functions (i.e. `completion-at-point-functions') +are not aware of Helm matching methods. + +By behaving like this, the benefit is that you can fully use Helm +matching methods but you can't start a full completion against a +prefix different than the initial text you have at point. Helm +warns you against this by colorizing the initial input and sends +a user-error message when trying to delete backward text beyond +this limit at first hit on DEL. A second hit on DEL within a +short delay (1s) quits Helm and delete-backward char in +current-buffer. + +** Helm help + +\\[helm-documentation]: Show all Helm documentations concatenated +in one org file. + +From a Helm session, just hit \\\\[helm-help] to have +the documentation for the current source followed by the global +Helm documentation. + +While in the help buffer, most of the Emacs regular key bindings +are available; the most important ones are shown in minibuffer. +However, due to implementation restrictions, no regular Emacs +keymap is used (it runs in a loop when reading the help buffer). +Only simple bindings are available and they are defined in +`helm-help-hkmap', which is a simple alist of (key . function). +You can define or redefine bindings in help with +`helm-help-define-key' or by adding/removing entries directly in +`helm-help-hkmap'. +See `helm-help-hkmap' for restrictions on bindings and functions. + +The documentation of default bindings are: + +| Key | Alternative keys | Command | +|-----------+------------------+---------------------| +| C-v | Space next | Scroll up | +| M-v | b prior | Scroll down | +| C-s | | Isearch forward | +| C-r | | Isearch backward | +| C-a | | Beginning of line | +| C-e | | End of line | +| C-f | right | Forward char | +| C-b | left | Backward char | +| C-n | down | Next line | +| C-p | up | Previous line | +| M-a | | Backward sentence | +| M-e | | Forward sentence | +| M-f | | Forward word | +| M-b | | Backward word | +| M-> | | End of buffer | +| M-< | | Beginning of buffer | +| C- | | Toggle mark | +| C-M-SPACE | | Mark sexp | +| RET | | Follow org link | +| C-% | | Push org mark | +| C-& | | Goto org mark-ring | +| TAB | | Org cycle | +| M- | | Toggle visibility | +| M-w | | Copy region | +| q | | Quit | + +** Customize Helm + +Helm provides a lot of user variables for extensive customization. +From any Helm session, type \\\\[helm-customize-group] +to jump to the current source `custom' group. Helm also has a +special group for faces you can access via `M-x customize-group +RET helm-faces'. + +Note: Some sources may not have their group set and default to +the `helm' group. + +** Display Helm in windows and frames + +You can display the Helm completion buffer in many different +window configurations, see the custom interface to discover the +different windows configurations available (See [[Customize Helm][Customize Helm]] to jump to custom interface). +When using Emacs in a graphic display (i.e. not in a terminal) you can as +well display your Helm buffers in separated frames globally for +all Helm commands or separately for specific Helm commands. +See `helm-display-function' and `helm-commands-using-frame'. +See also [[https://github.com/emacs-helm/helm/wiki/frame][helm wiki]] for more infos. + +There is a variable to allow reusing frame instead of deleting +and creating a new one at each session, see `helm-display-buffer-reuse-frame'. +Normally you don't have to use this, it have been made to workaround +slow frame popup in Emacs-26, to workaround this slowness in Emacs-26 use instead + +#+begin_src elisp + (when (= emacs-major-version 26) + (setq x-wait-for-event-timeout nil)) +#+end_src + +WARNING: +There is a package called posframe and also one called helm-posframe, +you DO NOT need these packages to display helm buffers in frames. + +** Helm's basic operations and default key bindings + +| Key| Command| +|----+--------| +| | +| \\[helm-previous-line]| Previous line | +| \\[helm-next-line]| Next line | +| \\[helm-scroll-up]| Scroll up | +| \\[helm-scroll-down]| Scroll down | +| \\[helm-scroll-other-window]| Scroll up other-window | +| \\[helm-scroll-other-window-down]| Scroll down other-window | +| \\[helm-maybe-exit-minibuffer]| Execute first (default) action / Select [1] | +| \\[helm-beginning-of-buffer]| Goto beginning of buffer | +| \\[helm-end-of-buffer]| Goto end of buffer | +| \\[helm-select-action]| Show actions menu | +| \\[helm-previous-source]| Previous source | +| \\[helm-next-source]| Next source | +| \\[helm-delete-minibuffer-contents]| Delete pattern (with prefix arg delete from point to end or all [2]) | +| \\[helm-execute-persistent-action]| Persistent action (Execute and keep Helm session) | +|\\[helm-toggle-resplit-and-swap-windows]|Rotate or swap windows. | +|\\[helm-exchange-minibuffer-and-header-line]|Exchange minibuffer and header-line. | +|\\[helm-quit-and-find-file]|Drop into `helm-find-files'. | +|\\[helm-kill-selection-and-quit]|Kill display value of candidate and quit (with prefix arg, kill the real value). | +|\\[helm-yank-selection]|Yank current selection into pattern. | +|\\[helm-insert-or-copy]|Insert or copy marked candidates (C-u) . | +|\\[helm-follow-mode]|Toggle automatic execution of persistent action. | +|\\[helm-follow-action-forward]|Run persistent action then select next line. | +|\\[helm-follow-action-backward]|Run persistent action then select previous line. | +|\\[helm-refresh]|Recalculate and redisplay candidates. | +|\\[helm-toggle-suspend-update]|Toggle candidate updates. | + +\[1] Behavior may change depending context in some source e.g. `helm-find-files'. + +\[2] Delete from point to end or all depending on the value of +`helm-delete-minibuffer-contents-from-point'. + +NOTE: Any of these bindings are from `helm-map' and may be +overriten by the map specific to the current source in use (each +source can have its own keymap). + +** The actions menu + +You can display the action menu in the same window +as helm candidates (default) or in a side window according to +`helm-show-action-window-other-window' value. + +When the action menu popup, the helm prompt is used to narrow +down this menu, no more candidates. + +When `helm-allow-mouse' is non nil, you can use as well +mouse-3 (right click) in the candidate zone to select actions +with the mouse once your candidate is selected. + +** Action transformers + +You may be surprized to see your actions list changing depending +on the context. This happen when a source has an action +transformer function which checks the current selected candidate +and adds specific actions for this candidate. + +** Shortcuts for n-th first actions + +f1-f12: Execute n-th action where n is 1 to 12. + +** Shortcuts for executing the default action on the n-th candidate + +Helm does not display line numbers by default, with Emacs-26+ you +can enable it permanently in all helm buffers with: + + (add-hook 'helm-after-initialize-hook 'helm-init-relative-display-line-numbers) + +You can also toggle line numbers with +\\\\[helm-display-line-numbers-mode] in current Helm +buffer. + +Of course when enabling `global-display-line-numbers-mode' Helm +buffers will have line numbers as well. \(Don't forget to +customize `display-line-numbers-type' to relative). + +In Emacs versions < to 26 you will have to use +[[https://github.com/coldnew/linum-relative][linum-relative]] +package and `helm-linum-relative-mode'. + +Then when line numbers are enabled with one of the methods above +the following keys are available([1]): + +C-x : Execute default action on the n-th candidate before +currently selected candidate. + +C-c : Execute default action on the n-th candidate after +current selected candidate. + +\"n\" is limited to 1-9. For larger jumps use other navigation +keys. + +\[1] Note that the key bindings are always available even if line +numbers are not displayed. They are just useless in this case. + +** Mouse control in Helm + +A basic support for the mouse is provided when the user sets +`helm-allow-mouse' to non-nil. + +- mouse-1 selects the candidate. +- mouse-2 executes the default action on selected candidate. +- mouse-3 pops up the action menu. + +Note: When mouse control is enabled in Helm, it also lets you +click around and lose the minibuffer focus: you'll have to click +on the Helm buffer or the minibuffer to retrieve control of your +Helm session. + +** Marked candidates + +You can mark candidates to execute an action on all of them +instead of the current selected candidate only. (See bindings +below.) Most Helm actions operate on marked candidates unless +candidate-marking is explicitely forbidden for a specific source. + +- To mark/unmark a candidate, use +\\[helm-toggle-visible-mark]. (See bindings below.) With a +numeric prefix arg mark ARG candidates forward, if ARG is +negative mark ARG candidates backward. + +- To mark all visible unmarked candidates at once in current +source use \\[helm-mark-all]. With a prefix argument, mark all +candidates in all sources. + +- To unmark all visible marked candidates at once use + \\[helm-unmark-all]. + +- To mark/unmark all candidates at once use +\\[helm-toggle-all-marks]. With a prefix argument, mark/unmark +all candidates in all sources. + +Note: When multiple candidates are selected across different +sources, only the candidates of the current source will be used +when executing most actions (as different sources can have +different actions). Some actions support multi-source marking +however. + +** Follow candidates + +When `helm-follow-mode' is on (\\\\[helm-follow-mode] +to toggle it), moving up and down Helm session or updating the +list of candidates will automatically execute the +persistent-action as specified for the current source. + +If `helm-follow-mode-persistent' is non-nil, the state of the +mode will be restored for the following Helm sessions. + +If you just want to follow candidates occasionally without +enabling `helm-follow-mode', you can use +\\\\[helm-follow-action-forward] or +\\[helm-follow-action-backward] instead. Conversely, when +`helm-follow-mode' is enabled, those commands go to previous/next +line without executing the persistent action. + +** Special yes, no or yes for all answers + +You may be prompted in the minibuffer to answer by [y,n,!,q] in +some places for confirmation. + +- y mean yes +- no mean no +- ! mean yes for all +- q mean quit or abort current operation. + +When using ! you will not be prompted for the same thing in +current operation any more, e.g. file deletion, file copy etc... + +** Moving in `helm-buffer' + +You can move in `helm-buffer' with the usual commands used in +Emacs: \(\\\\[helm-next-line], +\\\\[helm-previous-line], etc. See above basic +commands. When `helm-buffer' contains more than one source, +change source with \\\\[helm-next-source] and +\\[helm-previous-source]. + +Note: When reaching the end of a source, +\\\\[helm-next-line] will *not* go to the next source +when variable `helm-move-to-line-cycle-in-source' is non-nil, so +you will have to use \\\\[helm-next-source] and +\\[helm-previous-source]. + +** Resume previous session from current Helm session + +You can use `C-c n' (`helm-run-cycle-resume') to cycle in +resumables sources. `C-c n' is a special key set with +`helm-define-key-with-subkeys' which, after pressing it, allows +you to keep cycling with further `n'. + +Tip: You can bound the same key in `global-map' to + `helm-cycle-resume' with `helm-define-key-with-subkeys' to + let you transparently cycle sessions, Helm fired up or not. + You can also bind the cycling commands to single key + presses (e.g., `S-') this time with a simple + `define-key'. (Note that `S-' is not available in + terminals.) + +Note: `helm-define-key-with-subkeys' is available only once Helm +is loaded. + +You can also use +\\\\[helm-resume-previous-session-after-quit] to resume +the previous session, or +\\\\[helm-resume-list-buffers-after-quit] to have +completion on all resumable buffers. + +** Global commands + +*** Resume Helm session from outside Helm + +\\\\[helm-resume] revives the last Helm session. +Binding a key to this command will greatly improve Helm +interactivity, e.g. when quitting Helm accidentally. + +You can call \\\\[helm-resume] with a prefix argument +to choose \(with completion!) which session you'd like to resume. +You can also cycle in these sources with `helm-cycle-resume' (see +above). + +** Debugging Helm + +Helm exposes the special variable `helm-debug': setting it to +non-nil will enable Helm logging in a special outline-mode +buffer. Helm resets the variable to nil at the end of each +session. + +For convenience, \\\\[helm-enable-or-switch-to-debug] +allows you to turn on debugging for this session only. To avoid +accumulating log entries while you are typing patterns, you can +use \\\\[helm-toggle-suspend-update] to turn off +updating. When you are ready turn it on again to resume logging. + +Once you exit your Helm session you can access the debug buffer +with `helm-debug-open-last-log'. It is possible to save logs to +dated files when `helm-debug-root-directory' is set to a valid +directory. + +Note: Be aware that Helm log buffers grow really fast, so use +`helm-debug' only when needed. + +** Writing your own Helm sources + +Writing simple sources for your own usage is easy. When calling +the `helm' function, the sources are added the :sources slot +which can be a symbol or a list of sources. Sources can be built +with different EIEIO classes depending on what you want to do. To +simplify this, several `helm-build-*' macros are provided. Below +there are simple examples to start with. + +We will not go further here, see +[[https://github.com/emacs-helm/helm/wiki/Developing][Helm wiki]] +and the source code for more information and more complex +examples. + +#+begin_src elisp + + ;; Candidates are stored in a list. + (helm :sources (helm-build-sync-source \"test\" + ;; A function can be used as well + ;; to provide candidates. + :candidates '(\"foo\" \"bar\" \"baz\")) + :buffer \"*helm test*\") + + ;; Candidates are stored in a buffer. + ;; Generally faster but doesn't allow a dynamic updating + ;; of the candidates list i.e the list is fixed on start. + (helm :sources (helm-build-in-buffer-source \"test\" + :data '(\"foo\" \"bar\" \"baz\")) + :buffer \"*helm test*\") + +#+end_src + +** Helm Map +\\{helm-map}" + "Message string containing detailed help for `helm'. +It also accepts function or variable symbol.") + +(defvar helm-autoresize-mode) ;; Undefined in `helm-default-display-buffer'. + +(defvar helm-async-outer-limit-hook nil + "Run in async sources when process output is out of `candidate-number-limit'. +Should be set locally to `helm-buffer' with `helm-set-local-variable'.") + +(defvar helm-quit-hook nil + "A hook that runs when quitting Helm.") + +(defvar helm-resume-after-hook nil + "A hook that runs after resuming a Helm session. +The hook should takes one arg SOURCES.") + +(defvar helm-help-buffer-name "*Helm Help*" + "The name of helm help buffer.") + +;; See bug#2503. +(defvar helm-process-output-split-string-separator "\n" + "Separator to use when splitting helm async output.") + +(defvar helm-last-query "" + "The value of `helm-pattern' is stored here exit or quit.") + +;; Utility: logging +(defun helm-log (format-string &rest args) + "Log message `helm-debug' is non-nil. +Messages are written to the `helm-debug-buffer' buffer. + +Argument FORMAT-STRING is a string to use with `format'. +Use optional arguments ARGS like in `format'." + (when helm-debug + (with-current-buffer (get-buffer-create helm-debug-buffer) + (outline-mode) + (buffer-disable-undo) + (let ((inhibit-read-only t)) + (goto-char (point-max)) + (insert (let ((tm (time-convert nil 'list))) + (format (concat (if (string-match "Start session" format-string) + "* " "** ") + "%s.%06d (%s)\n %s\n") + (format-time-string "%H:%M:%S" tm) + (nth 2 tm) + (helm-log-get-current-function) + (apply #'format (cons format-string args))))))))) + +(defun helm-log-run-hook (hook) + "Run HOOK like `run-hooks' but write these actions to Helm log buffer." + (helm-log "Executing %s with value = %S" hook (symbol-value hook)) + (helm-log "Executing %s with global value = %S" hook (default-value hook)) + (run-hooks hook) + (helm-log "executed %s" hook)) + +(defvar helm--log-exclude-functions '(helm-interpret-value + helm-log + helm-log-get-current-function + helm-log-error + helm-log-save-maybe + helm-log-run-hook + helm-apply-functions-from-source + helm-funcall-with-source helm-funcall-foreach + helm-compute-attr-in-sources)) +(defun helm-log-get-current-function () + "Get name of function calling `helm-log'." + (cl-loop for btn from 1 to 40 + for btf = (cl-second (backtrace-frame btn)) + for fn = (if (symbolp btf) (symbol-name btf) "") + if (and (string-match "\\`helm" fn) + (not (memq btf helm--log-exclude-functions))) + return fn)) + +(defun helm-log-error (&rest args) + "Accumulate error messages into `helm-issued-errors'. +ARGS are args given to `format'. +E.g. (helm-log-error \"Error %s: %s\" (car err) (cdr err))." + (apply 'helm-log (concat "ERROR: " (car args)) (cdr args)) + (let ((msg (apply 'format args))) + (unless (member msg helm-issued-errors) + (cl-pushnew msg helm-issued-errors :test 'equal)))) + +(defun helm-log-save-maybe () + "Save log buffer when `helm-debug-root-directory' is non nil. +Create `helm-debug-root-directory' directory if necessary. +Messages are logged to a file named with today's date and time in +this directory." + (when (and (stringp helm-debug-root-directory) + (not (file-directory-p helm-debug-root-directory))) + (make-directory helm-debug-root-directory t)) + (when helm-debug + (let ((logdir (expand-file-name (concat "helm-debug-" + (format-time-string "%Y%m%d")) + helm-debug-root-directory))) + (make-directory logdir t) + (with-current-buffer (get-buffer-create helm-debug-buffer) + (goto-char (point-max)) + (insert "\ + + +Local Variables: +mode: outline +End:") + (write-region (point-min) (point-max) + (setq helm--last-log-file + (expand-file-name + (format-time-string "%Y%m%d-%H%M%S") + logdir)) + nil 'silent) + (kill-buffer)))) + (setq helm-debug nil)) + +;;;###autoload +(defun helm-debug-open-last-log () + "Open Helm log file or buffer of last Helm session." + (interactive) + (if helm--last-log-file + (progn + (find-file helm--last-log-file) + (outline-mode) (view-mode 1) (visual-line-mode 1)) + (switch-to-buffer helm-debug-buffer) + (view-mode 1) (visual-line-mode 1))) + +(defun helm-print-error-messages () + "Print error messages in `helm-issued-errors'." + (and helm-issued-errors + (message "Helm issued errors: %s" + (mapconcat 'identity (reverse helm-issued-errors) "\n")))) + + +;; Test tools +(defmacro with-helm-time-after-update (&rest body) + (helm-with-gensyms (start-time time-elapsed) + `(let ((,start-time (float-time)) ,time-elapsed) + (add-hook 'helm-after-update-hook + (lambda () + (setq ,time-elapsed (- (float-time) ,start-time)) + (keyboard-quit))) + (unwind-protect ,@body + (remove-hook 'helm-after-update-hook + (lambda () + (setq ,time-elapsed (- (float-time) ,start-time)) + (keyboard-quit)))) + ,time-elapsed))) + + +;; Helm API +(defmacro with-helm-default-directory (directory &rest body) + (declare (indent 1) (debug t)) + `(let ((default-directory (or (and ,directory + (file-name-as-directory ,directory)) + default-directory))) + ,@body)) + +(defun helm-default-directory () + "Return the local value of `default-directory' in `helm-buffer'." + (buffer-local-value 'default-directory (get-buffer helm-buffer))) + +(defmacro with-helm-temp-hook (hook &rest body) + "Execute temporarily BODY as a function for HOOK." + (declare (indent 1) (debug t)) + `(letrec ((helm--hook (lambda () + (unwind-protect + (progn ,@body) + (remove-hook ,hook helm--hook))))) + (push (cons helm--hook ,hook) helm--temp-hooks) + (add-hook ,hook helm--hook))) + +(defmacro with-helm-after-update-hook (&rest body) + "Execute BODY at end of `helm-update'." + (declare (indent 0) (debug t)) + `(with-helm-temp-hook 'helm-after-update-hook ,@body)) + +(defmacro with-helm-alive-p (&rest body) + "Return error when BODY run outside Helm context." + (declare (indent 0) (debug t)) + `(progn + (if helm-alive-p + (progn ,@body) + (error "Running helm command outside of context")))) + +(defmacro with-helm-in-frame (&rest body) + "Execute Helm function in BODY displaying `helm-buffer' in separate frame." + (declare (debug t) (indent 0)) + `(progn + (helm-set-local-variable + 'helm-display-function 'helm-display-buffer-in-own-frame) + ,@body)) + +;;; helm-attributes +;; +(defun helm-get-attr (attribute-name &optional source compute) + "Get the value of ATTRIBUTE-NAME of SRC. + +If SRC is omitted, use current source. + +If COMPUTE is non-`nil' compute value of ATTRIBUTE-NAME with +`helm-interpret-value'. COMPUTE can have also \\='ignorefn as value, +in this case `helm-interpret-value' will return a function as +value unchanged, but will eval a symbol which is bound. + +You can use `setf' to modify value of ATTRIBUTE-NAME unless +COMPUTE is specified, if attribute ATTRIBUTE-NAME is not found in +SOURCE `setf' will create new attribute ATTRIBUTE-NAME with +specified value. You can also use `helm-set-attr' to modify +ATTRIBUTE-NAME." + (declare (gv-setter + (lambda (val) + `(let* ((src (or ,source (helm-get-current-source))) + (attr (assq ,attribute-name src))) + (cl-assert (null ,compute) nil + "`setf' can't set the computed value of attribute `%s'" + ,attribute-name) + (if attr + (setcdr attr ,val) + (and (setcdr src (cons (cons ,attribute-name ,val) + (cdr src))) + ,val)))))) + (let ((src (or source (helm-get-current-source)))) + (helm-aif (assq attribute-name src) + (if compute + (helm-interpret-value (cdr it) src compute) + (cdr it))))) + +(defalias 'helm-attr 'helm-get-attr) +(make-obsolete 'helm-attr 'helm-get-attr "3.7.0") + +(cl-defun helm-set-attr (attribute-name value + &optional + (src (helm-get-current-source))) + "Set the value of ATTRIBUTE-NAME of source SRC to VALUE. + +If ATTRIBUTE-NAME doesn't exists in source it is created with +value VALUE. If SRC is omitted, use current source. If operation +succeed, return value, otherwise nil. + +Using this function is same as using `setf' on `helm-get-attr'." + (setf (helm-get-attr attribute-name src) value)) + +(defalias 'helm-attrset 'helm-set-attr) +(make-obsolete 'helm-attrset 'helm-set-attr "3.7.0") + +(defun helm-add-action-to-source (name fn source &optional index) + "Add new action NAME linked to function FN to SOURCE. +Function FN should be a valid function that takes one arg i.e. +candidate, argument NAME is a string that will appear in action +menu and SOURCE should be an existing helm source already loaded. +If INDEX is specified, action is added to the action list at INDEX, +otherwise added at end. +This allows users to add specific actions to an existing source +without modifying source code." + (let ((actions (helm-get-attr 'action source 'ignorefn)) + (new-action (list (cons name fn)))) + (when (functionp actions) + (setq actions (list (cons "Default action" actions)))) + (helm-set-attr 'action + (if index + (helm-append-at-nth actions new-action index) + (append actions new-action)) + source))) + +(defun helm-delete-action-from-source (action-or-name source) + "Delete ACTION-OR-NAME from SOURCE. +ACTION-OR-NAME can either be the name of action or the symbol +function associated to name." + (let* ((actions (helm-get-attr 'action source 'ignorefn)) + (del-action (if (symbolp action-or-name) + (rassoc action-or-name actions) + (assoc action-or-name actions)))) + (helm-set-attr 'action (delete del-action actions) source))) + +(cl-defun helm-add-action-to-source-if (name fn source predicate + &optional (index 4) test-only) + "Add new action NAME linked to function FN to SOURCE. +Action NAME will be available when the current candidate matches +PREDICATE. +This function adds an entry in the `action-transformer' attribute +of SOURCE (or creates one if not found). +Function PREDICATE must take one candidate as arg. +Function FN should be a valid function that takes one arg i.e. +candidate, argument NAME is a string that will appear in action +menu and SOURCE should be an existing Helm source already loaded. +If INDEX is specified, action is added in action list at INDEX. +Value of INDEX should be always >=1, default to 4. This allows +user to add a specific `action-transformer' to an existing source +without modifying source code. + +E.g. + +Add the action \"Byte compile file async\" linked to function +`async-byte-compile-file' to source `helm-source-find-files' only +when predicate helm-ff-candidates-lisp-p returns non-nil: + +\(helm-add-action-to-source-if \"Byte compile file async\" + \\='async-byte-compile-file + helm-source-find-files + \\='helm-ff-candidates-lisp-p)." + (let* ((actions (helm-get-attr 'action source 'ignorefn)) + (action-transformers (helm-get-attr 'action-transformer source)) + (new-action (list (cons name fn))) + (transformer (lambda (actions _candidate) + (let ((candidate (car (helm-marked-candidates)))) + (cond ((funcall predicate candidate) + (helm-append-at-nth + actions new-action index)) + (t actions)))))) + (when (functionp actions) + (helm-set-attr 'action (list (cons "Default action" actions)) source)) + (when (or (symbolp action-transformers) (functionp action-transformers)) + (setq action-transformers (list action-transformers))) + (if test-only ; debug + (delq nil (append (list transformer) action-transformers)) + (helm-set-attr 'action-transformer + (helm-fast-remove-dups + (delq nil (append (list transformer) action-transformers)) + :test 'equal) + source)))) + + +;;; Source filter +;; +(defun helm-set-source-filter (sources) + "Set the value of `helm-source-filter' to SOURCES and update. + +This function sets a filter for Helm sources and it may be called +while Helm is running. It can be used to toggle displaying of +sources dynamically. For example, additional keys can be bound +into `helm-map' to display only the file-related results if there +are too many matches from other sources and you're after files +only: + +Shift+F shows only file results from some sources: + +\(define-key helm-map \"F\" \\='helm-my-show-files-only) + +\(defun helm-my-show-files-only () + (interactive) + (helm-set-source-filter \\='(\"File Name History\" + \"Files from Current Directory\"))) + +Shift+A shows all results: + +\(define-key helm-map \"A\" \\='helm-my-show-all) + +\(defun helm-my-show-all () + (interactive) + (helm-set-source-filter nil)) + +The -my- part is added to avoid collisions with +existing Helm function names." + (with-helm-buffer + (let ((cur-disp-sel (helm-get-selection nil t))) + (set (make-local-variable 'helm-source-filter) + (helm--normalize-filter-sources sources)) + (helm-log "helm-source-filter = %S" helm-source-filter) + ;; Use force-update to run init/update functions. + (helm-update (and (stringp cur-disp-sel) + (regexp-quote cur-disp-sel)))))) + +(defun helm--normalize-filter-sources (sources) + (cl-loop for s in sources collect + (cl-typecase s + (symbol (assoc-default 'name (symbol-value s))) + (list (assoc-default 'name s)) + (string s)))) + +(defun helm-set-sources (sources &optional no-init no-update) + "Set SOURCES during `helm' invocation. +If NO-INIT is non-nil, skip executing init functions of SOURCES. +If NO-UPDATE is non-nil, skip executing `helm-update'." + (with-current-buffer helm-buffer + (setq helm-sources (helm-get-sources sources)) + (helm-log "helm-sources = %S" helm-sources)) + (unless no-init (helm-compute-attr-in-sources 'init)) + (unless no-update (helm-update))) + +(defun helm-show-all-candidates-in-source (arg) + "Toggle all or only candidate-number-limit cands in current source. +With a numeric prefix arg show only the ARG number of candidates. +The prefix arg has no effect when toggling to only +candidate-number-limit." + (interactive "p") + (with-helm-alive-p + (with-helm-buffer + (if helm-source-filter + (progn + (setq-local helm-candidate-number-limit + (default-value 'helm-candidate-number-limit)) + (helm-set-source-filter nil)) + (with-helm-default-directory (helm-default-directory) + (setq-local helm-candidate-number-limit (and (> arg 1) arg)) + (helm-set-source-filter + (list (helm-get-current-source)))))))) +(put 'helm-show-all-candidates-in-source 'helm-only t) + +(defun helm-display-all-sources () + "Display all sources previously hidden by `helm-set-source-filter'." + (interactive) + (with-helm-alive-p + (helm-set-source-filter nil))) +(put 'helm-display-all-sources 'helm-only t) + + +;;; Source infos fns. +;; +(defun helm-get-selection (&optional buffer force-display-part source) + "Return the currently selected candidate from BUFFER. + +If BUFFER is nil or unspecified, use `helm-buffer' as default value. + +If FORCE-DISPLAY-PART is non-nil, return the display part of candidate. + +If FORCE-DISPLAY-PART value is `withprop' the display part of +candidate is returned with its properties. + +When FORCE-DISPLAY-PART is nil the real part of candidate is returned. + +SOURCE default to current-source when unspecified but it is better to +specify SOURCE when it is already available to avoid to call +`helm-get-current-source' uselessly. + +Note that FORCE-DISPLAY-PART when specified takes precedence over +`display-to-real' attribute, that's mean don't use FORCE-DISPLAY-PART +when you want the `display-to-real' function(s) to be applied." + (with-current-buffer (or buffer helm-buffer) + (unless (or (helm-empty-buffer-p (current-buffer)) + (helm-pos-header-line-p)) + (let* ((beg (overlay-start helm-selection-overlay)) + (end (overlay-end helm-selection-overlay)) + (disp-fn (if (eq force-display-part 'withprop) + 'buffer-substring + 'buffer-substring-no-properties)) + ;; If there is no selection at point, the + ;; overlay is at its initial pos, (point-min) + ;; (point-min), that's mean the helm-buffer + ;; is not empty but have no selection yet, + ;; this happen with grep sentinel sending an + ;; error message in helm-buffer when no matches. + (disp (unless (= beg end) (funcall disp-fn beg (1- end)))) + (src (or source (helm-get-current-source))) + (selection (helm-acond (force-display-part disp) + ;; helm-realvalue always takes precedence + ;; over display-to-real. + ((get-text-property beg 'helm-realvalue) it) + ((assoc-default 'display-to-real src) + (helm-apply-functions-from-source source it disp)) + (t disp)))) + (unless (equal selection "") + (helm-log "selection = %S" selection) + selection))))) + +(defun helm-get-actions-from-current-source (&optional source) + "Return the associated action for the selected candidate. +It is a function symbol (sole action) or list +of (action-display . function)." + (unless (helm-empty-buffer-p (helm-buffer-get)) + (let* ((src (or source (helm-get-current-source))) + (marked (helm-marked-candidates)) + (action-transformer (helm-get-attr 'action-transformer src)) + (actions (helm-get-attr 'action src 'ignorefn))) + (if action-transformer + (helm-apply-functions-from-source + src action-transformer actions + ;; When there is marked candidates assume the set of + ;; candidates user selected contains candidates of the same + ;; type so that the actions added by transformer fit with + ;; all marked (previously we were looping on each marked + ;; but it is too costly for the benefit it brings). + (car marked)) + actions)))) + +(defun helm-get-current-source () + "Return the source for the current selection. +Return nil when `helm-buffer' is empty." + (or helm-current-source + (with-helm-buffer + (or (get-text-property (point) 'helm-cur-source) + (progn + ;; This is needed to not loose selection. + (goto-char (overlay-start helm-selection-overlay)) + (let ((header-pos (or (helm-get-previous-header-pos) + (helm-get-next-header-pos)))) + ;; Return nil when no--candidates. + (when header-pos + (cl-loop with source-name = (save-excursion + (goto-char header-pos) + (helm-current-line-contents)) + for source in helm-sources thereis + (and (equal (assoc-default 'name source) source-name) + source))))))))) + +(defun helm-run-after-exit (function &rest args) + "Execute FUNCTION with ARGS after exiting Helm. +The action is to call FUNCTION with arguments ARGS. +Unlike `helm-exit-and-execute-action', this can be used +to call non-actions functions with any ARGS or no ARGS at all. + +Use this on commands invoked from key bindings, but not on action +functions invoked as action from the action menu, i.e. functions +called with RET." + (helm-kill-async-processes) + (helm-log "function = %S" function) + (helm-log "args = %S" args) + (helm-exit-and-execute-action + (lambda (_candidate) + (apply function args)))) + +(defun helm-exit-and-execute-action (action) + "Exit current Helm session and execute ACTION. +Argument ACTION is a function called with one arg (candidate) and +part of the actions of current source. + +Use this on commands invoked from key bindings, but not +on action functions invoked as action from the action menu, +i.e. functions called with RET." + ;; If ACTION is not an action available in source 'action attribute, + ;; return an error. This allow to remove unneeded actions from + ;; source that inherit actions from type, note that ACTION have to + ;; be bound to a symbol and not to be an anonymous action + ;; i.e. lambda or byte-code. + (helm-log "Start executing action") + (let ((actions (helm-get-actions-from-current-source))) + (when actions + (cl-assert (or (eq action actions) + ;; Compiled lambdas + (byte-code-function-p action) + ;; Natively compiled (libgccjit) + (helm-subr-native-elisp-p action) + ;; Lambdas + (and (listp action) (functionp action)) + ;; One of current actions. + (rassq action actions)) + nil "No such action `%s' for this source" action))) + (setq helm-saved-action action) + (setq helm-saved-selection (or (helm-get-selection) "")) + (setq helm--executing-helm-action t) + ;; When toggling minibuffer and header-line, we want next action + ;; inherit this setting. + (helm-set-local-variable 'helm-echo-input-in-header-line + (with-helm-buffer helm-echo-input-in-header-line)) + ;; Ensure next action use same display function as initial helm-buffer when + ;; helm-actions-inherit-frame-settings is non nil. + (when (and helm-actions-inherit-frame-settings + helm--buffer-in-new-frame-p) + (helm-set-local-variable 'helm-display-function + (with-helm-buffer helm-display-function) + 'helm--last-frame-parameters + (with-helm-buffer + (helm--get-frame-parameters))) + ;; The helm-buffer keeps `helm-display-function' and + ;; `helm--get-frame-parameters' values during 0.5 seconds, just + ;; the time to execute the possible helm action with those values. + ;; If no helm based action run within 0.5 seconds, the next helm + ;; session will have to resolve again those variable values. + (run-with-idle-timer 0.5 nil + (lambda () (helm-set-local-variable 'helm-display-function nil + 'helm--last-frame-parameters nil)))) + (helm-exit-minibuffer)) + +(defun helm--get-frame-parameters (&optional frame) + (cl-loop with params = (frame-parameters frame) + for p in helm--frame-default-attributes + when (assq p params) collect it)) + +(defalias 'helm-run-after-quit 'helm-run-after-exit) +(make-obsolete 'helm-run-after-quit 'helm-run-after-exit "1.7.7") +(defalias 'helm-quit-and-execute-action 'helm-exit-and-execute-action) +(make-obsolete 'helm-quit-and-execute-action 'helm-exit-and-execute-action "1.7.7") + +(defun helm-interpret-value (value &optional source compute) + "Interpret VALUE as variable, function or literal and return it. +If VALUE is a function, call it with no arguments and return the value +unless COMPUTE value is \\='ignorefn. +If SOURCE compute VALUE for this source. +If VALUE is a variable, return the value. +If VALUE is a symbol, but it is not a function or a variable, cause an error. +Otherwise, return VALUE itself." + (cond ((and source (functionp value) (not (eq compute 'ignorefn))) + (helm-apply-functions-from-source source value)) + ((and (functionp value) (not (eq compute 'ignorefn))) + (funcall value)) + ((and (symbolp value) (boundp value)) + (symbol-value value)) + ((and (symbolp value) (not (functionp value))) + (error + "helm-interpret-value: Symbol must be a function or a variable")) + (t + value))) + +(defun helm-set-local-variable (&rest args) + "Bind each pair in ARGS locally to `helm-buffer'. + +Use this to set local vars before calling helm. + +When used from an init or update function +\(i.e. when `helm-force-update' is running) the variables are set +using `make-local-variable' within the `helm-buffer'. + +Usage: helm-set-local-variable ([VAR VALUE]...) +Just like `setq' except that the vars are not set sequentially. +IOW Don't use VALUE of previous VAR to set the VALUE of next VAR. + +\(fn VAR VALUE ...)" + (if helm--force-updating-p + (with-helm-buffer + (cl-loop for i on args by #'cddr + do (set (make-local-variable (car i)) (cadr i)))) + (setq helm--local-variables + (append (cl-loop for i on args by #'cddr + collect (cons (car i) (cadr i))) + helm--local-variables)))) + +(defun helm--set-local-variables-internal () + (cl-loop for (var . val) in helm--local-variables + ;; If `helm-set-local-variable' is called twice or more + ;; on same variable use the last value entered which is + ;; the first on stack e.g. + ;; (helm-set-local-variable 'helm-foo 1) + ;; (helm-set-local-variable 'helm-foo 2) + ;; helm--local-variables => + ;; '((helm-foo . 2) (helm-foo. 1)) + ;; (helm-foo . 2) is retained and (helm-foo . 1) + ;; ignored. + unless (memq var computed) + do (set (make-local-variable var) val) + collect var into computed + finally (setq helm--local-variables nil))) + + +;; API helper +(cl-defun helm-empty-buffer-p (&optional (buffer helm-buffer)) + "Check if BUFFER have candidates. +Default value for BUFFER is `helm-buffer'." + (zerop (buffer-size (and buffer (get-buffer buffer))))) + +(defun helm-empty-source-p () + "Check if current source contains candidates. +This could happen when for example the last element of a source +was deleted and the candidates list not updated." + (when (helm-window) + (with-helm-window + (or (helm-empty-buffer-p) + (and (helm-end-of-source-p) + (eq (point-at-bol) (point-at-eol)) + (or + (save-excursion + (forward-line -1) + (helm-pos-header-line-p)) + (bobp))))))) + + +;; Tools +;; +(defun helm-apply-functions-from-source (source functions &rest args) + "From SOURCE apply FUNCTIONS on ARGS. + +This function is used to process filter functions. When filter is +a `filtered-candidate-transformer', we pass to ARGS +candidates+source whereas when the filter is +`candidate-transformer' we pass to ARGS candidates only. +This function is also used to process functions called with no +args, e.g. init functions. In this case it is called without +ARGS. +See `helm-process-filtered-candidate-transformer' +\`helm-compute-attr-in-sources' and +\`helm-process-candidate-transformer'. + +Arg FUNCTIONS is either a symbol or a list of functions, each +function being applied on ARGS and called on the result of the +precedent function. Return the result of last function call." + (let ((helm--source-name (assoc-default 'name source)) + (helm-current-source source) + (funs (if (functionp functions) (list functions) functions))) + (cl-loop with result + for fn in funs + do (setq result (apply fn args)) + when (and args (cdr funs)) + ;; In filter functions, ARGS is a list of one or two elements where + ;; the first element is the list of candidates and the second + ;; a list containing the source. + ;; When more than one fn, set the candidates list to what returns + ;; this fn to compute the modified candidates with the next fn + ;; and so on. + do (setcar args result) + finally return result))) + +(defalias 'helm-funcall-with-source 'helm-apply-functions-from-source) +(make-obsolete 'helm-funcall-with-source 'helm-apply-functions-from-source "2.9.7") + +(defun helm-compute-attr-in-sources (attr &optional sources) + "Call the associated function(s) to ATTR for each source if any." + (let ((sources (or (helm-get-sources sources) + ;; Fix error no buffer named *helm... by checking + ;; if helm-buffer exists. + (and (buffer-live-p (get-buffer (helm-buffer-get))) + ;; `helm-sources' are local to helm-buffer. + (with-helm-buffer helm-sources))))) + (when sources + (cl-dolist (source sources) + (helm-aif (assoc-default attr source) + (helm-apply-functions-from-source source it)))))) + +(defalias 'helm-funcall-foreach 'helm-compute-attr-in-sources) +(make-obsolete 'helm-funcall-foreach 'helm-compute-attr-in-sources "2.9.7") + +(defun helm-normalize-sources (sources) + "If SOURCES is only one source, make a list of one element." + (if (or (and sources (symbolp sources)) + (and (listp sources) (assq 'name sources))) + (list sources) + sources)) + +(defun helm-get-candidate-number (&optional in-current-source) + "Return candidates number in `helm-buffer'. +If IN-CURRENT-SOURCE is provided return the number of candidates +of current source only." + (with-helm-buffer + (if (or (helm-empty-buffer-p) + (helm-empty-source-p)) + 0 + (save-excursion + (helm-aif (and in-current-source (helm-get-previous-header-pos)) + (goto-char it) + (goto-char (point-min))) + (forward-line 1) + (if (helm-pos-multiline-p) + (cl-loop with count-multi = 1 + while (and (not (if in-current-source + (save-excursion + (forward-line 2) + (or (helm-pos-header-line-p) (eobp))) + (eobp))) + (search-forward helm-candidate-separator nil t)) + do (cl-incf count-multi) + finally return count-multi) + (cl-loop with ln = 0 + while (not (if in-current-source + (or (helm-pos-header-line-p) (eobp)) + (eobp))) + ;; Don't count empty lines maybe added by popup (bug#1370). + unless (or (eq (point-at-bol) (point-at-eol)) + (helm-pos-header-line-p)) + do (cl-incf ln) + do (forward-line 1) finally return ln)))))) + +;; Entry point +;; `:allow-nest' is not in this list because it is treated before. +(defconst helm-argument-keys + '(:sources :input :prompt :resume + :preselect :buffer :keymap :default :history)) + +;;;###autoload +(defun helm (&rest plist) + "Main function to execute helm sources. + +PLIST is a list like + +\(:key1 val1 :key2 val2 ...) + + or + +\(&optional sources input prompt resume preselect + buffer keymap default history allow-nest). + +** Keywords + +Keywords supported: + +- :sources +- :input +- :prompt +- :resume +- :preselect +- :buffer +- :keymap +- :default +- :history +- :allow-nest + +Extra LOCAL-VARS keywords are supported, see the \"** Other +keywords\" section below. + +Basic keywords are the following: + +*** :sources + +One of the following: + +- List of sources +- Symbol whose value is a list of sources +- Alist representing a Helm source. + - In this case the source has no name and is referenced in + `helm-sources' as a whole alist. + +*** :input + +Initial input of minibuffer (temporary value of `helm-pattern') + +*** :prompt + +Minibuffer prompt. Default value is `helm--prompt'. + +*** :resume + +If t, allow resumption of the previous session of this Helm +command, skipping initialization. + +If \\='noresume, this instance of `helm' cannot be resumed. + +*** :preselect + +Initially selected candidate (string or regexp). + +*** :buffer + +Buffer name for this Helm session. `helm-buffer' will take this value. + +*** :keymap + +\[Obsolete] + +Keymap used at the start of this Helm session. + +It is overridden by keymaps specified in sources, and is kept +only for backward compatibility. + +Keymaps should be specified in sources using the :keymap slot +instead. See `helm-source'. + +This keymap is not restored by `helm-resume'. + +*** :default + +Default value inserted into the minibuffer \ with +\\\\[next-history-element]. + +It can be a string or a list of strings, in this case +\\\\[next-history-element] cycles through +the list items, starting with the first. + +If nil, `thing-at-point' is used. + +If `helm-maybe-use-default-as-input' is non-nil, display is +updated using this value if this value matches, otherwise it is +ignored. If :input is specified, it takes precedence on :default. + +*** :history + +Minibuffer input, by default, is pushed to `minibuffer-history'. + +When an argument HISTORY is provided, input is pushed to +HISTORY. HISTORY should be a valid symbol. + +*** :allow-nest + +Allow running this Helm command in a running Helm session. + +** Other keywords + +Other keywords are interpreted as local variables of this Helm +session. The `helm-' prefix can be omitted. For example, + +\(helm :sources \\='helm-source-buffers-list + :buffer \"*helm buffers*\" + :candidate-number-limit 10) + +Starts a Helm session with the variable +`helm-candidate-number-limit' set to 10. + +** Backward compatibility + +For backward compatibility, positional parameters are +supported: + +\(helm sources input prompt resume preselect + buffer keymap default history allow-nest) + +However, the use of non-keyword args is deprecated. + +\(fn &key SOURCES INPUT PROMPT RESUME PRESELECT BUFFER KEYMAP DEFAULT HISTORY ALLOW-NEST OTHER-LOCAL-VARS)" + (let ((fn (cond ((or (and helm-alive-p (plist-get plist :allow-nest)) + (and helm-alive-p (memq 'allow-nest plist))) + #'helm--nest) + ((keywordp (car plist)) + #'helm) + (t #'helm-internal)))) + (if (and helm-alive-p (eq fn #'helm)) + (if (helm--alive-p) + ;; A helm session is normally running. + (error "Error: Trying to run helm within a running helm session") + ;; A helm session is already running and user jump somewhere else + ;; without deactivating it. + (with-helm-buffer + (prog1 + (message "Aborting an helm session running in background") + ;; `helm-alive-p' will be reset in unwind-protect forms. + (helm-keyboard-quit)))) + (if (keywordp (car plist)) + ;; Parse `plist' and move not regular `helm-argument-keys' + ;; to `helm--local-variables', then calling helm on itself + ;; with normal arguments (the non--arguments-keys removed) + ;; will end up in [1]. + (progn + (setq helm--local-variables + (append helm--local-variables + ;; Vars passed by keyword on helm call + ;; take precedence on same vars + ;; that may have been passed before helm call. + (helm-parse-keys plist))) + (apply fn (mapcar (lambda (key) (plist-get plist key)) + helm-argument-keys))) + (apply fn plist))))) ; [1] fn == helm-internal. + +(defun helm--alive-p () + "[INTERNAL] Check if `helm' is alive. +An Helm session is considered alive if `helm-alive-p' value is +non-nil, the `helm-buffer' is visible, and cursor is in the +minibuffer." + (and helm-alive-p + (get-buffer-window (helm-buffer-get) 'visible) + (minibuffer-window-active-p (minibuffer-window)) + (minibufferp (current-buffer)))) + +(defun helm-parse-keys (keys) + "Parse the KEYS arguments of `helm'. +Return only those keys not in `helm-argument-keys', prefix them +with \"helm\", and then convert them to an alist. This allows +adding arguments that are not part of `helm-argument-keys', but +are valid helm variables nevertheless. For example, +:candidate-number-limit is bound to `helm-candidate-number-limit' +in the source. + + (helm-parse-keys \\='(:sources ((name . \"test\") + (candidates . (a b c))) + :buffer \"toto\" + :candidate-number-limit 4)) + ==> ((helm-candidate-number-limit . 4))." + + (cl-loop for (key value) on keys by #'cddr + for symname = (substring (symbol-name key) 1) + for sym = (intern (if (string-match "^helm-" symname) + symname + (concat "helm-" symname))) + unless (memq key helm-argument-keys) + collect (cons sym value))) + +(defun helm--maybe-load-tramp-archive () + ;; Should fix bug#2393 and bug#2394. `while-no-input-ignore-events' + ;; is also let-bounded in `helm--maybe-use-while-no-input'. + (let ((while-no-input-ignore-events + (and (boundp 'while-no-input-ignore-events) + (cons 'dbus-event while-no-input-ignore-events)))) + (unless helm--tramp-archive-maybe-loaded + ;; This for Emacs-27 not requiring tramp-archive. + (and (boundp 'tramp-archive-enabled) + (require 'tramp-archive nil t)) + (setq helm--tramp-archive-maybe-loaded t)))) + +;;; Entry point helper +(defun helm-internal (&optional + sources input + prompt resume + preselect buffer + keymap default history) + "The internal Helm function called by `helm'. +For SOURCES INPUT PROMPT RESUME PRESELECT BUFFER KEYMAP DEFAULT and +HISTORY args see `helm'." + (cl-assert (or (stringp input) + (null input)) + nil "Error in %S buffer: Initial input should be a string or nil" + buffer) + ;; Set all windows NON dedicated to avoid headaches with PA and + ;; helm-window (bug#2443) + (cl-loop for win in (window-list nil 1) + for state = (window-dedicated-p win) + when state + do (progn (set-window-dedicated-p win nil) + (push `(,win . ,state) helm--original-dedicated-windows-alist))) + (unless helm--nested (setq helm-initial-frame (selected-frame))) + ;; Launch tramp-archive with dbus-event in `while-no-input-ignore-events'. + (helm--maybe-load-tramp-archive) + ;; Activate the advices. + ;; Advices will be available only in >=emacs-24.4, but + ;; allow compiling without errors on lower emacs. + (when (fboundp 'advice-add) + (advice-add 'tramp-read-passwd :around #'helm--suspend-read-passwd) + (advice-add 'ange-ftp-get-passwd :around #'helm--suspend-read-passwd) + (advice-add 'epa-passphrase-callback-function + :around #'helm--suspend-read-passwd) + ;; Ensure linum-mode is disabled in Helm buffers to preserve + ;; performances (Bug#1894). + (advice-add 'linum-on :override #'helm--advice-linum-on '((depth . 100)))) + (helm-log (concat "[Start session] " (make-string 41 ?+))) + (helm-log "prompt = %S" prompt) + (helm-log "preselect = %S" preselect) + (helm-log "buffer = %S" buffer) + (helm-log "keymap = %S" keymap) + (helm-log "default = %S" default) + (helm-log "history = %S" history) + (setq helm--prompt (or prompt "pattern: ")) + (let ((non-essential t) + ;; Prevent mouse jumping to the upper-right + ;; hand corner of the frame (bug#1538). + mouse-autoselect-window + focus-follows-mouse + mode-line-in-non-selected-windows + minibuffer-completion-confirm + (ori--minibuffer-follows-selected-frame + (and (boundp 'minibuffer-follows-selected-frame) + (default-toplevel-value 'minibuffer-follows-selected-frame))) + (input-method-verbose-flag helm-input-method-verbose-flag) + (helm-maybe-use-default-as-input + (and (null input) + (or helm-maybe-use-default-as-input ; it is let-bounded so use it. + (cl-loop for s in (helm-normalize-sources sources) + thereis (memq s helm-sources-using-default-as-input)))))) + (unwind-protect + (condition-case-unless-debug _v + (let ( ;; `helm--source-name' is non-`nil' + ;; when `helm' is invoked by action, reset it. + helm--source-name + helm-current-source + helm-in-persistent-action + helm--quit + (helm-buffer (or buffer helm-buffer))) + (helm-initialize + resume input default sources) + ;; This allows giving the focus to a nested helm session which use + ;; a frame, like completion in + ;; `helm-eval-expression'. Unfortunately + ;; `minibuffer-follows-selected-frame' is available only in + ;; emacs-28+ (bug#2536). + ;; When non-nil (the default) the current active + ;; minibuffer is used in new frame, which is not what we + ;; want in helm when starting from an active minibuffer, + ;; either a helm minibuffer or something line M-:. + (and ori--minibuffer-follows-selected-frame + (setq minibuffer-follows-selected-frame + (unless (or helm--nested + ;; Allow keeping initial minibuffer visible + ;; e.g. completion-at-point from M-:. + (minibufferp helm-current-buffer)) + t))) + ;; We don't display helm-buffer here to avoid popping + ;; up a window or a frame when exiting immediately when + ;; only one candidate (this avoid having the helm frame + ;; flashing), lets first compute candidates and if more + ;; than one display helm-buffer (this is done later in + ;; helm-read-from-minibuffer). + (unless helm-execute-action-at-once-if-one + (helm-display-buffer helm-buffer resume) + (select-window (helm-window)) + (when (and resume helm-visible-mark-overlays) + (set-window-margins (selected-window) + (+ (string-width helm-visible-mark-prefix) + helm-left-margin-width)))) + ;; We are now in helm-buffer. + (unless helm-allow-mouse + (helm--remap-mouse-mode 1)) ; Disable mouse bindings. + (add-hook 'post-command-hook 'helm--maybe-update-keymap) + ;; Add also to update hook otherwise keymap is not updated + ;; until a key is hitted (Bug#1670). + (add-hook 'helm-after-update-hook 'helm--maybe-update-keymap) + (add-hook 'post-command-hook 'helm--update-header-line) + (helm-log "show prompt") + (unwind-protect + (helm-read-from-minibuffer + prompt input preselect + resume keymap default history) + (helm-cleanup)) + (prog1 + (unless helm--quit (helm-execute-selection-action)) + (helm-log (concat "[End session] " (make-string 41 ?-))))) + (quit + (helm-restore-position-on-quit) + (helm-log-run-hook 'helm-quit-hook) + (helm-log (concat "[End session (quit)] " (make-string 34 ?-))) + nil)) + (when (fboundp 'advice-remove) + (advice-remove 'tramp-read-passwd #'helm--suspend-read-passwd) + (advice-remove 'ange-ftp-get-passwd #'helm--suspend-read-passwd) + (advice-remove 'epa-passphrase-callback-function #'helm--suspend-read-passwd) + (advice-remove 'linum-on #'helm--advice-linum-on)) + (helm-log "helm-alive-p = %S" (setq helm-alive-p nil)) + (helm--remap-mouse-mode -1) ; Reenable mouse bindings. + (setq helm-alive-p nil) + (and ori--minibuffer-follows-selected-frame + (set-default-toplevel-value 'minibuffer-follows-selected-frame + ori--minibuffer-follows-selected-frame)) + ;; Prevent error "No buffer named *helm*" triggered by + ;; `helm-set-local-variable'. + (setq helm--force-updating-p nil) + (setq helm--buffer-in-new-frame-p nil) + ;; Reset helm-pattern so that lambda's using it + ;; before running helm will not start with its old value. + (setq helm-pattern "") + (setq helm--ignore-errors nil) + (helm-log-save-maybe)))) + +(defun helm--advice-linum-on () + (unless (or (minibufferp) + (string-match "\\`\\*helm" (buffer-name)) + (and (daemonp) (null (frame-parameter nil 'client)))) + (linum-mode 1))) + +;;; Helm resume +;; +;; +(defun helm-resume (arg) + "Resume a previous Helm session. +Call with a prefix arg to choose among existing Helm +buffers (sessions). When calling from Lisp, specify a +`buffer-name' as a string with ARG." + (interactive "P") + (let (buffer + cur-dir + narrow-pos + (helm-full-frame (default-value 'helm-full-frame)) + sources) + (if arg + (if (and (stringp arg) (bufferp (get-buffer arg))) + (setq buffer arg) + (setq buffer (helm-resume-select-buffer))) + (setq buffer helm-last-buffer)) + (cl-assert buffer nil + "helm-resume: No helm buffers found to resume") + (setq sources (buffer-local-value + 'helm-sources (get-buffer buffer))) + ;; Reset `cursor-type' to nil as it have been set to t + ;; when quitting previous session. + (with-current-buffer buffer (setq cursor-type nil)) + (setq helm-full-frame (buffer-local-value + 'helm-full-frame (get-buffer buffer))) + (setq cur-dir (buffer-local-value + 'default-directory (get-buffer buffer))) + (setq helm-saved-selection nil + helm-saved-action nil) + (unless (buffer-live-p helm-current-buffer) + ;; `helm-current-buffer' may have been killed. + (setq helm-current-buffer (current-buffer))) + (helm-aif (with-current-buffer buffer + helm--current-buffer-narrowed) + (progn + (set-buffer (car it)) + (setq narrow-pos (cdr it)))) + ;; This happen when calling C-x b within helm. + (helm-aif (get-buffer-window helm-marked-buffer-name 'visible) + (progn (delete-window it) (kill-buffer helm-marked-buffer-name))) + (save-restriction + (when narrow-pos (apply #'narrow-to-region narrow-pos)) + ;; Restart with same `default-directory' value this session + ;; was initially started with. + (with-helm-default-directory cur-dir + (unwind-protect + (helm + :sources sources + :input (buffer-local-value 'helm-input-local (get-buffer buffer)) + :prompt (buffer-local-value 'helm--prompt (get-buffer buffer)) + :resume t + :buffer buffer) + (run-hook-with-args 'helm-resume-after-hook sources)))))) + +(defun helm-resume-previous-session-after-quit () + "Resume previous Helm session within a running Helm." + (interactive) + (with-helm-alive-p + (let ((arg (if (null (member helm-buffer helm-buffers)) 0 1))) + (if (> (length helm-buffers) arg) + (helm-run-after-exit (lambda () (helm-resume (nth arg helm-buffers)))) + (message "No previous helm sessions available for resuming!"))))) +(put 'helm-resume-previous-session-after-quit 'helm-only t) + +(defun helm-resume-list-buffers-after-quit () + "List Helm buffers that can be resumed within a running Helm." + (interactive) + (with-helm-alive-p + (if (> (length helm-buffers) 0) + (helm-run-after-exit (lambda () (helm-resume t))) + (message "No previous helm sessions available for resuming!")))) +(put 'helm-resume-list-buffers-after-quit 'helm-only t) + +(defun helm-resume-p (resume) + "Whether current Helm session is resumed or not." + (eq resume t)) + +(defun helm-resume-select-buffer () + "Select an `helm-buffer' in `helm-buffers' list to resume a helm session. +Return nil if no `helm-buffer' found." + (when helm-buffers + (or (helm :sources (helm-build-sync-source "Resume helm buffer" + :candidates helm-buffers) + :resume 'noresume + :buffer "*helm resume*") + (keyboard-quit)))) + +;;;###autoload +(defun helm-cycle-resume () + "Cycle in `helm-buffers' list and resume when waiting more than 1.2s." + (interactive) + (cl-assert (and helm-buffers helm-last-buffer) + nil "No helm buffers to resume") + ;; Setup a new iterator only on first hit on + ;; `helm-run-cycle-resume', subsequents hits should reuse same + ;; iterator. + (unless (and (eq last-command 'helm-cycle-resume) + helm--cycle-resume-iterator) + (setq helm--cycle-resume-iterator + (helm-iter-sub-next-circular + helm-buffers helm-last-buffer :test 'equal))) + (helm--resume-or-iter)) + +(defun helm--resume-or-iter (&optional from-helm) + (message "Resuming helm buffer `%s'" helm-last-buffer) + (if (sit-for helm-cycle-resume-delay) + ;; Delay expire, run helm-resume. + (if from-helm + (helm-run-after-exit (lambda () (helm-resume helm-last-buffer))) + (helm-resume helm-last-buffer)) + ;; key pressed before delay, cycle. + (unless from-helm ; cycling to next item already done. + (message "Resuming helm buffer `%s'" + (setq helm-last-buffer + (helm-iter-next helm--cycle-resume-iterator)))))) + +(defun helm-run-cycle-resume () + "Same as `helm-cycle-resume' but intended to be called only from Helm." + (interactive) + (when (cdr helm-buffers) ; only one session registered. + ;; Setup a new iterator only on first hit on + ;; `helm-run-cycle-resume', subsequents hits should reuse same + ;; iterator. + (unless (and (eq last-command 'helm-run-cycle-resume) + helm--cycle-resume-iterator) + (setq helm--cycle-resume-iterator + (helm-iter-sub-next-circular + helm-buffers helm-last-buffer :test 'equal))) + ;; start at next buffer as we already are at `helm-last-buffer'. + (setq helm-last-buffer + (helm-iter-next helm--cycle-resume-iterator)) + (helm--resume-or-iter 'from-helm))) +(put 'helm-run-cycle-resume 'helm-only t) + + +;;;###autoload +(defun helm-other-buffer (sources buffer) + "Simplified Helm interface with other `helm-buffer'. +Call `helm' only with SOURCES and BUFFER as args." + (helm :sources sources :buffer buffer)) + +;;; Nested sessions +;; +;; +(defun helm--nest (&rest same-as-helm) + "[INTERNAL] Allow calling `helm' within a running Helm session. + +Arguments SAME-AS-HELM are the same as `helm'. + +Don't use this directly, use instead `helm' with the keyword +:allow-nest. + +\(fn &key SOURCES INPUT PROMPT RESUME PRESELECT BUFFER KEYMAP DEFAULT HISTORY OTHER-LOCAL-VARS)" + (with-helm-window + (let ((orig-helm-current-buffer helm-current-buffer) + (orig-helm-buffer helm-buffer) + (orig-helm--prompt helm--prompt) + (orig-helm-sources helm-sources) + (orig-helm--in-fuzzy helm--in-fuzzy) + (orig-helm--display-frame helm--buffer-in-new-frame-p) + (orig-helm-last-frame-or-window-configuration + helm-last-frame-or-window-configuration) + (orig-one-window-p helm-onewindow-p) + (helm--nested (if helm--buffer-in-new-frame-p 'share t))) + ;; FIXME Using helm-full-frame here allow showing the new + ;; helm-buffer in the same window as old helm-buffer, why? + (helm-set-local-variable 'helm-full-frame t) + (unwind-protect + (let (helm-current-position + helm-current-buffer + helm-pattern + (helm-buffer (or (cl-getf same-as-helm :buffer) + (nth 5 same-as-helm) + "*Helm*")) + (enable-recursive-minibuffers t)) + (setq helm-sources nil) + (apply #'helm same-as-helm)) + (with-current-buffer orig-helm-buffer + (setq helm-sources orig-helm-sources) + (setq helm--nested nil) + (setq helm--buffer-in-new-frame-p orig-helm--display-frame) + (setq helm-alive-p t) ; Nested session set this to nil on exit. + (setq helm-buffer orig-helm-buffer) + (setq helm-full-frame nil) + (setq helm--prompt orig-helm--prompt) + (setq helm--in-fuzzy orig-helm--in-fuzzy) + (helm-initialize-overlays helm-buffer) + (unless (helm-empty-buffer-p) (helm-mark-current-line t)) + (setq helm-last-frame-or-window-configuration + orig-helm-last-frame-or-window-configuration) + (setq cursor-type nil) + (setq helm-current-buffer orig-helm-current-buffer) + (setq helm-onewindow-p orig-one-window-p) + ;; Be sure advices, hooks, and local modes keep running. + (advice-add 'tramp-read-passwd + :around #'helm--suspend-read-passwd) + (advice-add 'ange-ftp-get-passwd + :around #'helm--suspend-read-passwd) + (advice-add 'epa-passphrase-callback-function + :around #'helm--suspend-read-passwd) + (unless helm-allow-mouse + (helm--remap-mouse-mode 1)) + (unless (cl-loop for h in post-command-hook + thereis (memq h '(helm--maybe-update-keymap + helm--update-header-line))) + (add-hook 'post-command-hook 'helm--maybe-update-keymap) + (add-hook 'post-command-hook 'helm--update-header-line)) + (helm-display-mode-line (helm-get-current-source))))))) + + +;;; Accessors +;; +(defun helm-current-position (save-or-restore) + "Save or restore current position in `helm-current-buffer'. +Argument SAVE-OR-RESTORE is either save or restore." + (cl-case save-or-restore + (save + (helm-log "Save position at %S" (cons (point) (window-start))) + (setq helm-current-position (cons (point) (window-start)))) + (restore + ;; Maybe `helm-current-buffer' have been deleted + ;; during helm session so check if it is here + ;; otherwise position in underlying buffer will be lost. + (when (get-buffer-window helm-current-buffer 'visible) + (helm-log "Restore position at %S in buffer %s" + helm-current-position + (buffer-name (current-buffer))) + (goto-char (car helm-current-position)) + ;; Fix this position with the NOFORCE arg of `set-window-start' + ;; otherwise, if there is some other buffer than `helm-current-buffer' + ;; one, position will be lost. + (set-window-start (selected-window) (cdr helm-current-position) t))))) + + +(defun helm-frame-or-window-configuration (save-or-restore) + "Save or restore last frame or window configuration. +Argument SAVE-OR-RESTORE is either save or restore of window or +frame configuration as per `helm-save-configuration-functions'." + (helm-log "helm-save-configuration-functions = %S" + helm-save-configuration-functions) + (let ((window-persistent-parameters (append '((no-other-window . t)) + window-persistent-parameters))) + (cl-case save-or-restore + (save (setq helm-last-frame-or-window-configuration + (funcall (cdr helm-save-configuration-functions)))) + (restore (funcall (car helm-save-configuration-functions) + helm-last-frame-or-window-configuration) + ;; Restore dedicated windows (bug#2443). + (when helm--original-dedicated-windows-alist + (cl-loop for (win . state) in helm--original-dedicated-windows-alist + when (window-live-p win) + do (set-window-dedicated-p win state)) + (setq helm--original-dedicated-windows-alist nil)) + ;; Restore frame focus. + ;; This is needed for minibuffer own-frame config + ;; when recursive minibuffers are in use. + ;; e.g M-: + helm-minibuffer-history. + (cl-letf ((frame (if (minibufferp helm-current-buffer) + (selected-frame) + (last-nonminibuffer-frame))) + ;; This is a workaround, because the i3 window + ;; manager developers are refusing to fix their + ;; broken timestamp and event handling. + ;; + ;; We basically just disable the part of + ;; select-frame-set-input-focus that would call + ;; XSetInputFocus in Xlib (x-focus-frame), that + ;; resets a timestamp in the xserver which the i3 + ;; developers fail to notice. + ;; + ;; Since they don't know about the new timestamp, + ;; their keyboard handling can break after a helm + ;; user quits emacs, as reported in bug#1641. + ;; + ;; Fortunately for us, we really don't need this + ;; XSetInputFocus call, since we already have focus + ;; for Emacs, the user is just using helm! We call + ;; select-frame-set-input-focus for the other + ;; side-effects, not for x-focus-frame. + ((symbol-function 'x-focus-frame) #'ignore)) + (select-frame-set-input-focus frame)))))) + +(defun helm-split-window-default-fn (window) + "Default function to split windows before displaying `helm-buffer'. + +It is used as default value for +`helm-split-window-preferred-function' which is then the +let-bounded value of `split-window-preferred-function' in +`helm-display-buffer'. When `helm-display-function' which default +to `helm-default-display-buffer' is called from +`helm-display-buffer' the value of +`split-window-preferred-function' will be used by +`display-buffer'." + (let* (split-width-threshold + (win (if (and (fboundp 'window-in-direction) + ;; Don't try to split when starting in a minibuffer + ;; e.g M-: and try to use helm-show-kill-ring. + (not (minibufferp helm-current-buffer))) + (if (or (one-window-p t) + helm-split-window-inside-p) + (split-window + (selected-window) nil + (if (eq helm-split-window-default-side 'other) + helm-split-window-other-side-when-one-window + helm-split-window-default-side)) + ;; If more than one window reuse one of them. + (cl-case helm-split-window-default-side + (left (or (helm-window-in-direction 'left) + (helm-window-in-direction 'above) + (selected-window))) + (above (or (helm-window-in-direction 'above) + (helm-window-in-direction 'left) + (selected-window))) + (right (or (helm-window-in-direction 'right) + (helm-window-in-direction 'below) + (selected-window))) + (below (or (helm-window-in-direction 'below) + (helm-window-in-direction 'right) + (selected-window))) + (same (selected-window)) + (other (or (helm-other-window-for-scrolling) + (selected-window))) + (t (or (window-next-sibling) (selected-window))))) + (split-window-sensibly window)))) + (setq helm-persistent-action-window-buffer (window-buffer win)) + win)) + +(defun helm-window-in-direction (direction) + "Same as `window-in-direction' but check if window is dedicated. +Return nil when window is dedicated." + (helm-aif (window-in-direction direction) + (and (not (window-dedicated-p it)) it))) + +(defun helm-other-window-for-scrolling () + "Same as `other-window-for-scrolling' but check if window is dedicated. +Returns nil when window is dedicated." + (helm-aif (other-window-for-scrolling) + (and (not (window-dedicated-p it)) it))) + + +;;; Display helm buffer +;; +;; +(defun helm-resolve-display-function (com) + "Decide which display function to use according to `helm-commands-using-frame'. + +The `helm-display-function' buffer local value takes precedence +on `helm-commands-using-frame'. +If `helm-initial-frame' has no minibuffer, use +`helm-display-buffer-in-own-frame' function. +Fallback to global value of `helm-display-function' when no local +value found and current command is not in +`helm-commands-using-frame'." + (let ((win (get-buffer-window helm-current-buffer))) + (or (with-helm-buffer helm-display-function) + (and (or (memq com helm-commands-using-frame) + (and helm-use-frame-when-no-suitable-window + (or (window-dedicated-p win) + (window-parameter win 'window-side))) + (and helm-use-frame-when-more-than-two-windows + (null helm--nested) + (> (length (window-list)) 2)) + ;; Frame parameter is unreliable for minibuffer on emacs-26. + (null (member helm-initial-frame (minibuffer-frame-list)))) + #'helm-display-buffer-in-own-frame) + (default-value 'helm-display-function)))) + +(defun helm-display-buffer (buffer &optional resume) + "Display BUFFER. + +The function used to display `helm-buffer' by calling +`helm-display-function' which splits window with +`helm-split-window-preferred-function'." + (let ((split-window-preferred-function + helm-split-window-preferred-function) + (helm-split-window-default-side + (if (and (not helm-full-frame) + helm-reuse-last-window-split-state) + (cond ((eq helm-split-window-default-side 'same) 'same) + ((eq helm-split-window-default-side 'other) 'other) + (helm--window-side-state) + (t helm-split-window-default-side)) + helm-split-window-default-side)) + (disp-fn (with-current-buffer buffer + (helm-resolve-display-function + (if helm-actions-inherit-frame-settings + (helm-this-command) this-command))))) + (prog1 + (funcall disp-fn buffer (or (helm-resume-p resume) + (and helm-actions-inherit-frame-settings + helm--executing-helm-action))) + (with-helm-buffer (setq-local helm-display-function disp-fn)) + (setq helm-onewindow-p (one-window-p t)) + ;; Don't allow other-window and friends switching out of minibuffer. + (when helm-prevent-escaping-from-minibuffer + (helm-prevent-switching-other-window))))) + +(cl-defun helm-prevent-switching-other-window (&key (enabled t)) + "Allow setting `no-other-window' parameter for all windows. +Arg ENABLE is the value of `no-other-window' window property." + (walk-windows + (lambda (w) + (unless (window-dedicated-p w) + (set-window-parameter w 'no-other-window enabled))) + 0)) + +(defun helm-default-display-buffer (buffer &optional _resume) + "Default function to display `helm-buffer' BUFFER. + +It is the default value of `helm-display-function'. +It uses `switch-to-buffer' or `display-buffer' depending on the +value of `helm-full-frame' or `helm-split-window-default-side'." + (let (pop-up-frames + (curwin (get-buffer-window helm-current-buffer))) + (if (or (buffer-local-value 'helm-full-frame (get-buffer buffer)) + (and (eq helm-split-window-default-side 'same) + (one-window-p t))) + (progn (and (not (minibufferp helm-current-buffer)) + ;; side-windows can't be the only window in frame, + ;; emacs refuse to delete other windows when + ;; current is a side-window [1]. + (not (window-parameter curwin 'window-side)) + (delete-other-windows)) + (switch-to-buffer buffer)) + (when (and (or helm-always-two-windows helm-autoresize-mode) + (not (eq helm-split-window-default-side 'same)) + (not (minibufferp helm-current-buffer)) + (not helm-split-window-inside-p) + ;; Same comment as in [1]. + (not (window-parameter curwin 'window-side))) + (delete-other-windows)) + (display-buffer + buffer `(,helm-default-display-buffer-functions + . ,(append helm-default-display-buffer-alist + `((window-height . ,helm-display-buffer-default-height) + (window-width . ,helm-display-buffer-default-width))))) + (helm-log-run-hook 'helm-window-configuration-hook)))) + +;; Shut up byte-compiler in emacs-26 +(defvar tab-bar-mode) +;; No warnings in Emacs built --without-x +(defvar x-display-name) + +(defun helm-display-buffer-in-own-frame (buffer &optional resume) + "Display Helm buffer BUFFER in a separate frame. + +Function suitable for `helm-display-function', +`helm-completion-in-region-display-function' and/or +`helm-show-completion-default-display-function'. + +See `helm-display-buffer-height' and `helm-display-buffer-width' +to configure frame size. + +Note that this feature is available only with emacs-25+. +Note also it is not working properly in helm nested session with emacs +version < emacs-28." + (cl-assert (and (fboundp 'window-absolute-pixel-edges) + (fboundp 'frame-geometry)) + nil "Helm buffer in own frame is only available starting at emacs-25+") + (if (not (display-graphic-p)) + ;; Fallback to default when frames are not usable. + (helm-default-display-buffer buffer) + (setq helm--buffer-in-new-frame-p t) + (let* ((pos (window-absolute-pixel-position)) + (half-screen-size (/ (display-pixel-height x-display-name) 2)) + (frame-info (frame-geometry)) + (prmt-size (length helm--prompt)) + (line-height (frame-char-height)) + tab-bar-mode + (new-frame-alist + (if resume + (buffer-local-value 'helm--last-frame-parameters + (get-buffer buffer)) + `((width . ,helm-display-buffer-width) + (height . ,helm-display-buffer-height) + (tool-bar-lines . 0) + (left . ,(- (car pos) + (* (frame-char-width) + (if (< (- (point) (point-at-bol)) prmt-size) + (- (point) (point-at-bol)) + prmt-size)))) + ;; Try to put frame at the best possible place. + ;; Frame should be below point if enough + ;; place, otherwise above point and + ;; current line should not be hidden + ;; by helm frame. + (top . ,(if (> (cdr pos) half-screen-size) + ;; Above point + (- (cdr pos) + ;; add 2 lines to make sure there is always a gap + (* (+ helm-display-buffer-height 2) line-height) + ;; account for title bar height too + (cddr (assq 'title-bar-size frame-info))) + ;; Below point + (+ (cdr pos) line-height))) + (title . "Helm") + (undecorated . ,helm-use-undecorated-frame-option) + (background-color . ,(or helm-frame-background-color + (face-attribute 'default :background))) + (foreground-color . ,(or helm-frame-foreground-color + (face-attribute 'default :foreground))) + (alpha . ,(or helm-frame-alpha 100)) + (font . ,(assoc-default 'font (frame-parameters))) + (vertical-scroll-bars . nil) + (menu-bar-lines . 0) + (fullscreen . nil) + (visibility . ,(null helm-display-buffer-reuse-frame)) + (minibuffer . t)))) + display-buffer-alist) + ;; Display minibuffer above or below only in initial session, + ;; not on a session triggered by action, this way if user have + ;; toggled minibuffer and header-line manually she keeps this + ;; setting in next action. + (unless (or helm--executing-helm-action resume) + ;; Add the hook inconditionally, if + ;; helm-echo-input-in-header-line is nil helm-hide-minibuffer-maybe + ;; will have anyway no effect so no need to remove the hook. + (add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe) + (with-helm-buffer + (setq-local helm-echo-input-in-header-line + (not (> (cdr pos) half-screen-size))))) + (helm-display-buffer-popup-frame buffer new-frame-alist) + ;; When frame size have been modified manually by user restore + ;; it to default value unless resuming or not using + ;; `helm-display-buffer-reuse-frame'. + ;; This have to be done AFTER raising the frame otherwise + ;; minibuffer visibility is lost until next session. + (unless (or resume (not helm-display-buffer-reuse-frame)) + (set-frame-size helm-popup-frame + helm-display-buffer-width + helm-display-buffer-height))) + (helm-log-run-hook 'helm-window-configuration-hook))) + +(defun helm-display-buffer-popup-frame (buffer frame-alist) + (if helm-display-buffer-reuse-frame + (let* ((x (cdr (assoc 'left frame-alist))) + (y (cdr (assoc 'top frame-alist))) + (width (cdr (assoc 'width frame-alist))) + (height (cdr (assoc 'height frame-alist)))) + (unless (and helm-popup-frame + (frame-live-p helm-popup-frame)) + (setq helm-popup-frame (make-frame frame-alist))) + (select-frame helm-popup-frame) + (set-frame-position helm-popup-frame x y) + (set-frame-width helm-popup-frame width) + (set-frame-height helm-popup-frame height) + (switch-to-buffer buffer) + (select-frame-set-input-focus helm-popup-frame t)) + ;; If user have changed `helm-display-buffer-reuse-frame' to nil + ;; maybe kill the frame. + (when (and helm-popup-frame + (frame-live-p helm-popup-frame)) + (delete-frame helm-popup-frame)) + (display-buffer + buffer `(display-buffer-pop-up-frame + . ((pop-up-frame-parameters . ,frame-alist)))))) + +;; Ensure to quit helm when user delete helm frame manually. +;; If user deletes another frame keep session running. +(defun helm--delete-frame-function (frame) + (when (and helm-alive-p + ;; FRAME is handling helm-buffer + (get-buffer-window helm-buffer frame)) + (helm-keyboard-quit))) +(add-hook 'delete-frame-functions 'helm--delete-frame-function) + +;;; Initialize +;; +(defun helm-get-sources (sources) + "Transform each element of SOURCES in alist. +Return the resulting list." + (when sources + (mapcar (lambda (source) + (if (listp source) + source (symbol-value source))) + (helm-normalize-sources sources)))) + +(defun helm-initialize (resume input default sources) + "Start initialization of Helm session. +For RESUME INPUT DEFAULT and SOURCES see `helm'." + (helm-log "start initialization: resume=%S input=%S" + resume input) + (helm-frame-or-window-configuration 'save) + (let ((sources-list (helm-get-sources sources))) + (setq helm--in-fuzzy + (cl-loop for s in sources-list + for matchfns = (helm-match-functions s) + for searchfns = (helm-search-functions s) + when (or (memq 'helm-fuzzy-match matchfns) + (memq 'helm-fuzzy-search searchfns)) + return t)) + (helm-log "sources-list = %S" sources-list) + (helm-set-local-variable 'helm-sources sources-list) + ;; Once `helm-buffer' is created `helm-sources' will be a local + ;; variable which value is a list of alists. + (helm-current-position 'save) + (if (helm-resume-p resume) + (helm-initialize-overlays (helm-buffer-get)) + (helm-initial-setup input default sources-list)) + (setq helm-alive-p t) + (unless (eq resume 'noresume) + (helm--push-and-remove-dups helm-buffer 'helm-buffers) + (setq helm-last-buffer helm-buffer)) + ;; If a `resume' attribute is present `helm-compute-attr-in-sources' + ;; will run its function. + (when (helm-resume-p resume) + (helm-compute-attr-in-sources 'resume)) + (helm-log "end initialization"))) + +(defun helm-initialize-overlays (buffer) + "Initialize Helm overlays in BUFFER." + (helm-log "overlay setup") + (if helm-selection-overlay + ;; make sure the overlay belongs to the helm buffer if + ;; it's newly created + (move-overlay helm-selection-overlay (point-min) (point-min) + (get-buffer buffer)) + + (setq helm-selection-overlay + (make-overlay (point-min) (point-min) (get-buffer buffer))) + (overlay-put helm-selection-overlay 'face 'helm-selection) + (overlay-put helm-selection-overlay 'priority 1))) + +(defun helm-initial-setup (input default sources) + "Initialize Helm settings and set up the Helm buffer." + ;; Run global hook. + (helm-log-run-hook 'helm-before-initialize-hook) + ;; Run local source hook. + (helm--run-init-hooks 'before-init-hook sources) + ;; For initialization of helm locals vars that need + ;; a value from current buffer, it is here. + (helm-set-local-variable 'current-input-method current-input-method) + (setq helm-current-prefix-arg nil + helm-saved-action nil + helm-saved-selection nil + helm-suspend-update-flag nil + ;; Ensure this is called BEFORE selecting helm-window. + helm-current-buffer (helm--current-buffer) + helm-buffer-file-name buffer-file-name + helm-issued-errors nil + helm-saved-current-source nil + helm--suspend-update-interactive-flag nil) + (when (and (with-helm-current-buffer + (and (buffer-narrowed-p) + (use-region-p))) + (not helm--nested)) + (helm-set-local-variable 'helm--current-buffer-narrowed + (list (current-buffer) + (region-beginning) (region-end)))) + (unless (and (or helm-split-window-state + helm--window-side-state) + helm-reuse-last-window-split-state) + (setq helm-split-window-state + (if (or (null split-width-threshold) + (and (integerp split-width-threshold) + (>= split-width-threshold (+ (frame-width) 4)))) + 'vertical 'horizontal)) + (setq helm--window-side-state + (or helm-split-window-default-side 'below))) + ;; Some sources like helm-mu are using input to init their + ;; candidates in init function, so setup initial helm-pattern here. + ;; See bug#2530 and https://github.com/emacs-helm/helm-mu/issues/54. + ;; Input should have precedence on default. + (cond (input + (setq helm-input input + helm-pattern input)) + ((and default helm-maybe-use-default-as-input) + (setq helm-pattern (if (listp default) + (car default) + default) + ;; Even if helm-pattern is set we want the + ;; prompt to be empty when using default as input, why + ;; helm-input is initialized to "". + helm-input "")) + (helm-maybe-use-default-as-input + (setq helm-pattern (or (with-helm-current-buffer + (thing-at-point 'symbol)) + "") + helm-input "")) + (t + (setq helm-pattern "" + helm-input ""))) + (helm--fuzzy-match-maybe-set-pattern) + ;; Call the init function for sources where appropriate + (helm-compute-attr-in-sources 'init sources) + (clrhash helm-candidate-cache) + (helm-create-helm-buffer) + (helm-clear-visible-mark) + ;; Run global hook. + (helm-log-run-hook 'helm-after-initialize-hook) + ;; Run local source hook. + (helm--run-init-hooks 'after-init-hook sources)) + +(defun helm--run-init-hooks (hook sources) + "Run after and before init hooks local to source. +See :after-init-hook and :before-init-hook in `helm-source'." + (cl-loop for s in sources + for hv = (assoc-default hook s) + when hv + do (pcase hv + ((and (pred (functionp)) + (guard (not (symbolp hv)))) + (funcall hv)) + ((and hook (pred (listp))) + (dolist (h hook) (funcall h))) + (_ (helm-log-run-hook hv))))) + +(defun helm-restore-position-on-quit () + "Restore position in `helm-current-buffer' when quitting." + (helm-current-position 'restore)) + +(defun helm--push-and-remove-dups (elm sym) + "Move ELM of SYM value on top and set SYM to this new value." + (set sym (cons elm (delete elm (symbol-value sym))))) + +(defun helm--current-buffer () + "[INTERNAL] Return `current-buffer' BEFORE `helm-buffer' is initialized. +Note that it returns the minibuffer in use after Helm has started +and is intended for `helm-initial-setup'. To get the buffer where +Helm was started, use `helm-current-buffer' instead." + (if (minibuffer-window-active-p (minibuffer-window)) + ;; If minibuffer is active be sure to use it's buffer + ;; as `helm-current-buffer', this allow to use helm + ;; from an already active minibuffer (M-: etc...) + (window-buffer (active-minibuffer-window)) + ;; Fix Bug#456 + ;; Use this instead of `current-buffer' to ensure + ;; helm session started in helm-mode from a completing-read + ;; Use really the buffer where we started and not the one + ;; where the completing-read is wrapped. i.e + ;; (with-current-buffer SOME-OTHER-BUFFER (completing-read [...]) + (window-buffer (with-selected-window (minibuffer-window) + (minibuffer-selected-window))))) + +(define-derived-mode helm-major-mode + fundamental-mode "Hmm" + "[INTERNAL] Provide major-mode name in Helm buffers. +Unuseful when used outside Helm, don't use it.") +(put 'helm-major-mode 'mode-class 'special) +(put 'helm-major-mode 'helm-only t) + +(defun helm-create-helm-buffer () + "Create and setup `helm-buffer'." + (let ((root-dir default-directory) + (inhibit-read-only t)) + (with-current-buffer (get-buffer-create helm-buffer) + (helm-log "Enabling major-mode %S" major-mode) + (helm-log "kill local variables: %S" (buffer-local-variables)) + (kill-all-local-variables) + (helm-major-mode) + (set (make-local-variable 'buffer-read-only) nil) + (buffer-disable-undo) + (erase-buffer) + ;; Use this instead of setting helm-map local ensure we have all + ;; our keys when helm loose minibuffer focus. And the map is + ;; made local as well AFAIU. + (use-local-map helm-map) + (set (make-local-variable 'helm-source-filter) nil) + (make-local-variable 'helm-sources) + (set (make-local-variable 'helm-display-function) nil) + (set (make-local-variable 'helm-selection-point) nil) + (set (make-local-variable 'scroll-margin) + (if helm-display-source-at-screen-top + 0 helm-completion-window-scroll-margin)) + (set (make-local-variable 'default-directory) root-dir) + (set (make-local-variable 'helm-marked-candidates) nil) + (set (make-local-variable 'helm--prompt) helm--prompt) + (helm-initialize-persistent-action) + (helm-log "helm-display-function = %S" helm-display-function) + (helm-log "helm--local-variables = %S" helm--local-variables) + (helm--set-local-variables-internal) + (setq truncate-lines helm-truncate-lines) ; already local. + (setq left-margin-width helm-left-margin-width) + (setq cursor-type nil)) + (helm-initialize-overlays helm-buffer) + (get-buffer helm-buffer))) + +(define-minor-mode helm--minor-mode + "[INTERNAL] Enable keymap in Helm minibuffer. +Since this mode has no effect when run outside of Helm context, +please don't use it outside of Helm. + +\\{helm-map}" + :group 'helm + :keymap (and helm-alive-p helm-map) + (unless helm-alive-p (setq helm--minor-mode nil))) +(put 'helm--minor-mode 'helm-only t) + +(defun helm--reset-default-pattern () + (setq helm-pattern "") + (setq helm-maybe-use-default-as-input nil)) + +(defun helm-read-from-minibuffer (prompt + input preselect resume + keymap default history) + "Read pattern with prompt PROMPT and initial input INPUT. +For PRESELECT RESUME KEYMAP DEFAULT HISTORY, see `helm'." + (with-helm-buffer + (if (and (helm-resume-p resume) + ;; When no source, helm-buffer is empty + ;; or contain non--candidate lines (e.g grep exit status) + (helm-get-current-source)) + (helm-mark-current-line t) + (helm-update preselect)) + (let* ((src (helm-get-current-source)) + (src-keymap (assoc-default 'keymap src)) + (hist (or (and history (symbolp history) history) + ;; Needed for resuming. + (assoc-default 'history src))) + (timer nil) + blink-matching-paren + (resize-mini-windows (and (null helm-echo-input-in-header-line) + resize-mini-windows)) + (first-src (car helm-sources)) + (source-process-p (or (assq 'candidates-process src) + (assq 'candidates-process first-src))) + ;; As we are using `helm-keyboard-quit' for `C-g' we have + ;; to prevent emacs command loop redefining `C-g' during + ;; helm-session. This happen only on async source with + ;; large output after a certain delay. The effect is that + ;; the minibuffer is exited but the helm async process + ;; continue running, and because minibuffer is lost `C-g' + ;; have no more effect. By binding `inhibit-quit' here we + ;; prevent this and allow `C-g' (the helm one aka + ;; `helm-keyboard-quit') to quit immediately. + (inhibit-quit source-process-p)) + (helm-log "helm-get-candidate-number => %S" + (helm-get-candidate-number)) + (helm-log "helm-execute-action-at-once-if-one = %S" + helm-execute-action-at-once-if-one) + (helm-log "helm-quit-if-no-candidate = %S" helm-quit-if-no-candidate) + (when (and src (helm-resume-p resume)) + (helm-display-mode-line src)) + ;; Reset `helm-pattern' and update + ;; display if no result found with precedent value of `helm-pattern' + ;; unless `helm-quit-if-no-candidate' is non-`nil', in this case + ;; Don't force update with an empty pattern. + ;; Reset also `helm-maybe-use-default-as-input' as this checking + ;; happen only on startup. + (when helm-maybe-use-default-as-input + ;; Store value of `default' temporarily here waiting next update + ;; to allow actions like helm-moccur-action matching pattern + ;; at the place it jump to. + (setq helm-input helm-pattern) + (if source-process-p + ;; Reset pattern to next update. + (with-helm-after-update-hook + (helm--reset-default-pattern)) + ;; Reset pattern right now. + (helm--reset-default-pattern)) + ;; Ensure force-update when no candidates + ;; when we start with an empty pattern. + (and (helm-empty-buffer-p) + (null helm-quit-if-no-candidate) + (helm-force-update preselect))) + ;; Handle `helm-execute-action-at-once-if-one' and + ;; `helm-quit-if-no-candidate' now. + (cond ((and (if (functionp helm-execute-action-at-once-if-one) + (funcall helm-execute-action-at-once-if-one) + helm-execute-action-at-once-if-one) + (= (helm-get-candidate-number + (eq helm-execute-action-at-once-if-one 'current-source)) + 1)) + (ignore)) ; Don't enter the minibuffer loop. + ((and helm-quit-if-no-candidate + (= (helm-get-candidate-number) 0)) + (setq helm--quit t) + (and (functionp helm-quit-if-no-candidate) + (funcall helm-quit-if-no-candidate))) + (t ; Enter now minibuffer and wait for input. + (let ((tap (or default + (with-helm-current-buffer + (thing-at-point 'symbol))))) + (when helm-execute-action-at-once-if-one + (helm-display-buffer helm-buffer resume) + (select-window (helm-window))) + (unwind-protect + (minibuffer-with-setup-hook + (lambda () + ;; Start minor-mode with global value of helm-map. + (helm--minor-mode 1) + ;; Now override the global value of `helm-map' with + ;; the local one which is in this order: + ;; - The keymap of current source. + ;; - The value passed in KEYMAP + ;; - Or fallback to the global value of helm-map. + (helm--maybe-update-keymap + (or src-keymap keymap helm-map)) + (helm-log-run-hook 'helm-minibuffer-set-up-hook) + (setq timer + (run-with-idle-timer + (max (with-helm-buffer helm-input-idle-delay) + 0.001) + 'repeat + (lambda () + ;; Stop updating in persistent action + ;; or when `helm-suspend-update-flag' + ;; is non-`nil'. + (unless (or helm-in-persistent-action + helm-suspend-update-flag) + (save-selected-window + (helm-check-minibuffer-input) + (helm-print-error-messages)))))) + ;; minibuffer has already been filled here. + (helm--update-header-line)) + (read-from-minibuffer (propertize (or prompt "pattern: ") + 'face 'helm-minibuffer-prompt) + input helm-map + nil hist tap + helm-inherit-input-method)) + (when timer (cancel-timer timer) (setq timer nil))))))))) + +(defun helm-toggle-suspend-update () + "Enable or disable display update in helm. +This can be useful for example for quietly writing a complex +regexp without Helm constantly updating." + (interactive) + (helm-suspend-update (not helm-suspend-update-flag) t) + (setq helm--suspend-update-interactive-flag + (not helm--suspend-update-interactive-flag))) +(put 'helm-toggle-suspend-update 'helm-only t) + +(defun helm-suspend-update (arg &optional verbose) + "Enable or disable display update in helm. +If ARG is 1 or non nil suspend update, if it is -1 or nil reenable +updating. When VERBOSE is specified display a message." + (with-helm-buffer + (when (setq helm-suspend-update-flag + (helm-acase arg + (1 t) + (-1 nil) + (t it))) + (helm-kill-async-processes) + (setq helm-pattern "")) + (when verbose + (message (if helm-suspend-update-flag + "Helm update suspended!" + "Helm update re-enabled!"))) + (helm-aif (helm-get-current-source) + (helm-display-mode-line it t)))) + +(defun helm-delete-backward-no-update (arg) + "Disable update and delete ARG chars backward. +Update is reenabled when idle 1s." + (interactive "p") + (with-helm-alive-p + (unless helm--suspend-update-interactive-flag + (helm-suspend-update 1)) + (backward-delete-char arg) + (run-with-idle-timer + 1 nil + (lambda () + (unless helm--suspend-update-interactive-flag + (helm-suspend-update -1) + (helm-check-minibuffer-input) + (helm-force-update)))))) +(put 'helm-delete-backward-no-update 'helm-only t) + +(defun helm--suspend-read-passwd (old--fn &rest args) + "Suspend Helm while reading password. +This is used to advice `tramp-read-passwd', `ange-ftp-get-passwd' +and `epa-passphrase-callback-function'." + ;; Suspend update when prompting for a tramp password. + (setq helm-suspend-update-flag t) + (setq overriding-terminal-local-map nil) + (setq helm--reading-passwd-or-string t) + (unwind-protect + ;; No need to suspend timer in emacs-24.4 + ;; it is fixed upstream. + (apply old--fn args) + (setq helm--reading-passwd-or-string nil) + (setq helm-suspend-update-flag nil))) + +(defun helm--maybe-update-keymap (&optional map) + "Handle different keymaps in multiples sources. + +Overrides `helm-map' with the local map of current source. If no +map is found in current source, does nothing (keeps previous +map)." + (with-helm-buffer + (helm-aif (or map (assoc-default 'keymap (helm-get-current-source))) + ;; We used a timer in the past to leave + ;; enough time to helm to setup its keymap + ;; when changing source from a recursive minibuffer. + ;; e.g C-x C-f M-y C-g + ;; => *find-files have now the bindings of *kill-ring. + ;; It is no more true now we are using `minor-mode-overriding-map-alist' + ;; and `helm--minor-mode' thus it fix Bug#1076 for emacs-24.3 + ;; where concurrent timers are not supported. + ;; i.e update keymap+check input. + (with-current-buffer (window-buffer (minibuffer-window)) + (setq minor-mode-overriding-map-alist `((helm--minor-mode . ,it))))))) + +;;; Prevent loosing focus when using mouse. +;; +(defvar helm--remap-mouse-mode-map + (let ((map (make-sparse-keymap))) + (cl-loop for k in '([mouse-1] [mouse-2] [mouse-3] + [down-mouse-1] [down-mouse-2] [down-mouse-3] + [drag-mouse-1] [drag-mouse-2] [drag-mouse-3] + [double-mouse-1] [double-mouse-2] [double-mouse-3] + [triple-mouse-1] [triple-mouse-2] [triple-mouse-3]) + do (define-key map k 'ignore)) + map)) + +(define-minor-mode helm--remap-mouse-mode + "[INTERNAL] Prevent escaping helm minibuffer with mouse clicks. +Do nothing when used outside of helm context. + +WARNING: Do not use this mode yourself, it is internal to Helm." + :group 'helm + :global t + :keymap helm--remap-mouse-mode-map + (unless helm-alive-p + (setq helm--remap-mouse-mode-map nil))) +(put 'helm--remap-mouse-mode 'helm-only t) + +;; Clean up + +(defun helm-cleanup () + "Clean up the mess when Helm exit or quit." + (helm-log "start cleanup") + (with-selected-window + ;; When exiting with `helm-execute-action-at-once-if-one', + ;; `helm-window' may not be created and we endup with an error + ;; e.g. in eshell completion when only one candidate to complete + ;; so fallback to selected-window in such cases. + (or (get-buffer-window helm-buffer) + (selected-window)) + (let ((frame (selected-frame))) + (setq cursor-type (default-value 'cursor-type)) + ;; Ensure restoring default-value of mode-line to allow user + ;; using the mouse when helm is inactive (Bug#1517,Bug#2377). + (setq mode-line-format (default-value 'mode-line-format)) + (remove-hook 'post-command-hook 'helm--maybe-update-keymap) + (remove-hook 'post-command-hook 'helm--update-header-line) + ;; Be sure we call cleanup functions from helm-buffer. + (helm-compute-attr-in-sources 'cleanup) + ;; Delete or make invisible helm frame. + (if (and helm--buffer-in-new-frame-p + ;; a helm session running in a frame that runs a nested + ;; session share the same frame for both sessions so + ;; don't delete the common frame. + ;; i.e. helm--nested == t => delete + ;; helm--nested == nil => delete + ;; helm--nested == share => don't delete + (not (eq helm--nested 'share))) + (progn + (setq-local helm--last-frame-parameters + (helm--get-frame-parameters)) + (bury-buffer) + (if helm-display-buffer-reuse-frame + (make-frame-invisible frame) (delete-frame frame))) + ;; bury-buffer from this window [1]. + ;; Do it at end to make sure buffer is still current. + (bury-buffer)))) + (helm-kill-async-processes) + ;; Remove the temporary hooks added + ;; by `with-helm-temp-hook' that + ;; may not have been consumed. + (when helm--temp-hooks + (cl-loop for (fn . hook) in helm--temp-hooks + do (remove-hook hook fn)) + (setq helm--temp-hooks nil)) + ;; When running helm from a dedicated frame + ;; with no minibuffer, helm will run in the main frame + ;; which have a minibuffer, so be sure to disable + ;; the `no-other-window' prop there. + (helm-prevent-switching-other-window :enabled nil) + (helm-log-run-hook 'helm-cleanup-hook) + (helm-frame-or-window-configuration 'restore) + ;; [1] now bury-buffer from underlying windows otherwise, + ;; if this window is killed the underlying buffer will + ;; be a helm buffer. + (replace-buffer-in-windows helm-buffer) + (setq helm-alive-p nil) + ;; Prevent error "No buffer named *helm*" triggered by + ;; `helm-set-local-variable'. + (setq helm--force-updating-p nil) + (setq helm--buffer-in-new-frame-p nil) + ;; Reset helm-pattern so that value of previous session doesn't + ;; interfere with next session (bug#2530), however store last value + ;; of helm-pattern in `helm-last-query'. + (setq helm-last-query helm-pattern + helm-pattern "" + helm-input "") + ;; This is needed in some cases where last input + ;; is yielded infinitely in minibuffer after helm session. + (helm-clean-up-minibuffer)) + +(defun helm-clean-up-minibuffer () + "Remove contents of minibuffer." + (let ((miniwin (minibuffer-window))) + ;; Clean only current minibuffer used by helm. + ;; i.e The precedent one is active. + (unless (minibuffer-window-active-p miniwin) + (with-current-buffer (window-buffer miniwin) + (delete-minibuffer-contents))))) + + +;;; Input handling +;; +;; +(defun helm-check-minibuffer-input () + "Check minibuffer content." + (with-selected-window (or (active-minibuffer-window) + (minibuffer-window)) + (helm-check-new-input (minibuffer-contents)))) + +(defun helm-check-new-input (input) + "Check INPUT string and update the helm buffer if necessary." + (unless (equal input helm-pattern) + (setq helm-pattern input) + (unless (helm-action-window) + (setq helm-input helm-pattern)) + (helm-log "helm-pattern = %S" helm-pattern) + (helm-log "helm-input = %S" helm-input) + (helm-log-run-hook 'helm-before-update-hook) + (setq helm--in-update t) + (helm-update))) + +(defun helm--reset-update-flag () + (run-with-idle-timer + helm-exit-idle-delay nil + (lambda () (setq helm--in-update nil)))) + +;; (add-hook 'helm-after-update-hook #'helm--reset-update-flag) + + +;; All candidates + +(defun helm-get-candidates (source) + "Retrieve and return the list of candidates from SOURCE." + (let* ((candidate-fn (assoc-default 'candidates source)) + (candidate-proc (assoc-default 'candidates-process source)) + ;; See comment in helm-get-cached-candidates (Bug#2113). + (inhibit-quit candidate-proc) + cfn-error + (notify-error + (lambda (&optional e) + (error + "In `%s' source: `%s' %s %s" + (assoc-default 'name source) + (or candidate-fn candidate-proc) + (if e "\n" "must be a list, a symbol bound to a list, or a function returning a list") + (if e (prin1-to-string e) "")))) + (candidates (condition-case-unless-debug err + ;; Process candidates-(process) function + ;; It may return a process or a list of candidates. + (if candidate-proc + ;; Calling `helm-interpret-value' with no + ;; SOURCE arg force the use of `funcall' + ;; and not `helm-apply-functions-from-source'. + (helm-interpret-value candidate-proc) + (helm-interpret-value candidate-fn source)) + (error (helm-log "Error: %S" (setq cfn-error err)) nil)))) + (cond ((and (processp candidates) (not candidate-proc)) + (warn "Candidates function `%s' should be called in a `candidates-process' attribute" + candidate-fn)) + ((and candidate-proc (not (processp candidates))) + (error "Candidates function `%s' should run a process" candidate-proc))) + (cond ((processp candidates) + ;; Candidates will be filtered later in process filter. + candidates) + ;; An error occured in candidates function. + (cfn-error (unless helm--ignore-errors + (funcall notify-error cfn-error))) + ;; Candidates function returns no candidates. + ((or (null candidates) + ;; Can happen when the output of a process + ;; is empty, and the candidates function call + ;; something like (split-string (buffer-string) "\n") + ;; which result in a list of one empty string (Bug#938). + ;; e.g (completing-read "test: " '("")) + (equal candidates '(""))) + nil) + ((listp candidates) + ;; Transform candidates with `candidate-transformer' functions or + ;; `real-to-display' functions if those are found, + ;; otherwise return candidates unmodified. + ;; `filtered-candidate-transformer' is NOT called here. + (helm-transform-candidates candidates source)) + (t (funcall notify-error))))) + +(defun helm-get-cached-candidates (source) + "Return the cached value of candidates for SOURCE. +Cache the candidates if there is no cached value yet." + (let* ((name (assoc-default 'name source)) + (candidate-cache (gethash name helm-candidate-cache)) + ;; Bind inhibit-quit to ensure function terminate in case of + ;; quit from `helm-while-no-input' and processes are added to + ;; helm-async-processes for further deletion (Bug#2113). + ;; FIXME: Is this still needed now `helm-while-no-input' + ;; handles quit-flag? + (inhibit-quit (assoc-default 'candidates-process source))) + (helm-aif candidate-cache + (prog1 it (helm-log "Use cached candidates")) + (helm-log "No cached candidates, calculate candidates") + (let ((candidates (helm-get-candidates source))) + (cond ((processp candidates) + (push (cons candidates + (append source + (list (cons 'item-count 0) + (cons 'incomplete-line "")))) + helm-async-processes) + (set-process-filter candidates 'helm-output-filter) + (setq candidates nil)) + ((not (assq 'volatile source)) + (puthash name candidates helm-candidate-cache))) + candidates)))) + + +;;; Candidate transformers + +(defun helm-process-candidate-transformer (candidates source) + "Execute `candidate-transformer' function(s) on CANDIDATES in SOURCE." + (helm-aif (assoc-default 'candidate-transformer source) + (helm-apply-functions-from-source source it candidates) + candidates)) + +(defun helm-process-filtered-candidate-transformer (candidates source) + "Execute `filtered-candidate-transformer' function(s) on CANDIDATES in SOURCE." + (helm-aif (assoc-default 'filtered-candidate-transformer source) + (helm-apply-functions-from-source source it candidates source) + candidates)) + +(defmacro helm--maybe-process-filter-one-by-one-candidate (candidate source) + "Execute `filter-one-by-one' function(s) on real value of CANDIDATE in SOURCE." + `(helm-aif (assoc-default 'filter-one-by-one ,source) + (let ((real (if (consp ,candidate) + (cdr ,candidate) + ,candidate))) + (if (and (listp it) + (not (functionp it))) ;; Don't treat lambda's as list. + (cl-loop for f in it + do (setq ,candidate (funcall f real)) + finally return ,candidate) + (setq ,candidate (funcall it real)))) + ,candidate)) + +(defun helm--initialize-one-by-one-candidates (candidates source) + "Process CANDIDATES with the `filter-one-by-one' function in SOURCE. +Return CANDIDATES unchanged when pattern is not empty." + (helm-aif (and (string= helm-pattern "") + (assoc-default 'filter-one-by-one source)) + (cl-loop for cand in candidates collect + (helm--maybe-process-filter-one-by-one-candidate cand source)) + candidates)) + +(defun helm-process-filtered-candidate-transformer-maybe + (candidates source process-p) + "Execute `filtered-candidate-transformer' function(s) on CANDIDATES in SOURCE. +When PROCESS-P is non-nil execute +`filtered-candidate-transformer' functions if some, otherwise +return CANDIDATES." + (if process-p + ;; When no filter return CANDIDATES unmodified. + (helm-process-filtered-candidate-transformer candidates source) + candidates)) + +(defun helm-process-real-to-display (candidates source) + "Execute real-to-display function on all CANDIDATES of SOURCE." + (helm-aif (assoc-default 'real-to-display source) + (setq candidates (helm-apply-functions-from-source + source 'mapcar + (lambda (cand) + (if (consp cand) + ;; override DISPLAY from candidate-transformer + (cons (funcall it (cdr cand)) (cdr cand)) + (cons (funcall it cand) cand))) + candidates)) + candidates)) + +(defun helm-transform-candidates (candidates source &optional process-p) + "Transform CANDIDATES from SOURCE according to candidate transformers. + +When PROCESS-P is non-nil executes the +`filtered-candidate-transformer' functions, otherwise processes +`candidate-transformer' functions only, +`filtered-candidate-transformer' functions being processed later, +after the candidates have been narrowed by +`helm-candidate-number-limit', see `helm-compute-matches'. When +`real-to-display' attribute is present, execute its functions on all +maybe filtered CANDIDATES." + (helm-process-real-to-display + (helm-process-filtered-candidate-transformer-maybe + (helm-process-candidate-transformer + candidates source) + source process-p) + source)) + + +;; Narrowing candidates +(defun helm-candidate-number-limit (source) + "Apply candidate-number-limit attribute value. +This overrides `helm-candidate-number-limit' variable. + +E.g.: +If (candidate-number-limit) is in SOURCE, show all candidates in SOURCE. +If (candidate-number-limit . 123) is in SOURCE limit candidate to 123." + (helm-aif (assq 'candidate-number-limit source) + ;; When assoc value is nil use by default 99999999 otherwise use + ;; the assoc value, when it is a symbol interpret its value (bug#1831). + (or (helm-aand (cdr it) (helm-interpret-value it)) 99999999) + (or helm-candidate-number-limit 99999999))) + +(defun helm-candidate-get-display (candidate) + "Get searched display part from CANDIDATE. +CANDIDATE is either a string, a symbol, or a (DISPLAY . REAL) +cons cell." + (cond ((car-safe candidate)) + ((symbolp candidate) + (symbol-name candidate)) + ((numberp candidate) + (number-to-string candidate)) + (t candidate))) + +(defun helm-process-pattern-transformer (pattern source) + "Execute pattern-transformer attribute function(s) on PATTERN in SOURCE." + (helm-aif (assoc-default 'pattern-transformer source) + (helm-apply-functions-from-source source it pattern) + pattern)) + +(defun helm-default-match-function (candidate) + "Check if `helm-pattern' match CANDIDATE. +Default function to match candidates according to `helm-pattern'." + (string-match helm-pattern candidate)) + + +;;; Fuzzy matching +;; +;; +(defvar helm--fuzzy-regexp-cache (make-hash-table :test 'eq)) +(defun helm--fuzzy-match-maybe-set-pattern () + ;; Computing helm-pattern with helm--mapconcat-pattern + ;; is costly, so cache it once time for all and reuse it + ;; until pattern change. + (when helm--in-fuzzy + (let ((fun (if (string-match "\\`\\^" helm-pattern) + #'identity + #'helm--mapconcat-pattern))) + (clrhash helm--fuzzy-regexp-cache) + ;; FIXME: Splitted part are not handled here, + ;; I must compute them in `helm-search-match-part' + ;; when negation and in-buffer are used. + (if (string-match "\\`!" helm-pattern) + (puthash 'helm-pattern + (if (> (length helm-pattern) 1) + (list (regexp-quote (substring helm-pattern 1 2)) + (funcall fun (substring helm-pattern 1))) + '("" "")) + helm--fuzzy-regexp-cache) + (puthash 'helm-pattern + (if (> (length helm-pattern) 0) + (list (regexp-quote (substring helm-pattern 0 1)) + (funcall fun helm-pattern)) + '("" "")) + helm--fuzzy-regexp-cache))))) + +(defun helm-fuzzy-match (candidate) + "Check if `helm-pattern' fuzzy matches CANDIDATE. +This function is used with sources built with `helm-source-sync'." + (unless (string-match " " helm-pattern) + ;; When pattern have one or more spaces, let + ;; multi-match doing the job with no fuzzy matching.[1] + (let ((regexp (cadr (gethash 'helm-pattern helm--fuzzy-regexp-cache)))) + (if (string-match "\\`!" helm-pattern) + (not (string-match regexp candidate)) + (string-match regexp candidate))))) + +(defun helm-fuzzy-search (pattern) + "Same as `helm-fuzzy-match' but for sources built with +`helm-source-in-buffer'." + (unless (string-match " " helm-pattern) + ;; Same as in `helm-fuzzy-match' ref[1]. + (let* ((regexps (gethash 'helm-pattern helm--fuzzy-regexp-cache)) + (partial-regexp (car regexps)) + (regexp (cadr regexps))) + (if (string-match "\\`!" pattern) + ;; Don't try to search here, just return + ;; the position of line and go ahead, + ;; letting `helm-search-match-part' checking if + ;; pattern match against this line. + (prog1 (list (point-at-bol) (point-at-eol)) + (forward-line 1)) + ;; We could use here directly `re-search-forward' + ;; on the regexp produced by `helm--mapconcat-pattern', + ;; but it is very slow because emacs have to do an incredible + ;; amount of loops to match e.g "[^f]*f[^o]*o..." in the whole buffer, + ;; more the regexp is long more the amount of loops grow. + ;; (Probably leading to a max-lisp-eval-depth error if both + ;; regexp and buffer are too big) + ;; So just search the first bit of pattern e.g "[^f]*f", and + ;; then search the corresponding line with the whole regexp, + ;; which increase dramatically the speed of the search. + (cl-loop while (re-search-forward partial-regexp nil t) + for bol = (point-at-bol) + for eol = (point-at-eol) + if (progn (goto-char bol) + (re-search-forward regexp eol t)) + do (goto-char eol) and return t + else do (goto-char eol) + finally return nil))))) + +(defvar helm-fuzzy-default-score-fn #'helm-fuzzy-flex-style-score) +(defun helm-score-candidate-for-pattern (candidate pattern) + "Assign score to CANDIDATE according to PATTERN." + ;; Unknown candidates always go on top. + (if (equal (get-text-property 0 'display candidate) "[?]") + 200.00 + (funcall helm-fuzzy-default-score-fn candidate pattern))) + +;; The flex scoring needs a regexp whereas the fuzzy scoring works +;; directly with helm-pattern, so cache the needed regexp for flex +;; scoring to not (re)compute it at each candidate. We could reuse +;; the regexp cached in `helm--fuzzy-regexp-cache' but it is not +;; exactly the same as the one needed for flex and also it is always +;; computed against the whole helm-pattern which is not usable for +;; e.g. file completion. +(defvar helm--fuzzy-flex-regexp-cache (make-hash-table :test 'equal)) +(defun helm-fuzzy-flex-style-score (candidate pattern) + "Give a score to CANDIDATE according to PATTERN. +A regexp is generated from PATTERN to calculate score. +Score is calculated with the emacs-27 flex algorithm using +`helm-flex--style-score'." + (let ((regexp (helm-aif (gethash pattern helm--fuzzy-flex-regexp-cache) + it + (clrhash helm--fuzzy-flex-regexp-cache) + (puthash pattern (helm--fuzzy-flex-pattern-to-regexp pattern) + helm--fuzzy-flex-regexp-cache)))) + (helm-flex--style-score candidate regexp t))) + +(defun helm--fuzzy-flex-pattern-to-regexp (pattern) + "Return a regexp from PATTERN compatible with emacs-27 flex algorithm." + (completion-pcm--pattern->regex + (helm-completion--flex-transform-pattern (list pattern)) 'group)) + +(defun helm-flex-add-score-as-prop (candidates regexp) + (cl-loop with case-fold-search = (helm-set-case-fold-search) + for cand in candidates + collect (helm-flex--style-score cand regexp))) + +(defun helm-completion--flex-transform-pattern (pattern) + ;; "fob" => '(prefix "f" any "o" any "b" any point) + (cl-loop for p in pattern + if (stringp p) nconc + (cl-loop for str across p + nconc (list (string str) 'any)) + else nconc (list p))) + +(defun helm-fuzzy-helm-style-score (candidate pattern) + "Give a score to CANDIDATE according to PATTERN. +Score is calculated for contiguous matches found with PATTERN. +Score is 100 (maximum) if PATTERN is fully matched in CANDIDATE. +One point bonus is added to score when PATTERN prefix matches +CANDIDATE. Contiguous matches get a coefficient of 2." + (let* ((cand (if (stringp candidate) + candidate (helm-stringify candidate))) + (pat-lookup (helm--collect-pairs-in-string pattern)) + (str-lookup (helm--collect-pairs-in-string cand)) + (inter (cl-nintersection pat-lookup str-lookup :test 'equal)) + ;; Prefix + (bonus (cond ((or (equal (car pat-lookup) (car str-lookup)) + (equal (caar pat-lookup) (caar str-lookup))) + 2) + ((and (null pat-lookup) ; length = 1 + (string= pattern (substring cand 0 1))) + 150) + (t 0))) + ;; Exact match e.g. foo -> foo == 200 + (bonus1 (and (string= cand pattern) 200)) + ;; Partial match e.g. foo -> aafooaa == 100 + ;; or foo -> fooaaa + (bonus2 (and (or (string-match + (concat "\\`" (regexp-quote pattern)) + cand) + (string-match + (concat "\\<" (regexp-quote pattern) "\\>") + cand)) + 100))) + (+ bonus + (or bonus1 bonus2 + ;; Give a coefficient of 2 for contiguous matches. + ;; That's mean that "wiaaaki" will not take precedence + ;; on "aaawiki" when matching on "wiki" even if "wiaaaki" + ;; starts by "wi". + (* (length inter) 2))))) + +(defun helm-fuzzy-matching-default-sort-fn-1 (candidates &optional use-real basename preserve-tie-order) + "The transformer for sorting candidates in fuzzy matching. +It sorts on the display part by default. + +It sorts CANDIDATES by their scores as calculated by +`helm-score-candidate-for-pattern'. Set USE-REAL to non-nil to +sort on the real part. If BASENAME is non-nil assume we are +completing filenames and sort on basename of candidates. If +PRESERVE-TIE-ORDER is nil, ties in scores are sorted by length of +the candidates." + (if (string= helm-pattern "") + candidates + (let ((table-scr (make-hash-table :test 'equal))) + (sort candidates + (lambda (s1 s2) + ;; Score and measure the length on real or display part of candidate + ;; according to `use-real'. + (let* ((real-or-disp-fn (if use-real #'cdr #'car)) + (cand1 (cond ((and basename (consp s1)) + (helm-basename (funcall real-or-disp-fn s1))) + ((consp s1) (funcall real-or-disp-fn s1)) + (basename (helm-basename s1)) + (t s1))) + (cand2 (cond ((and basename (consp s2)) + (helm-basename (funcall real-or-disp-fn s2))) + ((consp s2) (funcall real-or-disp-fn s2)) + (basename (helm-basename s2)) + (t s2))) + (data1 (or (gethash cand1 table-scr) + (puthash cand1 + (list (helm-score-candidate-for-pattern + cand1 helm-pattern) + (length (helm-stringify cand1))) + table-scr))) + (data2 (or (gethash cand2 table-scr) + (puthash cand2 + (list (helm-score-candidate-for-pattern + cand2 helm-pattern) + (length (helm-stringify cand2))) + table-scr))) + (len1 (cadr data1)) + (len2 (cadr data2)) + (scr1 (car data1)) + (scr2 (car data2))) + (cond ((= scr1 scr2) + (unless preserve-tie-order + (< len1 len2))) + ((> scr1 scr2))))))))) + +(defun helm-fuzzy-matching-default-sort-fn (candidates _source) + "Default `filtered-candidate-transformer' to sort in fuzzy matching." + (helm-fuzzy-matching-default-sort-fn-1 candidates)) + +(defun helm-fuzzy-matching-sort-fn-preserve-ties-order (candidates _source) + "Same as `helm-fuzzy-matching-default-sort-fn' but preserving order of ties. +The default function, `helm-fuzzy-matching-default-sort-fn', +sorts ties by length, shortest first. This function may be more +useful when the order of the candidates is meaningful, e.g. with +`recentf-list'." + (helm-fuzzy-matching-default-sort-fn-1 candidates nil nil t)) + +(defun helm--maybe-get-migemo-pattern (pattern &optional diacritics) + (or (and helm-migemo-mode + (assoc-default pattern helm-mm--previous-migemo-info)) + (if diacritics + (char-fold-to-regexp pattern) + pattern))) + +(defun helm-fuzzy-default-highlight-match (candidate &optional diacritics) + "The default function to highlight matches in fuzzy matching. +Highlight elements in CANDIDATE matching `helm-pattern' according +to the matching method in use." + (if (string= helm-pattern "") + ;; Empty pattern, do nothing. + candidate + ;; Else start highlighting. + (let* ((pair (and (consp candidate) candidate)) + (display (helm-stringify (if pair (car pair) candidate))) + (real (cdr pair)) + (regex (helm--maybe-get-migemo-pattern helm-pattern diacritics)) + (mp (pcase (get-text-property 0 'match-part display) + ((pred (string= display)) nil) + (str str))) + (count 0) + beg-str end-str) + ;; Extract all parts of display keeping original properties. + (when (and mp (ignore-errors + ;; Avoid error when candidate is a huge line. + (string-match (regexp-quote mp) display))) + (setq beg-str (substring display 0 (match-beginning 0)) + end-str (substring display (match-end 0) (length display)) + mp (substring display (match-beginning 0) (match-end 0)))) + (with-temp-buffer + ;; Insert the whole display part and remove non--match-part + ;; to keep their original face properties. + (insert (propertize (or mp display) 'read-only nil)) ; Fix (bug#1176) + (goto-char (point-min)) + (condition-case nil + (progn + ;; Try first matching against whole pattern. + (while (re-search-forward regex nil t) + (cl-incf count) + (helm-add-face-text-properties + (match-beginning 0) (match-end 0) 'helm-match)) + ;; If no matches start matching against multiples or fuzzy matches. + (when (zerop count) + (cl-loop with multi-match = (string-match-p " " helm-pattern) + with patterns = (if multi-match + (cl-loop for pat in (helm-mm-split-pattern + helm-pattern) + collect + (helm--maybe-get-migemo-pattern + pat diacritics)) + (split-string helm-pattern "" t)) + for p in patterns + ;; Multi matches (regexps patterns). + if multi-match do + (progn + (while (re-search-forward p nil t) + (helm-add-face-text-properties + (match-beginning 0) (match-end 0) + 'helm-match)) + (goto-char (point-min))) + ;; Fuzzy matches (literal patterns). + else do + (when (search-forward p nil t) + (helm-add-face-text-properties + (match-beginning 0) (match-end 0) + 'helm-match))))) + (invalid-regexp nil)) + ;; Now replace the original match-part with the part + ;; with face properties added. + (setq display (if mp (concat beg-str (buffer-string) end-str) (buffer-string)))) + (if real (cons display real) display)))) + +(defun helm-fuzzy-highlight-matches (candidates source) + "The filtered-candidate-transformer function to highlight fuzzy matches. +See `helm-fuzzy-default-highlight-match'." + (cl-assert helm-fuzzy-matching-highlight-fn nil "Wrong type argument functionp: nil") + (cl-loop with diac = (helm-get-attr 'diacritics source) + for c in candidates + collect (funcall helm-fuzzy-matching-highlight-fn c diac))) + + +;;; helm-flex style +;; +;; Provide the emacs-27 flex style for emacs<27. +;; Reuse the flex scoring algorithm of flex style in emacs-27. +(defun helm-flex--style-score (str regexp &optional score) + "Score STR candidate according to REGEXP. + +REGEXP should be generated from a pattern which is a list like +\\='(point \"f\" any \"o\" any \"b\" any) for \"fob\" as pattern. +Such pattern may be build with +`helm-completion--flex-transform-pattern' function, and the regexp +with `completion-pcm--pattern->regex'. For commodity, +`helm--fuzzy-flex-pattern-to-regexp' is used to build such regexp. + +Function extracted from `completion-pcm--hilit-commonality' in +emacs-27 to provide such scoring in emacs<27." + ;; Don't modify the string itself. + (setq str (copy-sequence str)) + (if (string-match regexp str) + (let* ((md (match-data)) + (start (pop md)) + (len (length str)) + (score-numerator 0) + (score-denominator 0) + (last-b 0) + (update-score + (lambda (a b) + "Update score variables given match range (A B)." + (setq score-numerator (+ score-numerator (- b a))) + (unless (or (= a last-b) + (zerop last-b) + (= a (length str))) + (setq score-denominator (+ score-denominator + 1 + (expt (- a last-b 1) + (/ 1.0 3))))) + (setq last-b b))) + result) + (funcall update-score start start) + (setq md (cdr md)) + (while md + (funcall update-score start (pop md)) + (setq start (pop md))) + (funcall update-score len len) + (unless (zerop (length str)) + (setq result (/ score-numerator (* len (1+ score-denominator)) 1.0)) + (put-text-property 0 1 'completion-score result str)) + (if (and score result) result str)) + (put-text-property 0 1 'completion-score 0.0 str) + (if score 0.0 str))) + + +;;; Matching candidates +;; +;; +(defun helm-match-functions (source) + (let ((matchfns (or (assoc-default 'match source) + (assoc-default 'match-strict source) + #'helm-default-match-function))) + (if (and (listp matchfns) (not (functionp matchfns))) + matchfns (list matchfns)))) + +(defun helm-search-functions (source) + (let ((searchfns (assoc-default 'search source))) + (if (and (listp searchfns) (not (functionp searchfns))) + searchfns (list searchfns)))) + +(defun helm-match-from-candidates (cands matchfns match-part-fn limit source) + (when cands ; nil in async sources. + (condition-case-unless-debug err + (cl-loop with hash = (make-hash-table :test 'equal) + with allow-dups = (assq 'allow-dups source) + with case-fold-search = (helm-set-case-fold-search) + with count = 0 + for iter from 1 + for fn in matchfns + when (< count limit) nconc + (cl-loop for c in cands + for dup = (gethash c hash) + for disp = (helm-candidate-get-display c) + while (< count limit) + for target = (if (helm-get-attr 'match-on-real source) + ;; Let's fails on error in + ;; case next block returns nil. + (or (cdr-safe c) + (get-text-property 0 'helm-realvalue disp)) + disp) + for prop-part = (get-text-property 0 'match-part target) + for part = (and match-part-fn + (or prop-part + (funcall match-part-fn target))) + ;; When allowing dups check if DUP + ;; have been already found in previous loop + ;; by comparing its value with ITER. + when (and (or (and allow-dups dup (= dup iter)) + (null dup)) + (condition-case nil + (funcall fn (or part target)) + (invalid-regexp nil))) + do + (progn + ;; Give as value the iteration number of + ;; inner loop to be able to check if + ;; the duplicate have not been found in previous loop. + (puthash c iter hash) + (helm--maybe-process-filter-one-by-one-candidate c source) + (cl-incf count)) + ;; Filter out nil candidates maybe returned by + ;; `helm--maybe-process-filter-one-by-one-candidate'. + and when c collect + (if (and part (not prop-part)) + (if (consp c) + (cons (propertize target 'match-part part) (cdr c)) + (propertize c 'match-part part)) + c))) + (error (unless (eq (car err) 'invalid-regexp) ; Always ignore regexps errors. + (helm-log-error "helm-match-from-candidates in source `%s': %s %s" + (assoc-default 'name source) (car err) (cdr err))) + nil)))) + +(defun helm-compute-matches (source) + "Start computing candidates in SOURCE." + (save-current-buffer + (let ((matchfns (helm-match-functions source)) + (matchpartfn (assoc-default 'match-part source)) + (helm--source-name (assoc-default 'name source)) + (helm-current-source source) + (limit (helm-candidate-number-limit source)) + (helm-pattern (helm-process-pattern-transformer + helm-pattern source))) + (helm--fuzzy-match-maybe-set-pattern) + ;; If source have a `filtered-candidate-transformer' attr + ;; Filter candidates with this func, otherwise just compute + ;; candidates. + ;; NOTE that this next block of code is returning nil on async sources, + ;; the candidates being processed directly in `helm-output-filter' + ;; process-filter. + (helm-process-filtered-candidate-transformer + ;; When using in-buffer method or helm-pattern is empty or + ;; using dynamic completion always compute all candidates. + (if (or (equal helm-pattern "") + (assq 'match-dynamic source) + (helm--candidates-in-buffer-p source)) + ;; Compute all candidates up to LIMIT. + ;; one-by-one are computed here only for sources that + ;; display a list of candidates even with an empty + ;; pattern. + (helm--initialize-one-by-one-candidates + (helm-take-first-elements + (helm-get-cached-candidates source) limit) + source) + ;; Compute candidates according to pattern with their match + ;; fns. + ;; one-by-one filtered candidates are computed during the + ;; execution of next loop in `helm-match-from-candidates'. + (helm-match-from-candidates + (helm-get-cached-candidates source) matchfns matchpartfn limit source)) + source)))) + +(defun helm--candidates-in-buffer-p (source) + (assq 'search source)) + +(defun helm-render-source (source matches) + "Display MATCHES from SOURCE according to its settings." + (helm-log "Source = %S" (remove (assq 'keymap source) source)) + (when matches + (helm-insert-header-from-source source) + (cl-loop with separate = nil + with start = (point) + with singleline = (null (assq 'multiline source)) + for m in matches + for count from 1 + if singleline + do (helm-insert-match m 'insert count source) + else + do (progn + (if separate + (helm-insert-candidate-separator) + (setq separate t)) + (helm-insert-match m 'insert count source)) + finally (and (null singleline) + (put-text-property start (point) + 'helm-multiline t))))) + +(defmacro helm-while-no-input (&rest body) + "Same as `while-no-input' but returns either BODY or nil. +Unlike `while-no-input' this macro ensure to not returns `t'." + (declare (debug t) (indent 0)) + (let ((catch-sym (make-symbol "input"))) + `(with-local-quit + (catch ',catch-sym + (let ((throw-on-input ',catch-sym) + val) + (setq val (progn ,@body)) + ;; See comments in `while-no-input' about resetting + ;; quit-flag. + (cond ((eq quit-flag throw-on-input) + (setq quit-flag nil)) + (quit-flag nil) + (t val))))))) + +(defmacro helm--maybe-use-while-no-input (&rest body) + "Wrap BODY in `helm-while-no-input' unless initializing a remote connection." + `(progn + (if (and (file-remote-p helm-pattern) + (not (file-remote-p helm-pattern nil t))) + ;; Tramp will ask for passwd, don't use `helm-while-no-input'. + ,@body + (helm-log "Using here `helm-while-no-input'") + ;; Emacs bug , unexpected + ;; dbus-event is triggered on dbus init. + ;; Ignoring the dbus-event work on emacs28+; for emacs27 or older + ;; version, require tramp-archive can workaround the issue. + (let ((while-no-input-ignore-events + (and (boundp 'while-no-input-ignore-events) + (cons 'dbus-event while-no-input-ignore-events)))) + (helm-while-no-input ,@body))))) + +(defun helm--collect-matches (src-list) + "Return a list of matches for each source in SRC-LIST. + +The resulting value is a list of lists, e.g. ((a b c) (c d) (e +f)) or (nil nil nil) for three sources when no matches found, +however this function can be interrupted by new input and in this +case returns a plain nil i.e. not (nil), in this case +`helm-update' is not rendering the source, keeping previous +candidates in display." + (let ((matches (helm--maybe-use-while-no-input + (cl-loop for src in src-list + collect (helm-compute-matches src))))) + (unless (eq matches t) matches))) + + +;;; Case fold search +;; +;; +(cl-defun helm-set-case-fold-search (&optional (pattern helm-pattern)) + "Used to set the value of `case-fold-search' in Helm. +Return t or nil depending on the value of `helm-case-fold-search' +and `helm-pattern'." + (if helm-alive-p + (let ((helm-case-fold-search + (helm-aif (assq 'case-fold-search (helm-get-current-source)) + (cdr it) + helm-case-fold-search)) + ;; Only parse basename for filenames + ;; to avoid setting case sensitivity + ;; when expanded directories contains upcase + ;; characters. + (bn-or-pattern (if (string-match "[~/]*" pattern) + (helm-basename pattern) + pattern))) + (helm-set-case-fold-search-1 bn-or-pattern)) + case-fold-search)) + +(defun helm-set-case-fold-search-1 (pattern) + (cl-case helm-case-fold-search + (smart (let ((case-fold-search nil)) + (if (string-match "[[:upper:]]" pattern) nil t))) + (t helm-case-fold-search))) + + +;;; Helm update +;; +(defun helm-update (&optional preselect source candidates) + "Update candidates list in `helm-buffer' based on `helm-pattern'. +Argument PRESELECT is a string or regexp used to move selection +to a particular place after finishing update. +When SOURCE is provided update mode-line for this source, otherwise +the current source will be used. +Argument CANDIDATES when provided is used to redisplay these candidates +without recomputing them, it should be a list of lists." + (helm-log "Start updating") + (helm-kill-async-processes) + ;; When persistent action have been called + ;; we have two windows even with `helm-full-frame'. + ;; So go back to one window when updating if `helm-full-frame' + ;; is non-`nil'. + (when (with-helm-buffer + (and helm-onewindow-p + ;; We are not displaying helm-buffer in a frame and + ;; helm-window is already displayed. + (not helm--buffer-in-new-frame-p) + (helm-window) + (not (helm-action-window)))) + (with-helm-window (delete-other-windows))) + (with-current-buffer (helm-buffer-get) + (set (make-local-variable 'helm-input-local) helm-pattern) + (unwind-protect + (let (sources matches) + ;; Collect sources ready to be updated. + (setq sources + (cl-loop for src in helm-sources + when (helm-update-source-p src) + collect src)) + ;; When no sources to update erase buffer + ;; to avoid duplication of header and candidates + ;; when next chunk of update will arrive, + ;; otherwise the buffer is erased AFTER [1] the results + ;; are computed. + (unless sources (erase-buffer)) + ;; Compute matches without rendering the sources. + ;; This prevent the helm-buffer flickering when constantly + ;; updating. + (helm-log "Matches: %S" + (setq matches (or candidates (helm--collect-matches sources)))) + ;; If computing matches finished and is not interrupted + ;; erase the helm-buffer and render results (Fix #1157). + (when matches ;; nil only when interrupted by while-no-input. + (erase-buffer) ; [1] + (cl-loop for src in sources + for mtc in matches + do (helm-render-source src mtc)) + ;; Move to first line only when there is matches + ;; to avoid cursor moving upside down (Bug#1703). + (helm--update-move-first-line))) + ;; When there is only one async source, update mode-line and run + ;; `helm-after-update-hook' in `helm-output-filter--post-process', + ;; when there is more than one source, update mode-line and run + ;; `helm-after-update-hook' now even if an async source is + ;; present and running in BG. + (let ((src (or source (helm-get-current-source)))) + (unless (assq 'candidates-process src) + (helm-display-mode-line src 'force) + (helm-log-run-hook 'helm-after-update-hook))) + (when preselect + (helm-log "Update preselect candidate %s" preselect) + (if (helm-window) + (with-helm-window (helm-preselect preselect source)) + (helm-preselect preselect source))) + (setq helm--force-updating-p nil) + (helm--reset-update-flag)) + (helm-log "end update"))) + +(defun helm-update-source-p (source) + "Whether SOURCE needs updating or not." + (let ((len (string-width + (if (assq 'multimatch source) + ;; Don't count spaces entered when using + ;; multi-match. + (replace-regexp-in-string " " "" helm-pattern) + helm-pattern)))) + (and (or (not helm-source-filter) + (member (assoc-default 'name source) helm-source-filter)) + (>= len + (helm-aif (assq 'requires-pattern source) (or (cdr it) 1) 0)) + ;; Entering repeatedly these strings (*, ?) takes 100% CPU + ;; and hang emacs on MacOs preventing deleting backward those + ;; characters (Bug#1802). Update: it seems it is no more true, + ;; thus this affect bug#2423, so let's remove this for now. + ;; (not (string-match-p "\\`[*]+\\'" helm-pattern)) + ;; These incomplete regexps hang helm forever + ;; so defer update. Maybe replace spaces quoted when using + ;; multi-match. + (not (member (replace-regexp-in-string "\\s\\ " " " helm-pattern) + helm-update-blacklist-regexps))))) + +(defun helm--update-move-first-line () + "Goto first line of `helm-buffer'." + (goto-char (point-min)) + (if (helm-window) + (helm-move-selection-common :where 'line + :direction 'next + :follow t) + (forward-line 1) + (helm-mark-current-line) + (helm-follow-execute-persistent-action-maybe))) + +(cl-defun helm-force-update (&optional preselect (recenter t)) + "Force recalculation and update of candidates. + +Unlike `helm-update', this function re-evaluates `init' and +`update' attributes when present; also `helm-candidate-cache' is +not reinitialized, meaning candidates are not recomputed unless +pattern has changed. + +Selection is preserved to current candidate if it still exists after +update or moved to PRESELECT, if specified. +The helm-window is re-centered at the end when RECENTER is t which +is the default. RECENTER can be also a number in this case it is +passed as argument to `recenter'." + (with-helm-buffer + (let* ((source (helm-get-current-source)) + (selection (helm-aif (helm-get-selection nil t source) + (regexp-quote it)))) + (setq helm--force-updating-p t) + (mapc 'helm-force-update--reinit helm-sources) + (helm-update (or preselect selection) source) + (when (and (helm-window) recenter) + (with-helm-window + (recenter (and (numberp recenter) recenter))))))) + +(defun helm-refresh () + "Force recalculation and update of candidates." + (interactive) + (with-helm-alive-p + (helm-force-update))) +(put 'helm-refresh 'helm-only t) + +(defun helm-force-update--reinit (source) + "Reinit SOURCE by calling its update and init functions." + ;; When using a specific buffer as cache, don't kill it. + (helm-aif (and (null (bufferp (assoc-default + (helm-get-attr 'name source) + helm--candidate-buffer-alist))) + (helm-apply-functions-from-source + source 'helm-candidate-buffer)) + (kill-buffer it)) + (cl-dolist (attr '(update init)) + (helm-aif (assoc-default attr source) + (helm-apply-functions-from-source source it))) + (helm-remove-candidate-cache source)) + +(defun helm-redisplay-buffer () + "Redisplay candidates in `helm-buffer'. + +Candidates are not recomputed, only redisplayed after modifying +the whole list of candidates in each source with functions found +in `redisplay' attribute of current source. Note that candidates +are redisplayed with their display part with all properties +included only. This function is used in async sources to +transform the whole list of candidates from the sentinel +functions (i.e. when all candidates have been computed) because +other filters like `candidate-transformer' are modifying only +each chunk of candidates from `process-filter' as they come in +and not the whole list. Use this for e.g. sorting the whole list +of async candidates once computed. + +Note: To ensure redisplay is done in async sources after Helm +reached `candidate-number-limit' you will have also to redisplay +your candidates from `helm-async-outer-limit-hook'." + (with-helm-buffer + (let ((get-cands (lambda (source) + (let ((fns (assoc-default 'redisplay source)) + candidates + helm-move-to-line-cycle-in-source + helm-allow-mouse) + (helm-goto-source source) + (helm-next-line) + (helm-awhile (condition-case-unless-debug nil + (and (not (helm-pos-header-line-p)) + (helm-get-selection + nil 'withprop source)) + (error nil)) + (push it candidates) + (when (save-excursion + (forward-line 1) (helm-end-of-source-p t)) + (cl-return nil)) + (helm-next-line)) + (helm-apply-functions-from-source + source fns (nreverse candidates))))) + (get-sources (lambda () + (let (sources helm-move-to-line-cycle-in-source) + (helm-awhile (helm-get-current-source) + (push it sources) + (when (save-excursion + (helm-move--end-of-source) + (forward-line 1) (eobp)) + (cl-return nil)) + (helm-next-source)) + (nreverse sources))))) + (goto-char (point-min)) + (helm-update nil (helm-get-current-source) + (cl-loop with sources = (funcall get-sources) + for s in helm-sources + for name = (assoc-default 'name s) collect + (when (cl-loop for src in sources thereis + (string= name + (assoc-default 'name src))) + (funcall get-cands s))))))) + +(defun helm-remove-candidate-cache (source) + "Remove SOURCE from `helm-candidate-cache'." + (remhash (assoc-default 'name source) helm-candidate-cache)) + +(defvar helm-drag-mouse-1-fn 'ignore) +(defun helm-insert-match (match insert-function &optional num source) + "Insert MATCH into `helm-buffer' with INSERT-FUNCTION. +If MATCH is a cons cell then insert the car as display with the +cdr stored as real value in a `helm-realvalue' text property. +Args NUM and SOURCE are also stored as text property when +specified as respectively `helm-cand-num' and `helm-cur-source'." + (let ((start (point-at-bol (point))) + (dispvalue (helm-candidate-get-display match)) + (realvalue (cdr-safe match)) + (map (when helm-allow-mouse (make-sparse-keymap))) + (inhibit-read-only t) + end) + (when (and (stringp dispvalue) + (not (zerop (length dispvalue)))) + (funcall insert-function dispvalue) + (setq end (point-at-eol)) + ;; Some strings may handle another keymap prop. + (remove-text-properties start end '(keymap nil)) + (put-text-property start end 'read-only nil) + ;; Some sources with candidates-in-buffer have already added + ;; 'helm-realvalue property when creating candidate buffer. + (unless (get-text-property start 'helm-realvalue) + (and realvalue + (put-text-property start end + 'helm-realvalue realvalue))) + (when map + (define-key map [drag-mouse-1] 'ignore) + (define-key map [mouse-1] 'helm-mouse-select-candidate) + (define-key map [mouse-2] 'ignore) + (define-key map [mouse-3] 'helm-menu-select-action) + (add-text-properties + start end + `(mouse-face highlight + keymap ,map + help-echo ,(pcase (get-text-property start 'help-echo) + ((and it (pred stringp)) + (concat it "\nmouse-1: select candidate\nmouse-3: menu actions")) + (_ "mouse-1: select candidate\nmouse-3: menu actions"))))) + (when num + (put-text-property start end 'helm-cand-num num)) + (when source + (put-text-property start end 'helm-cur-source source)) + (funcall insert-function "\n")))) + +(defun helm--mouse-reset-selection-help-echo () + (let* ((inhibit-read-only t) + (start (overlay-start helm-selection-overlay)) + (end (overlay-end helm-selection-overlay)) + (help-echo (get-text-property start 'help-echo))) + (when (and (stringp help-echo) + (string-match "mouse-2: execute action" help-echo)) + (put-text-property + start end + 'help-echo (replace-match "mouse-1: select candidate" + t t help-echo))))) + +(defun helm--bind-mouse-for-selection (pos) + (let ((inhibit-read-only t) + (map (get-text-property pos 'keymap))) + (when map + (define-key map [drag-mouse-1] helm-drag-mouse-1-fn) + (define-key map [mouse-2] 'helm-maybe-exit-minibuffer) + (put-text-property + helm-selection-point + (overlay-end helm-selection-overlay) + 'help-echo (helm-aif (get-text-property pos 'help-echo) + (if (and (stringp it) + (string-match "mouse-1: select candidate" it)) + (replace-match "mouse-2: execute action" t t it) + "mouse-2: execute action\nmouse-3: menu actions") + "mouse-2: execute action\nmouse-3: menu actions"))))) + +(defun helm-mouse-select-candidate (event) + (interactive "e") + (let* ((window (posn-window (event-end event))) + (pos (posn-point (event-end event)))) + (unwind-protect + (with-current-buffer (window-buffer window) + (if (and (helm-action-window) + (eql window (get-buffer-window helm-buffer))) + (user-error "selection in helm-window not available while selecting action") + (helm--mouse-reset-selection-help-echo) + (goto-char pos) + (when (helm-pos-multiline-p) + (goto-char (or (helm-get-previous-candidate-separator-pos) + (helm-get-previous-header-pos))) + (forward-line 1))) + (helm-mark-current-line) + (helm-follow-execute-persistent-action-maybe)) + (select-window (minibuffer-window)) + (set-buffer (window-buffer window))))) +(put 'helm-mouse-select-candidate 'helm-only t) + +(defun helm-insert-header-from-source (source) + "Insert SOURCE name in `helm-buffer' header. +Maybe insert, by overlay, additional info after the source name +if SOURCE has header-name attribute." + (let ((name (assoc-default 'name source))) + (helm-insert-header + name + (helm-aif (assoc-default 'header-name source) + (helm-apply-functions-from-source source it name))))) + +(defun helm-insert-header (name &optional display-string) + "Insert header of source NAME into the helm buffer. +If DISPLAY-STRING is non-nil and a string value then display this +additional info after the source name by overlay." + (unless (bobp) + (let ((start (point))) + (insert (propertize "\n" 'face 'helm-eob-line)) + (put-text-property start (point) 'helm-header-separator t))) + (let ((start (point))) + (insert name) + (put-text-property (point-at-bol) + (point-at-eol) 'helm-header t) + (when display-string + (overlay-put (make-overlay (point-at-bol) (point-at-eol)) + 'display display-string)) + (insert "\n") + (add-text-properties start (point) '(face helm-source-header + ;; Disable line numbers on + ;; source headers. + display-line-numbers-disable t)))) + +(defun helm-insert-candidate-separator () + "Insert separator of candidates into the Helm buffer." + (insert (propertize helm-candidate-separator 'face 'helm-separator)) + (put-text-property (point-at-bol) + (point-at-eol) 'helm-candidate-separator t) + (insert "\n")) + +(defun helm-init-relative-display-line-numbers () + "Enable `display-line-numbers' for Helm buffers. +This is intended to be added to `helm-after-initialize-hook'. +This will work only in Emacs-26+, i.e. Emacs versions that have +`display-line-numbers-mode'." + (when (boundp 'display-line-numbers) + (with-helm-buffer + (setq display-line-numbers 'relative)))) + +(define-minor-mode helm-display-line-numbers-mode + "Toggle display of line numbers in current Helm buffer." + :group 'helm + (with-helm-alive-p + (cl-assert (boundp 'display-line-numbers) nil + "`display-line-numbers' not available") + (if helm-display-line-numbers-mode + (with-helm-buffer + (setq display-line-numbers 'relative)) + (with-helm-buffer + (setq display-line-numbers nil))))) +(put 'helm-display-line-numbers-mode 'helm-only t) + + + +;;; Async process +;; +(defun helm-output-filter (process output-string) + "The `process-filter' function for Helm async sources." + (with-local-quit + (helm-output-filter-1 (assoc process helm-async-processes) output-string))) + +(defun helm-output-filter-1 (process-assoc output-string) + (helm-log "output-string = %S" output-string) + (with-current-buffer helm-buffer + (let ((source (cdr process-assoc))) + (save-excursion + (helm-aif (assoc-default 'insertion-marker source) + (goto-char it) + (goto-char (point-max)) + (helm-insert-header-from-source source) + (setcdr process-assoc + (append source `((insertion-marker . ,(point-marker)))))) + (helm-output-filter--process-source + (car process-assoc) output-string source + (helm-candidate-number-limit source)))) + (helm-output-filter--post-process))) + +(defun helm-output-filter--process-source (process output-string source limit) + (cl-dolist (candidate (helm-transform-candidates + (helm-output-filter--collect-candidates + (split-string output-string + helm-process-output-split-string-separator) + (assq 'incomplete-line source)) + source t)) + (setq candidate + (helm--maybe-process-filter-one-by-one-candidate candidate source)) + (if (assq 'multiline source) + (let ((start (point))) + (helm-insert-candidate-separator) + (helm-insert-match candidate 'insert-before-markers + (1+ (cdr (assq 'item-count source))) + source) + (put-text-property start (point) 'helm-multiline t)) + (helm-insert-match candidate 'insert-before-markers + (1+ (cdr (assq 'item-count source))) + source)) + (cl-incf (cdr (assq 'item-count source))) + (when (>= (assoc-default 'item-count source) limit) + (helm-kill-async-process process) + (helm-log-run-hook 'helm-async-outer-limit-hook) + (cl-return)))) + +(defun helm-output-filter--collect-candidates (lines incomplete-line-info) + "Collect LINES maybe completing the truncated first and last lines." + ;; The output of process may come in chunks of any size, so the last + ;; line of LINES could be truncated, this truncated line is stored + ;; in INCOMPLETE-LINE-INFO to be concatenated with the first + ;; incomplete line of the next arriving chunk. INCOMPLETE-LINE-INFO + ;; is an attribute of source; it is created with an empty string + ;; when the source is computed => (incomplete-line . "") + (helm-log "incomplete-line-info = %S" (cdr incomplete-line-info)) + (butlast + (cl-loop for line in lines + ;; On start `incomplete-line-info' value is empty string. + for newline = (helm-aif (cdr incomplete-line-info) + (prog1 + (concat it line) + (setcdr incomplete-line-info nil)) + line) + collect newline + ;; Store last incomplete line (last chunk truncated) until + ;; new output arrives. Previously storing 'line' in + ;; incomplete-line-info assumed output was truncated in + ;; only two chunks. But output could be large and + ;; truncated in more than two chunks. Therefore store + ;; 'newline' to contain the previous chunks (Bug#1187). + finally do (setcdr incomplete-line-info newline)))) + +(defun helm-output-filter--post-process () + (helm-aif (get-buffer-window helm-buffer 'visible) + (with-selected-window it + (helm-skip-noncandidate-line 'next) + (helm-mark-current-line nil 'nomouse) + ;; FIXME Don't hardcode follow delay. + (helm-follow-execute-persistent-action-maybe 0.5) + (helm-display-mode-line (helm-get-current-source)) + (helm-log-run-hook 'helm-after-update-hook) + (helm--reset-update-flag)))) + +(defun helm-process-deferred-sentinel-hook (process event file) + "Defer remote processes in sentinels. +Meant to be called at the beginning of a sentinel process +function." + (when (and (not (zerop helm-tramp-connection-min-time-diff)) + (string= event "finished\n") + (or (file-remote-p file) + ;; `helm-suspend-update-flag' + ;; is non-`nil' here only during a + ;; running process, this will never be called + ;; when user set it explicitly with `C-!'. + helm-suspend-update-flag)) + (setq helm-suspend-update-flag t) + ;; Kill the process but don't delete entry in + ;; `helm-async-processes'. + (helm-kill-async-process process) + ;; When tramp opens the same connection twice in less than 5 + ;; seconds, it throws 'suppress, which calls the real-handler on + ;; the main "Emacs". To avoid this [1] helm waits for 5 seconds + ;; before updates yet allows user input during this delay. [1] In + ;; recent Emacs versions, this has been fixed so tramp returns nil + ;; in such conditions. Note: `tramp-connection-min-time-diff' cannot + ;; have values less than 5 seconds otherwise the process dies. + (run-at-time helm-tramp-connection-min-time-diff + nil (lambda () + (when helm-alive-p ; Don't run timer fn after quit. + (setq helm-suspend-update-flag nil) + (helm-check-minibuffer-input)))))) + +(defun helm-kill-async-processes () + "Kill all asynchronous processes registered in `helm-async-processes'." + (while helm-async-processes + (helm-kill-async-process (caar helm-async-processes)) + (setq helm-async-processes (cdr helm-async-processes)))) + +(defun helm-kill-async-process (process) + "Stop output from `helm-output-filter' and kill associated PROCESS." + (set-process-filter process nil) + (delete-process process)) + + +;;; Actions +;; +(defun helm-execute-selection-action () + "Execute current action." + (helm-log-run-hook 'helm-before-action-hook) + ;; Position can be change when `helm-current-buffer' + ;; is split, so jump to this position before executing action. + (helm-current-position 'restore) + (unwind-protect + (prog1 (helm-execute-selection-action-1) + (helm-log-run-hook 'helm-after-action-hook)) + (setq helm--executing-helm-action nil))) + +(defun helm-execute-selection-action-1 (&optional + selection action + preserve-saved-action) + "Execute ACTION on current SELECTION. +If PRESERVE-SAVED-ACTION is non-nil, then save the action." + (helm-log "executing action") + (setq action (helm-get-default-action + (or action + helm-saved-action + (if (get-buffer helm-action-buffer) + (helm-get-selection helm-action-buffer) + (helm-get-actions-from-current-source))))) + (helm-aif (and (not helm-in-persistent-action) + (get-buffer helm-action-buffer)) + (kill-buffer it)) + (let ((source (or helm-saved-current-source + (helm-get-current-source))) + non-essential) + (setq selection (helm-coerce-selection + (or selection + helm-saved-selection + (helm-get-selection nil nil source) + (and (assq 'accept-empty source) "")) + source)) + (unless preserve-saved-action (setq helm-saved-action nil)) + (when (and selection action) (funcall action selection)))) + +(defun helm-coerce-selection (selection source) + "Apply coerce attribute function to SELECTION in SOURCE. +Coerce source with coerce function." + (helm-aif (assoc-default 'coerce source) + (helm-apply-functions-from-source source it selection) + selection)) + +(defun helm-get-default-action (action) + "Get the first ACTION value of action list in source." + (if (and (listp action) (not (functionp action))) + (cdar action) + action)) + +(defun helm--show-action-window-other-window-p () + (and helm-show-action-window-other-window + (or helm-always-two-windows + helm--buffer-in-new-frame-p) + (eq helm-split-window-state 'vertical))) + +(defun helm-select-action () + "Select an action for the currently selected candidate. +If action buffer is selected, back to the Helm buffer." + (interactive) + (with-helm-alive-p + (let ((src (helm-get-current-source))) + (helm-log-run-hook 'helm-select-action-hook) + (setq helm-saved-selection (helm-get-selection nil nil src)) + (with-selected-frame (with-helm-window (selected-frame)) + (prog1 + (helm-acond ((get-buffer-window helm-action-buffer 'visible) + (set-window-buffer it helm-buffer) + (helm--set-action-prompt 'restore) + (when (helm--show-action-window-other-window-p) + (delete-window it)) + (kill-buffer helm-action-buffer) + (setq helm-saved-selection nil) + (helm-set-pattern helm-input 'noupdate)) + (helm-saved-selection + (setq helm-saved-current-source src) + (let ((actions (helm-get-actions-from-current-source src)) + helm-onewindow-p) + (if (functionp actions) + (message "Sole action: %s" + (if (or (consp actions) + (byte-code-function-p actions) + (helm-subr-native-elisp-p actions)) + "Anonymous" actions)) + (helm-show-action-buffer actions) + ;; Be sure the minibuffer is entirely deleted (bug#907). + (helm--delete-minibuffer-contents-from "") + (helm--set-action-prompt) + (helm-check-minibuffer-input)))) + (t (message "No Actions available"))) + (helm-display-mode-line (helm-get-current-source)) + (run-hooks 'helm-window-configuration-hook)))))) +(put 'helm-select-action 'helm-only t) + +(defun helm-menu-select-action (_event) + "Popup action menu from mouse-3." + (interactive "e") + (if (get-buffer-window helm-action-buffer 'visible) + (helm-select-action) + (let ((src (helm-get-current-source))) + (helm-aif (helm-get-actions-from-current-source src) + (progn + (setq helm-saved-current-source src) + (if (functionp it) + (message "Sole action: %s" + (if (or (consp it) + (byte-code-function-p it) + (helm-subr-native-elisp-p it)) + "Anonymous" it)) + (setq helm-saved-action + (x-popup-menu + t (list "Available Actions" + (cons "" it)))) + (helm-maybe-exit-minibuffer)) + (message "No Actions available")))))) +(put 'helm-menu-select-action 'helm-only t) + +(defun helm--set-action-prompt (&optional restore) + (with-selected-window (minibuffer-window) + (let ((inhibit-read-only t) + (props '(face minibuffer-prompt + field t + read-only t + rear-nonsticky t + front-sticky t)) + (prt (if restore helm--prompt helm--action-prompt))) + (erase-buffer) + (insert (apply #'propertize prt props))))) + +(defun helm-show-action-buffer (actions) + (with-current-buffer (get-buffer-create helm-action-buffer) + (erase-buffer) + (buffer-disable-undo) + (setq cursor-type nil) + (set-window-buffer (if (helm--show-action-window-other-window-p) + (split-window (get-buffer-window helm-buffer) + nil helm-show-action-window-other-window) + (get-buffer-window helm-buffer)) + helm-action-buffer) + (set (make-local-variable 'helm-sources) + (list + (helm-build-sync-source "Actions" + :volatile t + :nomark t + :persistent-action #'ignore + :persistent-help "DoNothing" + :keymap 'helm-map + :candidates actions + :mode-line '("Action(s)" "\\\\[helm-select-action]:BackToCands RET/f1/f2/fn:NthAct") + :candidate-transformer + (lambda (candidates) + (cl-loop for (i . j) in candidates + for count from 1 + collect + (cons (concat (cond ((> count 12) + " ") + ((< count 10) + (format "[f%s] " count)) + (t (format "[f%s] " count))) + (propertize i 'face 'helm-action)) + j))) + :candidate-number-limit nil))) + (set (make-local-variable 'helm-source-filter) nil) + (set (make-local-variable 'helm-selection-overlay) nil) + (helm-initialize-overlays helm-action-buffer))) + + +;; Selection of candidates + +(defun helm-display-source-at-screen-top-maybe (unit) + "Display source at the top of screen when UNIT value is \\='source. +Return nil for any other value of UNIT." + (when (and helm-display-source-at-screen-top (eq unit 'source)) + (set-window-start (selected-window) + (save-excursion (forward-line -1) (point))))) + +(defun helm-skip-noncandidate-line (direction) + "Skip source header or candidates separator when going in DIRECTION. +DIRECTION is either \\='next or \\='previous. +Same as `helm-skip-header-and-separator-line' but ensure point is +moved to the right place when at bob or eob." + (helm-skip-header-and-separator-line direction) + (and (bobp) (forward-line 1)) ; Skip first header. + (and (eobp) (forward-line -1))) ; Avoid last empty line. + +(defun helm-skip-header-and-separator-line (direction) + "Skip source header or candidate separator when going to next/previous line. +DIRECTION is either \\='next or \\='previous." + (let ((fn (cl-ecase direction + (next 'eobp) + (previous 'bobp)))) + (while (and (not (funcall fn)) + (or (helm-pos-header-line-p) + (helm-pos-candidate-separator-p))) + (forward-line (if (and (eq direction 'previous) + (not (eq (point-at-bol) (point-min)))) + -1 1))))) + +(defun helm-display-mode-line (source &optional force) + "Set up mode-line and header-line for `helm-buffer'. + +SOURCE is a Helm source object. + +Optional argument FORCE forces redisplay of the Helm buffer's +mode and header lines." + (set (make-local-variable 'helm-mode-line-string) + (helm-interpret-value (or (and (listp source) ; Check if source is empty. + (assoc-default 'mode-line source)) + (default-value 'helm-mode-line-string)) + source)) + (let ((follow (and (or (helm-follow-mode-p source) + (and helm-follow-mode-persistent + (member (assoc-default 'name source) + helm-source-names-using-follow))) + " (HF)")) + (marked (and helm-marked-candidates + (cl-loop with cur-name = (assoc-default 'name source) + for c in helm-marked-candidates + for name = (assoc-default 'name (car c)) + when (string= name cur-name) + collect c)))) + ;; Setup mode-line. + (if helm-mode-line-string + (setq mode-line-format + `(:propertize + (" " mode-line-buffer-identification " " + (:eval (format "L%-3d" (helm-candidate-number-at-point))) + ,follow + " " + (:eval ,(and marked + (propertize + (format "M%d" (length marked)) + 'face 'helm-visible-mark))) + (:eval (when ,helm--mode-line-display-prefarg + (let ((arg (prefix-numeric-value + (or prefix-arg current-prefix-arg)))) + (unless (= arg 1) + (propertize (format " [prefarg:%s]" arg) + 'face 'helm-prefarg))))) + " " + (:eval (with-helm-buffer + (helm-show-candidate-number + (car-safe helm-mode-line-string)))) + " " helm--mode-line-string-real " " + (:eval (make-string (window-width) ? ))) + keymap (keymap (mode-line keymap + (mouse-1 . ignore) + (down-mouse-1 . ignore) + (drag-mouse-1 . ignore) + (mouse-2 . ignore) + (down-mouse-2 . ignore) + (drag-mouse-2 . ignore) + (mouse-3 . ignore) + (down-mouse-3 . ignore) + (drag-mouse-3 . ignore)))) + helm--mode-line-string-real + (substitute-command-keys (if (listp helm-mode-line-string) + (cadr helm-mode-line-string) + helm-mode-line-string))) + (setq mode-line-format (default-value 'mode-line-format))) + ;; Setup header-line. + (cond (helm-echo-input-in-header-line + (setq force t) + (helm--set-header-line)) + (helm-display-header-line + (let ((hlstr (helm-interpret-value + (and (listp source) + (assoc-default 'header-line source)) + source)) + (endstr (make-string (window-width) ? ))) + (setq header-line-format + (propertize (concat " " hlstr endstr) + 'face 'helm-header)))))) + (when force (force-mode-line-update))) + +(defun helm--set-header-line (&optional update) + (with-selected-window (minibuffer-window) + (when helm-display-header-line + ;; Prevent cursor movement over the overlay displaying + ;; persistent-help in minibuffer (Bug#2108). + (setq-local disable-point-adjustment t)) + (let* ((beg (save-excursion (vertical-motion 0 (helm-window)) (point))) + (end (save-excursion (end-of-visual-line) (point))) + ;; The visual line where the cursor is. + (cont (buffer-substring beg end)) + (pref (propertize + " " + 'display (if (string-match-p (regexp-opt `(,helm--prompt + ,helm--action-prompt)) + cont) + `(space :width ,helm-header-line-space-before-prompt) + (propertize + "->" + 'face 'helm-header-line-left-margin)))) + (pos (- (point) beg))) + ;; Increment pos each time we find a "%" up to current-pos (bug#1648). + (cl-loop for c across (buffer-substring-no-properties beg (point)) + when (eql c ?%) do (cl-incf pos)) + ;; Increment pos when cursor is on a "%" to make it visible in header-line + ;; i.e "%%|" and not "%|%" (bug#1649). + (when (eql (char-after) ?%) (setq pos (1+ pos))) + (setq cont (replace-regexp-in-string "%" "%%" cont)) + (with-helm-buffer + (setq header-line-format (concat pref cont " ")) + (funcall helm-default-prompt-display-function pos) + (when update (force-mode-line-update)))))) + +(defun helm-set-default-prompt-display (pos) + (put-text-property + ;; Increment pos to handle the space before prompt (i.e `pref'). + (+ 1 pos) (+ 2 pos) + 'face + ;; Don't just use cursor face, this can hide the current character. + (list :inverse-video t + :foreground (face-background 'cursor) + :background (face-background 'default)) + header-line-format)) + +(defun helm-exchange-minibuffer-and-header-line () + "Display minibuffer in header-line and vice versa for current Helm session. + +This is a toggle command." + (interactive) + (with-helm-window + (add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe) + (setq-local helm-echo-input-in-header-line + (not helm-echo-input-in-header-line)) + (with-selected-window (minibuffer-window) + (if (with-helm-buffer helm-echo-input-in-header-line) + (helm-hide-minibuffer-maybe) + (remove-overlays) + (setq cursor-type t))) + (helm-display-mode-line (helm-get-current-source) t))) +(put 'helm-exchange-minibuffer-and-header-line 'helm-only t) + +(defun helm--update-header-line () + ;; This should be used in `post-command-hook', + ;; nowhere else. + (when (with-helm-buffer helm-echo-input-in-header-line) + (helm--set-header-line t))) + +(defun helm-hide-minibuffer-maybe () + "Hide minibuffer contents in a Helm session. +This function should normally go to `helm-minibuffer-set-up-hook'. +It has no effect if `helm-echo-input-in-header-line' is nil." + (when (with-helm-buffer helm-echo-input-in-header-line) + (let ((ov (make-overlay (point-min) (point-max) nil nil t))) + (overlay-put ov 'window (selected-window)) + (helm-aif (and helm-display-header-line + (helm-get-attr 'persistent-help)) + (progn + (overlay-put ov 'display + (truncate-string-to-width + (substitute-command-keys + (concat "\\\\[helm-execute-persistent-action]: " + (format "%s (keeping session)" it))) + (- (window-width) 1))) + (overlay-put ov 'face 'helm-header)) + (overlay-put ov 'face (let ((bg-color (face-background 'default nil))) + `(:background ,bg-color :foreground ,bg-color)))) + + (setq cursor-type nil)))) + +(defun helm-show-candidate-number (&optional name) + "Used to display candidate number in mode-line. +You can specify NAME of candidates e.g. \"Buffers\" otherwise it +is \"Candidate(s)\" by default." + (when helm-alive-p + (unless (helm-empty-source-p) + ;; Build a fixed width string when candidate-number < 1000 + (let* ((cand-name (or name "Candidate(s)")) + (width (length (format "[999 %s]" cand-name)))) + (propertize + (format (concat "%-" (number-to-string width) "s") + (format "[%s %s]" + (helm-get-candidate-number 'in-current-source) + cand-name)) + 'face (if helm-suspend-update-flag + 'helm-candidate-number-suspended + 'helm-candidate-number)))))) + +(cl-defun helm-move-selection-common (&key where direction (follow t)) + "Move the selection marker to a new position. +Position is determined by WHERE and DIRECTION. +Key arg WHERE can be one of: + - line + - page + - edge + - source +Key arg DIRECTION can be one of: + - previous + - next + - A source or a source name when used with :WHERE \\='source." + (let ((move-func (cl-case where + (line (cl-ecase direction + (previous 'helm-move--previous-line-fn) + (next 'helm-move--next-line-fn))) + (page (cl-ecase direction + (previous 'helm-move--previous-page-fn) + (next 'helm-move--next-page-fn))) + (edge (cl-ecase direction + (previous 'helm-move--beginning-of-buffer-fn) + (next 'helm-move--end-of-buffer-fn))) + (source (cl-case direction + (previous 'helm-move--previous-source-fn) + (next 'helm-move--next-source-fn) + (t (lambda () ; A source is passed as DIRECTION arg. + (helm-move--goto-source-fn direction))))))) + source) + (unless (or (helm-empty-buffer-p (helm-buffer-get)) + (not (helm-window))) + (with-helm-window + (when helm-allow-mouse + (helm--mouse-reset-selection-help-echo)) + (helm-log-run-hook 'helm-move-selection-before-hook) + (funcall move-func) + (and (memq direction '(next previous)) + (helm-skip-noncandidate-line direction)) + (when (helm-pos-multiline-p) + (helm-move--beginning-of-multiline-candidate)) + (helm-display-source-at-screen-top-maybe where) + (helm-mark-current-line) + (when follow + (helm-follow-execute-persistent-action-maybe)) + (helm-display-mode-line (setq source (helm-get-current-source))) + (helm-log-run-hook 'helm-move-selection-after-hook) + (helm--set-minibuffer-completion-confirm source))))) + +(defun helm-move--beginning-of-multiline-candidate () + (let ((header-pos (helm-get-previous-header-pos)) + (separator-pos (helm-get-previous-candidate-separator-pos))) + (when header-pos + (goto-char (if (or (null separator-pos) + (< separator-pos header-pos)) + header-pos + separator-pos)) + (forward-line 1)))) + +(defun helm-move--previous-multi-line-fn () + (forward-line -1) + (unless (helm-pos-header-line-p) + (helm-skip-header-and-separator-line 'previous) + (helm-move--beginning-of-multiline-candidate))) + +(defun helm-move--previous-line-fn () + (if (not (helm-pos-multiline-p)) + (forward-line -1) + (helm-move--previous-multi-line-fn)) + (when (and helm-move-to-line-cycle-in-source + (helm-pos-header-line-p)) + (forward-line 1) + (helm-move--end-of-source) + ;; We are at end of helm-buffer + ;; check if last candidate is a multiline candidate + ;; and jump to it + (when (and (eobp) + (save-excursion (forward-line -1) (helm-pos-multiline-p))) + (helm-move--previous-multi-line-fn)))) + +(defun helm-move--next-multi-line-fn () + (let ((header-pos (helm-get-next-header-pos)) + (separator-pos (helm-get-next-candidate-separator-pos))) + (cond ((and separator-pos + (or (null header-pos) (< separator-pos header-pos))) + (goto-char separator-pos)) + (header-pos + (goto-char header-pos))))) + +(defun helm-move--next-line-fn () + (if (not (helm-pos-multiline-p)) + (forward-line 1) + (helm-move--next-multi-line-fn)) + (when (and helm-move-to-line-cycle-in-source + (or (save-excursion (and (helm-pos-multiline-p) + (goto-char (overlay-end + helm-selection-overlay)) + (helm-end-of-source-p t))) + (helm-end-of-source-p t))) + (helm-move--beginning-of-source) + (helm-display-source-at-screen-top-maybe 'source))) + +(defun helm-move--previous-page-fn () + (condition-case nil + (scroll-down helm-scroll-amount) + (beginning-of-buffer (goto-char (point-min))))) + +(defun helm-move--next-page-fn () + (condition-case nil + (scroll-up helm-scroll-amount) + (end-of-buffer (goto-char (point-max))))) + +(defun helm-move--beginning-of-buffer-fn () + (goto-char (point-min))) + +(defun helm-move--end-of-buffer-fn () + (goto-char (point-max))) + +(defun helm-move--end-of-source () + (helm-aif (helm-get-next-header-pos) + (progn (goto-char it) (forward-line -2)) + (goto-char (point-max)))) + +(defun helm-move--beginning-of-source () + (helm-aif (helm-get-previous-header-pos) + (progn (goto-char it) + (forward-line 1)) + (goto-char (point-min)))) + +(defun helm-move--previous-source-fn () + (forward-line -1) + (if (bobp) + (goto-char (point-max)) + (helm-skip-header-and-separator-line 'previous)) + (goto-char (helm-get-previous-header-pos)) + (forward-line 1)) + +(defun helm-move--next-source-fn () + (goto-char (or (and (not (save-excursion + (forward-line 1) (eobp))) + ;; Empty source at eob are just + ;; not displayed unless they are dummy. + ;; Bug#1117. + (helm-get-next-header-pos)) + (point-min)))) + +(defun helm-move--goto-source-fn (source-or-name) + (goto-char (point-min)) + (let ((name (if (stringp source-or-name) + source-or-name + (assoc-default 'name source-or-name)))) + (if (or (null name) (string= name "")) + (forward-line 1) + (condition-case err + (while (not (string= name (helm-current-line-contents))) + (goto-char (helm-get-next-header-pos))) + (error (helm-log "%S" err)))))) + +(defun helm-candidate-number-at-point () + (if helm-alive-p + (with-helm-buffer + (or (get-text-property (point) 'helm-cand-num) 1)) + (or (get-text-property (point) 'helm-cand-num) 1))) + +(defun helm--next-or-previous-line (direction &optional arg) + ;; Be sure to not use this in non--interactives calls. + (let ((helm-move-to-line-cycle-in-source + (and helm-move-to-line-cycle-in-source arg))) + (if (and arg (> arg 1)) + (cl-loop with pos = (helm-candidate-number-at-point) + with cand-num = (helm-get-candidate-number t) + with iter = (min arg (if (eq direction 'next) + (- cand-num pos) + (min arg (1- pos)))) + for count from 1 + while (<= count iter) + do + (helm-move-selection-common :where 'line :direction direction)) + (helm-move-selection-common :where 'line :direction direction)))) + +(defun helm-previous-line (&optional arg) + "Move selection to the ARG previous line(s). +Same behavior as `helm-next-line' when called with a numeric +prefix arg." + (interactive "p") + (with-helm-alive-p + (helm--next-or-previous-line 'previous arg))) +(put 'helm-previous-line 'helm-only t) + +(defun helm-next-line (&optional arg) + "Move selection to the next ARG line(s). +When numeric prefix arg is > than the number of candidates, then +move to the last candidate of current source (i.e. don't move to +next source)." + (interactive "p") + (with-helm-alive-p + (helm--next-or-previous-line 'next arg))) +(put 'helm-next-line 'helm-only t) + +(defun helm-scroll-up () + "Scroll up helm-buffer by `helm-scroll-amount' lines." + (interactive) + (with-helm-alive-p + (helm-move-selection-common :where 'page :direction 'previous))) +(put 'helm-scroll-up 'helm-only t) + +(defun helm-previous-page () + "Move selection back with a pageful." + (interactive) + (with-helm-alive-p + (let (helm-scroll-amount) + (helm-move-selection-common :where 'page :direction 'previous)))) +(put 'helm-previous-page 'helm-only t) + +(defun helm-scroll-down () + "Scroll down helm-buffer by `helm-scroll-amount' lines." + (interactive) + (with-helm-alive-p + (helm-move-selection-common :where 'page :direction 'next))) +(put 'helm-scroll-down 'helm-only t) + +(defun helm-next-page () + "Move selection forward with a pageful." + (interactive) + (with-helm-alive-p + (let (helm-scroll-amount) + (helm-move-selection-common :where 'page :direction 'next)))) +(put 'helm-next-page 'helm-only t) + +(defun helm-beginning-of-buffer () + "Move selection at the top." + (interactive) + (with-helm-alive-p + (helm-move-selection-common :where 'edge :direction 'previous))) +(put 'helm-beginning-of-buffer 'helm-only t) + +(defun helm-end-of-buffer () + "Move selection at the bottom." + (interactive) + (with-helm-alive-p + (helm-move-selection-common :where 'edge :direction 'next))) +(put 'helm-end-of-buffer 'helm-only t) + +(defun helm-previous-source () + "Move selection to the previous source." + (interactive) + (with-helm-alive-p + (helm-move-selection-common :where 'source :direction 'previous))) +(put 'helm-previous-source 'helm-only t) + +(defun helm-next-source () + "Move selection to the next source." + (interactive) + (with-helm-alive-p + (helm-move-selection-common :where 'source :direction 'next))) +(put 'helm-next-source 'helm-only t) + +(defun helm-goto-source (&optional source-or-name) + "Move the selection to the source named SOURCE-OR-NAME. + +If SOURCE-OR-NAME is empty string or nil go to the first +candidate of first source." + (helm-move-selection-common :where 'source :direction source-or-name)) + +(defvar helm-follow-action-white-list-commands + '(helm-ff-decrease-image-size-persistent + helm-ff-increase-image-size-persistent + helm-ff-rotate-left-persistent + helm-ff-rotate-right-persistent) + "Allow `helm-follow-action-forward/backward' switching to next file +when one of these commands is the `last-command'. + +For example when browsing files with `C-` and rotate the current file, +hitting `C-` again will not switch to next file but kill its buffer.") + +(defun helm--follow-action (arg) + (let ((helm--temp-follow-flag t) ; Needed in HFF. + (in-follow-mode (helm-follow-mode-p))) + ;; When follow-mode is already enabled, just go to next or + ;; previous line. + (when (or (eq last-command 'helm-follow-action-forward) + (eq last-command 'helm-follow-action-backward) + (eq last-command 'helm-execute-persistent-action) + (memq last-command helm-follow-action-white-list-commands) + in-follow-mode) + (if (> arg 0) + (helm-move-selection-common :where 'line + :direction 'next + :follow nil) + (helm-move-selection-common :where 'line + :direction 'previous + :follow nil))) + (unless in-follow-mode + (helm-execute-persistent-action)))) + +(defun helm-follow-action-forward () + "Go to next line and execute persistent action." + (interactive) + (with-helm-alive-p (helm--follow-action 1))) +(put 'helm-follow-action-forward 'helm-only t) + +(defun helm-follow-action-backward () + "Go to previous line and execute persistent action." + (interactive) + (with-helm-alive-p (helm--follow-action -1))) +(put 'helm-follow-action-backward 'helm-only t) + +(defun helm-mark-current-line (&optional resumep nomouse) + "Move `helm-selection-overlay' to current line. +When RESUMEP is non nil move overlay to `helm-selection-point'. +When NOMOUSE is specified do not set mouse bindings. + +Note that selection is unrelated to visible marks used for +marking candidates." + (with-helm-buffer + (when resumep + (goto-char helm-selection-point)) + (move-overlay + helm-selection-overlay (point-at-bol) + (if (helm-pos-multiline-p) + (let ((header-pos (helm-get-next-header-pos)) + (separator-pos (helm-get-next-candidate-separator-pos))) + (or (and (null header-pos) separator-pos) + (and header-pos separator-pos + (< separator-pos header-pos) + separator-pos) + header-pos + (point-max))) + (1+ (point-at-eol)))) + (setq helm-selection-point (overlay-start helm-selection-overlay)) + (when (and helm-allow-mouse (null nomouse)) + (helm--bind-mouse-for-selection helm-selection-point)))) + +(defun helm-confirm-and-exit-minibuffer () + "Maybe ask for confirmation when exiting helm. +It is similar to `minibuffer-complete-and-exit' adapted to Helm. +If `minibuffer-completion-confirm' value is \\='confirm, send +minibuffer confirm message and exit on next hit. If +`minibuffer-completion-confirm' value is t, don't exit and send +message \\='no match'." + (interactive) + (with-helm-alive-p + (if (and (helm--updating-p) + (null helm--reading-passwd-or-string)) + (progn (message "[Display not ready]") + (sit-for 0.5) (message nil) + (helm-update)) + (let* ((src (helm-get-current-source)) + (empty-buffer-p (with-current-buffer helm-buffer + (eq (point-min) (point-max)))) + (unknown (and (not empty-buffer-p) + (helm-aif (get-text-property + 0 'display + (helm-get-selection nil 'withprop src)) + (when (stringp it) + (string-match-p "\\`\\[\\?\\]" it)))))) + (cond ((and (or empty-buffer-p unknown) + (memq minibuffer-completion-confirm + '(confirm confirm-after-completion))) + (setq helm-minibuffer-confirm-state + 'confirm) + (setq minibuffer-completion-confirm nil) + (minibuffer-message " [confirm]")) + ;; When require-match is strict (i.e. `t'), buffer + ;; should be either empty or in read-file-name have an + ;; unknown candidate ([?] prefix), if it's not the case + ;; fix it in helm-mode but not here. + ((and (or empty-buffer-p unknown) + (eq minibuffer-completion-confirm t)) + (minibuffer-message " [No match]")) + (empty-buffer-p + ;; This is used when helm-buffer is totally empty, + ;; i.e. the [?] have not been added because must-match + ;; is used from outside helm-comp-read i.e. from a helm + ;; source built with :must-match. + (setq helm-saved-selection helm-pattern + helm-saved-action (helm-get-default-action + (assoc-default + 'action + (car (with-helm-buffer helm-sources)))) + helm-minibuffer-confirm-state nil) + (helm-exit-minibuffer)) + (t + (setq helm-minibuffer-confirm-state nil) + (helm-exit-minibuffer))))))) +(put 'helm-confirm-and-exit-minibuffer 'helm-only t) + +(defun helm-confirm-and-exit-hook () + "Restore `minibuffer-completion-confirm' when helm update." + (unless (or (eq minibuffer-completion-confirm t) + (not helm-minibuffer-confirm-state)) + (setq minibuffer-completion-confirm + helm-minibuffer-confirm-state))) +(add-hook 'helm-after-update-hook 'helm-confirm-and-exit-hook) + +(defun helm--set-minibuffer-completion-confirm (src) + (with-helm-buffer + (helm-aif (helm-get-attr 'must-match src) + (setq minibuffer-completion-confirm it)))) + +(defun helm-read-string (prompt &optional initial-input history + default-value inherit-input-method) + "Same as `read-string' but for reading string from a helm session." + (let ((helm--reading-passwd-or-string t)) + (read-string + prompt initial-input history default-value inherit-input-method))) + +(defun helm--updating-p () + ;; helm timer is between two cycles. + ;; IOW `helm-check-minibuffer-input' haven't yet compared input + ;; and `helm-pattern'. + (or (not (equal (minibuffer-contents) helm-pattern)) + ;; `helm-check-minibuffer-input' have launched `helm-update'. + helm--in-update)) + +(defun helm-maybe-exit-minibuffer () + (interactive) + (with-helm-alive-p + (if (and (helm--updating-p) + (null helm--reading-passwd-or-string)) + (progn + (message "[Display not ready]") + (sit-for 0.5) (message nil) + (helm-update)) + (helm-exit-minibuffer)))) +(put 'helm-maybe-exit-minibuffer 'helm-only t) + +(defun helm-exit-minibuffer () + "Select the current candidate by exiting the minibuffer." + (unless helm-current-prefix-arg + (setq helm-current-prefix-arg current-prefix-arg)) + (setq helm-exit-status 0) + (helm-log-run-hook 'helm-exit-minibuffer-hook) + (exit-minibuffer)) + +(defun helm-keyboard-quit () + "Quit minibuffer in helm. +If action buffer is displayed, kill it." + (interactive) + (with-helm-alive-p + (when (get-buffer-window helm-action-buffer 'visible) + (kill-buffer helm-action-buffer)) + (setq helm-exit-status 1) + (abort-recursive-edit))) +(put 'helm-keyboard-quit 'helm-only t) + +(defun helm-get-next-header-pos () + "Return the position of the next header from point." + (next-single-property-change (point) 'helm-header)) + +(defun helm-get-previous-header-pos () + "Return the position of the previous header from point." + (previous-single-property-change (point) 'helm-header)) + +(defun helm-pos-multiline-p () + "Return non-`nil' if the current position is in the multiline source region." + (get-text-property (point) 'helm-multiline)) + +(defun helm-get-next-candidate-separator-pos () + "Return the position of the next candidate separator from point." + (let ((hp (helm-get-next-header-pos))) + (helm-aif (next-single-property-change (point) 'helm-candidate-separator) + (or + ;; Be sure we don't catch + ;; the separator of next source. + (and hp (< it hp) it) + ;; The separator found is in next source + ;; we are at last cand, so use the header pos. + (and hp (< hp it) hp) + ;; A single source, just try next separator. + it)))) + +(defun helm-get-previous-candidate-separator-pos () + "Return the position of the previous candidate separator from point." + (previous-single-property-change (point) 'helm-candidate-separator)) + +(defun helm-pos-header-line-p () + "Return t if the current line is a header line." + (or (get-text-property (point-at-bol) 'helm-header) + (get-text-property (point-at-bol) 'helm-header-separator))) + +(defun helm-pos-candidate-separator-p () + "Return t if the current line is a candidate separator." + (get-text-property (point-at-bol) 'helm-candidate-separator)) + + +;;; Debugging +;; +;; +(defun helm-debug-output () + "Show all Helm-related variables at this time." + (interactive) + (with-helm-alive-p + (helm-help-internal " *Helm Debug*" 'helm-debug-output-function))) +(put 'helm-debug-output 'helm-only t) + +(defun helm-debug-output-function (&optional vars) + (message "Calculating all helm-related values...") + (insert "If you debug some variables or forms, set `helm-debug-variables' +to a list of forms.\n\n") + (cl-dolist (v (or vars + helm-debug-variables + (apropos-internal "^helm-" 'boundp))) + (insert "** " + (pp-to-string v) "\n" + (pp-to-string (with-current-buffer helm-buffer (eval v))) "\n")) + (message "Calculating all helm-related values...Done")) + +(defun helm-enable-or-switch-to-debug () + "First hit enable helm debugging, second hit switch to debug buffer." + (interactive) + (with-helm-alive-p + (if helm-debug + (helm-run-after-exit + #'helm-debug-open-last-log) + (setq helm-debug t) + (with-helm-buffer (setq truncate-lines nil)) + (message "Debugging enabled")))) +(put 'helm-enable-or-switch-to-debug 'helm-only t) + + +;; Misc + +(defun helm-preselect (candidate-or-regexp &optional source) + "Move selection to CANDIDATE-OR-REGEXP on Helm start. + +CANDIDATE-OR-REGEXP can be a: + +- String +- Cons cell of two strings +- Nullary function, which moves to a candidate + +When CANDIDATE-OR-REGEXP is a cons cell, tries moving to first +element of the cons cell, then the second, and so on. This allows +selection of duplicate candidates after the first. + +Optional argument SOURCE is a Helm source object." + (with-helm-buffer + (when candidate-or-regexp + (if source + (helm-goto-source source) + (goto-char (point-min)) + (forward-line 1)) + (if (functionp candidate-or-regexp) + (funcall candidate-or-regexp) + (let ((start (point)) mp) + (helm-awhile (if (consp candidate-or-regexp) + (and (re-search-forward (car candidate-or-regexp) nil t) + (re-search-forward (cdr candidate-or-regexp) nil t)) + (re-search-forward candidate-or-regexp nil t)) + ;; If search fall on an header line continue loop + ;; until it match or fail (Bug#1509). + (unless (helm-pos-header-line-p) (cl-return (setq mp it)))) + (goto-char (or mp start))))) + (forward-line 0) ; Avoid scrolling right on long lines. + (when (helm-pos-multiline-p) + (helm-move--beginning-of-multiline-candidate)) + (when (helm-pos-header-line-p) (forward-line 1)) + (when helm-allow-mouse + (helm--mouse-reset-selection-help-echo)) + (helm-mark-current-line) + (helm-display-mode-line (or source (helm-get-current-source))) + (helm-log-run-hook 'helm-after-preselection-hook))) + +(defun helm-delete-current-selection () + "Delete the currently selected item." + (with-helm-window + (cond ((helm-pos-multiline-p) + (helm-aif (helm-get-next-candidate-separator-pos) + (delete-region (point-at-bol) + (1+ (progn (goto-char it) (point-at-eol)))) + ;; last candidate + (goto-char (helm-get-previous-candidate-separator-pos)) + (delete-region (point-at-bol) (point-max))) + (when (helm-end-of-source-p) + (goto-char (or (helm-get-previous-candidate-separator-pos) + (point-min))) + (forward-line 1))) + (t + (delete-region (point-at-bol) (1+ (point-at-eol))) + (when (helm-end-of-source-p t) + (let ((headp (save-excursion + (forward-line -1) + (not (helm-pos-header-line-p))))) + (and headp (forward-line -1)))))) + (unless (helm-end-of-source-p t) + (helm-mark-current-line)))) + +(defun helm-end-of-source-1 (n at-point) + (save-excursion + (if (and (helm-pos-multiline-p) (null at-point)) + (null (helm-get-next-candidate-separator-pos)) + (forward-line (if at-point 0 n)) + (or (eq (point-at-bol) (point-at-eol)) + (helm-pos-header-line-p) + (if (< n 0) (bobp) (eobp)))))) + +(defun helm-end-of-source-p (&optional at-point) + "Return non-nil if we are at EOB or end of source." + (helm-end-of-source-1 1 at-point)) + +(defun helm-beginning-of-source-p (&optional at-point) + "Return non-nil if we are at BOB or beginning of source." + (helm-end-of-source-1 -1 at-point)) + +(defun helm--edit-current-selection-internal (func) + (with-helm-window + (forward-line 0) + (let ((realvalue (get-text-property (point) 'helm-realvalue)) + (multiline (get-text-property (point) 'helm-multiline))) + (funcall func) + (forward-line 0) + (and realvalue + (put-text-property (point) (point-at-eol) + 'helm-realvalue realvalue)) + (and multiline + (put-text-property (point) + (or (helm-get-next-candidate-separator-pos) + (point-max)) + 'helm-multiline multiline)) + (helm-mark-current-line)))) + +(defmacro helm-edit-current-selection (&rest forms) + "Evaluate FORMS at current selection in the helm buffer. +Used generally to modify current selection." + (declare (indent 0) (debug t)) + `(helm--edit-current-selection-internal + (lambda () ,@forms))) + +(defun helm--delete-minibuffer-contents-from (from-str) + ;; Giving an empty string value to FROM-STR delete all. + (let ((input (minibuffer-contents))) + (helm-reset-yank-point) + (if (> (length input) 0) + ;; minibuffer is not empty, delete contents from end + ;; of FROM-STR and update. + (helm-set-pattern from-str) + ;; minibuffer is already empty, force update. + (helm-force-update)))) + +(defun helm-delete-minibuffer-contents (&optional arg) + "Delete minibuffer contents. +When `helm-delete-minibuffer-contents-from-point' is non-nil, +delete minibuffer contents from point instead of deleting all. +With a prefix arg reverse this behaviour. When at the end of +minibuffer, delete all." + (interactive "P") + (with-helm-alive-p + (let ((str (if helm-delete-minibuffer-contents-from-point + (if (or arg (eobp)) + "" (helm-minibuffer-completion-contents)) + (if (and arg (not (eobp))) + (helm-minibuffer-completion-contents) "")))) + (helm--delete-minibuffer-contents-from str)))) +(put 'helm-delete-minibuffer-contents 'no-helm-mx t) + + +;;; helm-source-in-buffer. +;; +(defun helm-candidates-in-buffer (&optional source) + "The top level function used to store candidates with `helm-source-in-buffer'. + +Candidates are stored in a buffer generated internally by +`helm-candidate-buffer' function. Each candidate must be placed +in one line. + +The buffer is created and fed in the init attribute function of +Helm. + +E.g.: + + (helm-build-in-buffer-source \"test\" + :init (lambda () + (helm-init-candidates-in-buffer + \\='global \\='(foo foa fob bar baz)))) + +A shortcut can be used to simplify: + + (helm-build-in-buffer-source \"test\" + :data \\='(foo foa fob bar baz)) + +By default, Helm makes candidates by evaluating the candidates +function, then narrows them by `string-match' for each candidate. + +But this is slow for large number of candidates. The new way is +to store all candidates in a buffer and then narrow with +`re-search-forward'. Search function is customizable by search +attribute. The important point is that buffer processing is MUCH +FASTER than string list processing and is the Emacs way. + +The init function writes all candidates to a newly-created +candidate buffer. The candidates buffer is created or specified +by `helm-candidate-buffer'. Candidates are stored in a line. + +The candidates function narrows all candidates, IOW creates a +subset of candidates dynamically. + +Class `helm-source-in-buffer' is implemented with three attributes: + + (candidates . helm-candidates-in-buffer) + (volatile) + (match identity) + +The volatile attribute is needed because +`helm-candidates-in-buffer' creates candidates dynamically and +need to be called every time `helm-pattern' changes. + +Because `helm-candidates-in-buffer' plays the role of `match' +attribute function, specifying `(match identity)' makes the +source slightly faster. + +However if source contains `match-part' attribute, match is +computed only on part of candidate returned by the call of +function provided by this attribute. The function should have one +arg, candidate, and return only a specific part of candidate. + +To customize `helm-candidates-in-buffer' behaviour, use `search', +`get-line' and `match-part' attributes." + (let ((src (or source (helm-get-current-source)))) + (helm-candidates-in-buffer-1 + (helm-candidate-buffer) + helm-pattern + (or (assoc-default 'get-line src) + #'buffer-substring-no-properties) + (or (assoc-default 'search src) + '(helm-candidates-in-buffer-search-default-fn)) + (helm-candidate-number-limit src) + (helm-get-attr 'match-part) + src))) + +(defun helm-candidates-in-buffer-search-default-fn (pattern) + "Search PATTERN with `re-search-forward' with bound and noerror args." + (condition-case _err + (re-search-forward pattern nil t) + (invalid-regexp nil))) + +(defun helm-candidates-in-buffer-1 (buffer pattern get-line-fn + search-fns limit + match-part-fn source) + "Return the list of candidates inserted in BUFFER matching PATTERN." + ;; buffer == nil when candidates buffer does not exist. + (when buffer + (with-current-buffer buffer + (let ((inhibit-point-motion-hooks t) + (start-point (1- (point-min)))) + (goto-char start-point) + (if (string= pattern "") + (helm-initial-candidates-from-candidate-buffer + get-line-fn limit) + (helm-search-from-candidate-buffer + pattern get-line-fn search-fns limit + start-point match-part-fn source)))))) + + +(defun helm-search-from-candidate-buffer (pattern get-line-fn search-fns + limit start-point match-part-fn source) + (let ((inhibit-read-only t) + (diacritics (assoc-default 'diacritics source))) + (helm--search-from-candidate-buffer-1 + (lambda () + (cl-loop with hash = (make-hash-table :test 'equal) + with allow-dups = (assq 'allow-dups source) + with case-fold-search = (helm-set-case-fold-search) + with count = 0 + for iter from 1 + for searcher in search-fns + do (progn + (goto-char start-point) + ;; The character at start-point is a newline, + ;; if pattern match it that's mean we are + ;; searching for newline in buffer, in this + ;; case skip this false line. + ;; See comment >>>[1] in + ;; `helm--search-from-candidate-buffer-1'. + (and (condition-case nil + (looking-at pattern) + (invalid-regexp nil)) + (forward-line 1))) + nconc + (cl-loop with pos-lst + ;; POS-LST is used as a flag to decide if we + ;; run `helm-search-match-part' even if + ;; MATCH-PART isn't specified on source. This + ;; happen when fuzzy matching or using a + ;; negation (!) in one of the patterns, in + ;; these case the searcher returns a list + ;; '(BEG END) instead of an integer like + ;; `re-search-forward'. + while (and (setq pos-lst (funcall searcher pattern)) + (not (eobp)) + (< count limit)) + for cand = (apply get-line-fn + (if (and pos-lst (listp pos-lst)) + pos-lst + (list (point-at-bol) (point-at-eol)))) + when (and match-part-fn + (not (get-text-property 0 'match-part cand))) + do (setq cand + (propertize cand 'match-part (funcall match-part-fn cand))) + for dup = (gethash cand hash) + when (and (or (and allow-dups dup (= dup iter)) + (null dup)) + (or + ;; Always collect when cand is matched + ;; by searcher funcs and match-part attr + ;; is not present. + (and (not match-part-fn) + (not (consp pos-lst))) + ;; If match-part attr is present, or if SEARCHER fn + ;; returns a cons cell, collect PATTERN only if it + ;; match the part of CAND specified by + ;; the match-part func. + (helm-search-match-part cand pattern diacritics))) + do (progn + (puthash cand iter hash) + (helm--maybe-process-filter-one-by-one-candidate cand source) + (cl-incf count)) + and collect cand)))))) + +(defun helm-search-match-part (candidate pattern diacritics) + "Match PATTERN only on match-part property value of CANDIDATE. + +Because `helm-search-match-part' may be called even if +unspecified in source (negation or fuzzy), the part to match +falls back to the whole candidate even if match-part hasn't been +computed by match-part-fn and stored in the match-part property." + (let ((part (or (get-text-property 0 'match-part candidate) + candidate)) + (fuzzy-regexp (cadr (gethash 'helm-pattern helm--fuzzy-regexp-cache))) + (matchfn (cond (helm-migemo-mode 'helm-mm-migemo-string-match) + (diacritics 'helm-mm-diacritics-string-match) + (t 'string-match)))) + (if (string-match " " pattern) + (cl-loop for i in (helm-mm-split-pattern pattern) always + (if (string-match "\\`!" i) + (not (funcall matchfn (substring i 1) part)) + (funcall matchfn i part))) + (if (string-match "\\`!" pattern) + (if helm--in-fuzzy + ;; Fuzzy regexp have already been + ;; computed with substring 1. + (not (string-match fuzzy-regexp part)) + (not (funcall matchfn (substring pattern 1) part))) + (funcall matchfn (if helm--in-fuzzy fuzzy-regexp pattern) part))))) + +(defun helm-initial-candidates-from-candidate-buffer (get-line-fn limit) + (cl-loop repeat limit + until (eobp) + for line = (funcall get-line-fn (point-at-bol) (point-at-eol)) + when line collect line + do (forward-line 1))) + +(defun helm--search-from-candidate-buffer-1 (search-fn) + ;; We are adding a newline at bob and at eol + ;; and removing these newlines afterward. + ;; This is a bad hack that should be removed. + ;; To avoid matching the empty line at first line + ;; when searching with e.g occur and "^$" just + ;; forward-line before searching (See >>>[1] above). + (goto-char (point-min)) + (insert "\n") + (goto-char (point-max)) + (insert "\n") + (unwind-protect + (funcall search-fn) + (goto-char (point-min)) + (delete-char 1) + (goto-char (1- (point-max))) + (delete-char 1) + (set-buffer-modified-p nil))) + +(defun helm-candidate-buffer (&optional buffer-spec) + "Register and return a buffer storing candidates of current source. + +This is used to initialize a buffer for storing candidates for a +candidates-in-buffer source, candidates will be searched in this +buffer and displayed in `helm-buffer'. This should be used only +in init functions, don't relay on this in other places unless you +know what you are doing. + +This function is still in public API only for backward +compatibility, you should use instead +`helm-init-candidates-in-buffer' for initializing your sources. + +Internally, this function is called without argument and returns +the buffer corresponding to current source i.e. +`helm--source-name' which is available in only some places. + +Acceptable values of BUFFER-SPEC: + +- global (a symbol) + Create a new global candidates buffer, + named \" *helm candidates:SOURCE*\". + This is used by `helm-init-candidates-in-buffer' and it is + the most common usage of BUFFER-SPEC. + The buffer will be killed and recreated at each new + helm-session. + +- local (a symbol) + Create a new local candidates buffer, + named \" *helm candidates:SOURCE*HELM-CURRENT-BUFFER\". + You may want to use this when you want to have a different + buffer each time source is used from a different + `helm-current-buffer'. + The buffer is erased and refilled at each new session but not + killed. You probably don't want to use this value for + BUFFER-SPEC. + +- nil (omit) + Only return the candidates buffer of current source if found. + +- A buffer + Register a buffer as a candidates buffer. + The buffer needs to exists, it is not created. + This allow you to use the buffer as a cache, it is faster + because the buffer is already drawn, but be careful when using + this as you may mangle your buffer depending on what you write + in your init(s) function, IOW don't modify the contents of the + buffer in init(s) function but in a transformer. + The buffer is not erased nor deleted. + Generally it is safer to use a copy of buffer inserted + in a global or local buffer. + +If for some reasons a global buffer and a local buffer exist and +are belonging to the same source, the local buffer takes +precedence on the global one and is used instead. + +When forcing update only the global and local buffers are killed +before running again the init function." + (let ((global-bname (format " *helm candidates:%s*" + helm--source-name)) + (local-bname (format " *helm candidates:%s*%s" + helm--source-name + (buffer-name helm-current-buffer)))) + (when buffer-spec + ;; Register buffer in `helm--candidate-buffer-alist'. + ;; This is used only to retrieve buffer associated to current source + ;; when using named buffer as value of BUFFER-SPEC. + (setq helm--candidate-buffer-alist + (cons (cons helm--source-name buffer-spec) + (delete (assoc helm--source-name + helm--candidate-buffer-alist) + helm--candidate-buffer-alist))) + ;; When using global or local as value of BUFFER-SPEC + ;; create the buffer global-bname or local-bname, otherwise + ;; reuse the buffer named BUFFER-SPEC. + (unless (bufferp buffer-spec) + ;; Global buffers are killed and recreated. + (and (eq buffer-spec 'global) + (buffer-live-p (get-buffer global-bname)) + (kill-buffer global-bname)) + ;; Create global or local buffer. + ;; Local buffer, once created are reused and a new one + ;; is created when `helm-current-buffer' change across sessions. + (with-current-buffer (get-buffer-create + (helm-acase buffer-spec + (global global-bname) + (local local-bname) + (t (and (stringp buffer-spec) + buffer-spec)))) + ;; We need a buffer not read-only to perhaps insert later + ;; text coming from read-only buffers (Bug#1176). + (set (make-local-variable 'buffer-read-only) nil) + ;; Undo is automatically disabled in buffer names starting + ;; with a space, so no need to disable it. + (erase-buffer) + (font-lock-mode -1)))) + ;; Finally return the candidates buffer. + (helm-acond ((get-buffer local-bname)) + ((get-buffer global-bname)) + ((assoc-default helm--source-name helm--candidate-buffer-alist) + (and (or (stringp it) (bufferp it)) + (buffer-live-p (get-buffer it)) + it))))) + +(defvar helm-candidate-buffer-longest-len 0 + "May store the longest length of candidates in a in-buffer source. +It is a local variable set from `helm-init-candidates-in-buffer' in +`helm-candidate-buffer'. +Allow getting the longest length of initial candidates in transformers +without looping again through the whole list.") + +(defun helm-init-candidates-in-buffer (buffer-spec data) + "Register BUFFER-SPEC with DATA for a helm candidates-in-buffer session. + +Arg BUFFER-SPEC can be a `buffer-name' (stringp), a buffer-spec +object (bufferp), or a symbol, either \\='local or \\='global which is +passed to `helm-candidate-buffer'. +The most common usage of BUFFER-SPEC is \\='global. + +Arg DATA can be either a list or a plain string. +Returns the resulting buffer. + +Use this in your init function to register a buffer for a +`helm-source-in-buffer' session and feed it with DATA. You +probably don't want to bother with this and use the :data slot +when initializing a source with `helm-source-in-buffer' class." + (declare (indent 1)) + (let ((caching (and (or (stringp buffer-spec) + (bufferp buffer-spec)) + (buffer-live-p (get-buffer buffer-spec)))) + (buf (helm-candidate-buffer buffer-spec))) + (unless caching + (with-current-buffer buf + (erase-buffer) + (cond ((listp data) + (insert (mapconcat (lambda (i) + (let ((cand (cond ((symbolp i) (symbol-name i)) + ((numberp i) (number-to-string i)) + (t i)))) + (setq-local helm-candidate-buffer-longest-len + (max helm-candidate-buffer-longest-len + (length cand))) + cand)) + data "\n"))) + ((stringp data) (insert data)))) + buf))) + + +;;; Resplit helm window +;; +;; +(defun helm-toggle-resplit-window () + "Toggle resplit helm window, vertically or horizontally." + (interactive) + (with-helm-alive-p + (if (and (= (length (window-list nil 1)) 2) + (not (window-dedicated-p + (get-buffer-window helm-current-buffer)))) + (progn + (when helm-prevent-escaping-from-minibuffer + (helm-prevent-switching-other-window :enabled nil)) + (unwind-protect + (with-helm-window + (cond ((or helm-full-frame (one-window-p t)) + (user-error "Attempt to resplit a single window")) + ((helm-action-window) + (user-error "Can't resplit while selecting actions")) + (t + (let ((before-height (window-height))) + (delete-window) + (set-window-buffer + (select-window + (if (= (window-height) before-height) ; initial split was horizontal. + ;; Split window vertically with `helm-buffer' placed + ;; on the good side according to actual value of + ;; `helm-split-window-default-side'. + (prog1 + (cond ((or (eq helm-split-window-default-side 'above) + (eq helm-split-window-default-side 'left)) + (split-window + (selected-window) nil 'above)) + (t (split-window-vertically))) + (setq helm-split-window-state 'vertical)) + ;; Split window vertically, same comment as above. + (setq helm-split-window-state 'horizontal) + (cond ((or (eq helm-split-window-default-side 'left) + (eq helm-split-window-default-side 'above)) + (split-window (selected-window) nil 'left)) + (t (split-window-horizontally))))) + helm-buffer)))) + (setq helm--window-side-state (helm--get-window-side-state))) + (when helm-prevent-escaping-from-minibuffer + (helm-prevent-switching-other-window :enabled t)))) + (error "current window configuration not suitable for splitting")))) +(put 'helm-toggle-resplit-window 'helm-only t) + +;; Utility: Resize helm window. +(defun helm-enlarge-window-1 (n) + "Enlarge or narrow helm window. +If N is positive enlarge, if negative narrow." + (unless helm-full-frame + (let ((horizontal-p (eq helm-split-window-state 'horizontal))) + (with-helm-window + (enlarge-window n horizontal-p))))) + +(defun helm-narrow-window () + "Narrow helm window." + (interactive) + (with-helm-alive-p + (helm-enlarge-window-1 -1))) +(put 'helm-narrow-window 'helm-only t) + +(defun helm-enlarge-window () + "Enlarge helm window." + (interactive) + (with-helm-alive-p + (helm-enlarge-window-1 1))) +(put 'helm-enlarge-window 'helm-only t) + +(defun helm-toggle-full-frame (&optional arg) + "Toggle `helm-buffer' full-frame view." + (interactive "p") + (cl-assert (null (helm-action-window)) + nil "Unable to toggle full frame from action window") + (when arg ; Called interactively + (cl-assert (null helm--buffer-in-new-frame-p) + nil "Can't toggle full frame when using helm own frame")) + (if (or helm-onewindow-p + (buffer-local-value 'helm-full-frame (get-buffer helm-buffer))) + (with-helm-window + (setq-local helm-full-frame nil) + (setq helm-onewindow-p nil) + (let ((split-window-preferred-function + helm-split-window-preferred-function)) + (switch-to-buffer helm-current-buffer) + (helm-display-buffer helm-buffer) + (select-window (minibuffer-window)))) + (with-helm-window + (delete-other-windows) + (setq-local helm-full-frame t) + (setq helm-onewindow-p t)))) +(put 'helm-toggle-full-frame 'helm-only t) + +(defun helm-swap-windows () + "Swap window holding `helm-buffer' with other window." + (interactive) + (with-helm-alive-p + (if (= (length (window-list nil 1)) 2) + (cond ((and helm-full-frame (one-window-p t)) + (user-error "Can't swap windows in a single window")) + ((helm-action-window) + (user-error "Can't resplit while selecting actions")) + (t + (let* ((w1 (helm-window)) + (split-state (eq helm-split-window-state 'horizontal)) + (w1size (window-total-size w1 split-state)) + (b1 (window-buffer w1)) ; helm-buffer + (s1 (window-start w1)) + (cur-frame (window-frame w1)) + (w2 (with-selected-window (helm-window) + ;; Don't try to display helm-buffer + ;; in a dedicated window. + (get-window-with-predicate + (lambda (w) (not (window-dedicated-p w))) + 1 cur-frame))) + (w2size (window-total-size w2 split-state)) + (b2 (window-buffer w2)) ; probably helm-current-buffer + (s2 (window-start w2)) + resize) + (with-selected-frame (window-frame w1) + (helm-replace-buffer-in-window w1 b1 b2) + (helm-replace-buffer-in-window w2 b2 b1) + (setq resize + (cond ( ;; helm-window is smaller than other window. + (< w1size w2size) + (- (- (max w2size w1size) + (min w2size w1size)))) + ( ;; helm-window is larger than other window. + (> w1size w2size) + (- (max w2size w1size) + (min w2size w1size))) + ( ;; windows have probably same size. + t nil))) + ;; Maybe resize the window holding helm-buffer. + (and resize (window-resize w2 resize split-state)) + (set-window-start w1 s2 t) + (set-window-start w2 s1 t)) + (setq helm--window-side-state (helm--get-window-side-state))))) + (error "current window configuration not suitable for splitting")))) +(put 'helm-swap-windows 'helm-only t) + +(defun helm--get-window-side-state () + "Return the position of `helm-window' from `helm-current-buffer'. +Possible values are \\='left \\='right \\='below or \\='above." + (let ((side-list '(left right below above))) + (cl-loop for side in side-list + thereis (and (equal (helm-window) + (window-in-direction + side (get-buffer-window helm-current-buffer t) + t)) + side)))) + +(defun helm-replace-buffer-in-window (window buffer1 buffer2) + "Replace BUFFER1 by BUFFER2 in WINDOW registering BUFFER1." + (when (get-buffer-window buffer1) + (unrecord-window-buffer window buffer1) + (set-window-buffer window buffer2))) + +;; Utility: select another action by key +(defun helm-select-nth-action (n) + "Select the N nth action for the currently selected candidate." + (let ((src (helm-get-current-source))) + (setq helm-saved-selection (helm-get-selection nil nil src)) + (unless helm-saved-selection + (error "Nothing is selected")) + (setq helm-saved-action + (helm-get-nth-action + n + (if (get-buffer-window helm-action-buffer 'visible) + (assoc-default 'candidates src) + (helm-get-actions-from-current-source src)))) + (helm-maybe-exit-minibuffer))) + +(defun helm-get-nth-action (n action) + (cond ((and (zerop n) (functionp action)) + action) + ((listp action) + (or (cdr (elt action n)) + (error "No such action"))) + ((and (functionp action) (< 0 n)) + (error "Sole action")) + (t + (error "Error in `helm-select-nth-action'")))) + +(defun helm-execute-selection-action-at-nth (linum) + "Execute default action on candidate at LINUM lines from selection." + (let ((prefarg current-prefix-arg)) + (if (>= linum 0) + (helm-next-line linum) + (helm-previous-line (lognot (1- linum)))) + (setq current-prefix-arg prefarg) + (helm-exit-minibuffer))) + +;;; Persistent Action +;; +(defun helm-initialize-persistent-action () + (set (make-local-variable 'helm-persistent-action-display-window) nil)) + +(cl-defun helm-execute-persistent-action (&optional attr split) + "Perform the associated action ATTR without quitting helm. + +Arg ATTR default will be `persistent-action' or +`persistent-action-if' if unspecified depending on what's found +in source, but it can be anything else. +In this case you have to add this new attribute to your source. +See `persistent-action' and `persistent-action-if' slot +documentation in `helm-source'. + +When `helm-full-frame' is non-nil, and `helm-buffer' is displayed +in only one window, the helm window is split to display +`helm-select-persistent-action-window' in other window to +maintain visibility. The argument SPLIT can be used to force +splitting inconditionally, it is unused actually." + (interactive) + (with-helm-alive-p + (let ((source (helm-get-current-source))) + (unless attr + (setq attr (or (car (assq 'persistent-action source)) + (car (assq 'persistent-action-if source))))) + (helm-log "executing persistent-action") + (let* ((selection (and source (helm-get-selection nil nil source))) + (attr-val (if (eq attr 'persistent-action-if) + (funcall (assoc-default attr source) selection) + (assoc-default attr source))) + ;; If attr value is a cons, use its car as persistent function + ;; and its car to decide if helm window should be splitted. + (fn (if (and (consp attr-val) + ;; maybe a lambda. + (not (functionp attr-val))) + (car attr-val) attr-val)) + (no-split (and (consp attr-val) + (not (functionp attr-val)) + (cdr attr-val))) + (cursor-in-echo-area t) + mode-line-in-non-selected-windows) + (progn + (when (and helm-onewindow-p (null no-split) + (null helm--buffer-in-new-frame-p)) + (helm-toggle-full-frame)) + (when (eq fn 'ignore) + (cl-return-from helm-execute-persistent-action nil)) + (when source + (with-helm-window + (save-selected-window + (if no-split + (helm-select-persistent-action-window :split 'never) + (helm-select-persistent-action-window + :split (or split helm-onewindow-p))) + (helm-log "current-buffer = %S" (current-buffer)) + (let ((helm-in-persistent-action t) + (display-buffer-alist '((".*" (display-buffer-same-window)))) + display-buffer-function pop-up-windows pop-up-frames + special-display-regexps special-display-buffer-names) + (helm-execute-selection-action-1 + selection (or fn (helm-get-actions-from-current-source source)) t) + (unless (helm-action-window) + (helm-log-run-hook 'helm-after-persistent-action-hook))) + ;; A typical case is when a persistent action delete + ;; the buffer already displayed in + ;; `helm-persistent-action-display-window' and `helm-full-frame' + ;; is enabled, we end up with the `helm-buffer' + ;; displayed in two windows. + (when (and helm-onewindow-p + (> (length (window-list)) 1) + (equal (buffer-name + (window-buffer + helm-persistent-action-display-window)) + (helm-buffer-get))) + (delete-other-windows)))))))))) +(put 'helm-execute-persistent-action 'helm-only t) + +(cl-defun helm-persistent-action-display-window (&key split) + "Return the window that will be used for persistent action. +If SPLIT is t window is split in persistent action, if it has the +special symbol `never' don't split, if it is nil don't split either. +The symbol `never' is kept for backward compatibility." + (with-helm-window + (setq helm-persistent-action-display-window + (cond ((and (window-live-p helm-persistent-action-display-window) + (not (member helm-persistent-action-display-window + (get-buffer-window-list helm-buffer)))) + helm-persistent-action-display-window) + ((and helm--buffer-in-new-frame-p helm-initial-frame) + (with-selected-frame helm-initial-frame (selected-window))) + ((and split (not (eq split 'never))) (split-window)) + ((get-buffer-window helm-current-buffer)) + (t (previous-window (selected-window) 1)))))) + +(cl-defun helm-select-persistent-action-window (&key split) + "Select the window that will be used for persistent action. +See `helm-persistent-action-display-window' for how to use SPLIT." + (select-window (get-buffer-window (helm-buffer-get))) + (prog1 + (select-window + (setq minibuffer-scroll-window + (helm-persistent-action-display-window :split split))) + (helm-log "Selected window is %S" minibuffer-scroll-window))) + +;;; Scrolling - recentering +;; +;; +(defun helm-other-window-base (command &optional arg) + (let ((minibuffer-scroll-window + (helm-persistent-action-display-window))) + (funcall command (or arg helm-scroll-amount)))) + +(defun helm-scroll-other-window (&optional arg) + "Scroll other window upward ARG many lines. +When arg is not provided scroll `helm-scroll-amount' lines. +See `scroll-other-window'." + (interactive "P") + (with-helm-alive-p (helm-other-window-base 'scroll-other-window arg))) +(put 'helm-scroll-other-window 'helm-only t) + +(defun helm-scroll-other-window-down (&optional arg) + "Scroll other window downward ARG many lines. +When arg is not provided scroll `helm-scroll-amount' lines. +See `scroll-other-window-down'." + (interactive "P") + (with-helm-alive-p (helm-other-window-base 'scroll-other-window-down arg))) +(put 'helm-scroll-other-window-down 'helm-only t) + +(defun helm-recenter-top-bottom-other-window (&optional arg) + "Run `recenter-top-bottom' in other window. +Meaning of prefix ARG is the same as in `recenter-top-bottom'." + (interactive "P") + (with-helm-alive-p + (with-helm-window + (with-selected-window (helm-persistent-action-display-window) + (recenter-top-bottom arg))))) +(put 'helm-recenter-top-bottom-other-window 'helm-only t) + +(defun helm-reposition-window-other-window (&optional arg) + "Run `reposition-window' in other window. +Meaning of prefix ARG is the same as in `reposition-window'." + (interactive "P") + (with-helm-alive-p + (with-helm-window + (with-selected-window (helm-persistent-action-display-window) + (reposition-window arg))))) +(put 'helm-reposition-window-other-window 'helm-only t) + + +;; Utility: Visible Mark + +(defun helm-clear-visible-mark () + (with-current-buffer (helm-buffer-get) + (mapc 'delete-overlay helm-visible-mark-overlays) + (set (make-local-variable 'helm-visible-mark-overlays) nil))) + +(defun helm-this-visible-mark () + (cl-loop for o in (overlays-at (point)) + when (overlay-get o 'visible-mark) + return o)) + +(defun helm-delete-visible-mark (overlay) + (let ((src (helm-get-current-source))) + (setq helm-marked-candidates + (remove + (cons src (helm-get-selection nil nil src)) + helm-marked-candidates)) + (delete-overlay overlay) + (setq helm-visible-mark-overlays + (delq overlay helm-visible-mark-overlays)))) + +(defun helm-make-visible-mark (&optional src selection) + (let* ((source (or src (helm-get-current-source))) + (sel (or selection (helm-get-selection + nil (helm-get-attr 'marked-with-props source) + source))) + (selection-end (if (helm-pos-multiline-p) + ;; Stays within source + (or (helm-get-next-candidate-separator-pos) + (helm-get-next-header-pos) + (point-max)) + ;; Not multiline + (1+ (point-at-eol)))) + (o (make-overlay (point-at-bol) selection-end))) + (overlay-put o 'priority 0) + (overlay-put o 'face 'helm-visible-mark) + (overlay-put o 'source source) + (overlay-put o 'string (buffer-substring (overlay-start o) (overlay-end o))) + (overlay-put o 'real sel) + (overlay-put o 'before-string (propertize " " 'display + `((margin left-margin) + ,(propertize + helm-visible-mark-prefix + 'face 'helm-mark-prefix)))) + (overlay-put o 'visible-mark t) + (overlay-put o 'evaporate t) + (cl-pushnew o helm-visible-mark-overlays) + (push (cons source sel) helm-marked-candidates))) + +(defun helm-toggle-visible-mark (arg) + "Toggle Helm visible mark at point ARG times. +If ARG is negative toggle backward." + (interactive "p") + (with-helm-alive-p + (with-helm-window + (let ((nomark (assq 'nomark (helm-get-current-source))) + (next-fns (if (< arg 0) + '(helm-beginning-of-source-p . helm-previous-line) + '(helm-end-of-source-p . helm-next-line)))) + (if nomark + (message "Marking not allowed in this source") + (cl-loop with n = (if (< arg 0) (* arg -1) arg) + repeat n do + (progn + (helm-aif (helm-this-visible-mark) + (helm-delete-visible-mark it) + (helm-make-visible-mark)) + (if (funcall (car next-fns)) + (progn + (helm-display-mode-line (helm-get-current-source)) + (cl-return nil)) + (funcall (cdr next-fns))))) + (set-window-margins (selected-window) + (if helm-visible-mark-overlays + (+ (string-width helm-visible-mark-prefix) + helm-left-margin-width) + helm-left-margin-width))))))) +(put 'helm-toggle-visible-mark 'helm-only t) + +(defun helm-toggle-visible-mark-forward () + (interactive) + (helm-toggle-visible-mark 1)) + +(defun helm-toggle-visible-mark-backward () + (interactive) + (helm-toggle-visible-mark -1)) + +(defun helm-file-completion-source-p (&optional source) + "Return non-nil if current source is a file completion source." + (or helm--completing-file-name ; helm-read-file-name + (let ((cur-source (cdr (assq 'name + (or source (helm-get-current-source)))))) + (cl-loop for i in helm--file-completion-sources + thereis (string= cur-source i))))) + +(defun helm-mark-all (&optional all) + "Mark all visible unmarked candidates in current source. + +With a prefix arg mark all visible unmarked candidates in all +sources." + (interactive "P") + (with-helm-alive-p + (with-helm-window ; Using `with-helm-buffer' for some unknow + ; reasons infloop. + (set-window-margins (selected-window) + (+ (string-width helm-visible-mark-prefix) + helm-left-margin-width)) + (if (null all) + (helm-mark-all-1 t) + (let ((pos (point))) + (goto-char (point-min)) + (helm-awhile (helm-get-next-header-pos) + (goto-char it) + (forward-line 1) + (helm-mark-current-line) + (helm-mark-all-1)) + ;; `save-excursion' seems confused if used in addition of + ;; the one used in `helm-mark-all-1', so save POS and back + ;; to it when loop is finished. + (goto-char pos) + (helm-mark-current-line) + (helm-display-mode-line (helm-get-current-source) t)))))) +(put 'helm-mark-all 'helm-only t) + +(defun helm-mark-all-1 (&optional ensure-beg-of-source) + "Mark all visible unmarked candidates in current source. +Need to be wrapped in `with-helm-window'. +Arg ENSURE-BEG-OF-SOURCE ensure we are at beginning of source +when starting to mark candidates, if handled elsewhere before +starting it is not needed." + (let* ((src (helm-get-current-source)) + (follow (if (helm-follow-mode-p src) 1 -1)) + (nomark (assq 'nomark src)) + (src-name (assoc-default 'name src)) + (filecomp-p (or (helm-file-completion-source-p src) + (string= src-name "Files from Current Directory")))) + ;; Note that `cl-letf' prevents edebug working properly. + (cl-letf (((symbol-function 'message) #'ignore)) + (helm-follow-mode -1) + (unwind-protect + (if nomark + (user-error "Marking not allowed in this source") + (save-excursion + (when ensure-beg-of-source + (goto-char (helm-get-previous-header-pos)) + (forward-line 1)) + (let* ((next-head (helm-get-next-header-pos)) + (end (and next-head + (save-excursion + (goto-char next-head) + (forward-line -1) + (point)))) + (maxpoint (or end (point-max)))) + (while (< (point) maxpoint) + (helm-mark-current-line) + (let* ((prefix (get-text-property (point-at-bol) 'display)) + (cand (helm-get-selection + nil (helm-get-attr 'marked-with-props src) + src)) + (bn (and filecomp-p (helm-basename cand)))) + ;; Don't mark possibles directories ending with . or .. + ;; autosave files/links and non--existent files. + (unless + (or (helm-this-visible-mark) + ;; Non existing files in HFF and + ;; RFN. Display may be an image. See + ;; https://github.com/yyoncho/helm-treemacs-icons/issues/5 + ;; and also Bug#2296. + (equal prefix "[?]") + (and filecomp-p + (or + ;; autosave files + (string-match-p "\\`[.]?#.*#?\\'" bn) + ;; dot files + (member bn '("." ".."))))) + (helm-make-visible-mark src cand))) + (when (helm-pos-multiline-p) + (goto-char + (or (helm-get-next-candidate-separator-pos) + (point-max)))) + (forward-line 1)))) + (helm-mark-current-line)) + (helm-follow-mode follow))))) + +(defun helm-unmark-all () + "Unmark all candidates in all sources of current helm session." + (interactive) + (with-helm-alive-p + (with-helm-window + (save-excursion + (helm-clear-visible-mark)) + (setq helm-marked-candidates nil) + (helm-mark-current-line) + (helm-display-mode-line (helm-get-current-source)) + (set-window-margins (selected-window) helm-left-margin-width)))) +(put 'helm-unmark-all 'helm-only t) + +(defun helm-toggle-all-marks (&optional all) + "Toggle all marks. + +Mark all visible candidates of current source or unmark all +candidates visible or invisible in all sources of current Helm +session. + +With a prefix argument mark all candidates in all sources." + (interactive "P") + (with-helm-alive-p + (let ((marked (helm-marked-candidates))) + (if (and (>= (length marked) 1) + (with-helm-window helm-visible-mark-overlays)) + (helm-unmark-all) + (helm-mark-all all))))) +(put 'helm-toggle-all-marks 'helm-only t) + +(defun helm--compute-marked (real source &optional wildcard) + (let* ((coerced (helm-coerce-selection real source)) + (wilds (and wildcard + (condition-case nil + (helm-file-expand-wildcards + coerced t) + (error nil))))) + ;; Avoid returning a not expanded wildcard fname. + ;; e.g assuming "/tmp" doesn't contain "*.el" + ;; return nil when coerced is "/tmp/*.el". + (unless (or wilds (null wildcard) + (string-match-p helm--url-regexp coerced) + (file-exists-p coerced) + (and (stringp coerced) + (null (string-match-p "[[*?]" coerced)))) + (setq coerced nil)) + (or wilds (and coerced (list coerced))))) + +(cl-defun helm-marked-candidates (&key with-wildcard all-sources) + "Return marked candidates of current source, if any. + +Otherwise return one element list consisting of the current +selection. When key WITH-WILDCARD is specified, expand it. When +ALL-SOURCES key value is non-nil returns marked candidates of all +sources." + (with-current-buffer helm-buffer + (let* ((current-src (helm-get-current-source)) + (candidates + (cl-loop for (source . real) in (reverse helm-marked-candidates) + for use-wc = (and with-wildcard + (string-match-p "\\*" real) + (null (file-exists-p real))) + when (or all-sources + (equal (assq 'name source) + (assq 'name current-src))) + append (helm--compute-marked real source use-wc))) + sel) + (unless candidates + (setq sel (helm-get-selection + nil (helm-get-attr 'marked-with-props + current-src) + current-src)) + (setq candidates + (helm--compute-marked + sel current-src + (and with-wildcard (null (file-exists-p sel)))))) + (helm-log "Marked candidates = %S" candidates) + candidates))) + +(defun helm--remove-marked-and-update-mode-line (elm) + (with-helm-buffer + (setq helm-marked-candidates + (delete (rassoc elm helm-marked-candidates) + helm-marked-candidates)) + (helm-display-mode-line (helm-get-current-source)))) + +(defun helm-current-source-name= (name) + (save-excursion + (goto-char (helm-get-previous-header-pos)) + (equal name (helm-current-line-contents)))) + +(defun helm-revive-visible-mark () + "Restore marked candidates when helm updates display." + (with-current-buffer helm-buffer + (save-excursion + (cl-dolist (o helm-visible-mark-overlays) + (let* ((source (overlay-get o 'source)) + (ov-src-name (assoc-default 'name source)) + (ov-str (overlay-get o 'string)) + (ov-real (overlay-get o 'real)) + (ov-ml-str (helm-aif (helm-get-attr 'multiline source) + (if (numberp it) + ;; Assume display have been computed + ;; against real e.g. kill-ring. + (helm--multiline-get-truncated-candidate + ov-real it) + ov-str) + ov-str)) + beg end) + ;; Move point to end of source header line. + (goto-char (point-min)) + (search-forward ov-src-name nil t) + (while (and (search-forward ov-ml-str nil t) + (cl-loop for ov in (overlays-at (point-at-bol 0)) + never (overlay-get ov 'visible-mark)) + (helm-current-source-name= ov-src-name)) + (setq beg (match-beginning 0) + end (if (string= ov-ml-str ov-str) + (match-end 0) (1+ (match-end 0)))) + ;; Calculate real value of candidate. + ;; It can be nil if candidate have only a display value. + (let ((real (get-text-property (point-at-bol 0) 'helm-realvalue))) + (if real + ;; Check if real value of current candidate is the same + ;; than the one stored in overlay. + ;; This is needed when some cands have same display names. + ;; Using equal allow testing any type of value for real cand. + ;; bug#706. + (and (equal ov-real real) + (move-overlay o beg end)) + (and (equal ov-str (buffer-substring beg end)) + (move-overlay o beg end)))))))))) +(add-hook 'helm-after-update-hook 'helm-revive-visible-mark) + +(defun helm-next-point-in-list (curpos points &optional prev) + (cond + ;; rule out special cases. + ((null points) curpos) + ((and prev (<= curpos (car points))) + (nth (1- (length points)) points)) + ((< (car (last points)) curpos) + (if prev (car (last points)) (nth 0 points))) + ((and (not prev) (>= curpos (car (last points)))) + (nth 0 points)) + (t + (nth (if prev + (cl-loop for pt in points + for i from 0 + if (<= curpos pt) return (1- i)) + (cl-loop for pt in points + for i from 0 + if (< curpos pt) return i)) + points)))) + +(defun helm-next-visible-mark (&optional prev) + "Move next Helm visible mark. +If PREV is non-nil move to precedent." + (interactive) + (with-helm-alive-p + (with-helm-window + (ignore-errors + (goto-char (helm-next-point-in-list + (point) + (sort (mapcar 'overlay-start helm-visible-mark-overlays) '<) + prev))) + (helm-mark-current-line)))) +(put 'helm-next-visible-mark 'helm-only t) + +(defun helm-prev-visible-mark () + "Move previous helm visible mark." + (interactive) + (with-helm-alive-p + (helm-next-visible-mark t))) +(put 'helm-prev-visible-mark 'helm-only t) + +;;; Utility: Selection Paste +;; +(defun helm-yank-selection (arg) + "Set minibuffer contents to current display selection. +With a prefix arg set to real value of current selection." + (interactive "P") + (with-helm-alive-p + (let ((str (format "%s" (helm-get-selection nil (not arg))))) + (kill-new str) + (helm-set-pattern str)))) +(put 'helm-yank-selection 'helm-only t) + +(defun helm-kill-selection-and-quit (arg) + "Store display value of current selection to kill ring. +With a prefix arg use real value of current selection. +Display value is shown in `helm-buffer' and real value is used to +perform actions." + (interactive "P") + (with-helm-alive-p + (helm-run-after-exit + (lambda (sel) + (kill-new sel) + ;; Return nil to force `helm-mode--keyboard-quit' + ;; in `helm-comp-read' otherwise the value "Saved to kill-ring: foo" + ;; is used as exit value for `helm-comp-read'. + (prog1 nil (message "Saved to kill-ring: %s" sel) (sit-for 1))) + (format "%s" (helm-get-selection nil (not arg)))))) +(put 'helm-kill-selection-and-quit 'helm-only t) + +(defun helm-insert-or-copy (&optional arg) + "Insert selection or marked candidates in current buffer. + +With a prefix arg copy marked candidates to kill-ring. +The real value of each candidate is used." + (interactive "P") + (with-helm-alive-p + (helm-run-after-exit + (lambda (cands) + (with-helm-current-buffer + (let ((sels (mapconcat (lambda (c) + (format "%s" c)) + cands "\n"))) + (if arg (kill-new sels) (insert sels))))) + (helm-marked-candidates)))) +(put 'helm-insert-or-copy 'helm-only t) + + +;;; Follow-mode: Automatic execution of persistent-action +;; +;; +(defvar helm-follow-input-idle-delay nil + "`helm-follow-mode' will execute its persistent action after this delay. +Note that if the `follow-delay' attr is present in source, it +will take precedence over this.") + +(defun helm-follow-mode (&optional arg) + "Execute persistent action every time the cursor is moved. + +This mode is source local, i.e. It applies on current source only. +\\ +This mode can be enabled or disabled interactively at anytime during +a helm session with \\[helm-follow-mode]. + +When enabling interactively `helm-follow-mode' in a source, you +can keep it enabled for next Emacs sessions by setting +`helm-follow-mode-persistent' to a non-nil value. + +When `helm-follow-mode' is called with a prefix arg and +`helm-follow-mode-persistent' is non-nil `helm-follow-mode' will +be persistent only for this Emacs session, but not for the next +Emacs sessions, i.e. the current source will not be saved to +`helm-source-names-using-follow'. + +A prefix arg with `helm-follow-mode' already enabled will have no +effect. + +Note that you can use instead of this mode the commands +`helm-follow-action-forward' and `helm-follow-action-backward' at +anytime in all Helm sessions. + +They are bound by default to \\[helm-follow-action-forward] and +\\[helm-follow-action-backward]." + (interactive (list (helm-aif (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)) + (unless (helm-follow-mode-p) it)))) + (with-helm-alive-p + (with-current-buffer helm-buffer + (let* ((src (helm-get-current-source)) + (name (assoc-default 'name src)) + (fol-attr (assq 'follow src)) + (enabled (or (helm-follow-mode-p src) + (and helm-follow-mode-persistent + (member (assoc-default 'name src) + helm-source-names-using-follow))))) + (if src + (progn + (if (eq (cdr fol-attr) 'never) + (message "helm-follow-mode not allowed in this source") + ;; Make follow attr persistent for this emacs session. + (helm-follow-mode-set-source + (if (or enabled (and (numberp arg) (< arg 0))) -1 1) + src) + ;; When arg is nil assume the call is interactive. + ;; However if user call helm-follow-mode with a prefix arg, + ;; the call will be considered non--interactive and + ;; src-name will NOT be saved to helm-source-names-using-follow. + ;; When called from lisp (non--interactive) src-name + ;; will never be saved. + (when (and helm-follow-mode-persistent (null arg)) + (if (null enabled) + (unless (member name helm-source-names-using-follow) + (push name helm-source-names-using-follow) + (customize-save-variable 'helm-source-names-using-follow + helm-source-names-using-follow)) + (when (member name helm-source-names-using-follow) + (setq helm-source-names-using-follow + (delete name helm-source-names-using-follow)) + (customize-save-variable 'helm-source-names-using-follow + helm-source-names-using-follow)))) + (message "helm-follow-mode is %s" + (if (helm-follow-mode-p src) + "enabled" "disabled")) + (helm-display-mode-line src t))) + (message "Not enough candidates for helm-follow-mode")))))) +(put 'helm-follow-mode 'helm-only t) + +(defun helm-follow-execute-persistent-action-maybe (&optional delay) + "Execute persistent action in mode `helm-follow-mode'. + +This happen after: DELAY or the \\='follow-attr value of current +source or `helm-follow-input-idle-delay' or +`helm-input-idle-delay' secs." + (let* ((src (helm-get-current-source)) + (at (or delay + (assoc-default 'follow-delay src) + helm-follow-input-idle-delay + (or (and helm-input-idle-delay + (max helm-input-idle-delay 0.01)) + 0.01)))) + (when (and (not (get-buffer-window helm-action-buffer 'visible)) + (not (helm-pos-header-line-p)) + (or (helm-follow-mode-p src) + (and helm-follow-mode-persistent + (member (assoc-default 'name src) + helm-source-names-using-follow))) + (null (eq (assoc-default 'follow src) 'never)) + (helm-get-selection nil nil src)) + (helm-follow-mode-set-source 1 src) + (run-with-idle-timer at nil (lambda () + (when helm-alive-p + (helm-execute-persistent-action))))))) + +(defun helm-follow-mode-p (&optional source) + (with-helm-buffer + (eq (helm-get-attr 'follow (or source (helm-get-current-source))) 1))) + +(defun helm-follow-mode-set-source (value &optional source) + (with-helm-buffer + (helm-set-attr 'follow value (or source (helm-get-current-source))))) + +;;; Auto-resize mode +;; +(defun helm--autoresize-hook (&optional max-height min-height) + (when (helm-window) + (with-helm-window + (fit-window-to-buffer nil + (/ (* (frame-height) + (or max-height helm-autoresize-max-height)) + 100) + (/ (* (frame-height) + (or min-height helm-autoresize-min-height)) + 100))))) + +(define-minor-mode helm-autoresize-mode + "Auto resize helm window when enabled. +Helm window is re-sized according to `helm-autoresize-max-height' +and `helm-autoresize-min-height'. Note that when this mode is +enabled, Helm behaves as if `helm-always-two-windows' is enabled. + +See `fit-window-to-buffer' for more infos." + :group 'helm + :global t + (if helm-autoresize-mode + (progn (add-hook 'helm-after-update-hook 'helm--autoresize-hook) + (add-hook 'helm-window-configuration-hook 'helm--autoresize-hook)) + (remove-hook 'helm-after-update-hook 'helm--autoresize-hook) + (remove-hook 'helm-window-configuration-hook 'helm--autoresize-hook))) + +(defun helm-help () + "Generate Helm's help according to `help-message' attribute. + +If `helm-buffer' is empty, provide completions on `helm-sources' +to choose its local documentation. +If source doesn't have any `help-message' attribute, a generic +message explaining this is added instead. +The global `helm-help-message' is always added after this local +help." + (interactive) + (require 'helm-mode) ; for helm-comp-read. + (with-helm-alive-p + (let ((source (or (helm-get-current-source) + (helm-comp-read + "Help for: " + (cl-loop for src in (with-helm-buffer helm-sources) + collect `(,(assoc-default 'name src) . + ,src)) + :allow-nest t + :exec-when-only-one t)))) + (save-selected-window + (helm-help-internal + helm-help-buffer-name + (lambda () + (helm-aif (assoc-default 'help-message source) + (insert (substitute-command-keys + (helm-interpret-value it))) + (insert "* No specific help for this source available.")) + (insert "\n\n" + (substitute-command-keys + (helm-interpret-value helm-help-message))))))))) +(put 'helm-help 'helm-only t) + +(defun helm-toggle-truncate-line () + "Toggle `truncate-lines' value in `helm-buffer'" + (interactive) + (with-helm-alive-p + (with-helm-buffer + (setq truncate-lines (not truncate-lines)) + (when (helm-get-previous-header-pos) + (helm-update (regexp-quote (helm-get-selection nil t)))) + (message "%sisplaying continuation lines" + (if truncate-lines "Not D" "D"))))) +(put 'helm-toggle-truncate-line 'helm-only t) + + +(provide 'helm-core) +;;; helm-core.el ends here diff --git a/code/elpa/helm-core-20220824.1925/helm-lib.el b/code/elpa/helm-core-20220824.1925/helm-lib.el new file mode 100644 index 0000000..90b191a --- /dev/null +++ b/code/elpa/helm-core-20220824.1925/helm-lib.el @@ -0,0 +1,1939 @@ +;;; helm-lib.el --- Helm routines. -*- lexical-binding: t -*- + +;; Copyright (C) 2015 ~ 2020 Thierry Volpiatto + +;; Author: Thierry Volpiatto +;; URL: http://github.com/emacs-helm/helm + +;; 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 . + +;;; Commentary: +;; All helm functions that don't require specific helm code should go here. + +;;; Code: + +(require 'cl-lib) + +(declare-function ansi-color--find-face "ansi-color.el") +(declare-function ansi-color-apply-sequence "ansi-color.el") +(declare-function dired-current-directory "dired.el") +(declare-function dired-log-summary "dired.el") +(declare-function dired-mark-remembered "dired.el") +(declare-function ffap-file-remote-p "ffap.el") +(declare-function ffap-url-p "ffap.el") +(declare-function helm-get-attr "helm-core.el") +(declare-function helm-set-attr "helm-core.el") +(declare-function helm-follow-mode-p "helm-core.el") +(declare-function helm-get-current-source "helm-core.el") +(declare-function helm-get-selection "helm-core.el") +(declare-function helm-get-sources "helm-core.el") +(declare-function helm-interpret-value "helm-core.el") +(declare-function helm-log-run-hook "helm-core.el") +(declare-function helm-marked-candidates "helm-core.el") +(declare-function helm-set-case-fold-search "helm-core.el") +(declare-function helm-source--cl--print-table "helm-source.el") +(declare-function helm-update "helm-core.el") +(declare-function org-content "org.el") +(declare-function org-mark-ring-goto "org.el") +(declare-function org-mark-ring-push "org.el") +(declare-function org-table-p "org-compat.el") +(declare-function org-table-align "org-table.el") +(declare-function org-table-end "org-table.el") +(declare-function org-open-at-point "org.el") +(declare-function wdired-change-to-dired-mode "wdired.el") +(declare-function wdired-do-perm-changes "wdired.el") +(declare-function wdired-do-renames "wdired.el") +(declare-function wdired-do-symlink-changes "wdired.el") +(declare-function wdired-flag-for-deletion "wdired.el") +(declare-function wdired-get-filename "wdired.el") +(declare-function wdired-normalize-filename "wdired.el") +(declare-function helm-read-file-name "helm-mode.el") +(declare-function find-function-library "find-func.el") +(declare-function find-library-name "find-func.el") + +(defvar helm-sources) +(defvar helm-initial-frame) +(defvar helm-current-position) +(defvar wdired-old-marks) +(defvar wdired-keep-marker-rename) +(defvar wdired-allow-to-change-permissions) +(defvar wdired-allow-to-redirect-links) +(defvar helm-persistent-action-display-window) +(defvar helm--buffer-in-new-frame-p) +(defvar helm-completion-style) +(defvar helm-completion-styles-alist) +(defvar helm-persistent-action-window-buffer) +(defvar completion-flex-nospace) +(defvar find-function-source-path) + +;;; User vars. +;; +(defcustom helm-file-globstar t + "Same as globstar bash shopt option. +When non-nil a pattern beginning with two stars will expand +recursively. +Directories expansion is not supported yet." + :group 'helm + :type 'boolean) + +(defcustom helm-yank-text-at-point-function nil + "The function used to forward point with `helm-yank-text-at-point'. +With a nil value, fallback to default `forward-word'. +The function should take one arg, an integer like `forward-word'. +NOTE: Using `forward-symbol' here is not very useful as it is +already provided by \\\\[next-history-element]." + :type 'function + :group 'helm) + +(defcustom helm-scroll-amount nil + "Scroll amount when scrolling helm window or other window in a helm session. +It is used by `helm-scroll-other-window', `helm-scroll-up', `helm-scroll-down' +and `helm-scroll-other-window-down'. + +If you prefer scrolling line by line, set this value to 1." + :group 'helm + :type 'integer) + +(defcustom helm-help-full-frame t + "Display help window in full frame when non nil. + +Even when nil probably the same result (full frame) can be +reached by tweaking `display-buffer-alist', but it is much more +convenient to use a simple boolean value here." + :type 'boolean + :group 'helm-help) + +(defvar helm-ff--boring-regexp nil) +(defun helm-ff--setup-boring-regex (var val) + (set var val) + (setq helm-ff--boring-regexp + (cl-loop with last = (car (last val)) + for r in (butlast val) + if (string-match "\\$\\'" r) + concat (concat r "\\|") into result + else concat (concat r "$\\|") into result + finally return + (concat result last + (if (string-match "\\$\\'" last) "" "$"))))) + +(defcustom helm-boring-file-regexp-list + (mapcar (lambda (f) + (let ((rgx (regexp-quote f))) + (if (string-match-p "[^/]$" f) + ;; files: e.g .o => \\.o$ + (concat rgx "$") + ;; directories: e.g .git/ => \.git\\(/\\|$\\) + (concat (substring rgx 0 -1) "\\(/\\|$\\)")))) + completion-ignored-extensions) + "A list of regexps matching boring files. + +This list is build by default on `completion-ignored-extensions'. +The directory names should end with \"/?\" e.g. \"\\.git/?\" and +the file names should end with \"$\" e.g. \"\\.o$\". + +These regexps may be used to match the entire path, not just the +file name, so for example to ignore files with a prefix +\".bak.\", use \"\\.bak\\..*$\" as the regexp. + +NOTE: When modifying this, be sure to use customize interface or +the customize functions e.g. `customize-set-variable' and NOT +`setq'." + :group 'helm-files + :type '(repeat (choice regexp)) + :set 'helm-ff--setup-boring-regex) + +(defcustom helm-describe-function-function 'describe-function + "Function used to describe functions in Helm." + :group 'helm-elisp + :type 'function) + +(defcustom helm-describe-variable-function 'describe-variable + "Function used to describe variables in Helm." + :group 'helm-elisp + :type 'function) + + +;;; Internal vars +;; +(defvar helm-yank-point nil) +(defvar helm-pattern "" + "The input pattern used to update the helm buffer.") +(defvar helm-buffer "*helm*" + "Buffer showing completions.") +(defvar helm-current-buffer nil + "Current buffer when `helm' is invoked.") +(defvar helm-suspend-update-flag nil) +(defvar helm-action-buffer "*helm action*" + "Buffer showing actions.") +(defvar helm-current-prefix-arg nil + "Record `current-prefix-arg' when exiting minibuffer.") +(defvar helm-current-error nil + "Same as `compilation-current-error' but for helm-occur and helm-grep.") + +;;; Compatibility +;; +(defun helm-add-face-text-properties (beg end face &optional append object) + "Add the face property to the text from START to END. +It is a compatibility function which behaves exactly like +`add-face-text-property' if available, otherwise like +`add-text-properties'. When only `add-text-properties' is +available APPEND is ignored." + (if (fboundp 'add-face-text-property) + (add-face-text-property beg end face append object) + (add-text-properties beg end `(face ,face) object))) + +;; Override `wdired-finish-edit'. +;; Fix emacs bug in `wdired-finish-edit' where +;; Wdired is not handling the case where `dired-directory' is a cons +;; cell instead of a string. +(defun helm--advice-wdired-finish-edit () + (interactive) + (wdired-change-to-dired-mode) + ;; `wdired-old-marks' has been removed in emacs-28+. + (unless (boundp 'wdired-old-marks) + (setq-local wdired-old-marks nil)) + (let ((changes nil) + (errors 0) + files-deleted + files-renamed + some-file-names-unchanged + file-old file-new tmp-value) + (save-excursion + (when (and wdired-allow-to-redirect-links + (fboundp 'make-symbolic-link)) + (setq tmp-value (wdired-do-symlink-changes)) + (setq errors (cdr tmp-value)) + (setq changes (car tmp-value))) + (when (and wdired-allow-to-change-permissions + (boundp 'wdired-col-perm)) ; could have been changed + (setq tmp-value (wdired-do-perm-changes)) + (setq errors (+ errors (cdr tmp-value))) + (setq changes (or changes (car tmp-value)))) + (goto-char (point-max)) + (while (not (bobp)) + (setq file-old (wdired-get-filename nil t)) + (when file-old + (setq file-new (wdired-get-filename)) + (if (equal file-new file-old) + (setq some-file-names-unchanged t) + (setq changes t) + (if (not file-new) ;empty filename! + (push file-old files-deleted) + (when wdired-keep-marker-rename + (let ((mark (cond ((integerp wdired-keep-marker-rename) + wdired-keep-marker-rename) + (wdired-keep-marker-rename + (cdr (assoc file-old wdired-old-marks))) + (t nil)))) + (when mark + (push (cons (substitute-in-file-name file-new) mark) + wdired-old-marks)))) + (push (cons file-old (substitute-in-file-name file-new)) + files-renamed)))) + (forward-line -1))) + (when files-renamed + (setq errors (+ errors (wdired-do-renames files-renamed)))) + (if changes + (progn + ;; If we are displaying a single file (rather than the + ;; contents of a directory), change dired-directory if that + ;; file was renamed. (This ought to be generalized to + ;; handle the multiple files case, but that's less trivial) + ;; fixit [1]. + (cond ((and (stringp dired-directory) + (not (file-directory-p dired-directory)) + (null some-file-names-unchanged) + (= (length files-renamed) 1)) + (setq dired-directory (cdr (car files-renamed)))) + ;; Fix [1] i.e dired buffers created with + ;; (dired '(foo f1 f2 f3)). + ((and (consp dired-directory) + (cdr dired-directory) + files-renamed) + (setq dired-directory + ;; Replace in `dired-directory' files that have + ;; been modified with their new name keeping + ;; the ones that are unmodified at the same place. + (cons (car dired-directory) + (cl-loop for f in (cdr dired-directory) + collect (or (assoc-default f files-renamed) + f)))))) + ;; Re-sort the buffer if all went well. + (unless (> errors 0) (revert-buffer)) + (let ((inhibit-read-only t)) + (dired-mark-remembered wdired-old-marks))) + (let ((inhibit-read-only t)) + (remove-text-properties (point-min) (point-max) + '(old-name nil end-name nil old-link nil + end-link nil end-perm nil + old-perm nil perm-changed nil)) + (message "(No changes to be performed)"))) + (when files-deleted + (wdired-flag-for-deletion files-deleted)) + (when (> errors 0) + (dired-log-summary (format "%d rename actions failed" errors) nil))) + (set-buffer-modified-p nil) + (setq buffer-undo-list nil)) + +;; Override `wdired-get-filename'. +;; Fix emacs bug in `wdired-get-filename' which returns the current +;; directory concatened with the filename i.e +;; "/home/you//home/you/foo" when filename is absolute in dired +;; buffer. +;; In consequence Wdired try to rename files even when buffer have +;; been modified and corrected, e.g delete one char and replace it so +;; that no change to file is done. +;; This also lead to ask confirmation for every files even when not +;; modified and when `wdired-use-interactive-rename' is nil. +(defun helm--advice-wdired-get-filename (&optional no-dir old) + ;; FIXME: Use dired-get-filename's new properties. + (let (beg end file) + (save-excursion + (setq end (line-end-position)) + (beginning-of-line) + (setq beg (next-single-property-change (point) 'old-name nil end)) + (unless (eq beg end) + (if old + (setq file (get-text-property beg 'old-name)) + ;; In the following form changed `(1+ beg)' to `beg' so that + ;; the filename end is found even when the filename is empty. + ;; Fixes error and spurious newlines when marking files for + ;; deletion. + (setq end (next-single-property-change beg 'end-name)) + (setq file (buffer-substring-no-properties (1+ beg) end))) + ;; Don't unquote the old name, it wasn't quoted in the first place + (and file (setq file (condition-case _err + ;; emacs-25+ + (apply #'wdired-normalize-filename + (list file (not old))) + (wrong-number-of-arguments + ;; emacs-24 + (wdired-normalize-filename file)))))) + (if (or no-dir old (and file (file-name-absolute-p file))) + file + (and file (> (length file) 0) + (expand-file-name file (dired-current-directory))))))) + +;;; Override `push-mark' +;; +;; Fix duplicates in `mark-ring' and `global-mark-ring' and update +;; buffers in `global-mark-ring' to recentest mark. +(defun helm--advice-push-mark (&optional location nomsg activate) + (unless (null (mark t)) + (let ((marker (copy-marker (mark-marker)))) + (setq mark-ring (cons marker (delete marker mark-ring)))) + (when (> (length mark-ring) mark-ring-max) + ;; Move marker to nowhere. + (set-marker (car (nthcdr mark-ring-max mark-ring)) nil) + (setcdr (nthcdr (1- mark-ring-max) mark-ring) nil))) + (set-marker (mark-marker) (or location (point)) (current-buffer)) + ;; Now push the mark on the global mark ring. + (setq global-mark-ring (cons (copy-marker (mark-marker)) + ;; Avoid having multiple entries + ;; for same buffer in `global-mark-ring'. + (cl-loop with mb = (current-buffer) + for m in global-mark-ring + for nmb = (marker-buffer m) + unless (eq mb nmb) + collect m))) + (when (> (length global-mark-ring) global-mark-ring-max) + (set-marker (car (nthcdr global-mark-ring-max global-mark-ring)) nil) + (setcdr (nthcdr (1- global-mark-ring-max) global-mark-ring) nil)) + (or nomsg executing-kbd-macro (> (minibuffer-depth) 0) + (message "Mark set")) + (when (or activate (not transient-mark-mode)) + (set-mark (mark t))) + nil) + +(defcustom helm-advice-push-mark t + "Override `push-mark' with a version avoiding duplicates when non-nil." + :group 'helm + :type 'boolean + :set (lambda (var val) + (set var val) + (if val + (advice-add 'push-mark :override #'helm--advice-push-mark '((depth . 100))) + (advice-remove 'push-mark #'helm--advice-push-mark)))) + +;; This the version of Emacs-27 written by Stefan +(defun helm-advice--ffap-read-file-or-url (prompt guess) + (or guess (setq guess default-directory)) + (if (ffap-url-p guess) + (read-string prompt guess nil nil t) + (unless (ffap-file-remote-p guess) + (setq guess (abbreviate-file-name (expand-file-name guess)))) + (read-file-name prompt (file-name-directory guess) nil nil + (file-name-nondirectory guess)))) + +;; The native-comp branch of emacs "is a modified Emacs capable of compiling +;; and running Emacs Lisp as native code in form of re-loadable elf files." +;; (https://akrl.sdf.org/gccemacs.html). The function subr-native-elisp-p is a +;; native function available only in this branch and evaluates to true if the +;; argument supplied is a natively compiled lisp function. Use this function +;; if it's available, otherwise return nil. Helm needs to distinguish compiled +;; functions from other symbols in a various places. +(defun helm-subr-native-elisp-p (object) + (when (fboundp 'subr-native-elisp-p) + (subr-native-elisp-p object))) + +;;; Macros helper. +;; +(defmacro helm-with-gensyms (symbols &rest body) + "Bind the SYMBOLS to fresh uninterned symbols and eval BODY." + (declare (indent 1)) + `(let ,(mapcar (lambda (s) + ;; Use cl-gensym here instead of make-symbol + ;; to ensure a symbol that have a live that go + ;; beyond the live of its macro have different name. + ;; i.e symbols created with `with-helm-temp-hook' + ;; should have random names. + `(,s (cl-gensym (symbol-name ',s)))) + symbols) + ,@body)) + +;;; Command loop helper +;; +(defconst helm-this-command-black-list + '(helm-maybe-exit-minibuffer + helm-confirm-and-exit-minibuffer + helm-exit-minibuffer + exit-minibuffer + helm-M-x)) + +(defun helm-this-command () + "Return the actual command in action. +Like `this-command' but return the real command, and not +`exit-minibuffer' or other unwanted functions." + (cl-loop for count from 1 to 50 + for btf = (backtrace-frame count) + for fn = (cl-second btf) + if (and + ;; In some case we may have in the way an + ;; advice compiled resulting in byte-code, + ;; ignore it (Bug#691). + (symbolp fn) + (commandp fn) + (not (memq fn helm-this-command-black-list))) + return fn + else + if (and (eq fn 'call-interactively) + (> (length btf) 2)) + return (cadr (cdr btf)))) + + +;;; Iterators +;; +(cl-defmacro helm-position (item seq &key test all) + "A simple and faster replacement of CL `position'. + +Returns ITEM first occurence position found in SEQ. +When SEQ is a string, ITEM have to be specified as a char. +Argument TEST when unspecified default to `eq'. +When argument ALL is non-nil return a list of all ITEM positions +found in SEQ." + (let ((key (if (stringp seq) 'across 'in))) + `(cl-loop with deftest = 'eq + for c ,key ,seq + for index from 0 + when (funcall (or ,test deftest) c ,item) + if ,all collect index into ls + else return index + finally return ls))) + +(defun helm-iter-list (seq &optional cycle) + "Return an iterator object from SEQ. +The iterator die and return nil when it reach end of SEQ. +When CYCLE is specified the iterator never ends." + (let ((lis seq)) + (lambda () + (let ((elm (car lis))) + (setq lis (if cycle + (or (cdr lis) seq) + (cdr lis))) + elm)))) + +(defun helm-iter-circular (seq) + "Infinite iteration on SEQ." + (helm-iter-list seq 'cycle)) + +(cl-defun helm-iter-sub-next-circular (seq elm &key (test 'eq)) + "Infinite iteration of SEQ starting at ELM." + (let* ((pos (1+ (helm-position elm seq :test test))) + (sub (append (nthcdr pos seq) (cl-subseq seq 0 pos))) + (iterator (helm-iter-circular sub))) + (lambda () + (helm-iter-next iterator)))) + +(defun helm-iter-next (iterator) + "Return next elm of ITERATOR." + (and iterator (funcall iterator))) + + +;;; Anaphoric macros. +;; +(defmacro helm-aif (test-form then-form &rest else-forms) + "Anaphoric version of `if'. +Like `if' but set the result of TEST-FORM in a temporary variable +called `it'. THEN-FORM and ELSE-FORMS are then executed just like +in `if'." + (declare (indent 2) (debug t)) + `(let ((it ,test-form)) + (if it ,then-form ,@else-forms))) + +(defmacro helm-awhile (sexp &rest body) + "Anaphoric version of `while'. +Same usage as `while' except that SEXP is bound to a temporary +variable called `it' at each turn. +An implicit nil block is bound to the loop so usage of +`cl-return' is possible to exit the loop." + (declare (indent 1) (debug t)) + (helm-with-gensyms (flag) + `(let ((,flag t)) + (cl-block nil + (while ,flag + (helm-aif ,sexp + (progn ,@body) + (setq ,flag nil))))))) + +(defmacro helm-acond (&rest clauses) + "Anaphoric version of `cond'. +In each clause of CLAUSES, the result of the car of clause is +stored in a temporary variable called `it' and usable in the cdr +of this same clause. Each `it' variable is independent of its +clause. The usage is the same as `cond'." + (declare (debug cond)) + (unless (null clauses) + (helm-with-gensyms (sym) + (let ((clause1 (car clauses))) + `(let ((,sym ,(car clause1))) + (helm-aif ,sym + (if (cdr ',clause1) + (progn ,@(cdr clause1)) + it) + (helm-acond ,@(cdr clauses)))))))) + +(defmacro helm-aand (&rest conditions) + "Anaphoric version of `and'. +Each condition is bound to a temporary variable called `it' which +is usable in next condition." + (declare (debug (&rest form))) + (cond ((null conditions) t) + ((null (cdr conditions)) (car conditions)) + (t `(helm-aif ,(car conditions) + (helm-aand ,@(cdr conditions)))))) + +(defmacro helm-acase (expr &rest clauses) + "A simple anaphoric `cl-case' implementation handling strings. +EXPR is bound to a temporary variable called `it' which is usable +in CLAUSES to refer to EXPR. +NOTE: Duplicate keys in CLAUSES are deliberately not handled. + +\(fn EXPR (KEYLIST BODY...)...)" + (declare (indent 1) (debug (form &rest (sexp body)))) + (unless (null clauses) + (let ((clause1 (car clauses))) + `(let ((key ',(car clause1)) + (it ,expr)) + (if (or (equal it key) + (and (listp key) (member it key)) + (eq key t)) + (progn ,@(cdr clause1)) + (helm-acase it ,@(cdr clauses))))))) + +;;; Fuzzy matching routines +;; +(defsubst helm--mapconcat-pattern (pattern) + "Transform string PATTERN in regexp for further fuzzy matching. +E.g.: helm.el$ + => \"[^h]*?h[^e]*?e[^l]*?l[^m]*?m[^.]*?[.][^e]*?e[^l]*?l$\" + ^helm.el$ + => \"helm[.]el$\"." + (let ((ls (split-string-and-unquote pattern ""))) + (if (string= "^" (car ls)) + ;; Exact match. + (mapconcat (lambda (c) + (if (and (string= c "$") + (string-match "$\\'" pattern)) + c (regexp-quote c))) + (cdr ls) "") + ;; Fuzzy match. + (mapconcat (lambda (c) + (if (and (string= c "$") + (string-match "$\\'" pattern)) + c (format "[^%s]*?%s" c (regexp-quote c)))) + ls "")))) + +(defsubst helm--collect-pairs-in-string (string) + (cl-loop for str on (split-string string "" t) by 'cdr + when (cdr str) + collect (list (car str) (cadr str)))) + +;;; Help routines. +;; +(defvar helm-help--iter-org-state nil) + +(defvar helm-help-mode-before-hook nil + "A hook that runs before helm-help starts.") + +(defvar helm-help-mode-after-hook nil + "A hook that runs when helm-help exits.") + +(defcustom helm-help-default-prompt + "[SPC,C-v,next:ScrollUp b,M-v,prior:ScrollDown TAB:Cycle M-TAB:All C-s/r:Isearch q:Quit]" + "The prompt used in `helm-help'." + :type 'string + :group 'helm) + +(defcustom helm-help-hkmap + '(("C-v" . helm-help-scroll-up) + ("SPC" . helm-help-scroll-up) + ("" . helm-help-scroll-up) + ("M-v" . helm-help-scroll-down) + ("b" . helm-help-scroll-down) + ("" . helm-help-scroll-down) + ("C-s" . isearch-forward) + ("C-r" . isearch-backward) + ("C-a" . move-beginning-of-line) + ("C-e" . move-end-of-line) + ("C-f" . forward-char) + ("" . forward-char) + ("C-b" . backward-char) + ("" . backward-char) + ("C-n" . helm-help-next-line) + ("C-p" . helm-help-previous-line) + ("" . helm-help-next-line) + ("" . helm-help-previous-line) + ("M-a" . backward-sentence) + ("M-e" . forward-sentence) + ("M-f" . forward-word) + ("M-b" . backward-word) + ("M->" . end-of-buffer) + ("M-<" . beginning-of-buffer) + ("C-SPC" . helm-help-toggle-mark) + ("C-M-SPC" . mark-sexp) + ("TAB" . org-cycle) + ("C-m" . helm-help-org-open-at-point) + ("C-&" . helm-help-org-mark-ring-goto) + ("C-%" . org-mark-ring-push) + ("M-TAB" . helm-help-org-cycle) + ("M-w" . helm-help-copy-region-as-kill) + ("q" . helm-help-quit)) + "Alist of (KEY . FUNCTION) for `helm-help'. + +This is not a standard keymap, just an alist where it is possible to +define a simple KEY (a string with no spaces) associated with a +FUNCTION. More complex key like \"C-x C-x\" are not supported. +Interactive functions will be called interactively whereas other +functions will be called with funcall except commands that are in +`helm-help-not-interactive-command'. +For convenience you can add bindings here with `helm-help-define-key'." + :type '(alist :key-type string :key-value symbol) + :group 'helm) + +(defvar helm-help-not-interactive-command '(isearch-forward isearch-backward) + "Commands that we don't want to call interactively in `helm-help'.") + +(defun helm-help-internal (bufname insert-content-fn) + "Show long message during Helm session in BUFNAME. +INSERT-CONTENT-FN is the function that inserts text to be +displayed in BUFNAME." + (let ((winconf (current-frame-configuration)) + (hframe (selected-frame))) + (helm-log-run-hook 'helm-help-mode-before-hook) + (with-selected-frame helm-initial-frame + (select-frame-set-input-focus helm-initial-frame) + (unwind-protect + (progn + (setq helm-suspend-update-flag t) + (set-buffer (get-buffer-create bufname)) + (switch-to-buffer bufname) + (when helm-help-full-frame (delete-other-windows)) + (delete-region (point-min) (point-max)) + (org-mode) + (save-excursion + (funcall insert-content-fn) + (goto-char (point-min)) + (while (re-search-forward "^[|]" nil t) + (when (org-table-p t) + (org-table-align) + (goto-char (org-table-end))))) + (org-mark-ring-push) ; Put mark at bob + (buffer-disable-undo) + (helm-help-event-loop)) + (raise-frame hframe) + (helm-log-run-hook 'helm-help-mode-after-hook) + (setq helm-suspend-update-flag nil) + (set-frame-configuration winconf))))) + +(cl-defun helm-help-scroll-up (&optional (amount helm-scroll-amount)) + "Scroll up in `helm-help'." + (condition-case _err + (scroll-up-command amount) + (beginning-of-buffer nil) + (end-of-buffer nil))) + +(cl-defun helm-help-scroll-down (&optional (amount helm-scroll-amount)) + "Scroll down in `helm-help'." + (condition-case _err + (scroll-down-command amount) + (beginning-of-buffer nil) + (end-of-buffer nil))) + +(defun helm-help-next-line () + "Next line function for `helm-help'." + (condition-case _err + (call-interactively #'next-line) + (beginning-of-buffer nil) + (end-of-buffer nil))) + +(defun helm-help-previous-line () + "Previous line function for `helm-help'." + (condition-case _err + (call-interactively #'previous-line) + (beginning-of-buffer nil) + (end-of-buffer nil))) + +(defun helm-help-toggle-mark () + "Toggle mark in `helm-help'." + (if (region-active-p) + (deactivate-mark) + (push-mark nil nil t))) + +(defun helm-help-org-cycle () + "Runs `org-cycle' in `helm-help'." + (pcase (helm-iter-next helm-help--iter-org-state) + ((pred numberp) (org-content)) + ((and state) (org-cycle state)))) + +(defun helm-help-copy-region-as-kill () + "Copy region function for `helm-help'" + (copy-region-as-kill + (region-beginning) (region-end)) + (deactivate-mark)) + +(defun helm-help-quit () + "Quit `helm-help'." + (throw 'helm-help-quit nil)) + +(defun helm-help-org-open-at-point () + "Calls `org-open-at-point' ignoring errors." + (ignore-errors + (org-open-at-point))) + +(defun helm-help-org-mark-ring-goto () + "Calls `org-mark-ring-goto' ignoring errors." + (ignore-errors + (org-mark-ring-goto))) + +(defun helm-help-event-loop () + "The loop in charge of scanning keybindings in `helm-help'." + (let ((prompt (propertize + helm-help-default-prompt + 'face 'helm-helper)) + scroll-error-top-bottom + (helm-help--iter-org-state (helm-iter-circular '(1 (16) (64))))) + (catch 'helm-help-quit + (helm-awhile (read-key prompt) + (let ((fun (cl-loop for (k . v) in helm-help-hkmap + when (eql (aref (kbd k) 0) it) + return v))) + (when fun + (if (and (commandp fun) + (not (memq fun helm-help-not-interactive-command))) + ;; For movement of cursor in help buffer we need to + ;; call interactively commands for impaired people + ;; using a synthetizer (Bug#1347). + (call-interactively fun) + (funcall fun)))))))) + +(defun helm-help-define-key (key function &optional override) + "Add KEY bound to fUNCTION in `helm-help-hkmap'. + +If OVERRIDE is non nil, all bindings associated with FUNCTION are +removed and only (KEY . FUNCTION) is kept. +If FUNCTION is nil (KEY . FUNCTION) is not added and removed from +alist if already present. +See `helm-help-hkmap' for supported keys and functions." + (cl-assert (not (cdr (split-string key))) nil + (format "Error: Unsuported key `%s'" key)) + (when override + (helm-awhile (rassoc function helm-help-hkmap) + (setq helm-help-hkmap (delete it helm-help-hkmap)))) + (helm-aif (and (null function) (assoc key helm-help-hkmap)) + (setq helm-help-hkmap (delete it helm-help-hkmap)) + (and function (add-to-list 'helm-help-hkmap `(,key . ,function))))) + +;;; Multiline transformer +;; +(defun helm-multiline-transformer (candidates _source) + (cl-loop with offset = (helm-interpret-value + (assoc-default 'multiline (helm-get-current-source))) + for cand in candidates + for disp = (or (car-safe cand) cand) + for real = (or (cdr-safe cand) cand) + if (numberp offset) + collect (cons (helm--multiline-get-truncated-candidate disp offset) + real) + else collect (cons disp real))) + +(defun helm--multiline-get-truncated-candidate (candidate offset) + "Truncate CANDIDATE when its length is > than OFFSET." + (with-temp-buffer + (insert candidate) + (goto-char (point-min)) + (if (and offset + (> (buffer-size) offset)) + (let ((end-str "[...]")) + (concat + (buffer-substring + (point) + (save-excursion + (forward-char offset) + (setq end-str (if (looking-at "\n") + end-str (concat "\n" end-str))) + (point))) + end-str)) + (buffer-string)))) + +;;; List processing +;; +(defun helm-flatten-list (seq) + "Return a list of all single elements of sublists in SEQ. + + Example: + (helm-flatten-list \\='(1 (2 . 3) nil (4 5 (6) 7) 8 (9 . 10))) + => (1 2 3 4 5 6 7 8 9 10)" + (let (result) + (cl-labels ((flatten + (seq) + (cl-loop for elm in seq + if (consp elm) + do (flatten + (if (atom (cdr elm)) + (list (car elm) (cdr elm)) + elm)) + else do (and elm (push elm result))))) + (flatten seq)) + (nreverse result))) + +(defun helm-mklist (obj) + "If OBJ is a list (but not lambda), return itself. +Otherwise make a list with one element." + (if (and (listp obj) (not (functionp obj))) + obj + (list obj))) + +(cl-defun helm-fast-remove-dups (seq &key (test 'eq)) + "Remove duplicates elements in list SEQ. + +This is same as `remove-duplicates' but with memoisation. +It is much faster, especially in large lists. +A test function can be provided with TEST argument key. +Default is `eq'. +NOTE: Comparison of special Elisp objects (e.g., markers etc.) +fails because their printed representations which are stored in +hash-table can't be compared with with the real object in SEQ. +This is a bug in `puthash' which store the printable +representation of object instead of storing the object itself, +this to provide at the end a printable representation of +hashtable itself." + (cl-loop with cont = (make-hash-table :test test) + for elm in seq + unless (gethash elm cont) + collect (puthash elm elm cont))) + +(defsubst helm--string-join (strings &optional separator) + "Join all STRINGS using SEPARATOR." + (mapconcat 'identity strings separator)) + +(defun helm--concat-regexps (regexp-list) + "Return a regexp which matches any of the regexps in REGEXP-LIST." + (if regexp-list + (concat "\\(?:" (helm--string-join regexp-list "\\)\\|\\(?:") "\\)") + "\\`\\'")) ; Match nothing + +(defun helm-skip-entries (seq black-regexp-list &optional white-regexp-list) + "Remove entries which match one of REGEXP-LIST from SEQ." + (let ((black-regexp (helm--concat-regexps black-regexp-list)) + (white-regexp (helm--concat-regexps white-regexp-list))) + (cl-loop for i in seq + unless (and (stringp i) + (string-match-p black-regexp i) + (null + (string-match-p white-regexp i))) + collect i))) + +(defun helm-boring-directory-p (directory black-list) + "Check if one regexp in BLACK-LIST matches DIRECTORY." + (helm-awhile (helm-basedir (directory-file-name + (expand-file-name directory))) + ;; Break at root to avoid infloop, root is / or on Windows + ;; C:/ i.e. :/ (Bug#2308). + (when (string-match-p "\\`[A-Za-z]?:?/\\'" it) + (cl-return nil)) + (when (cl-loop for r in black-list + thereis (string-match-p + r (directory-file-name directory))) + (cl-return t)) + (setq directory it))) + +(defun helm-shadow-entries (seq regexp-list) + "Put shadow property on entries in SEQ matching a regexp in REGEXP-LIST." + (let ((face 'italic)) + (cl-loop for i in seq + if (cl-loop for regexp in regexp-list + thereis (and (stringp i) + (string-match regexp i))) + collect (propertize i 'face face) + else collect i))) + +(defun helm-remove-if-not-match (regexp seq) + "Remove all elements of SEQ that don't match REGEXP." + (cl-loop for s in seq + for str = (cond ((symbolp s) + (symbol-name s)) + ((consp s) + (car s)) + (t s)) + when (string-match-p regexp str) + collect s)) + +(defun helm-remove-if-match (regexp seq) + "Remove all elements of SEQ that match REGEXP." + (cl-loop for s in seq + for str = (cond ((symbolp s) + (symbol-name s)) + ((consp s) + (car s)) + (t s)) + unless (string-match-p regexp str) + collect s)) + +(defun helm-transform-mapcar (function args) + "`mapcar' for candidate-transformer. + +ARGS is (cand1 cand2 ...) or ((disp1 . real1) (disp2 . real2) ...) + +\(helm-transform-mapcar \\='upcase \\='(\"foo\" \"bar\")) +=> (\"FOO\" \"BAR\") +\(helm-transform-mapcar \\='upcase \\='((\"1st\" . \"foo\") (\"2nd\" . \"bar\"))) +=> ((\"1st\" . \"FOO\") (\"2nd\" . \"BAR\")) +" + (cl-loop for arg in args + if (consp arg) + collect (cons (car arg) (funcall function (cdr arg))) + else + collect (funcall function arg))) + +(defsubst helm-append-1 (elm seq) + "Append ELM to SEQ. +If ELM is not a list transform it in list." + (append (helm-mklist elm) seq)) + +(defun helm-append-at-nth (seq elm index) + "Append ELM at INDEX in SEQ." + (let ((len (length seq))) + (setq index (min (max index 0) len)) + (if (zerop index) + (helm-append-1 elm seq) + (cl-loop for i in seq + for count from 1 collect i + when (= count index) + if (and (listp elm) (not (functionp elm))) + append elm + else collect elm)))) + +(defun helm-take-first-elements (seq n) + "Return the first N elements of SEQ if SEQ is longer than N. +It is used for narrowing list of candidates to the +`helm-candidate-number-limit'." + (if (> (length seq) n) (cl-subseq seq 0 n) seq)) + +(defun helm-source-by-name (name &optional sources) + "Get a Helm source in SOURCES by NAME. + +Optional argument SOURCES is a list of Helm sources which default +to `helm-sources'." + (cl-loop with src-list = (if sources + (cl-loop for src in sources + collect (if (listp src) + src + (symbol-value src))) + helm-sources) + for source in src-list + thereis (and (string= name (assoc-default 'name source)) source))) + +(defun helm-make-actions (&rest args) + "Build an alist with (NAME . ACTION) elements with each pairs in ARGS. +Where NAME is a string or a function returning a string or nil +and ACTION a function. +If NAME returns nil the pair is skipped. + +\(fn NAME ACTION ...)" + (cl-loop for (name fn) on args by #'cddr + when (functionp name) + do (setq name (funcall name)) + when name + collect (cons name fn))) + +(defun helm-closest-number-in-list (num list) + "Return closest number to NUM found in LIST. +LIST is a list of numbers and NUM a number." + (cl-loop for i in list + for diff = (if (> num i) (- num i) (- i num)) + collect (cons diff i) into res + minimize diff into min + finally return (cdr (assq min res)))) + +(defun helm-group-candidates-by (candidates function &optional selection separate) + "Group similar items in CANDIDATES according to FUNCTION. +Items not matching FUNCTION are grouped as well in a separate group. + +Example: + + (setq B \\='(1 2 3 4 5 6 7 8 9)) + + (helm-group-candidates-by B #'cl-oddp 2 \\='separate) + => ((2 4 6 8) (1 3 5 7 9)) + +SELECTION specify where to start in CANDIDATES. +Similar candidates to SELECTION will be listed on top. + +If SEPARATE is non-nil returns a list of groups i.e. a list of lists, +otherwise a plain list is returned." + (cl-loop with sel = (or selection (helm-get-selection) "") + with lst = (copy-sequence candidates) + while lst + for group = (cl-loop for c in lst + when (equal (funcall function c) + (funcall function sel)) + collect c into grp + and do (setq lst (delete c lst)) + finally return (prog1 grp + (setq sel (car lst)))) + if separate collect group + else append group)) + +(defun helm-reorganize-sequence-from-elm (sequence elm &optional reverse) + "Reorganize SEQUENCE from ELM. + +Examples: + + (helm-reorganize-sequence-from-elm \\='(a b c d e f g h i j k l) \\='e) + => (f g h i j k l a b c d e) + (helm-reorganize-sequence-from-elm \\='(a b c d e f g h i j k l) \\='e t) + => (d c b a l k j i h g f e) +" + (let* ((new-seq (if reverse + (reverse sequence) + sequence)) + (pos (1+ (cl-position elm new-seq :test 'equal)))) + (append (nthcdr pos new-seq) (cl-subseq new-seq 0 pos)))) + +;;; Strings processing. +;; +(defun helm-stringify (elm) + "Return the representation of ELM as a string. +ELM can be a string, a number or a symbol." + (pcase elm + ((pred stringp) elm) + ((pred numberp) (number-to-string elm)) + ((pred symbolp) (symbol-name elm)))) + +(defun helm-substring (str width) + "Return the substring of string STR from 0 to WIDTH. +Handle multibyte characters by moving by columns." + (with-temp-buffer + (save-excursion + (insert str)) + (move-to-column width) + (buffer-substring (point-at-bol) (point)))) + +(defun helm-substring-by-width (str width &optional endstr) + "Truncate string STR to end at column WIDTH. +Similar to `truncate-string-to-width'. +Add ENDSTR at end of truncated STR. +Add spaces at end if needed to reach WIDTH when STR is shorter +than WIDTH." + (cl-loop for ini-str = str + then (substring ini-str 0 (1- (length ini-str))) + for sw = (string-width ini-str) + when (<= sw width) return + (concat ini-str endstr (make-string (- width sw) ? )))) + +(defun helm-string-multibyte-p (str) + "Check if string STR contains multibyte characters." + (cl-loop for c across str + thereis (> (char-width c) 1))) + +(defun helm-get-pid-from-process-name (process-name) + "Get pid from running process PROCESS-NAME." + ;; Protect system processes calls (Issue #2497) + ;; Ensure `list-system-processes' and `process-attributes' don't run + ;; on remote (only Emacs-28/29+). + (cl-loop with default-directory = temporary-file-directory + with process-list = (list-system-processes) + for pid in process-list + for process = (assoc-default 'comm (process-attributes pid)) + when (and process (string-match process-name process)) + return pid)) + +(defun helm-ff-find-printers () + "Return a list of available printers on Unix systems." + (when (executable-find "lpstat") + (let ((printer-list (with-temp-buffer + (call-process "lpstat" nil t nil "-a") + (split-string (buffer-string) "\n")))) + (cl-loop for p in printer-list + for printer = (car (split-string p)) + when printer + collect printer)))) + +(defun helm-region-active-p () + (and transient-mark-mode mark-active (/= (mark) (point)))) + +(defun helm-quote-whitespace (candidate) + "Quote whitespace, if some, in string CANDIDATE." + (replace-regexp-in-string " " "\\\\ " candidate)) + +(defun helm-current-line-contents () + "Current line string without properties." + (buffer-substring-no-properties (point-at-bol) (point-at-eol))) + +(defun helm--replace-regexp-in-buffer-string (regexp rep str &optional fixedcase literal subexp start) + "Replace REGEXP by REP in string STR. + +Same as `replace-regexp-in-string' but handle properly REP as +function with SUBEXP specified. + +E.g.: + + (helm--replace-regexp-in-buffer-string + \"e\\\\(m\\\\)acs\" \\='upcase \"emacs\" t nil 1) + => \"eMacs\" + + (replace-regexp-in-string + \"e\\\\(m\\\\)acs\" \\='upcase \"emacs\" t nil 1) + => \"eEMACSacs\" + +Also START argument behaves as expected unlike +`replace-regexp-in-string'. + +E.g.: + + (helm--replace-regexp-in-buffer-string \"f\" \"r\" \"foofoo\" t nil nil 3) + => \"fooroo\" + + (replace-regexp-in-string \"f\" \"r\" \"foofoo\" t nil nil 3) + => \"roo\" + +Unlike `replace-regexp-in-string' this function is buffer-based +implemented i.e. replacement is computed inside a temp buffer, so +REGEXP should be used differently than with +`replace-regexp-in-string'. + +NOTE: This function is used internally for +`helm-ff-query-replace-on-filenames' and builded for this. +You should use `replace-regexp-in-string' instead unless the +behaviour of this function is really needed." + (with-temp-buffer + (insert str) + (goto-char (or start (point-min))) + (while (re-search-forward regexp nil t) + (replace-match (cond ((and (functionp rep) subexp) + (funcall rep (match-string subexp))) + ((functionp rep) + (funcall rep str)) + (t rep)) + fixedcase literal nil subexp)) + (buffer-string))) + +(defun helm-url-unhex-string (str) + "Same as `url-unhex-string' but ensure STR is completely decoded." + (setq str (or str "")) + (with-temp-buffer + (save-excursion (insert str)) + (while (re-search-forward "%[A-Za-z0-9]\\{2\\}" nil t) + (replace-match (byte-to-string (string-to-number + (substring (match-string 0) 1) + 16)) + t t) + ;; Restart from beginning until string is completely decoded. + (goto-char (point-min))) + (decode-coding-string (buffer-string) 'utf-8))) + +(defun helm-read-answer (prompt answer-list) + "Prompt user for an answer. +Arg PROMPT is the prompt to present user the different possible +answers, ANSWER-LIST is a list of strings. +If user enters an answer which is one of ANSWER-LIST return this +answer, otherwise keep prompting for a valid answer. +Note that answer should be a single char, only short answer are +accepted. + +Example: + + (pcase (helm-read-answer + \"answer [y,n,!,q]: \" + \\='(\"y\" \"n\" \"!\" \"q\")) + (\"y\" \"yes\") + (\"n\" \"no\") + (\"!\" \"all\") + (\"q\" \"quit\")) + +" + (helm-awhile (read-key (propertize prompt 'face 'minibuffer-prompt)) + (let ((str (and (characterp it) (string it)))) + (if (and str (member str answer-list)) + (cl-return str) + (message "Please answer by %s" (mapconcat 'identity answer-list ", ")) + (sit-for 1))))) + +(defun helm-read-answer-dolist-with-action (prompt list action) + "Read answer with PROMPT and execute ACTION on each element of LIST. + +Argument PROMPT is a format spec string e.g. \"Do this on %s?\" +which take each elements of LIST as argument, no need to provide +the help part i.e. [y,n,!,q] it will be already added. + +While looping through LIST, ACTION is executed on each elements +differently depending of answer: + +- y Execute ACTION on element. +- n Skip element. +- ! Don't ask anymore and execute ACTION on remaining elements. +- q Skip all remaining elements." + (let (dont-ask) + (catch 'break + (dolist (elm list) + (if dont-ask + (funcall action elm) + (pcase (helm-read-answer + (format (concat prompt "[y,n,!,q]") elm) + '("y" "n" "!" "q")) + ("y" (funcall action elm)) + ("n" (ignore)) + ("!" (prog1 + (funcall action elm) + (setq dont-ask t))) + ("q" (throw 'break nil)))))))) + +;;; Symbols routines +;; +(defun helm-symbolify (str-or-sym) + "Get symbol of STR-OR-SYM." + (cond ((symbolp str-or-sym) + str-or-sym) + ((equal str-or-sym "") nil) + (t (intern str-or-sym)))) + +(defun helm-symbol-name (obj) + (if (or (and (consp obj) (functionp obj)) + (byte-code-function-p obj) + (helm-subr-native-elisp-p obj)) + "Anonymous" + (symbol-name obj))) + +(defun helm-describe-class (class) + "Display documentation of Eieio CLASS, a symbol or a string." + (advice-add 'cl--print-table :override #'helm-source--cl--print-table '((depth . 100))) + (unwind-protect + (let ((helm-describe-function-function 'describe-function)) + (helm-describe-function class)) + (advice-remove 'cl--print-table #'helm-source--cl--print-table))) + +(defun helm-describe-function (func) + "Display documentation of FUNC, a symbol or string." + (cl-letf (((symbol-function 'message) #'ignore)) + (funcall helm-describe-function-function (helm-symbolify func)))) + +(defun helm-describe-variable (var) + "Display documentation of VAR, a symbol or a string." + (cl-letf (((symbol-function 'message) #'ignore)) + (funcall helm-describe-variable-function (helm-symbolify var)))) + +(defun helm-describe-face (face) + "Display documentation of FACE, a symbol or a string." + (let ((faces (helm-marked-candidates))) + (cl-letf (((symbol-function 'message) #'ignore)) + (describe-face (if (cdr faces) + (mapcar 'helm-symbolify faces) + (helm-symbolify face)))))) + +(defun helm-elisp--persistent-help (candidate fun &optional name) + "Used to build persistent actions describing CANDIDATE with FUN. +Argument NAME is used internally to know which command to use +when symbol CANDIDATE refers at the same time to a variable and a +function. +See `helm-elisp-show-help'." + (let ((hbuf (get-buffer (help-buffer)))) + (cond ((helm-follow-mode-p) + (if name + (funcall fun candidate name) + (funcall fun candidate))) + ((or (and (helm-get-attr 'help-running-p) + (string= candidate (helm-get-attr 'help-current-symbol)))) + (progn + ;; When started from a help buffer, + ;; Don't kill this buffer as it is helm-current-buffer. + (unless (equal hbuf helm-current-buffer) + (kill-buffer hbuf) + (set-window-buffer (get-buffer-window hbuf) + ;; It is generally + ;; helm-current-buffer but it may + ;; be another buffer when helm have + ;; been started from a dedicated window. + (if helm--buffer-in-new-frame-p + helm-current-buffer + helm-persistent-action-window-buffer))) + (helm-set-attr 'help-running-p nil)) + ;; Force running update hook to may be delete + ;; helm-persistent-action-display-window, this is done in + ;; helm-persistent-action-display-window (the function). + (unless helm--buffer-in-new-frame-p + (helm-update (regexp-quote (helm-get-selection))))) + (t + (if name + (funcall fun candidate name) + (funcall fun candidate)) + (helm-set-attr 'help-running-p t))) + (helm-set-attr 'help-current-symbol candidate))) + +(defcustom helm-find-function-default-project nil + "Default directories to search symbols definitions from `helm-apropos'. +A list of directories or a single directory name. +Helm will allow you selecting one of those directories with `M-n' when +using a prefix arg with the `find-function' action in `helm-apropos'. +This is a good idea to add the directory names of the projects you are +working on to quickly jump to the definitions in the project source +files instead of jumping to the loaded files located in `load-path'." + :type '(choice (repeat string) + string) + :group 'helm-elisp) + +(defun helm-find-function-noselect (func &optional root-dir type) + "Find FUNC definition without selecting buffer. +FUNC can be a symbol or a string. +Instead of looking in LOAD-PATH to find library, this function +search in all subdirs of ROOT-DIR, if ROOT-DIR is unspecified ask for +it with completion. +TYPE when nil specify function, for other values see +`find-function-regexp-alist'." + (require 'find-func) + (let* ((sym (helm-symbolify func)) + (dir (or root-dir (helm-read-file-name + "Project directory: " + :test 'file-directory-p + :default (helm-mklist helm-find-function-default-project) + :must-match t))) + (find-function-source-path + (cons dir (helm-walk-directory dir + :directories 'only + :path 'full))) + (symbol-lib (helm-acase type + ((defvar defface) + (or (symbol-file sym it) + (help-C-file-name sym 'var))) + (t (cdr (find-function-library sym))))) + (library (find-library-name + (helm-basename symbol-lib t)))) + (find-function-search-for-symbol sym type library))) + +(defun helm-find-function (func) + "Try to jump to FUNC definition. +With a prefix arg ask for the project directory to search in instead of +using LOAD-PATH." + (if (not helm-current-prefix-arg) + (find-function (helm-symbolify func)) + (let ((place (helm-find-function-noselect func))) + (when place + (switch-to-buffer (car place)) (goto-char (cdr place)))))) + +(defun helm-find-variable (var) + "Try to jump to VAR definition. +With a prefix arg ask for the project directory to search in instead of +using LOAD-PATH." + (if (not helm-current-prefix-arg) + (find-variable (helm-symbolify var)) + (let ((place (helm-find-function-noselect var nil 'defvar))) + (when place + (switch-to-buffer (car place)) (goto-char (cdr place)))))) + +(defun helm-find-face-definition (face) + "Try to jump to FACE definition. +With a prefix arg ask for the project directory to search in instead of +using LOAD-PATH." + (if (not helm-current-prefix-arg) + (find-face-definition (helm-symbolify face)) + (let ((place (helm-find-function-noselect face nil 'defface))) + (when place + (switch-to-buffer (car place)) (goto-char (cdr place)))))) + +(defun helm-kill-new (candidate &optional replace) + "CANDIDATE is symbol or string. +See `kill-new' for argument REPLACE." + (kill-new (helm-stringify candidate) replace)) + + +;;; Modes +;; +(defun helm-same-major-mode-p (start-buffer alist) + "Decide if current-buffer is related to START-BUFFER. +Argument ALIST is an alist of associated major modes." + ;; START-BUFFER is the current-buffer where we start searching. + ;; Determine the major-mode of START-BUFFER as `cur-maj-mode'. + ;; Each time the loop go in another buffer we try from this buffer + ;; to determine if its `major-mode' is: + ;; - same as the `cur-maj-mode' + ;; - derived from `cur-maj-mode' and from + ;; START-BUFFER if its mode is derived from the one in START-BUFFER. + ;; - have an assoc entry (major-mode . cur-maj-mode) + ;; - have an rassoc entry (cur-maj-mode . major-mode) + ;; - check if one of these entries inherit from another one in + ;; `alist'. + (let* ((cur-maj-mode (with-current-buffer start-buffer major-mode)) + (maj-mode major-mode) + (c-assoc-mode (assq cur-maj-mode alist)) + (c-rassoc-mode (rassq cur-maj-mode alist)) + (o-assoc-mode (assq major-mode alist)) + (o-rassoc-mode (rassq major-mode alist)) + (cdr-c-assoc-mode (cdr c-assoc-mode)) + (cdr-o-assoc-mode (cdr o-assoc-mode))) + (or (eq major-mode cur-maj-mode) + (derived-mode-p cur-maj-mode) + (with-current-buffer start-buffer + (derived-mode-p maj-mode)) + (or (eq cdr-c-assoc-mode major-mode) + (eq (car c-rassoc-mode) major-mode) + (eq (cdr (assq cdr-c-assoc-mode alist)) + major-mode) + (eq (car (rassq cdr-c-assoc-mode alist)) + major-mode)) + (or (eq cdr-o-assoc-mode cur-maj-mode) + (eq (car o-rassoc-mode) cur-maj-mode) + (eq (cdr (assq cdr-o-assoc-mode alist)) + cur-maj-mode) + (eq (car (rassq cdr-o-assoc-mode alist)) + cur-maj-mode))))) + +;;; Files routines +;; +(defun helm-file-name-sans-extension (filename) + "Same as `file-name-sans-extension' but remove all extensions." + (helm-aif (file-name-sans-extension filename) + ;; Start searching at index 1 for files beginning with a dot + ;; (bug#1335). + (if (string-match "\\." (helm-basename it) 1) + (helm-file-name-sans-extension it) + it))) + +(defsubst helm-file-name-extension (file) + "Returns FILE extension if it is not a number." + (helm-aif (file-name-extension file) + (and (not (string-match "\\`0+\\'" it)) + (zerop (string-to-number it)) + it))) + +(defun helm-basename (fname &optional ext) + "Print FNAME with any leading directory components removed. +If specified, also remove filename extension EXT. +Arg EXT can be specified as a string with or without dot, in this +case it should match `file-name-extension'. +It can also be non-nil (t) in this case no checking of +`file-name-extension' is done and the extension is removed +unconditionally." + (let ((non-essential t)) + (if (and ext (or (string= (file-name-extension fname) ext) + (string= (file-name-extension fname t) ext) + (eq ext t)) + (not (file-directory-p fname))) + (file-name-sans-extension (file-name-nondirectory fname)) + (file-name-nondirectory (directory-file-name fname))))) + +(defun helm-basedir (fname &optional parent) + "Return the base directory of filename ending by a slash. +If PARENT is specified and FNAME is a directory return the parent +directory of FNAME." + (helm-aif (and fname + (or (and (string= fname "~") "~") + (file-name-directory + (if parent + (directory-file-name fname) + fname)))) + (file-name-as-directory it))) + +(defun helm-current-directory () + "Return current-directory name at point. +Useful in dired buffers when there is inserted subdirs." + (expand-file-name + (if (eq major-mode 'dired-mode) + (dired-current-directory) + default-directory))) + +(defun helm-shadow-boring-files (files) + "Files matching `helm-boring-file-regexp' will be +displayed with the `file-name-shadow' face if available." + (helm-shadow-entries files helm-boring-file-regexp-list)) + +(defun helm-skip-boring-files (files) + "Files matching `helm-boring-file-regexp' will be skipped." + (helm-skip-entries files helm-boring-file-regexp-list)) + +(defun helm-skip-current-file (files) + "Current file will be skipped." + (remove (buffer-file-name helm-current-buffer) files)) + +(defun helm-w32-pathname-transformer (args) + "Change undesirable features of windows pathnames to ones more acceptable to +other candidate transformers." + (if (eq system-type 'windows-nt) + (helm-transform-mapcar + (lambda (x) + (replace-regexp-in-string + "/cygdrive/\\(.\\)" "\\1:" + (replace-regexp-in-string "\\\\" "/" x))) + args) + args)) + +(defun helm-w32-prepare-filename (file) + "Convert filename FILE to something usable by external w32 executables." + (replace-regexp-in-string ; For UNC paths + "/" "\\" + (replace-regexp-in-string ; Strip cygdrive paths + "/cygdrive/\\(.\\)" "\\1:" + file nil nil) nil t)) + +(defun helm-w32-shell-execute-open-file (file) + (with-no-warnings + (w32-shell-execute "open" (helm-w32-prepare-filename file)))) + +;; Same as `vc-directory-exclusion-list'. +(defvar helm-walk-ignore-directories + '("SCCS/" "RCS/" "CVS/" "MCVS/" ".svn/" ".git/" ".hg/" ".bzr/" + "_MTN/" "_darcs/" "{arch}/" ".gvfs/")) + +(defsubst helm--dir-file-name (file dir) + (expand-file-name + (substring file 0 (1- (length file))) dir)) + +(defsubst helm--dir-name-p (str) + (char-equal (aref str (1- (length str))) ?/)) + +(cl-defun helm-walk-directory (directory &key (path 'basename) + directories + match skip-subdirs + noerror) + "Walk through DIRECTORY tree. + +Argument PATH can be one of basename, relative, full, or a +function called on file name, default to basename. + +Argument DIRECTORIES when t return also directories names, +otherwise skip directories names, with a value of `only' returns +only subdirectories, i.e. files are skipped. + +Argument MATCH is a regexp matching files or directories. + +Argument SKIP-SUBDIRS when t will skip +`helm-walk-ignore-directories', otherwise if it is given as a +list of directories, this list will be used instead of +`helm-walk-ignore-directories'. + +Argument NOERROR when t will skip directories which are not +accessible." + (let ((fn (cl-case path + (basename 'file-name-nondirectory) + (relative 'file-relative-name) + (full 'identity) + (t path)))) ; A function. + (setq skip-subdirs (if (listp skip-subdirs) + skip-subdirs + helm-walk-ignore-directories)) + (cl-labels ((ls-rec (dir) + (unless (file-symlink-p dir) + (cl-loop for f in (sort (file-name-all-completions "" dir) + 'string-lessp) + unless (member f '("./" "../")) + ;; A directory. + ;; Use `helm--dir-file-name' to remove the final slash. + ;; Needed to avoid infloop on directory symlinks. + if (and (helm--dir-name-p f) + (helm--dir-file-name f dir)) + nconc + (unless (or (member f skip-subdirs) + (and noerror + (not (file-accessible-directory-p it)))) + (if (and directories + (or (null match) + (string-match match f))) + (nconc (list (concat (funcall fn it) "/")) + (ls-rec it)) + (ls-rec it))) + ;; A regular file. + else nconc + (when (and (null (eq directories 'only)) + (or (null match) (string-match match f))) + (list (funcall fn (expand-file-name f dir)))))))) + (ls-rec directory)))) + +(defun helm-file-expand-wildcards (pattern &optional full) + "Same as `file-expand-wildcards' but allow recursion. +Recursion happens when PATTERN starts with two stars. +Directories expansion is not supported." + (let ((bn (helm-basename pattern)) + (case-fold-search nil)) + (if (and helm-file-globstar + (string-match "\\`\\*\\{2\\}\\(.*\\)" bn)) + (helm-walk-directory (helm-basedir pattern) + :path (cl-case full + (full 'full) + (relative 'relative) + ((basename nil) 'basename) + (t 'full)) + :directories nil + :match (or (helm-wildcard-to-regexp bn) + (wildcard-to-regexp bn)) + :skip-subdirs t) + (helm-aif (helm-wildcard-to-regexp bn) + (directory-files (helm-basedir pattern) full it) + ;; `file-expand-wildcards' fails to expand weird directories + ;; like "[ foo.zz ] bar.*.avi", fallback to `directory-files' + ;; in such cases. + (or (file-expand-wildcards pattern full) + (directory-files (helm-basedir pattern) + full (wildcard-to-regexp bn))))))) + +(defun helm-wildcard-to-regexp (wc) + "Transform wilcard WC like \"**.{jpg,jpeg}\" in REGEXP." + (when (string-match ".*\\(\\*\\{1,2\\}\\)\\.[{]\\(.*\\)[}]\\'" wc) + (format ".*\\.\\(%s\\)$" + (replace-regexp-in-string + "," "\\\\|" (match-string 2 wc))))) + +;;; helm internals +;; +(defun helm-set-pattern (pattern &optional noupdate) + "Set minibuffer contents to PATTERN. +If optional NOUPDATE is non-nil, the Helm buffer is not changed." + (with-selected-window (or (active-minibuffer-window) (minibuffer-window)) + (delete-minibuffer-contents) + (insert pattern)) + (when noupdate + (setq helm-pattern pattern))) + +(defun helm-minibuffer-completion-contents () + "Return the user input in a minibuffer before point as a string. +That is what completion commands operate on." + (buffer-substring (field-beginning) (point))) + +(defmacro with-helm-buffer (&rest body) + "Eval BODY inside `helm-buffer'." + (declare (indent 0) (debug t)) + `(with-current-buffer (helm-buffer-get) + ,@body)) + +(defmacro with-helm-current-buffer (&rest body) + "Eval BODY inside `helm-current-buffer'." + (declare (indent 0) (debug t)) + `(with-current-buffer (or (and (buffer-live-p helm-current-buffer) + helm-current-buffer) + (setq helm-current-buffer + (current-buffer))) + ,@body)) + +(defun helm-buffer-get () + "Return `helm-action-buffer' if shown otherwise `helm-buffer'." + (if (helm-action-window) + helm-action-buffer + helm-buffer)) + +(defun helm-window () + "Window of `helm-buffer'." + (get-buffer-window (helm-buffer-get) 0)) + +(defun helm-action-window () + "Window of `helm-action-buffer'." + (get-buffer-window helm-action-buffer 'visible)) + +(defmacro with-helm-window (&rest body) + "Be sure BODY is excuted in the helm window." + (declare (indent 0) (debug t)) + `(with-selected-window (helm-window) + ,@body)) + +(defmacro helm-without-follow (&rest body) + "Ensure BODY runs without following. +I.e. when using `helm-next-line' and friends in BODY." + (declare (indent 0) (debug t)) + `(cl-letf (((symbol-function 'helm-follow-mode-p) + (lambda (&optional _) nil))) + (let (helm-follow-mode-persistent) + (progn ,@body)))) + +;; Completion styles related functions +;; +(defun helm--setup-completion-styles-alist () + (cl-pushnew '(helm helm-completion-try-completion + helm-completion-all-completions + "helm multi completion style.") + completion-styles-alist + :test 'equal) + (unless (assq 'flex completion-styles-alist) + ;; Add helm-fuzzy style only if flex is not available. + (cl-pushnew '(helm-flex helm-flex-completion-try-completion + helm-flex-completion-all-completions + "helm flex completion style.\nProvide flex matching for emacs-26.") + completion-styles-alist + :test 'equal))) + +(defvar helm-blacklist-completion-styles '(emacs21 emacs22)) +(defun helm--prepare-completion-styles (&optional nomode styles) + "Return a suitable list of styles for `completion-styles'. + +When `helm-completion-style' is not `emacs' the Emacs vanilla +default `completion-styles' is used except for +`helm-dynamic-completion' which uses inconditionally `emacs' as +value for `helm-completion-style'. + +If styles are specified in `helm-completion-styles-alist' for a +particular mode, use these styles unless NOMODE is non nil. +If STYLES is specified as a list of styles suitable for +`completion-styles' these styles are used in the given order. +Otherwise helm style is added to `completion-styles' always after +flex or helm-flex completion style if present." + ;; For `helm-completion-style' and `helm-completion-styles-alist'. + (require 'helm-mode) + (if (memq helm-completion-style '(helm helm-fuzzy)) + ;; Keep default settings, but probably nil is fine as well. + '(basic partial-completion emacs22) + (or + styles + (pcase (and (null nomode) + (cdr (assq major-mode helm-completion-styles-alist))) + (`(,_l . ,ll) ll)) + ;; We need to have flex always behind helm, otherwise + ;; when matching against e.g. '(foo foobar foao frogo bar + ;; baz) with pattern "foo" helm style if before flex will + ;; return foo and foobar only defeating flex that would + ;; return foo foobar foao and frogo. + (let* ((wflex (car (or (assq 'flex completion-styles-alist) + (assq 'helm-flex completion-styles-alist)))) + (styles (append (and (memq wflex completion-styles) + (list wflex)) + (cl-loop for s in completion-styles + unless (or (memq s helm-blacklist-completion-styles) + (memq wflex completion-styles)) + collect s)))) + (helm-append-at-nth + styles '(helm) + (if (memq wflex completion-styles) + 1 0)))))) + +(defun helm-dynamic-completion (collection predicate &optional point metadata nomode styles) + "Build a completion function for `helm-pattern' in COLLECTION. + +Only the elements of COLLECTION that satisfy PREDICATE are considered. + +Argument POINT is the same as in `completion-all-completions' and +is meaningful only when using some kind of `completion-at-point'. + +The return value is a list of completions that may be sorted by +the sort function provided by the completion-style in +use (emacs-27 only), otherwise (emacs-26) the sort function has +to be provided if needed either with an FCT function in source or +by passing the sort function with METADATA +E.g.: (metadata (display-sort-function . foo)). + +If you don't want the sort fn provided by style to kick +in (emacs-27) you can use as metadata value the symbol `nosort'. + +Example: + + (helm :sources (helm-build-sync-source \"test\" + :candidates (helm-dynamic-completion + \\='(foo bar baz foab) + \\='symbolp) + :match-dynamic t) + :buffer \"*helm test*\") + +When argument NOMODE is non nil don't use `completion-styles' as +specified in `helm-completion-styles-alist' for specific modes. + +When STYLES is specified use these `completion-styles', see +`helm--prepare-completion-styles'. + +Also `helm-completion-style' settings have no effect here, +`emacs' being used inconditionally as value." + (lambda () + (let* (;; Force usage of emacs style otherwise + ;; helm--prepare-completion-styles will reset + ;; completion-styles to default value i.e. (basic partial + ;; emacs22). + (helm-completion-style 'emacs) + (completion-styles + (with-helm-current-buffer + (helm--prepare-completion-styles nomode styles))) + (completion-flex-nospace t) + (nosort (eq metadata 'nosort)) + (compsfn (lambda (str pred _action) + (let* ((completion-ignore-case (helm-set-case-fold-search)) + (comps (completion-all-completions + str + (if (functionp collection) + (funcall collection str pred t) + collection) + pred + (or point 0) + (or (and (listp metadata) metadata) + (setq metadata '(metadata))))) + (last-data (last comps)) + (sort-fn (unless nosort + (completion-metadata-get + metadata 'display-sort-function))) + all) + (when (cdr last-data) + (setcdr last-data nil)) + (setq all (copy-sequence comps)) + (if (and sort-fn (> (length str) 0)) + (funcall sort-fn all) + all))))) + ;; Ensure circular objects are removed. + (complete-with-action t compsfn helm-pattern predicate)))) + +;; Yank text at point. +;; +;; +(defun helm-yank-text-at-point (arg) + "Yank text at point in `helm-current-buffer' into minibuffer." + (interactive "p") + (with-helm-current-buffer + (let ((fwd-fn (or helm-yank-text-at-point-function #'forward-word)) + diff) + ;; Start to initial point if C-w have never been hit. + (unless helm-yank-point + (setq helm-yank-point (car helm-current-position))) + (save-excursion + (goto-char helm-yank-point) + (helm-set-pattern + (if (< arg 0) + (with-temp-buffer + (insert helm-pattern) + (let ((end (point-max))) + (goto-char end) + (funcall fwd-fn -1) + (setq diff (- end (point))) + (delete-region (point) end) + (buffer-string))) + (funcall fwd-fn arg) + (concat + ;; Allow yankink beyond eol allow inserting e.g long + ;; urls in mail buffers. + helm-pattern (replace-regexp-in-string + "\\`\n" "" + (buffer-substring-no-properties + helm-yank-point (point)))))) + (setq helm-yank-point (if diff (- (point) diff) (point))))))) +(put 'helm-yank-text-at-point 'helm-only t) + +(defun helm-undo-yank-text-at-point () + "Undo last entry added by `helm-yank-text-at-point'." + (interactive) + (helm-yank-text-at-point -1)) +(put 'helm-undo-yank-text-at-point 'helm-only t) + +(defun helm-reset-yank-point () + (setq helm-yank-point nil)) + +(add-hook 'helm-cleanup-hook 'helm-reset-yank-point) +(add-hook 'helm-after-initialize-hook 'helm-reset-yank-point) + +;;; Ansi +;; +;; +(defvar helm--ansi-color-regexp + "\033\\[\\(K\\|[0-9;]*m\\)") +(defvar helm--ansi-color-drop-regexp + "\033\\[\\([ABCDsuK]\\|[12][JK]\\|=[0-9]+[hI]\\|[0-9;]*[Hf]\\)") +(defun helm--ansi-color-apply (string) + "A version of `ansi-color-apply' immune to upstream changes. + +Similar to the emacs-24.5 version without support to +`ansi-color-context' which is buggy in Emacs. + +Modify also `ansi-color-regexp' by using own variable +`helm--ansi-color-regexp' that matches whole STRING. + +This is needed to provide compatibility for both emacs-25 and +emacs-24.5 as emacs-25 version of `ansi-color-apply' is partially +broken." + (require 'ansi-color) + (let ((start 0) + codes end escape-sequence + result colorized-substring) + ;; Find the next escape sequence. + (while (setq end (string-match helm--ansi-color-regexp string start)) + (setq escape-sequence (match-string 1 string)) + ;; Colorize the old block from start to end using old face. + (when codes + (put-text-property + start end 'font-lock-face (ansi-color--find-face codes) string)) + (setq colorized-substring (substring string start end) + start (match-end 0)) + ;; Eliminate unrecognized ANSI sequences. + (while (string-match helm--ansi-color-drop-regexp colorized-substring) + (setq colorized-substring + (replace-match "" nil nil colorized-substring))) + (push colorized-substring result) + ;; Create new face, by applying escape sequence parameters. + (setq codes (ansi-color-apply-sequence escape-sequence codes))) + ;; If the rest of the string should have a face, put it there. + (when codes + (put-text-property + start (length string) + 'font-lock-face (ansi-color--find-face codes) string)) + ;; Save the remainder of the string to the result. + (if (string-match "\033" string start) + (push (substring string start (match-beginning 0)) result) + (push (substring string start) result)) + (apply 'concat (nreverse result)))) + +(when (< emacs-major-version 26) + (advice-add 'ansi-color-apply :override #'helm--ansi-color-apply)) + + +;;; Fontlock +(cl-dolist (mode '(emacs-lisp-mode lisp-interaction-mode)) + (font-lock-add-keywords + mode + '(("(\\<\\(with-helm-after-update-hook\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(with-helm-temp-hook\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(with-helm-window\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(with-helm-current-buffer\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(with-helm-buffer\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(with-helm-show-completion\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(with-helm-default-directory\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(with-helm-restore-variables\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-multi-key-defun\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-while-no-input\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-aif\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-awhile\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-acond\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-aand\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-with-gensyms\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-read-answer-dolist-with-action\\)\\>" 1 font-lock-keyword-face) + ("(\\<\\(helm-read-answer\\)\\>" 1 font-lock-keyword-face)))) + +(provide 'helm-lib) + +;;; helm-lib ends here diff --git a/code/elpa/helm-core-20220824.1925/helm-multi-match.el b/code/elpa/helm-core-20220824.1925/helm-multi-match.el new file mode 100644 index 0000000..173df2a --- /dev/null +++ b/code/elpa/helm-core-20220824.1925/helm-multi-match.el @@ -0,0 +1,409 @@ +;;; helm-multi-match.el --- Multiple regexp matching methods for helm -*- lexical-binding: t -*- + +;; Original Author: rubikitch + +;; Copyright (C) 2008 ~ 2011 rubikitch +;; Copyright (C) 2011 ~ 2020 Thierry Volpiatto + +;; Author: Thierry Volpiatto +;; URL: http://github.com/emacs-helm/helm + +;; 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 . + +;;; Code: + +(require 'cl-lib) +(require 'helm-lib) + + +(defgroup helm-multi-match nil + "Helm multi match." + :group 'helm) + +(defcustom helm-mm-matching-method 'multi3 + "Matching method for helm match plugin. +You can set here different methods to match candidates in helm. +Here are the possible value of this symbol and their meaning: +- multi1: Respect order, prefix of pattern must match. +- multi2: Same but with partial match. +- multi3: The best, multiple regexp match, allow negation. +- multi3p: Same but prefix must match. + +Default is multi3, you should keep this for a better experience. + +Note that multi1 and multi3p are incompatible with fuzzy matching +in file completion and by the way fuzzy matching will be disabled there +when these options are used." + :type '(radio :tag "Matching methods for helm" + (const :tag "Multiple regexp 1 ordered with prefix match" multi1) + (const :tag "Multiple regexp 2 ordered with partial match" multi2) + (const :tag "Multiple regexp 3 matching no order, partial, best." multi3) + (const :tag "Multiple regexp 3p matching with prefix match" multi3p)) + :group 'helm-multi-match) + + +;; Internal +(defvar helm-mm-default-match-functions + '(helm-mm-exact-match helm-mm-match)) +(defvar helm-mm-default-search-functions + '(helm-mm-exact-search helm-mm-search)) + + +;;; Build regexps +;; +;; +(defconst helm-mm-space-regexp "\\s\\\\s-" + "Regexp to represent space itself in multiple regexp match.") + +(defun helm-mm-split-pattern (pattern &optional grep-space) + "Split PATTERN if it contains spaces and return resulting list. +If spaces in PATTERN are escaped, don't split at this place. +i.e \"foo bar baz\"=> (\"foo\" \"bar\" \"baz\") +but \"foo\\ bar baz\"=> (\"foo\\s-bar\" \"baz\"). +If GREP-SPACE is used translate escaped space to \"\\s\" instead of \"\\s-\"." + (split-string + ;; Match spaces litteraly because candidate buffer syntax-table + ;; doesn't understand "\s-" properly. + (replace-regexp-in-string + helm-mm-space-regexp + (if grep-space "\\s" "\\s-") pattern nil t))) + +(defun helm-mm-1-make-regexp (pattern) + "Replace spaces in PATTERN with \".*\"." + (mapconcat 'identity (helm-mm-split-pattern pattern) ".*")) + + +;;; Exact match. +;; +;; +;; Internal. +(defvar helm-mm-exact-pattern-str nil) +(defvar helm-mm-exact-pattern-real nil) + +(defun helm-mm-exact-get-pattern (pattern) + (unless (equal pattern helm-mm-exact-pattern-str) + (setq helm-mm-exact-pattern-str pattern + helm-mm-exact-pattern-real (concat "\n" pattern "\n"))) + helm-mm-exact-pattern-real) + + +(cl-defun helm-mm-exact-match (candidate &optional (pattern helm-pattern)) + (if case-fold-search + (progn + (setq candidate (downcase candidate) + pattern (downcase pattern)) + (string= candidate pattern)) + (string= candidate pattern))) + +(defun helm-mm-exact-search (pattern &rest _ignore) + (and (search-forward (helm-mm-exact-get-pattern pattern) nil t) + (forward-line -1))) + + +;;; Prefix match +;; +;; +;; Internal +(defvar helm-mm-prefix-pattern-str nil) +(defvar helm-mm-prefix-pattern-real nil) + +(defun helm-mm-prefix-get-pattern (pattern) + (unless (equal pattern helm-mm-prefix-pattern-str) + (setq helm-mm-prefix-pattern-str pattern + helm-mm-prefix-pattern-real (concat "\n" pattern))) + helm-mm-prefix-pattern-real) + +(defun helm-mm-prefix-match (candidate &optional pattern) + ;; In filename completion basename and basedir may be + ;; quoted, unquote them for string comparison (Bug#1283). + (setq pattern (replace-regexp-in-string + "\\\\" "" (or pattern helm-pattern))) + (let ((len (length pattern))) + (and (<= len (length candidate)) + (string= (substring candidate 0 len) pattern )))) + +(defun helm-mm-prefix-search (pattern &rest _ignore) + (search-forward (helm-mm-prefix-get-pattern pattern) nil t)) + + +;;; Multiple regexp patterns 1 (order is preserved / prefix). +;; +;; +;; Internal +(defvar helm-mm-1-pattern-str nil) +(defvar helm-mm-1-pattern-real nil) + +(defun helm-mm-1-get-pattern (pattern) + (unless (equal pattern helm-mm-1-pattern-str) + (setq helm-mm-1-pattern-str pattern + helm-mm-1-pattern-real + (concat "^" (helm-mm-1-make-regexp pattern)))) + helm-mm-1-pattern-real) + +(cl-defun helm-mm-1-match (candidate &optional (pattern helm-pattern)) + (string-match (helm-mm-1-get-pattern pattern) candidate)) + +(defun helm-mm-1-search (pattern &rest _ignore) + (re-search-forward (helm-mm-1-get-pattern pattern) nil t)) + + +;;; Multiple regexp patterns 2 (order is preserved / partial). +;; +;; +;; Internal +(defvar helm-mm-2-pattern-str nil) +(defvar helm-mm-2-pattern-real nil) + +(defun helm-mm-2-get-pattern (pattern) + (unless (equal pattern helm-mm-2-pattern-str) + (setq helm-mm-2-pattern-str pattern + helm-mm-2-pattern-real + (concat "^.*" (helm-mm-1-make-regexp pattern)))) + helm-mm-2-pattern-real) + +(cl-defun helm-mm-2-match (candidate &optional (pattern helm-pattern)) + (string-match (helm-mm-2-get-pattern pattern) candidate)) + +(defun helm-mm-2-search (pattern &rest _ignore) + (re-search-forward (helm-mm-2-get-pattern pattern) nil t)) + + +;;; Multiple regexp patterns 3 (permutation). +;; +;; +;; Internal +(defvar helm-mm--3-pattern-str nil) +(defvar helm-mm--3-pattern-list nil) + +(defun helm-mm-3-get-patterns (pattern) + "Return a list of predicate/regexp cons cells. +E.g., ((identity . \"foo\") (not . \"bar\")). +If PATTERN is unchanged, don't recompute PATTERN and return the +previous value stored in `helm-mm--3-pattern-list'." + (unless (equal pattern helm-mm--3-pattern-str) + (setq helm-mm--3-pattern-str pattern + helm-mm--3-pattern-list + (helm-mm-3-get-patterns-internal pattern))) + helm-mm--3-pattern-list) + +(defun helm-mm-3-get-patterns-internal (pattern) + "Return a list of predicate/regexp cons cells. +E.g., ((identity . \"foo\") (not . \"bar\"))." + (unless (string= pattern "") + (cl-loop for pat in (helm-mm-split-pattern pattern) + collect (if (char-equal ?! (aref pat 0)) + (cons 'not (substring pat 1)) + (cons 'identity pat))))) + +(defun helm-mm-regexp-p (string) + (string-match-p "[][*+^$.?]" string)) + +(defvar helm-mm--match-on-diacritics nil) + +(cl-defun helm-mm-3-match (candidate &optional (pattern helm-pattern)) + "Check if PATTERN match CANDIDATE. +When PATTERN contains a space, it is splitted and matching is +done with the several resulting regexps against CANDIDATE. +E.g., \"bar foo\" will match \"foobar\" and \"barfoo\". +Argument PATTERN, a string, is transformed in a list of cons cell +with `helm-mm-3-get-patterns' if it contains a space. +E.g., \"foo bar\"=>((identity . \"foo\") (identity . \"bar\")). +Then each predicate of cons cell(s) is called with the regexp of +the same cons cell against CANDIDATE. +I.e. (identity (string-match \"foo\" \"foo bar\")) => t." + (let ((pat (helm-mm-3-get-patterns pattern))) + (cl-loop for (predicate . regexp) in pat + for re = (if (and (not (helm-mm-regexp-p regexp)) + helm-mm--match-on-diacritics) + (char-fold-to-regexp regexp) + regexp) + always (funcall predicate + (condition-case _err + ;; FIXME: Probably do nothing when + ;; using fuzzy leaving the job + ;; to the fuzzy fn. + (string-match re candidate) + (invalid-regexp nil)))))) + +(defun helm-mm-3-search-base (pattern searchfn1 searchfn2) + "Try to find PATTERN in `helm-buffer' with SEARCHFN1 and SEARCHFN2. +This is the search function for `candidates-in-buffer' enabled sources. +Use the same method as `helm-mm-3-match' except it search in buffer +instead of matching on a string. +i.e (identity (re-search-forward \"foo\" (point-at-eol) t)) => t." + (cl-loop with pat = (if (stringp pattern) + (helm-mm-3-get-patterns pattern) + pattern) + with regex = (cdar pat) + with regex1 = (if (and regex + (not (helm-mm-regexp-p regex)) + helm-mm--match-on-diacritics) + (char-fold-to-regexp regex) + regex) + when (eq (caar pat) 'not) return + ;; Pass the job to `helm-search-match-part'. + (prog1 (list (point-at-bol) (point-at-eol)) + (forward-line 1)) + while (condition-case _err + (funcall searchfn1 (or regex1 "") nil t) + (invalid-regexp nil)) + for bol = (point-at-bol) + for eol = (point-at-eol) + if (cl-loop for (pred . str) in (cdr pat) + for regexp = (if (and (not (helm-mm-regexp-p str)) + helm-mm--match-on-diacritics) + (char-fold-to-regexp str) + str) + always + (progn (goto-char bol) + (funcall pred (condition-case _err + (funcall searchfn2 regexp eol t) + (invalid-regexp nil))))) + do (goto-char eol) and return t + else do (goto-char eol) + finally return nil)) + +(defun helm-mm-3-search (pattern &rest _ignore) + (helm-mm-3-search-base + pattern 're-search-forward 're-search-forward)) + +(defun helm-mm-3-search-on-diacritics (pattern &rest _ignore) + (let ((helm-mm--match-on-diacritics t)) + (helm-mm-3-search pattern))) + +;;; mp-3 with migemo +;; Needs https://github.com/emacs-jp/migemo +;; +(defvar helm-mm--previous-migemo-info nil + "[Internal] Cache previous migemo query.") +(make-local-variable 'helm-mm--previous-migemo-info) + +(declare-function migemo-get-pattern "ext:migemo.el") +(declare-function migemo-search-pattern-get "ext:migemo.el") + +(define-minor-mode helm-migemo-mode + "Enable migemo in helm. +It will be available in the sources handling it, +i.e. the sources which have the slot :migemo with non--nil value." + :lighter " Hmio" + :group 'helm + :global t + (cl-assert (featurep 'migemo) + nil "No feature called migemo found, install migemo.el.")) + +(defun helm-mm-migemo-get-pattern (pattern) + (let ((regex (migemo-get-pattern pattern))) + (if (ignore-errors (string-match regex "") t) + (concat regex "\\|" pattern) pattern))) + +(defun helm-mm-migemo-search-pattern-get (pattern) + (let ((regex (migemo-search-pattern-get pattern))) + (if (ignore-errors (string-match regex "") t) + (concat regex "\\|" pattern) pattern))) + +(defun helm-mm-migemo-string-match (pattern str) + "Migemo version of `string-match'." + (unless (assoc pattern helm-mm--previous-migemo-info) + (with-helm-buffer + (setq helm-mm--previous-migemo-info + (push (cons pattern (helm-mm-migemo-get-pattern pattern)) + helm-mm--previous-migemo-info)))) + (string-match (assoc-default pattern helm-mm--previous-migemo-info) str)) + +(defun helm-mm-diacritics-string-match (pattern str) + "Check if PATTERN match STR ignoring diacritics. + +If PATTERN is a regexp (i.e. `helm-mm-regexp-p') use PATTERN +unmodified, otherwise transform PATTERN with `char-fold-to-regexp'. + +This function is used to search match-part of candidate in in-buffer +sources." + (string-match (if (helm-mm-regexp-p pattern) + pattern + (char-fold-to-regexp pattern)) + str)) + +(cl-defun helm-mm-3-migemo-match (candidate &optional (pattern helm-pattern)) + (and helm-migemo-mode + (cl-loop for (pred . re) in (helm-mm-3-get-patterns pattern) + always (funcall pred (helm-mm-migemo-string-match re candidate))))) + +(defun helm-mm-migemo-forward (word &optional bound noerror count) + (with-helm-buffer + (unless (assoc word helm-mm--previous-migemo-info) + (setq helm-mm--previous-migemo-info + (push (cons word (if (delq 'ascii (find-charset-string word)) + word + (helm-mm-migemo-search-pattern-get word))) + helm-mm--previous-migemo-info)))) + (re-search-forward + (assoc-default word helm-mm--previous-migemo-info) bound noerror count)) + +(defun helm-mm-3-migemo-search (pattern &rest _ignore) + (and helm-migemo-mode + (helm-mm-3-search-base + pattern 'helm-mm-migemo-forward 'helm-mm-migemo-forward))) + + +;;; mp-3p- (multiple regexp pattern 3 with prefix search) +;; +;; +(defun helm-mm-3p-match (candidate &optional pattern) + "Check if PATTERN match CANDIDATE. +Same as `helm-mm-3-match' but only for the cdr of patterns, the car of +patterns must always match CANDIDATE prefix. +E.g. \"bar foo baz\" will match \"barfoobaz\" or \"barbazfoo\" but not +\"foobarbaz\" whereas `helm-mm-3-match' would match all." + (let* ((pat (helm-mm-3-get-patterns (or pattern helm-pattern))) + (first (car pat))) + (and (funcall (car first) (helm-mm-prefix-match candidate (cdr first))) + (cl-loop for (predicate . regexp) in (cdr pat) + always (funcall predicate (string-match regexp candidate)))))) + +(defun helm-mm-3p-search (pattern &rest _ignore) + (helm-mm-3-search-base + pattern 'helm-mm-prefix-search 're-search-forward)) + + +;;; Generic multi-match/search functions +;; +;; +(cl-defun helm-mm-match (candidate &optional (pattern helm-pattern)) + "Call `helm-mm-matching-method' function against CANDIDATE." + (let ((fun (cl-ecase helm-mm-matching-method + (multi1 #'helm-mm-1-match) + (multi2 #'helm-mm-2-match) + (multi3 #'helm-mm-3-match) + (multi3p #'helm-mm-3p-match)))) + (funcall fun candidate pattern))) + +(cl-defun helm-mm-3-match-on-diacritics (candidate &optional (pattern helm-pattern)) + "Same as `helm-mm-3-match' but match on diacritics if possible." + (let ((helm-mm--match-on-diacritics t)) + (helm-mm-match candidate pattern))) + +(defun helm-mm-search (pattern &rest _ignore) + "Search for PATTERN with `helm-mm-matching-method' function." + (let ((fun (cl-ecase helm-mm-matching-method + (multi1 #'helm-mm-1-search) + (multi2 #'helm-mm-2-search) + (multi3 #'helm-mm-3-search) + (multi3p #'helm-mm-3p-search)))) + (funcall fun pattern))) + + +(provide 'helm-multi-match) + + +;;; helm-multi-match.el ends here diff --git a/code/elpa/helm-core-20220824.1925/helm-source.el b/code/elpa/helm-core-20220824.1925/helm-source.el new file mode 100644 index 0000000..f97bff1 --- /dev/null +++ b/code/elpa/helm-core-20220824.1925/helm-source.el @@ -0,0 +1,1302 @@ +;;; helm-source.el --- Helm source creation. -*- lexical-binding: t -*- + +;; Copyright (C) 2015 ~ 2020 Thierry Volpiatto + +;; Author: Thierry Volpiatto +;; URL: http://github.com/emacs-helm/helm + +;; 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 . + +;;; Commentary: + +;; Interface to create helm sources easily. +;; Actually the eieo objects are transformed in alist for compatibility. +;; In the future this package should allow creating source as eieo objects +;; without conversion to alist, teaching helm to read such a structure. +;; The compatibility with alists would be kept. + +;;; Code: + +(require 'cl-lib) +(require 'eieio) +(require 'helm-lib) + +(defvar helm-fuzzy-sort-fn) +(defvar helm-fuzzy-match-fn) +(defvar helm-fuzzy-search-fn) + +(declare-function helm-init-candidates-in-buffer "helm-core.el") +(declare-function helm-interpret-value "helm-core.el") +(declare-function helm-fuzzy-highlight-matches "helm-core.el") + +;;; Advice Emacs fn +;; Make Classes's docstrings more readable by removing al the +;; unnecessary crap. + +(defun helm-source--cl--print-table (&rest args) + "Advice for `cl--print-table' to make readable class slots docstrings." + (cl-flet ((print-rows (rows) + (let ((format "%s\n\n Initform=%s\n\n%s")) + (dolist (row rows) + (setcar row (propertize (car row) 'face 'bold)) + (setcdr row (nthcdr 1 (cdr row))) + (insert "\n* " (apply #'format format row) "\n"))))) + (print-rows (cadr args)))) + +(cl-defgeneric helm--setup-source (source) + "Prepare slots and handle slot errors before creating a helm source.") + +(cl-defgeneric helm-setup-user-source (source) + "Allow users modifying slots in SOURCE just before creation.") + + +;;; Classes for sources +;; +;; +(defclass helm-source () + ((name + :initarg :name + :initform nil + :custom string + :documentation + " The name of the source. + A string which is also the heading which appears + above the list of matches from the source. Must be unique.") + + (header-name + :initarg :header-name + :initform nil + :custom function + :documentation + " A function returning the display string of the header. + Its argument is the name of the source. This attribute is useful to + add an additional information with the source name. + It doesn't modify the name of the source.") + + (init + :initarg :init + :initform nil + :custom function + :documentation + " Function called with no parameters when helm is started. + It is useful for collecting current state information which can be + used to create the list of candidates later. + Initialization of `candidates-in-buffer' is done here + with `helm-init-candidates-in-buffer'.") + + (candidates + :initarg :candidates + :initform nil + :custom (choice function list) + :documentation + " Specifies how to retrieve candidates from the source. + It can either be a variable name, a function called with no parameters + or the actual list of candidates. + + Do NOT use this for asynchronous sources, use `candidates-process' + instead. + + The list must be a list whose members are strings, symbols + or (DISPLAY . REAL) pairs. + + In case of (DISPLAY . REAL) pairs, the DISPLAY string is shown + in the Helm buffer, but the REAL one is used as action + argument when the candidate is selected. This allows a more + readable presentation for candidates which would otherwise be, + for example, too long or have a common part shared with other + candidates which can be safely replaced with an abbreviated + string for display purposes. + + Note that if the (DISPLAY . REAL) form is used then pattern + matching is done on the displayed string, not on the real + value. + + This function, generally should not compute candidates according to + `helm-pattern' which defeat all the Helm's matching mechanism + i.e. multiple pattern matching and/or fuzzy matching. + If you want to do so, use :match-dynamic slot to be sure matching + occur only in :candidates function and there is no conflict with + other match functions.") + + (update + :initarg :update + :initform nil + :custom function + :documentation + " Function called with no parameters at before \"init\" function + when `helm-force-update' is called.") + + (cleanup + :initarg :cleanup + :initform nil + :custom function + :documentation + " Function called with no parameters when *helm* buffer is + closed. It is useful for killing unneeded candidates buffer. + + Note that the function is executed BEFORE performing action.") + + (keymap + :initarg :keymap + :initform 'helm-map + :custom sexp + :documentation + " Specific keymap for this source. + default value is `helm-map'.") + + (action + :initarg :action + :initform 'identity + :custom (alist :key-type string + :value-type function) + :documentation + " An alist of (DISPLAY . FUNCTION) pairs, a variable name or a function. + FUNCTION is called with one parameter: the selected candidate. + + An action other than the default can be chosen from this list + of actions for the currently selected candidate (by default + with TAB). The DISPLAY string is shown in the completions + buffer and the FUNCTION is invoked when an action is + selected. The first action of the list is the default. + + You should use `helm-make-actions' to build this alist easily.") + + (persistent-action + :initarg :persistent-action + :initform nil + :custom function + :documentation + " Can be a either a Function called with one parameter (the + selected candidate) or a cons cell where first element is this + same function and second element a symbol (e.g never-split) + that inform `helm-execute-persistent-action' to not split his + window to execute this persistent action. + Example: + + (defun foo-persistent-action (candidate) + (do-something candidate)) + + :persistent-action \\='(foo-persistent-action . never-split) ; Don't split + or + :persistent-action \\='foo-persistent-action ; Split + + When specifying :persistent-action by slot directly, foo-persistent-action + will be executed without quitting helm when hitting `C-j'. + + Note that other persistent actions can be defined using other + bindings than `C-j' by simply defining an interactive function bound + to a key in the keymap source. + The function should create a new attribute in source before calling + `helm-execute-persistent-action' on this attribute. + Example: + + (defun helm-ff-persistent-delete () + \"Delete current candidate without quitting.\" + (interactive) + (with-helm-alive-p + (helm-set-attr \\='quick-delete \\='(helm-ff-quick-delete . never-split)) + (helm-execute-persistent-action \\='quick-delete))) + + This function is then bound in `helm-find-files-map'.") + + (persistent-action-if + :initarg :persistent-action-if + :initform nil + :custom function + :documentation + " Similar from persistent action but it is a function that should + return an object suitable for persistent action when called , i.e. a + function or a cons cell. + Example: + + (defun foo-persistent-action (candidate) + (cond (something + ;; Don't split helm-window. + (cons (lambda (_ignore) + (do-something candidate)) + \\='no-split)) + ;; Split helm-window. + (something-else + (lambda (_ignore) + (do-something-else candidate))))) + + :persistent-action-if \\='foo-persistent-action + + Here when hitting `C-j' one of the lambda's will be executed + depending on something or something-else condition, splitting or not + splitting as needed. + See `helm-find-files-persistent-action-if' definition as another example.") + + (persistent-help + :initarg :persistent-help + :initform nil + :custom string + :documentation + " A string to explain persistent-action of this source. + It is a facility to display what persistent action does in + header-line, once your source is loaded don't use it directly, it will + have no effect, use instead `header-line' attribute. + It also accepts a function or a variable name. + It will be displayed in `header-line' or in `minibuffer' depending + of value of `helm-echo-input-in-header-line' and `helm-display-header-line'.") + + (help-message + :initarg :help-message + :initform nil + :custom (choice string function) + :documentation + " Help message for this source. + If not present, `helm-help-message' value will be used.") + + (multiline + :initarg :multiline + :initform nil + :custom (choice boolean integer) + :documentation + " Allow multiline candidates. + When non-nil candidates will be separated by `helm-candidate-separator'. + You can customize the color of this separator with `helm-separator' face. + Value of multiline can be an integer which specify the maximum size of the + multiline string to display, if multiline string is longer than this value + it will be truncated.") + + (requires-pattern + :initarg :requires-pattern + :initform 0 + :custom integer + :documentation + " If present matches from the source are shown only if the + pattern is not empty. Optionally, it can have an integer + parameter specifying the required length of input which is + useful in case of sources with lots of candidates.") + + (candidate-transformer + :initarg :candidate-transformer + :initform nil + :custom (choice function list) + :documentation + " It's a function or a list of functions called with one argument + when the completion list from the source is built. The argument + is the list of candidates retrieved from the source. The + function should return a transformed list of candidates which + will be used for the actual completion. If it is a list of + functions, it calls each function sequentially. + + This can be used to transform or remove items from the list of + candidates. + + Note that `candidates' is run already, so the given transformer + function should also be able to handle candidates with (DISPLAY + . REAL) format.") + + (filtered-candidate-transformer + :initarg :filtered-candidate-transformer + :initform nil + :custom (choice function list) + :documentation + " It has the same format as `candidate-transformer', except the + function is called with two parameters: the candidate list and + the source. + + This transformer is run on the candidate list which is already + filtered by the current pattern. While `candidate-transformer' + is run only once, it is run every time the input pattern is + changed. + + It can be used to transform the candidate list dynamically, for + example, based on the current pattern. + + In some cases it may also be more efficent to perform candidate + transformation here, instead of with `candidate-transformer' + even if this transformation is done every time the pattern is + changed. For example, if a candidate set is very large then + `candidate-transformer' transforms every candidate while only + some of them will actually be displayed due to the limit + imposed by `helm-candidate-number-limit'. + + Note that `candidates' and `candidate-transformer' is run + already, so the given transformer function should also be able + to handle candidates with (DISPLAY . REAL) format.") + + (filter-one-by-one + :initarg :filter-one-by-one + :initform nil + :custom (choice function list) + :documentation + " A transformer function that treat candidates one by one. + It is called with one arg the candidate. + It is faster than `filtered-candidate-transformer' or + `candidate-transformer', but should be used only in sources + that recompute constantly their candidates, e.g `helm-source-find-files'. + Filtering happen early and candidates are treated + one by one instead of re-looping on the whole list. + If used with `filtered-candidate-transformer' or `candidate-transformer' + these functions should treat the candidates transformed by the + `filter-one-by-one' function in consequence.") + + (display-to-real + :initarg :display-to-real + :initform nil + :custom function + :documentation + " Transform the selected candidate when passing it to action. + + Function called with one parameter, the selected candidate. + + Avoid recomputing all candidates with candidate-transformer + or filtered-candidate-transformer to give a new value to REAL, + instead the selected candidate is transformed only when passing it + to action. This works (and make sense) only with plain string + candidates, it will NOT work when candidate is a cons cell, in this + case the real value of candidate will be used. + Example: + + (helm :sources (helm-build-sync-source \"test\" + :candidates \\='(a b c d e) + :display-to-real (lambda (c) (concat c \":modified by d-t-r\"))) + :buffer \"*helm test*\") + + Note that this is NOT a transformer, + so the display will not be modified by this function.") + + (real-to-display + :initarg :real-to-display + :initform nil + :custom function + :documentation + " Recompute all candidates computed previously with other transformers. + + Function called with one parameter, the selected candidate. + + The real value of candidates will be shown in display and of course + be used by action. + Example: + + (helm :sources (helm-build-sync-source \"test\" + :candidates \\='((\"foo\" . 1) (\"bar\" . 2) (\"baz\". 3)) + :real-to-display (lambda (c) (format \"%s\" (1+ c)))) + :buffer \"*helm test*\") + + Mostly deprecated, kept only for backward compatibility.") + + (marked-with-props + :initarg :marked-with-props + :initform nil + :custom (choice boolean symbol) + :documentation + " Get candidates with their properties in `helm-marked-candidates'. + Allow using the FORCE-DISPLAY-PART of `helm-get-selection' in marked + candidates, use t or \\='withprop to pass it to `helm-get-selection'.") + + (action-transformer + :initarg :action-transformer + :initform nil + :custom (choice function list) + :documentation + " It's a function or a list of functions called with two + arguments when the action list from the source is + assembled. The first argument is the list of actions, the + second is the current selection. If it is a list of functions, + it calls each function sequentially. + + The function should return a transformed action list. + + This can be used to customize the list of actions based on the + currently selected candidate.") + + (pattern-transformer + :initarg :pattern-transformer + :initform nil + :custom (choice function list) + :documentation + " It's a function or a list of functions called with one argument + before computing matches. Its argument is `helm-pattern'. + Functions should return transformed `helm-pattern'. + + It is useful to change interpretation of `helm-pattern'.") + + (candidate-number-limit + :initarg :candidate-number-limit + :initform nil + :custom integer + :documentation + " Override `helm-candidate-number-limit' only for this source.") + + (volatile + :initarg :volatile + :initform nil + :custom boolean + :documentation + " Indicates the source assembles the candidate list dynamically, + so it shouldn't be cached within a single Helm + invocation. It is only applicable to synchronous sources, + because asynchronous sources are not cached.") + + (match + :initarg :match + :initform nil + :custom (choice function list) + :documentation + " List of functions called with one parameter: a candidate. The + function should return non-nil if the candidate matches the + current pattern (see variable `helm-pattern'). + + When using `candidates-in-buffer' its default value is `identity' and + don't have to be changed, use the `search' slot instead. + + This attribute allows the source to override the default + pattern matching based on `string-match'. It can be used, for + example, to implement a source for file names and do the + pattern matching on the basename of files, since it's more + likely one is typing part of the basename when searching for a + file, instead of some string anywhere else in its path. + + If the list contains more than one function then the list of + matching candidates from the source is constructed by appending + the results after invoking the first function on all the + potential candidates, then the next function, and so on. The + matching candidates supplied by the first function appear first + in the list of results and then results from the other + functions, respectively. + + This attribute has no effect for asynchronous sources (see + attribute `candidates'), and sources using `match-dynamic' + since they perform pattern matching themselves. + + Note that FUZZY-MATCH slot will overhide value of this slot.") + + (diacritics + :initarg :diacritics + :initform nil + :custom boolean + :documentation + " Ignore diacritics when searching.") + + (match-on-real + :initarg :match-on-real + :initform nil + :custom boolean + :documentation + " Match the real value of candidates when non nil.") + + (fuzzy-match + :initarg :fuzzy-match + :initform nil + :custom boolean + :documentation + " Enable fuzzy matching in this source. + This will overwrite settings in MATCH slot, and for + sources built with child class `helm-source-in-buffer' the SEARCH slot. + This is an easy way of enabling fuzzy matching, but you can use the MATCH + or SEARCH slots yourself if you want something more elaborated, mixing + different type of match (See `helm-source-buffers' class for example). + + This attribute is not supported for asynchronous sources + since they perform pattern matching themselves.") + + (redisplay + :initarg :redisplay + :initform 'identity + :custom (choice list function) + :documentation + " A function or a list of functions to apply to current list + of candidates when redisplaying buffer with `helm-redisplay-buffer'. + This is only interesting for modifying and redisplaying the whole list + of candidates in async sources. + It uses `identity' by default for when async sources are mixed with + normal sources, in this case these normal sources are not modified and + redisplayed as they are.") + + (nomark + :initarg :nomark + :initform nil + :custom boolean + :documentation + " Don't allow marking candidates when this attribute is present.") + + (nohighlight + :initarg :nohighlight + :initform nil + :custom boolean + :documentation + " Disable highlighting matches in this source. + This will disable generic highlighting of matches, + but some specialized highlighting can be done from elsewhere, + i.e from `filtered-candidate-transformer' or `filter-one-by-one' slots. + So use this to either disable completely highlighting in your source, + or to disable highlighting and use a specialized highlighting matches + function for this source. + Remember that this function should run AFTER all filter functions if those + filter functions are modifying face properties, though it is possible to + avoid this by using new `add-face-text-property' in your filter functions.") + + (allow-dups + :initarg :allow-dups + :initform nil + :custom boolean + :documentation + " Allow helm collecting duplicates candidates.") + + (history + :initarg :history + :initform nil + :custom symbol + :documentation + " Allow passing history variable to helm from source. + It should be a quoted symbol. + Passing the history variable here have no effect + so add it also in the `helm' call with the :history keyword. + The main point of adding the variable here + is to make it available when resuming.") + + (coerce + :initarg :coerce + :initform nil + :custom function + :documentation + " It's a function called with one argument: the selected candidate. + This function is intended for type convertion. In normal case, + the selected candidate (string) is passed to action + function. If coerce function is specified, it is called just + before action function. + + Example: converting string to symbol + (coerce . intern)") + + (mode-line + :initarg :mode-line + :initform nil + :custom (choice string sexp) + :documentation + " Source local `helm-mode-line-string' (included in + `mode-line-format'). It accepts also variable/function name.") + + (header-line + :initarg :header-line + :initform nil + :custom (choice string function) + :documentation + " Source local `header-line-format'. + It will be displayed in `header-line' or in `minibuffer' depending + of value of `helm-echo-input-in-header-line' and `helm-display-header-line'. + It accepts also variable/function name.") + + (resume + :initarg :resume + :initform nil + :custom function + :documentation + " Function called with no parameters at end of initialization + when `helm-resume' is started. + If this function try to do something against `helm-buffer', (e.g updating, + searching etc...) probably you should run it in a timer to ensure + `helm-buffer' is ready.") + + (follow + :initarg :follow + :initform nil + :custom integer + :documentation + " Enable `helm-follow-mode' for this source only. + With a value of 1 enable, a value of -1 or nil disable the mode. + See `helm-follow-mode' for more infos.") + + (follow-delay + :initarg :follow-delay + :initform nil + :custom integer + :documentation + " `helm-follow-mode' will execute persistent-action after this delay. + Otherwise value of `helm-follow-input-idle-delay' is used if non--nil, + If none of these are found fallback to `helm-input-idle-delay'.") + + (multimatch + :initarg :multimatch + :initform t + :custom boolean + :documentation + " Use the multi-match algorithm when non-nil. + I.e Allow specifying multiple patterns separated by spaces. + When a pattern is prefixed by \"!\" the negation of this pattern is used, + i.e match anything but this pattern. + It is the standard way of matching in helm and is enabled by default. + It can be used with fuzzy-matching enabled, but as soon helm detect a space, + each pattern will match by regexp and will not be fuzzy.") + + (match-part + :initarg :match-part + :initform nil + :custom function + :documentation + " Allow matching only one part of candidate. + If source contain match-part attribute, match is computed only + on part of candidate returned by the call of function provided + by this attribute. The function should have one arg, candidate, + and return only a specific part of candidate. + On async sources, as matching is done by the backend, this have + no effect apart for highlighting matches.") + + (before-init-hook + :initarg :before-init-hook + :initform nil + :custom symbol + :documentation + " A local hook that run at beginning of initilization of this source. + i.e Before the creation of `helm-buffer'. + + Should be a variable (a symbol) bound to a list of + functions or a single function (see `run-hooks' documentation). + Even better is to use `add-hook' to feed this variable. + Usage of an anonymous function, or a list of functions is still + supported but not recommended.") + + (after-init-hook + :initarg :after-init-hook + :initform nil + :custom symbol + :documentation + " A local hook that run at end of initilization of this source. + i.e After the creation of `helm-buffer'. + + Should be a variable (a symbol) bound to a list of + functions or a single function (see `run-hooks' documentation). + Even better is to use `add-hook' to feed this variable. + Usage of an anonymous function, or a list of functions is still + supported but not recommended.") + + (delayed + :initarg :delayed + :initform nil + :custom (choice null integer) + :documentation + " This slot have no more effect and is just kept for backward compatibility. + Please don't use it.") + + (must-match + :initarg :must-match + :initform nil + :custom symbol + :documentation + " Same as `completing-read' require-match arg. + Possible values are: + - `t' which prevent exiting with an empty helm-buffer i.e. no matches. + - `confirm' which ask for confirmation i.e. need to press a second + time RET. + - `nil' is the default and is doing nothing i.e. returns nil when + pressing RET with an empty helm-buffer. + - Any other non nil values e.g. `ignore' allow exiting with + minibuffer contents as candidate value (in this case helm-buffer + is empty).") + + (find-file-target + :initarg :find-file-target + :initform nil + :custom function + :documentation + " Determine the target file when running `helm-quit-and-find-file'. + It is a function called with one arg SOURCE.") + + (group + :initarg :group + :initform 'helm + :custom symbol + :documentation + " The current source group, default to `helm' when not specified.")) + + "Main interface to define helm sources." + :abstract t) + +(defclass helm-source-sync (helm-source) + ((candidates + :initform '("ERROR: You must specify the `candidates' slot, either with a list or a function")) + + (migemo + :initarg :migemo + :initform nil + :custom boolean + :documentation + " Enable migemo. + When multimatch is disabled, you can give the symbol \\='nomultimatch as value + to force not using generic migemo matching function. + In this case you have to provide your own migemo matching funtion + that kick in when `helm-migemo-mode' is enabled. + Otherwise it will be available for this source once `helm-migemo-mode' + is enabled when non-nil.") + + (match-strict + :initarg :match-strict + :initform nil + :custom function + :documentation + " When specifying a match function within a source and + helm-multi-match is enabled, the result of all matching + functions will be concatened, which in some cases is not what + is wanted. When using `match-strict' only this or these + functions will be used. You can specify those functions as a + list of functions or a single symbol function. + + NOTE: This have the same effect as using :MULTIMATCH nil.") + + (match-dynamic + :initarg :match-dynamic + :initform nil + :custom boolean + :documentation + " Disable all helm matching functions when non nil. + The :candidates function in this case is in charge of fetching + candidates dynamically according to `helm-pattern'. + Note that :volatile is automatically enabled when using this, so no + need to specify it.")) + + "Use this class to make helm sources using a list of candidates. +This list should be given as a normal list, a variable handling a list +or a function returning a list. +Matching is done basically with `string-match' against each candidate.") + +(defclass helm-source-async (helm-source) + ((candidates-process + :initarg :candidates-process + :initform nil + :custom function + :documentation + " This attribute is used to define a process as candidate. + The function called with no arguments must return a process + i.e. `processp', it use typically `start-process' or `make-process', + see (info \"(elisp) Asynchronous Processes\"). + + + NOTE: + When building the source at runtime you can give directly a process + as value, otherwise wrap the process call into a function. + The process buffer should be nil, otherwise, if you use + `helm-buffer' give to the process a sentinel.") + + (multimatch :initform nil)) + + "Use this class to define a helm source calling an external process. +The external process is called typically in a `start-process' call to be +asynchronous. + +Note that using multiples asynchronous sources is not fully working, +expect weird behavior if you try this. + +The :candidates slot is not allowed even if described because this class +inherit from `helm-source'.") + +(defclass helm-source-in-buffer (helm-source) + ((init + :initform 'helm-default-init-source-in-buffer-function) + + (data + :initarg :data + :initform nil + :custom (choice list string) + :documentation + " A string, a list or a buffer that will be used to feed the `helm-candidates-buffer'. + This data will be passed in a function added to the init slot and + the buffer will be build with `helm-init-candidates-in-buffer' or directly + with `helm-candidates-buffer' if data is a buffer. + This is an easy and fast method to build a `candidates-in-buffer' source.") + + (migemo + :initarg :migemo + :initform nil + :custom boolean + :documentation + " Enable migemo. + When multimatch is disabled, you can give the symbol \\='nomultimatch as value + to force not using generic migemo matching function. + In this case you have to provide your own migemo matching funtion + that kick in when `helm-migemo-mode' is enabled. + Otherwise it will be available for this source once `helm-migemo-mode' + is enabled when non-nil.") + + (candidates + :initform 'helm-candidates-in-buffer) + + (volatile + :initform t) + + (match + :initform '(identity)) + + (get-line + :initarg :get-line + :initform 'buffer-substring-no-properties + :custom function + :documentation + " A function like `buffer-substring-no-properties' or `buffer-substring'. + This function converts region from point at line-beginning and point + at line-end in the `helm-candidate-buffer' to a string which will be displayed + in the `helm-buffer', it takes two args BEG and END. + By default, `helm-candidates-in-buffer' uses + `buffer-substring-no-properties' which does no conversion and doesn't carry + text properties.") + + (search + :initarg :search + :initform '(helm-candidates-in-buffer-search-default-fn) + :custom (choice function list) + :documentation + " List of functions like `re-search-forward' or `search-forward'. + Buffer search function used by `helm-candidates-in-buffer'. + By default, `helm-candidates-in-buffer' uses `re-search-forward'. + The function should take one arg PATTERN. + If your search function needs to handle negation like multimatch, + this function should returns in such case a cons cell of two integers defining + the beg and end positions to match in the line previously matched by + `re-search-forward' or similar, and move point to next line + (See how the `helm-mm-3-search-base' and `helm-fuzzy-search' functions are working). + + NOTE: FUZZY-MATCH slot will overhide value of this slot.") + + (search-strict + :initarg :search-strict + :initform nil + :custom function + :documentation + " When specifying a search function within a source and + helm-multi-match is enabled, the result of all searching + functions will be concatened, which in some cases is not what + is wanted. When using `search-strict' only this or these + functions will be used. You can specify those functions as a + list of functions or a single symbol function. + + NOTE: This have the same effect as using a nil value for + :MULTIMATCH slot.")) + + "Use this source to make helm sources storing candidates inside a buffer. + +The buffer storing candidates is generated by `helm-candidate-buffer' function +and all search are done in this buffer, results are transfered to the `helm-buffer' +when done. +Contrarily to `helm-source-sync' candidates are matched using a function +like `re-search-forward' (see below documentation of `:search' slot) which makes +the search much faster than matching candidates one by one. +If you want to add search functions to your sources, don't use `:match' which +will raise an error, but `:search'. +See `helm-candidates-in-buffer' for more infos.") + +(defclass helm-source-dummy (helm-source) + ((candidates + :initform '("dummy")) + + (filtered-candidate-transformer + :initform (lambda (_candidates _source) (list helm-pattern))) + + (multimatch + :initform nil) + + (accept-empty + :initarg :accept-empty + :initform t + :custom boolean + :documentation + " Allow exiting with an empty string. + You should keep the default value.") + + (match + :initform 'identity) + + (volatile + :initform t))) + +(defclass helm-source-in-file (helm-source-in-buffer) + ((init :initform (lambda () + (let ((file (helm-get-attr 'candidates-file)) + (count 1)) + (with-current-buffer (helm-candidate-buffer 'global) + (insert-file-contents file) + (goto-char (point-min)) + (when (helm-get-attr 'linum) + (while (not (eobp)) + (add-text-properties + (point-at-bol) (point-at-eol) + `(helm-linum ,count)) + (cl-incf count) + (forward-line 1))))))) + (get-line :initform #'buffer-substring) + (candidates-file + :initarg :candidates-file + :initform nil + :custom string + :documentation + " The file used to fetch candidates.") + (linum + :initarg :linum + :initform nil + :documentation + " Store line number in each candidate when non nil. + Line number is stored in `helm-linum' text property.")) + + "The contents of the FILE will be used as candidates in buffer.") + + +;;; Error functions +;; +;; +(defun helm-default-init-source-in-buffer-function () + (helm-init-candidates-in-buffer 'global + '("ERROR: No buffer handling your data, use either the `init' slot or the `data' slot."))) + + +;;; Internal Builder functions. +;; +;; +(defun helm--create-source (object) + "[INTERNAL] Build a helm source from OBJECT. +Where OBJECT is an instance of an eieio class." + (cl-loop for sd in (eieio-class-slots (eieio-object-class object)) + for s = (eieio-slot-descriptor-name sd) + for slot-val = (slot-value object s) + when slot-val + collect (cons s slot-val))) + +(defun helm-make-source (name class &rest args) + "Build a `helm' source named NAME with ARGS for CLASS. +Argument NAME is a string which define the source name, so no need to use +the keyword :name in your source, NAME will be used instead. +Argument CLASS is an eieio class object. +Arguments ARGS are keyword value pairs as defined in CLASS." + (declare (indent 2)) + (let ((source (apply #'make-instance class name args))) + (setf (slot-value source 'name) name) + (helm--setup-source source) + (helm-setup-user-source source) + (helm--create-source source))) + +(defun helm-make-type (class &rest args) + (let ((source (apply #'make-instance class args))) + (setf (slot-value source 'name) nil) + (helm--setup-source source) + (helm--create-source source))) + +(defvar helm-mm-default-search-functions) +(defvar helm-mm-default-match-functions) + +(defun helm-source-mm-get-search-or-match-fns (source method) + "Prepare match or search functions for class SOURCE. +Argument METHOD is the matching method used by SOURCE either `match' +or `search'." + (let* ((diacritics (slot-value source 'diacritics)) + (defmatch (helm-aif (slot-value source 'match) + (helm-mklist it))) + (defmatch-strict (helm-aif (and (eq method 'match) + (slot-value source 'match-strict)) + (helm-mklist it))) + (defsearch (helm-aif (and (eq method 'search) + (slot-value source 'search)) + (helm-mklist it))) + (defsearch-strict (helm-aif (and (eq method 'search-strict) + (slot-value source 'search-strict)) + (helm-mklist it))) + (migemo (slot-value source 'migemo))) + (cl-case method + (match (cond (defmatch-strict) + ((and migemo diacritics) + (append (list 'helm-mm-exact-match + 'helm-mm-3-match-on-diacritics) + defmatch '(helm-mm-3-migemo-match))) + (migemo + (append helm-mm-default-match-functions + defmatch '(helm-mm-3-migemo-match))) + (diacritics + (delq nil + `(helm-mm-exact-match + ,@defmatch helm-mm-3-match-on-diacritics))) + (defmatch + (append helm-mm-default-match-functions defmatch)) + (t helm-mm-default-match-functions))) + (search (cond (defsearch-strict) + ((and migemo diacritics) + (append '(helm-mm-exact-search) + defsearch + '(helm-mm-3-migemo-search + helm-mm-3-search-on-diacritics))) + (migemo + (append helm-mm-default-search-functions + defsearch '(helm-mm-3-migemo-search))) + (diacritics + (delq nil + `(helm-mm-exact-search + ,@defsearch helm-mm-3-search-on-diacritics))) + (defsearch + (append helm-mm-default-search-functions defsearch)) + (t helm-mm-default-search-functions)))))) + + +;;; Modifiers +;; +(cl-defun helm-source-add-action-to-source-if (name fn source predicate + &optional (index 4)) + "Same as `helm-add-action-to-source-if' but for SOURCE defined as eieio object. +You can use this inside a `helm--setup-source' method for a SOURCE defined as +an eieio class." + (let* ((actions (slot-value source 'action)) + (action-transformers (slot-value source 'action-transformer)) + (new-action (list (cons name fn))) + (transformer (lambda (actions _candidate) + (let ((candidate (car (helm-marked-candidates)))) + (cond ((funcall predicate candidate) + (helm-append-at-nth + actions new-action index)) + (t actions)))))) + (cond ((functionp actions) + (setf (slot-value source 'action) (list (cons "Default action" actions)))) + ((listp actions) + (setf (slot-value source 'action) (helm-interpret-value actions source)))) + (when (or (symbolp action-transformers) (functionp action-transformers)) + (setq action-transformers (list action-transformers))) + (setf (slot-value source 'action-transformer) + (delq nil (append (list transformer) action-transformers))))) + + +;;; Methods to build sources. +;; +;; +(defun helm-source--persistent-help-string (value source) + "Format `persistent-help' VALUE in SOURCE. +Argument VALUE can be a string, a variable or a function." + (substitute-command-keys + (format "\\\\[helm-execute-persistent-action]: %s (keeping session)" + (helm-aif value + (helm-interpret-value value source) + (slot-value source 'header-line))))) + +(defun helm-source--header-line (source) + "Compute a default header line for SOURCE. + +The header line is based on one of `persistent-action-if', +`persistent-action', or `action' (in this order of precedence)." + (substitute-command-keys + (concat "\\\\[helm-execute-persistent-action]: " + (helm-acond + ((slot-value source 'persistent-action-if) + (helm-symbol-name it)) + ((or (slot-value source 'persistent-action) + (slot-value source 'action)) + (cond ((and (symbolp it) + (functionp it) + (eq it 'identity)) + "Do Nothing") + ((and (symbolp it) + (boundp it) + (listp (symbol-value it)) + (stringp (caar (symbol-value it)))) + (caar (symbol-value it))) + ((or (symbolp it) (functionp it)) + (helm-symbol-name it)) + ((listp it) + (let ((action (car it))) + ;; It comes from :action ("foo" . function). + (if (stringp (car action)) + (car action) + ;; It comes from :persistent-action + ;; (function . 'nosplit) Fix Bug#788. + (if (or (symbolp action) + (functionp action)) + (helm-symbol-name action))))) + (t ""))) + (t "")) + " (keeping session)"))) + +(cl-defmethod helm--setup-source ((_source helm-source))) + +(cl-defmethod helm--setup-source :before ((source helm-source)) + (unless (slot-value source 'group) + (setf (slot-value source 'group) 'helm)) + (when (slot-value source 'delayed) + (warn "Deprecated usage of helm `delayed' slot in `%s'" + (slot-value source 'name))) + (helm-aif (slot-value source 'keymap) + (let* ((map (if (symbolp it) + (symbol-value it) + it)) + (must-match-map (when (slot-value source 'must-match) + (let ((map (make-sparse-keymap))) + (define-key map (kbd "RET") + 'helm-confirm-and-exit-minibuffer) + map))) + (loc-map (if must-match-map + (make-composed-keymap + must-match-map map) + map))) + (setf (slot-value source 'keymap) loc-map))) + (helm-aif (slot-value source 'persistent-help) + (setf (slot-value source 'header-line) + (helm-source--persistent-help-string it source)) + (setf (slot-value source 'header-line) (helm-source--header-line source))) + (when (slot-value source 'fuzzy-match) + (cl-assert helm-fuzzy-sort-fn nil "Wrong type argument functionp: nil") + (setf (slot-value source 'filtered-candidate-transformer) + (helm-aif (slot-value source 'filtered-candidate-transformer) + (append (helm-mklist it) + (list helm-fuzzy-sort-fn)) + (list helm-fuzzy-sort-fn)))) + (unless (slot-value source 'nohighlight) + (setf (slot-value source 'filtered-candidate-transformer) + (helm-aif (slot-value source 'filtered-candidate-transformer) + (append (helm-mklist it) + (list #'helm-fuzzy-highlight-matches)) + (list #'helm-fuzzy-highlight-matches)))) + (when (numberp (helm-interpret-value (slot-value source 'multiline))) + (setf (slot-value source 'filtered-candidate-transformer) + (helm-aif (slot-value source 'filtered-candidate-transformer) + (append (helm-mklist it) + (list #'helm-multiline-transformer)) + (list #'helm-multiline-transformer)))) + (helm-aif (slot-value source 'requires-pattern) + (let ((val (if (symbolp it) + (symbol-value it) + it))) + (setf (slot-value source 'requires-pattern) val))) + (let ((sname (slot-value source 'name))) + (pcase (slot-value source 'before-init-hook) + ((or (and val (pred (functionp)) (guard (not (symbolp val)))) + (pred (consp))) + (warn "Helm source `%s': before-init-hook Should be defined as a symbol" sname))) + (pcase (slot-value source 'after-init-hook) + ((or (and (pred (functionp)) (pred (not symbolp))) + (pred (consp))) + (warn "Helm source `%s': after-init-hook Should be defined as a symbol" sname))))) + +(cl-defmethod helm-setup-user-source ((_source helm-source))) + +(cl-defmethod helm--setup-source ((source helm-source-sync)) + (when (slot-value source 'fuzzy-match) + (helm-aif (slot-value source 'match) + (setf (slot-value source 'match) + (append (helm-mklist it) + (list helm-fuzzy-match-fn))) + (setf (slot-value source 'match) helm-fuzzy-match-fn))) + (when (slot-value source 'multimatch) + (setf (slot-value source 'match) + (helm-source-mm-get-search-or-match-fns source 'match))) + (helm-aif (and (null (slot-value source 'multimatch)) + (slot-value source 'migemo)) + (unless (eq it 'nomultimatch) ; Use own migemo fn. + (setf (slot-value source 'match) + (append (helm-mklist (slot-value source 'match)) + '(helm-mm-3-migemo-match))))) + (when (slot-value source 'match-dynamic) + (setf (slot-value source 'match) 'identity) + (setf (slot-value source 'match-part) nil) + (setf (slot-value source 'multimatch) nil) + (setf (slot-value source 'fuzzy-match) nil) + (setf (slot-value source 'volatile) t))) + +(cl-defmethod helm--setup-source ((source helm-source-in-buffer)) + (cl-assert (eq (slot-value source 'candidates) 'helm-candidates-in-buffer) + nil + (format "Wrong usage of `candidates' attr in `%s' use `data' or `init' instead" + (slot-value source 'name))) + (let ((cur-init (slot-value source 'init))) + (helm-aif (slot-value source 'data) + (setf (slot-value source 'init) + (delq + nil + (list + (and (null (eq 'helm-default-init-source-in-buffer-function + cur-init)) + cur-init) + (lambda () + (helm-init-candidates-in-buffer + 'global + (cond ((functionp it) (funcall it)) + ((and (bufferp it) (buffer-live-p it)) + (with-current-buffer it (buffer-string))) + (t it))))))))) + (when (slot-value source 'fuzzy-match) + (helm-aif (slot-value source 'search) + (setf (slot-value source 'search) + (append (helm-mklist it) + (list helm-fuzzy-search-fn))) + (setf (slot-value source 'search) (list helm-fuzzy-search-fn)))) + (when (slot-value source 'multimatch) + (setf (slot-value source 'search) + (helm-source-mm-get-search-or-match-fns source 'search))) + (helm-aif (and (null (slot-value source 'multimatch)) + (slot-value source 'migemo)) + (unless (eq it 'nomultimatch) + (setf (slot-value source 'search) + (append (helm-mklist (slot-value source 'search)) + '(helm-mm-3-migemo-search))))) + (let ((mtc (slot-value source 'match))) + (cl-assert (or (equal '(identity) mtc) + (eq 'identity mtc)) + nil "Invalid slot value for `match'") + (cl-assert (eq (slot-value source 'volatile) t) + nil "Invalid slot value for `volatile'"))) + +(cl-defmethod helm--setup-source ((source helm-source-async)) + (cl-assert (null (slot-value source 'candidates)) + nil "Incorrect use of `candidates' use `candidates-process' instead") + (cl-assert (null (slot-value source 'multimatch)) + nil "`multimatch' not allowed in async sources.") + (cl-assert (null (slot-value source 'fuzzy-match)) + nil "`fuzzy-match' not supported in async sources.")) + +(cl-defmethod helm--setup-source ((source helm-source-dummy)) + (let ((mtc (slot-value source 'match))) + (cl-assert (or (equal '(identity) mtc) + (eq 'identity mtc)) + nil "Invalid slot value for `match'") + (cl-assert (eq (slot-value source 'volatile) t) + nil "Invalid slot value for `volatile'") + (cl-assert (equal (slot-value source 'candidates) '("dummy")) + nil "Invalid slot value for `candidates'") + (cl-assert (eq (slot-value source 'accept-empty) t) + nil "Invalid slot value for `accept-empty'"))) + + +;;; User functions +;; +;; Sources +(defmacro helm-build-sync-source (name &rest args) + "Build a synchronous helm source with name NAME. +Args ARGS are keywords provided by `helm-source-sync'." + (declare (indent 1)) + `(helm-make-source ,name 'helm-source-sync ,@args)) + +(defmacro helm-build-async-source (name &rest args) + "Build a asynchronous helm source with name NAME. +Args ARGS are keywords provided by `helm-source-async'." + (declare (indent 1)) + `(helm-make-source ,name 'helm-source-async ,@args)) + +(defmacro helm-build-in-buffer-source (name &rest args) + "Build a helm source with name NAME using `candidates-in-buffer' method. +Args ARGS are keywords provided by `helm-source-in-buffer'." + (declare (indent 1)) + `(helm-make-source ,name 'helm-source-in-buffer ,@args)) + +(defmacro helm-build-dummy-source (name &rest args) + "Build a helm source with name NAME using `dummy' method. +Args ARGS are keywords provided by `helm-source-dummy'." + (declare (indent 1)) + `(helm-make-source ,name 'helm-source-dummy ,@args)) + +(defmacro helm-build-in-file-source (name file &rest args) + "Build a helm source with NAME name using `candidates-in-files' method. +Arg FILE is a filename, the contents of this file will be +used as candidates in buffer. +Args ARGS are keywords provided by `helm-source-in-file'." + (declare (indent 2)) + `(helm-make-source ,name 'helm-source-in-file + :candidates-file ,file ,@args)) + + +(provide 'helm-source) + +;;; helm-source ends here diff --git a/code/elpa/helm-ls-git-20220818.553/helm-ls-git-autoloads.el b/code/elpa/helm-ls-git-20220818.553/helm-ls-git-autoloads.el new file mode 100644 index 0000000..b150c98 --- /dev/null +++ b/code/elpa/helm-ls-git-20220818.553/helm-ls-git-autoloads.el @@ -0,0 +1,47 @@ +;;; helm-ls-git-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "helm-ls-git" "helm-ls-git.el" (0 0 0 0)) +;;; Generated autoloads from helm-ls-git.el + +(add-to-list 'auto-mode-alist '("/COMMIT_EDITMSG$" . helm-ls-git-commit-mode)) + +(autoload 'helm-ls-git-commit-mode "helm-ls-git" "\ +Mode to edit COMMIT_EDITMSG files. + +Commands: +\\{helm-ls-git-commit-mode-map} + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("/git-rebase-todo$" . helm-ls-git-rebase-todo-mode)) + +(autoload 'helm-ls-git-rebase-todo-mode "helm-ls-git" "\ +Major Mode to edit git-rebase-todo files when using git rebase -i. + +Commands: +\\{helm-ls-git-rebase-todo-mode-map} + +\(fn)" t nil) + +(autoload 'helm-ls-git "helm-ls-git" "\ + + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "helm-ls-git" '("helm-")) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; helm-ls-git-autoloads.el ends here diff --git a/code/elpa/helm-ls-git-20220818.553/helm-ls-git-pkg.el b/code/elpa/helm-ls-git-20220818.553/helm-ls-git-pkg.el new file mode 100644 index 0000000..f9a17ec --- /dev/null +++ b/code/elpa/helm-ls-git-20220818.553/helm-ls-git-pkg.el @@ -0,0 +1,2 @@ +;;; Generated package description from helm-ls-git.el -*- no-byte-compile: t -*- +(define-package "helm-ls-git" "20220818.553" "list git files." '((helm "1.7.8")) :commit "fc44fc1015bbc75d16e7d7aa5d971ff1ad85e9e1") diff --git a/code/elpa/helm-ls-git-20220818.553/helm-ls-git.el b/code/elpa/helm-ls-git-20220818.553/helm-ls-git.el new file mode 100644 index 0000000..b1c7f6c --- /dev/null +++ b/code/elpa/helm-ls-git-20220818.553/helm-ls-git.el @@ -0,0 +1,1979 @@ +;;; helm-ls-git.el --- list git files. -*- lexical-binding: t -*- + +;; Copyright (C) 2012 ~ 2022 Thierry Volpiatto + +;; Package-Requires: ((helm "1.7.8")) +;; Package-Version: 20220818.553 +;; Package-Commit: fc44fc1015bbc75d16e7d7aa5d971ff1ad85e9e1 + +;; 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 . + +;;; Code + +(require 'cl-lib) +(require 'vc) +(require 'vc-git) +(require 'helm-files) ; helm-grep is required in helm-files. +(require 'helm-types) + +(defvaralias 'helm-c-source-ls-git 'helm-source-ls-git) +(make-obsolete-variable 'helm-c-source-ls-git 'helm-source-ls-git "1.5.1") +(defvaralias 'helm-c-source-ls-git-status 'helm-source-ls-git-status) +(make-obsolete-variable 'helm-c-source-ls-git-status 'helm-source-ls-git-status "1.5.1") + +;; Now the git-grep command is defined in helm-grep.el, +;; alias it for backward compatibility. +(defvar helm-ls-git-grep-command) +(defvaralias 'helm-ls-git-grep-command 'helm-grep-git-grep-command) +(make-obsolete-variable 'helm-ls-git-grep-command 'helm-grep-git-grep-command "1.8.0") + +(defvar server-clients) +(declare-function helm-comp-read "ext:helm-mode.el") +(declare-function server-running-p "server.el") +(declare-function server-edit "server.el") +(declare-function server-send-string "server.el") +(declare-function server-quote-arg "server.el") +;; Define the sources. +(defvar helm-source-ls-git-status nil + "This source will built at runtime. +It can be build explicitly with function +`helm-ls-git-build-git-status-source'.") +(defvar helm-source-ls-git nil + "This source will built at runtime. +It can be build explicitly with function +`helm-ls-git-build-ls-git-source'.") +(defvar helm-source-ls-git-buffers nil + "This source will built at runtime. +It can be build explicitly with function +`helm-ls-git-build-buffers-source'.") + + + +(defgroup helm-ls-git nil + "Helm completion for git repos." + :group 'helm) + +(defcustom helm-ls-git-show-abs-or-relative 'relative + "Show full path or relative path to repo when using `helm-ff-toggle-basename'. +Valid values are symbol \\='absolute' or \\='relative' (default)." + :type '(radio :tag "Show full path or relative path to Git repo when toggling" + (const :tag "Show full path" absolute) + (const :tag "Show relative path" relative))) + +(defcustom helm-ls-git-status-command nil + "Favorite git-status command for emacs. + +When set, you will have an additional action allowing to +switch to a git status buffer e.g. `vc-dir' or `magit-status'. + +If you want to use magit use `magit-status-setup-buffer' and not +`magit-status' which is working only interactively." + :type 'symbol) + +(defcustom helm-ls-git-fuzzy-match nil + "Enable fuzzy matching in `helm-source-ls-git-status' and `helm-source-ls-git'." + :set (lambda (var val) + (set var val) + (setq helm-source-ls-git nil + helm-source-ls-git-status nil + helm-source-ls-git-buffers nil)) + :type 'boolean) + +(defcustom helm-ls-git-default-sources '(helm-source-ls-git-status + helm-ls-git-branches-source + helm-source-ls-git-buffers + helm-source-ls-git + helm-ls-git-stashes-source + helm-ls-git-create-branch-source) + "Default sources for `helm-ls-git-ls'." + :type '(repeat symbol)) + +(defcustom helm-ls-git-format-glob-string "'%s'" + "String to format globs in `helm-grep-get-file-extensions'. +Glob are enclosed in single quotes by default." + :type 'string) + +(defcustom helm-ls-git-ls-switches '("ls-files" "--full-name" "--") + "A list of arguments to pass to `git-ls-files'. +To see files in submodules add the option \"--recurse-submodules\". +If you have problems displaying unicode filenames use +\'(\"-c\" \"core.quotePath=false\" \"ls-files\" \"--full-name\" \"--\"). +See Issue #52." + :type '(repeat string)) + +(defcustom helm-ls-git-auto-checkout nil + "Stash automatically uncommited changes before checking out a branch." + :type 'boolean) + +(defcustom helm-ls-git-log-max-commits "100" + "Max number of commits to show in git log (git log -n option). +NOTE: This reflects the number of candidates fetched and stored, not +the number of candidates displayed which is relative to +`helm-candidate-number-limit'. IOW if `helm-candidate-number-limit' +== 500 and `helm-ls-git-log-max-commits' == 600, only 500 candidates +will be displayed but if you search for a candidate which is in the +range 500/600 you will find it." + :type 'string) + +(defcustom helm-ls-git-delete-branch-on-remote nil + "Delete remote branch without asking when non nil. +This happen only when deleting a remote branch e.g. remotes/origin/foo." + :type 'boolean) + +(defcustom helm-ls-git-auto-refresh-at-eob t + "Increase git log by `window-height' lines when non nil. +When non nil this disable `helm-move-to-line-cycle-in-source'." + :type 'boolean) + + +(defgroup helm-ls-git-faces nil + "Customize the appearance of helm-files." + :prefix "helm-ls-git-" + :group 'helm-ls-git + :group 'helm-faces) + +(defface helm-ls-git-modified-not-staged-face + '((t :foreground "yellow")) + "Files which are modified but not yet staged.") + +(defface helm-ls-git-modified-and-staged-face + '((t :foreground "Goldenrod")) + "Files which are modified and already staged.") + +(defface helm-ls-git-renamed-modified-face + '((t :foreground "Goldenrod")) + "Files which are renamed or renamed and modified.") + +(defface helm-ls-git-untracked-face + '((t :foreground "red")) + "Files which are not yet tracked by git.") + +(defface helm-ls-git-added-copied-face + '((t :foreground "green")) + "Files which are newly added or copied.") + +(defface helm-ls-git-added-modified-face + '((t :foreground "blue")) + "Files which are newly added and have unstaged modifications.") + +(defface helm-ls-git-deleted-not-staged-face + '((t :foreground "Darkgoldenrod3")) + "Files which are deleted but not staged.") + +(defface helm-ls-git-deleted-and-staged-face + '((t :foreground "DimGray")) + "Files which are deleted and staged.") + +(defface helm-ls-git-conflict-face + '((t :foreground "MediumVioletRed")) + "Files which contain rebase/merge conflicts.") + +(defface helm-ls-git-branches-current + '((t :foreground "gold")) + "Color of the star prefixing current branch.") + +(defface helm-ls-git-branches-name + '((t :foreground "red")) + "Color of branches names.") + +(defface helm-ls-git-branches-name-current + '((t :foreground "green")) + "Color of current branch name.") + + +(defvar helm-ls-git-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-generic-files-map) + (define-key map (kbd "C-s") 'helm-ff-run-grep) + (define-key map (kbd "M-g g") 'helm-ls-git-run-grep) + (define-key map (kbd "C-c g") 'helm-ff-run-gid) + (define-key map (kbd "C-c i") 'helm-ls-git-ls-files-show-others) + (define-key map (kbd "M-e") 'helm-ls-git-run-switch-to-shell) + map)) + +(defvar helm-ls-git-buffer-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-buffer-map) + (define-key map (kbd "C-c i") 'helm-ls-git-ls-files-show-others) + map)) + +(defvar helm-ls-git-branches-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-c b") 'helm-ls-git-branches-toggle-show-all) + (define-key map (kbd "M-L") 'helm-ls-git-run-show-log) + (define-key map (kbd "C-c P") 'helm-ls-git-run-push) + (define-key map (kbd "C-c F") 'helm-ls-git-run-pull) + (define-key map (kbd "C-c f") 'helm-ls-git-run-fetch) + (define-key map (kbd "M-e") 'helm-ls-git-run-switch-to-shell) + map)) + +(defvar helm-ls-git-status-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-ls-git-map) + (define-key map (kbd "C-c c") 'helm-ls-git-run-stage-marked-and-commit) + (define-key map (kbd "C-c a") 'helm-ls-git-run-stage-marked-and-amend-commit) + (define-key map (kbd "C-c s") 'helm-ls-git-run-stage-files) + (define-key map (kbd "C-c e") 'helm-ls-git-run-stage-marked-and-extend-commit) + (define-key map (kbd "C-c z") 'helm-ls-git-run-stash) + (define-key map (kbd "C-c Z") 'helm-ls-git-run-stash-snapshot) + (define-key map (kbd "C-c R") 'helm-ls-git-run-status-revert-files) + (define-key map (kbd "M-e") 'helm-ls-git-run-switch-to-shell) + map)) + +(defvar helm-ls-git-help-message + "* Helm ls git + +** Tips + +*** Start helm-ls-git + +You can start with `helm-ls-git' but you can also use the generic +`helm-browse-project' which will use `helm-ls-git' if you are in +a git project (actually supported backends are git and hg though +helm-ls-hg is no more maintained). + +*** Git status command + +By default `helm-ls-git-status-command' is nil, +but you can set it if needed to `magit-status', `vc-dir' or whatever. + +However helm-ls-git provides most of what you need to basically +manage your git repo so you may not need to use another tool, +here an overview of its features: + +- Status of modified files +- List project branches +- List project files +- List project buffers +- List of Stashs +- Git log for each branches +- Create a new branch from current one +- Commit your changes from status source +- Rebase interactively +- Amend +- Diffs +- Pull and fetch +- Push +- Stash +- Revert +- Reset +- Format patches +- Git AM +- Cherry pick + +etc... + +Of course all these features are not enhanced as what you could +find in a Git specialized tool like Magit but it may fit most of +your needs. + +*** Git log + +From branches source, you can launch git log. With a numeric +prefix arg specify the number of commits to show, once you are in +git log and you want more commits, use a numeric prefix arg with +\\\\[helm-refresh] to specify the number of commits to show. + +When scrolling down with an empty pattern, helm can increase +automatically the number of candidates displayed when you reach +end of buffer if `helm-ls-git-auto-refresh-at-eob' is non nil. + +NOTE: When searching in git log, Helm search in the candidates +computed initially, this mean that when you have 100 candidates +displayed (see `helm-ls-git-log-max-commits') and you search for +a commit containing \"foo\", this commit will not be found if it +is located in the 101 commit which is not displayed. So if you +don't find something you are looking for, increase the number of +commits with \\\\[universal-argument] \\\\[helm-refresh]. + +**** Specify a range of commits + +Once you are in Git log you can specify with 2 marked +candidates a range of commits, specifying more than two marked +candidate for actions accepting only ranges will fail. When +specifying a range of commits, the top commit will be included in +range whereas the bottom commit will not be included, e.g. if you +mark commit-2 and commit-5, and use the format-patch action, git +will make 01-commit-4.patch, 02-commit-3.patch, and +03-commit-2.patch files taking care of naming files in the +reverse order for applying patches later, commit-5 beeing +excluded. + +NOTE: For commodity, commits are specified as short hash for all actions, witch +may clash if more than one commit have the same short ID (rare +but may happen), you should have an error in such case. + +**** Apply patches from one branch to current + +You can apply patches from one branch to current +branch using git AM action. +Patches are specified as a range of commits, see [[Specify a range of commits][Specify a range of commits]]. + +**** Persistent action in git log + +Persistent action in git log shows diff of selected commit, if you +want to always show diff while moving from one commit to the +other use follow-mode (C-c C-f). + +*** Git commit + +Commits will be done using emacsclient as GIT_EDITOR, with +major-mode `helm-ls-git-commmit-mode' which provide following commands: + +\\ +|Keys|Description +|-------------+--------------| +|\\[helm-ls-git-server-edit]|Exit when done +|\\[helm-ls-git-server-edit-abort]|Abort + +If you want to specify another author, use a prefix arg when +calling commit action, you will be prompted for author name and +email. + +NOTE: This mode is based on diff-mode, this to show a colorized +diff of your commit, you can use any regular emacs editing +commands from there. + +*** Git rebase + +helm-ls-git provide two rebase actions, one that run +interactively from git log source and one that work +non-interactively from branches source. With the former you can +rebase interactively from a given commit you selected from git log +and this ONLY for current branch, once done you can rebase one +branch into the other from branches source. This is one workflow +that helm-ls-git provide, other workflows may not work, so for +more complex usage switch to command line or a more enhaced tool +like Magit. For editing the first file git rebase use for +rebasing (\"git-rebase-todo\") helm-ls-git use a major-mode +called `helm-ls-git-rebase-todo-mode' which provide several commands: + +\\ +|Keys|Description +|-------------+--------------| +|p|pick +|r|reword +|e|edit +|s|squash +|f|fixup +|x|exec +|d|drop +|\\[helm-ls-git-rebase-todo-move-down]|Move line down +|\\[helm-ls-git-rebase-todo-move-up]|Move line up +|\\[helm-ls-git-server-edit]|Exit when done +|\\[helm-ls-git-server-edit-abort]|Abort + +*** Git grep usage + +The behavior is not exactly the same as what you have when you +launch git-grep from `helm-find-files', here in what it differ: + +1) The prefix arg allow to grep only the `default-directory' whereas +with `helm-find-files' the prefix arg allow browsing the whole repo. +So with `helm-ls-git' the default is to grep the whole repo. + +2) With `helm-ls-git', because you have the whole list of files of the repo +you can mark some of the files to grep only those, if no files are marked grep +the whole repo or the files under current directory depending of prefix arg. + +NOTE: The previous behavior was prompting user for the file +extensions to grep, this is non sense because we have here the +whole list of files (recursive) of current repo and not only the +file under current directory, so we have better time +selectionning the files we want to grep. + +**** Grep a subdirectory of current repository. + +Switch to `helm-find-files' with `C-x C-f', navigate to your directory +and launch git-grep from there. + +*** Problem with unicode filenames (chinese etc...) + +See docstring of `helm-ls-git-ls-switches'. + +** Commands +\\ +|Keys|Description +|-----------+----------| +|\\[helm-ls-git-run-grep]|Run git-grep. +|\\[helm-ff-run-gid]|Run Gid. +|\\[helm-ls-git-ls-files-show-others]|Toggle tracked/non tracked files view. +|\\[helm-ls-git-run-switch-to-shell]|Switch to shell +|\\ +|\\[helm-ff-run-toggle-basename]|Toggle basename. +|\\[helm-ff-run-zgrep]|Run zgrep. +|\\[helm-ff-run-pdfgrep]|Run Pdfgrep on marked files. +|\\[helm-ff-run-copy-file]|Copy file(s) +|\\[helm-ff-run-rename-file]|Rename file(s). +|\\[helm-ff-run-symlink-file]|Symlink file(s). +|\\[helm-ff-run-hardlink-file]|Hardlink file(s). +|\\[helm-ff-run-delete-file]|Delete file(s). +|\\[helm-ff-run-byte-compile-file]|Byte compile file(s) (C-u load) (elisp). +|\\[helm-ff-run-load-file]|Load file(s) (elisp). +|\\[helm-ff-run-ediff-file]|Ediff file. +|\\[helm-ff-run-ediff-merge-file]|Ediff merge file. +|\\[helm-ff-run-switch-other-window]|Switch other window. +|\\[helm-ff-properties-persistent]|Show file properties. +|\\[helm-ff-run-etags]|Run etags (C-u use tap, C-u C-u reload DB). +|\\[helm-yank-text-at-point]|Yank text at point. +|\\[helm-ff-run-open-file-externally]|Open file with external program (C-u to choose). +|\\[helm-ff-run-open-file-with-default-tool]|Open file externally with default tool. +|\\[helm-ff-run-insert-org-link]|Insert org link.") + + + +;; Append visited files from `helm-source-ls-git' to `file-name-history'. +(add-to-list 'helm-files-save-history-extra-sources "Git files") + + +(defvar helm-ls-git-log-file nil) ; Set it for debugging. + + +(defun helm-ls-git-list-files () + (when (and helm-ls-git-log-file + (file-exists-p helm-ls-git-log-file)) + (delete-file helm-ls-git-log-file)) + ;; `helm-resume' will use the local value of `default-directory' + ;; in `helm-buffer' as value for `default-directory'. + (helm-aif (helm-ls-git-root-dir) + (with-helm-default-directory it + (with-output-to-string + (with-current-buffer standard-output + (apply #'process-file + "git" + nil (list t helm-ls-git-log-file) nil + helm-ls-git-ls-switches)))) + ;; Return empty string to give to `split-string' + ;; in `helm-ls-git-init'. + "")) + +(defun helm-ls-git-ls-files-show-others () + "Toggle view of tracked/non tracked files." + (interactive) + (with-helm-alive-p + (setq helm-ls-git-ls-switches + (if (member "-o" helm-ls-git-ls-switches) + (remove "-o" helm-ls-git-ls-switches) + (helm-append-at-nth helm-ls-git-ls-switches "-o" 1))) + (helm-force-update))) +(put 'helm-ls-git-ls-files-show-others 'no-helm-mx t) + +(cl-defun helm-ls-git-root-dir (&optional (directory default-directory)) + (locate-dominating-file directory ".git")) + +(defun helm-ls-git-not-inside-git-repo () + (not (helm-ls-git-root-dir))) + +(defun helm-ls-git-transformer (candidates _source) + (cl-loop with root = (helm-ls-git-root-dir) + with untracking = (member "-o" helm-ls-git-ls-switches) + for file in candidates + for abs = (expand-file-name file root) + for disp = (if (and helm-ff-transformer-show-only-basename + (not (string-match "[.]\\{1,2\\}\\'" file))) + (helm-basename file) file) + collect + (cons (propertize (if untracking (concat "? " disp) disp) + 'face (if untracking + 'helm-ls-git-untracked-face + 'helm-ff-file)) + abs))) + +(defun helm-ls-git-sort-fn (candidates _source) + "Transformer for sorting candidates." + (helm-ff-sort-candidates candidates nil)) + +(defun helm-ls-git-init () + (let ((data (cl-loop with root = (helm-ls-git-root-dir) + for c in (split-string (helm-ls-git-list-files) "\n" t) + collect (if (eq helm-ls-git-show-abs-or-relative 'relative) + c (expand-file-name c root))))) + (when (null data) + (setq data + (if helm-ls-git-log-file + (with-current-buffer + (find-file-noselect helm-ls-git-log-file) + (prog1 + (buffer-substring-no-properties + (point-min) (point-max)) + (kill-buffer))) + data))) + (helm-init-candidates-in-buffer 'global data))) + +(defvar helm-ls-git--current-branch nil) +(defun helm-ls-git--branch () + (or helm-ls-git--current-branch + (with-temp-buffer + (let ((ret (process-file "git" nil t nil "symbolic-ref" "--short" "HEAD"))) + ;; Use sha of HEAD when branch name is missing. + (unless (zerop ret) + (erase-buffer) + (process-file "git" nil t nil "rev-parse" "--short" "HEAD"))) + ;; We use here (goto-char (point-min)) instead of (point-min) + ;; to not endup with a ^J control char at end of branch name. + (buffer-substring-no-properties (goto-char (point-min)) + (line-end-position))))) + +(defun helm-ls-git-header-name (name) + (format "%s (%s)" name (helm-ls-git--branch))) + +(defun helm-ls-git-actions-list (&optional actions) + (helm-append-at-nth + actions + (helm-make-actions (lambda () + (and helm-ls-git-status-command "Git status")) + (lambda (_candidate) + (funcall helm-ls-git-status-command + (helm-default-directory))) + "Switch to shell" 'helm-ls-git-switch-to-shell + "Git grep files (`C-u' only current directory)" + 'helm-ls-git-grep + "Gid" 'helm-ff-gid) + 1)) + +(defun helm-ls-git-match-part (candidate) + (if (with-helm-buffer helm-ff-transformer-show-only-basename) + (helm-basename candidate) + candidate)) + +(defclass helm-ls-git-source (helm-source-in-buffer) + ((header-name :initform 'helm-ls-git-header-name) + (init :initform 'helm-ls-git-init) + (cleanup :initform (lambda () + (setq helm-ls-git-ls-switches (remove "-o" helm-ls-git-ls-switches)))) + (update :initform (lambda () + (helm-set-local-variable + 'helm-ls-git--current-branch nil))) + (keymap :initform 'helm-ls-git-map) + (help-message :initform 'helm-ls-git-help-message) + (match-part :initform 'helm-ls-git-match-part) + (filtered-candidate-transformer + :initform '(helm-ls-git-transformer + helm-ls-git-sort-fn)) + (action-transformer :initform 'helm-transform-file-load-el) + (group :initform 'helm-ls-git))) + +(defclass helm-ls-git-status-source (helm-source-in-buffer) + ((header-name :initform 'helm-ls-git-header-name) + (init :initform + (lambda () + (helm-init-candidates-in-buffer 'global + (helm-ls-git-status)))) + (keymap :initform 'helm-ls-git-status-map) + (filtered-candidate-transformer :initform 'helm-ls-git-status-transformer) + (persistent-action :initform 'helm-ls-git-diff) + (persistent-help :initform "Diff") + (help-message :initform 'helm-ls-git-help-message) + (action-transformer :initform 'helm-ls-git-status-action-transformer) + (action :initform + (helm-make-actions + "Find file" 'helm-find-many-files + (lambda () + (and helm-ls-git-status-command "Git status")) + (lambda (_candidate) + (funcall helm-ls-git-status-command + (helm-default-directory))) + "Switch to shell" #'helm-ls-git-switch-to-shell)) + (group :initform 'helm-ls-git))) + +(defun helm-ls-git-revert-buffers-in-project () + (cl-loop for buf in (helm-browse-project-get-buffers (helm-ls-git-root-dir)) + for fname = (buffer-file-name (get-buffer buf)) + when (and fname (file-exists-p fname)) + do (with-current-buffer buf (revert-buffer nil t)))) + +(defun helm-ls-git-diff (candidate) + (let ((default-directory + (expand-file-name (file-name-directory candidate))) + (win (get-buffer-window "*vc-diff*" 'visible))) + (if (and win + (eq last-command 'helm-execute-persistent-action)) + (with-helm-window + (kill-buffer "*vc-diff*") + (if (and helm-persistent-action-display-window + (window-dedicated-p (next-window win 1))) + (delete-window helm-persistent-action-display-window) + (set-window-buffer win helm-current-buffer))) + (when (buffer-live-p (get-buffer "*vc-diff*")) + (kill-buffer "*vc-diff*")) + (vc-git-diff (helm-marked-candidates)) + (pop-to-buffer "*vc-diff*") + (diff-mode)))) + +;;; Git grep +;; +(defun helm-ls-git-grep (_candidate) + (let* ((helm-grep-default-command helm-ls-git-grep-command) + helm-grep-default-recurse-command + (mkd (helm-marked-candidates)) + (files (if (cdr mkd) mkd '(""))) + ;; Expand filename of each candidate with the git root dir. + ;; The filename will be in the help-echo prop. + (helm-grep-default-directory-fn 'helm-ls-git-root-dir) + ;; set `helm-ff-default-directory' to the root of project. + (helm-ff-default-directory (if helm-current-prefix-arg + default-directory + (helm-ls-git-root-dir)))) + (helm-do-grep-1 files))) + +(defun helm-ls-git-run-grep () + "Run Git Grep action from helm-ls-git." + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ls-git-grep))) +(put 'helm-ls-git-run-grep 'no-helm-mx t) + +;;; Git log +;; +(defvar helm-ls-git-log--last-log "" + "Cache for git log during the helm-ls-git-log session.") +(defvar helm-ls-git-log--last-number-commits "0" + "The number of commits actually displayed in this session.") +(defvar helm-ls-git-log--is-full nil) + +(defun helm-ls-git-auto-refresh-and-scroll () + "Increase git log by `window-height' lines." + (with-helm-window + (let ((wlines (window-height))) + (when (and (helm-end-of-source-p) + (string= helm-pattern "") + (eq this-command 'helm-next-line)) + (let ((current-prefix-arg wlines)) + (with-helm-after-update-hook + (setq unread-command-events nil)) + (helm-force-update)))))) + +(defun helm-ls-git-log (&optional branch num) + "Run git log branch -n num and return the resulting string." + (when (and branch (string-match "->" branch)) + (setq branch (car (last (split-string branch "->"))))) + (let* ((last-number-commits (string-to-number + helm-ls-git-log--last-number-commits)) + (commits-number (if num + (number-to-string + (if (> num last-number-commits) + (- num last-number-commits) + num)) + helm-ls-git-log-max-commits)) + (switches `("log" "--color" + "--date=local" + "--pretty=format:%C(yellow)%h%Creset \ + %C(green)%ad%Creset %<(60,trunc)%s %Cred%an%Creset %C(auto)%d%Creset" + "-n" ,commits-number + "--skip" ,(helm-stringify + helm-ls-git-log--last-number-commits) + ,(or branch ""))) + output) + (unless helm-ls-git-log--is-full + (setq helm-ls-git-log--last-number-commits + (number-to-string + (+ last-number-commits + (string-to-number commits-number)))) + (helm-set-attr 'candidate-number-limit + (string-to-number helm-ls-git-log--last-number-commits)) + (message "Git log on `%s' updating to `%s' commits..." + branch helm-ls-git-log--last-number-commits) + (with-helm-default-directory (helm-ls-git-root-dir) + (setq helm-ls-git-log--last-log + (concat helm-ls-git-log--last-log + ;; Avoid adding a newline at first run. + (unless (zerop last-number-commits) "\n") + (setq output + (with-output-to-string + (with-current-buffer standard-output + (apply #'process-file "git" nil t nil switches))))))) + (when (and (stringp output) (string= output "")) + (setq helm-ls-git-log--is-full t))) + (if helm-ls-git-log--is-full + (message "No more commits on `%s' branch" branch) + (message "Git log on `%s' updating to `%s' commits done" + branch helm-ls-git-log--last-number-commits)) + helm-ls-git-log--last-log)) + +(defun helm-ls-git-show-log (branch) + (let ((name (replace-regexp-in-string "[ *]" "" branch)) + ;; Use helm-current-prefix-arg only on first call + ;; of init function. + (prefarg helm-current-prefix-arg)) + (when (buffer-live-p "*git log diff*") + (kill-buffer "*git log diff*")) + (helm :sources (helm-build-in-buffer-source "Git log" + :header-name (lambda (sname) (format "%s (%s)" sname name)) + :init (lambda () + (helm-init-candidates-in-buffer 'global + (helm-ls-git-log name (helm-aif (or prefarg + ;; for force-update. + current-prefix-arg) + (prefix-numeric-value it)))) + (setq prefarg nil)) + :get-line 'buffer-substring + :marked-with-props 'withprop + :cleanup (lambda () + (setq helm-ls-git-log--last-log "" + helm-ls-git-log--last-number-commits "0" + helm-ls-git-log--is-full nil)) + :help-message 'helm-ls-git-help-message + :action '(("Show commit" . helm-ls-git-log-show-commit) + ("Find file at rev" . helm-ls-git-log-find-file) + ("Ediff file at revs" . helm-ls-git-ediff-file-at-revs) + ("Kill rev as short hash" . + helm-ls-git-log-kill-short-hash) + ("Kill rev as long hash" . + helm-ls-git-log-kill-long-hash) + ("Cherry-pick" . helm-ls-git-log-cherry-pick) + ("Format patches (range between 2 marked)" . helm-ls-git-log-format-patch) + ("Git am (range between 2 marked)" . helm-ls-git-log-am) + ("Git interactive rebase" . helm-ls-git-log-interactive-rebase) + ("Hard reset" . helm-ls-git-log-hard-reset) + ("Soft reset" . helm-ls-git-log-soft-reset) + ("Git revert" . helm-ls-git-log-revert)) + :candidate-transformer + (lambda (candidates) + (cl-loop for c in candidates + collect (ansi-color-apply c))) + :group 'helm-ls-git) + :move-selection-before-hook (and helm-ls-git-auto-refresh-at-eob + 'helm-ls-git-auto-refresh-and-scroll) + :move-to-line-cycle-in-source (unless helm-ls-git-auto-refresh-at-eob + (default-value 'helm-move-to-line-cycle-in-source)) + :buffer "*helm-ls-git log*"))) + +(defun helm-ls-git-log-show-commit-1 (candidate) + (let ((sha (car (split-string candidate)))) + (with-current-buffer (get-buffer-create "*git log diff*") + (let ((inhibit-read-only t)) + (erase-buffer) + (insert (with-helm-default-directory (helm-ls-git-root-dir + (helm-default-directory)) + (with-output-to-string + (with-current-buffer standard-output + (process-file + "git" nil (list t helm-ls-git-log-file) nil + "show" "-p" sha))))) + (goto-char (point-min)) + (diff-mode)) + (display-buffer (current-buffer))))) + +(defun helm-ls-git-log-kill-short-hash (candidate) + (kill-new (car (split-string candidate)))) + +(defun helm-ls-git-log-kill-long-hash (_candidate) + (helm-ls-git-log-get-long-hash 'kill)) + +(defun helm-ls-git-log-get-long-hash (&optional kill) + (with-helm-buffer + (let* ((cand (helm-get-selection nil 'withprop)) + (short-hash (car (split-string cand))) + str) + (setq str + (replace-regexp-in-string + "\n" "" + (shell-command-to-string + (format "git rev-parse --default %s" short-hash)))) + (if kill (kill-new str) str)))) + +(defun helm-ls-git-log-format-patch (_candidate) + (helm-ls-git-log-format-patch-1)) + +(defun helm-ls-git-log-am (_candidate) + (helm-ls-git-log-format-patch-1 'am)) + +(defun helm-ls-git-log-format-patch-1 (&optional am) + (let ((commits (cl-loop for c in (helm-marked-candidates) + collect (car (split-string c)))) + range switches) + (cond ((= 2 (length commits)) + ;; Using "..." makes a range from top marked (included) to + ;; bottom marked (not included) e.g. when we have commit-2 + ;; marked and commit-5 marked the serie of patches will be + ;; 01-commit-4.patch, 02-commit-3.patch, 03-commit-2.patch, + ;; git taking care of numering the patch in reversed order + ;; for further apply. + (setq range (mapconcat 'identity (sort commits #'string-lessp) "...") + switches `("format-patch" ,range))) + ((not (cdr commits)) + (setq range (car commits) + switches `("format-patch" "-1" ,range))) + ((> (length commits) 2) + (error "Specify either a single commit or a range with only two marked commits"))) + (with-helm-default-directory (helm-ls-git-root-dir + (helm-default-directory)) + (if am + (with-current-buffer-window "*git am*" '(display-buffer-below-selected + (window-height . fit-window-to-buffer) + (preserve-size . (nil . t))) + nil + (process-file-shell-command + (format "git %s | git am -3 -k" + (mapconcat 'identity (helm-append-at-nth switches '("-k --stdout") 1) " ")) + nil t t)) + (apply #'process-file "git" nil "*git format-patch*" nil switches))))) + +(defun helm-ls-git-log-reset-1 (hard-or-soft) + (let ((rev (car (split-string (helm-get-selection nil 'withprop)))) + (arg (cl-case hard-or-soft + (hard "--hard") + (soft "--soft")))) + (with-helm-default-directory (helm-ls-git-root-dir + (helm-default-directory)) + (when (and (y-or-n-p (format "%s reset to <%s>?" + (capitalize (symbol-name hard-or-soft)) rev)) + (= (process-file "git" nil nil nil "reset" arg rev) 0)) + (message "Now at `%s'" (helm-ls-git-oneline-log + (helm-ls-git--branch))))))) + +(defun helm-ls-git-log-hard-reset (_candidate) + (helm-ls-git-log-reset-1 'hard)) + +(defun helm-ls-git-log-soft-reset (_candidate) + (helm-ls-git-log-reset-1 'soft)) + +(defun helm-ls-git-log-revert (_candidate) + (let ((rev (car (split-string (helm-get-selection nil 'withprop))))) + (helm-ls-git-with-editor "revert" rev))) + +(defun helm-ls-git-log-revert-continue (_candidate) + (helm-ls-git-with-editor "revert" "--continue")) + +(defun helm-ls-git-log-revert-abort (_candidate) + (with-helm-default-directory (helm-default-directory) + (process-file "git" nil nil nil "revert" "--abort"))) + +(defun helm-ls-git-log-show-commit (candidate) + (if (and (eq last-command 'helm-execute-persistent-action) + (get-buffer-window "*git log diff*" 'visible)) + (kill-buffer "*git log diff*") + (helm-ls-git-log-show-commit-1 candidate))) + +(defun helm-ls-git-log-find-file-1 (candidate &optional file buffer-only) + (with-helm-default-directory (helm-default-directory) + (let* ((rev (substring-no-properties (car (split-string candidate)))) + (file (or file + (helm :sources (helm-build-in-buffer-source "Git cat-file" + :data (helm-ls-git-list-files)) + :buffer "*helm-ls-git cat-file*"))) + ;; Git command line needs 1234:lisp/foo. + (fname (concat rev ":" file)) + ;; Whereas the file created will be lisp/1234:foo. + (path (expand-file-name + (concat (helm-basedir file) rev ":" (helm-basename file)) + (helm-ls-git-root-dir))) + str status buf) + (setq str (with-output-to-string + (with-current-buffer standard-output + (setq status (process-file + "git" nil t nil "cat-file" "-p" fname))))) + (if (zerop status) + (progn + (with-current-buffer (setq buf (find-file-noselect path)) + (insert str) + ;; Prevent kill-buffer asking after ediff ends. + (set-buffer-modified-p (not buffer-only)) + (goto-char (point-min)) + (unless buffer-only + (save-buffer))) + (if buffer-only buf (find-file path))) + (error "No such file %s at %s" file rev))))) + +(defun helm-ls-git-log-find-file (candidate) + (helm-ls-git-log-find-file-1 candidate)) + +(defun helm-ls-git-ediff-file-at-revs (_candidate) + (let* ((marked (helm-marked-candidates)) + (tip (unless (cdr marked) + (helm-ls-git-oneline-log (helm-ls-git--branch)))) + buf1 buf2 file) + (setq file (helm :sources (helm-build-in-buffer-source "Git cat-file" + :data (helm-ls-git-list-files)) + :buffer "*helm-ls-git cat-file*")) + (setq buf1 (helm-ls-git-log-find-file-1 (or tip (car marked)) file :buffer-only) + buf2 (helm-ls-git-log-find-file-1 (if tip (car marked) (cadr marked)) + file :buffer-only)) + (cl-assert (not (eql buf1 buf2)) + nil (format "Can't ediff file `%s' at same revision" file)) + (ediff-buffers buf1 buf2))) + +(defun helm-ls-git-log-cherry-pick (_candidate) + (let* ((commits (cl-loop for c in (helm-marked-candidates) + collect (car (split-string c)) into revs + finally return (sort revs #'string-greaterp)))) + (with-helm-default-directory (helm-ls-git-root-dir + (helm-default-directory)) + (with-current-buffer-window "*git cherry-pick*" '(display-buffer-below-selected + (window-height . fit-window-to-buffer) + (preserve-size . (nil . t))) + nil + (apply #'process-file "git" nil "*git cherry-pick*" nil "cherry-pick" commits))))) + +(defun helm-ls-git-cherry-pick-abort (_candidate) + (with-helm-default-directory (helm-default-directory) + (process-file "git" nil nil nil "cherry-pick" "--abort"))) + +(defun helm-ls-git-rebase-abort (_candidate) + (with-helm-default-directory (helm-default-directory) + (process-file "git" nil nil nil "rebase" "--abort"))) + +(defun helm-ls-git-merge-abort (_candidate) + (with-helm-default-directory (helm-default-directory) + (process-file "git" nil nil nil "merge" "--abort"))) + +(defun helm-ls-git-rebase-continue (_candidate) + (helm-ls-git-with-editor "rebase" "--continue")) + +(defun helm-ls-git-cherry-pick-continue (_candidate) + (helm-ls-git-with-editor "cherry-pick" "--continue")) + +(defun helm-ls-git-am-continue (_candidate) + (helm-ls-git-with-editor "am" "--continue")) + +(defun helm-ls-git-merge-continue (_candidate) + (helm-ls-git-with-editor "merge" "--continue")) + +(defun helm-ls-git-rebase-running-p () + (with-helm-buffer + (with-helm-default-directory (helm-ls-git-root-dir) + (let ((git-dir (expand-file-name ".git" default-directory))) + (or (file-exists-p (expand-file-name "rebase-merge" git-dir)) + (file-exists-p (expand-file-name "rebase-apply/onto" git-dir))))))) + +(defun helm-ls-git-log-interactive-rebase (_candidate) + "Rebase interactively current branch from CANDIDATE. +Where CANDIDATE is a candidate from git log source and its commit +object will be passed git rebase i.e. git rebase -i ." + (if (helm-ls-git-rebase-running-p) + (if (y-or-n-p "A rebase is already running, continue ?") + (helm-ls-git-rebase-continue nil) + (helm-ls-git-rebase-abort nil)) + (let ((hash (helm-ls-git-log-get-long-hash))) + (helm-ls-git-with-editor "rebase" "-i" hash)))) + +(defun helm-ls-git-run-show-log () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action #'helm-ls-git-show-log))) +(put 'helm-ls-git-run-show-log 'no-helm-mx t) + + +;;; Git branch basic management +;; +(defvar helm-ls-git-branches-show-all nil) + +(defun helm-ls-git-collect-branches (&optional arg) + (helm-aif (helm-ls-git-root-dir) + (with-helm-default-directory it + (with-output-to-string + (with-current-buffer standard-output + (cond ((null arg) + ;; Only local branches. + (apply #'process-file "git" nil t nil '("branch"))) + (t + (apply #'process-file "git" nil t nil '("branch" "-a"))))))) + "")) + +(defun helm-ls-git-branches-toggle-show-all () + (interactive) + (setq helm-ls-git-branches-show-all (not helm-ls-git-branches-show-all)) + (helm-force-update)) +(put 'helm-ls-git-branches-toggle-show-all 'no-helm-mx t) + +(defun helm-ls-git-checkout (candidate) + (let ((default-directory (helm-default-directory))) + (if (and helm-ls-git-auto-checkout + (helm-ls-git-modified-p t)) + (helm-ls-git-stash-1 "") + (cl-assert (not (helm-ls-git-modified-p t)) + nil "Please commit or stash your changes before proceeding")) + (with-helm-default-directory (helm-ls-git-root-dir) + (let* ((branch (replace-regexp-in-string "[ ]" "" candidate)) + (real (replace-regexp-in-string "\\`\\*" "" branch))) + (if (string-match "\\`[*]" candidate) + (message "Already on %s branch" real) + (let* ((switches (if (string-match "\\`[Rr]emotes" real) + `("checkout" "-b" + ,(car (last (split-string real "/" t))) + "-t" ,real) + `("checkout" ,real))) + (status (apply #'process-file "git" + nil nil nil + switches))) + (if (= status 0) + (progn (message "Switched to %s branch" real) + (helm-ls-git-revert-buffers-in-project)) + (error "Process exit with non zero status")))))))) + +(defun helm-ls-git-branches-create (candidate) + (with-helm-default-directory (helm-ls-git-root-dir) + (process-file "git" nil nil nil + "checkout" "-B" candidate "-t" (helm-ls-git--branch)))) + +(defun helm-ls-git-branches-delete (candidate) + (with-helm-default-directory (helm-ls-git-root-dir) + (let* ((branch (helm-ls-git-normalize-branch-name candidate)) + (remote (string-match "remotes/" candidate)) + (switches (if remote + `("-D" "-r" ,branch) + `("-D" ,branch)))) + (cl-assert (not (string-match "\\`[*]" candidate)) + nil "Can't delete current branch") + (if (= (apply #'process-file "git" nil nil nil "branch" switches) 0) + (progn + (when (and remote + (or helm-ls-git-delete-branch-on-remote + (y-or-n-p (format "Delete `%s' branch on remote as well ?" branch)))) + (let ((proc (start-file-process + "git" "*helm-ls-git branch delete*" + "git" "push" "origin" "--delete" + (car (last (split-string branch "/" t)))))) + (set-process-sentinel + proc + (lambda (_process event) + (if (string= event "finished\n") + (message "Remote branch %s deleted successfully" branch) + (message "Failed to delete remote branch %s" branch)))))) + (message "Local branch %s deleted successfully" branch)) + (message "failed to delete branch %s" branch))))) + +(defun helm-ls-git-normalize-branch-names (names) + (cl-loop for name in names collect + (helm-ls-git-normalize-branch-name name))) + +(defun helm-ls-git-normalize-branch-name (name) + (helm-aand name + (replace-regexp-in-string " " "" it) + (replace-regexp-in-string "[*]" "" it) + (replace-regexp-in-string "remotes/" "" it))) + +(defun helm-ls-git-delete-marked-branches (_candidate) + (let* ((branches (helm-marked-candidates)) + (bnames (helm-ls-git-normalize-branch-names branches)) + (display-buf "*helm-ls-git deleted branches*")) + (with-helm-display-marked-candidates + display-buf bnames + (when (y-or-n-p "Really delete branche(s) ?") + (cl-loop for b in branches + do (helm-ls-git-branches-delete b)))))) + +(defun helm-ls-git-modified-p (&optional ignore-untracked) + (with-helm-default-directory (helm-ls-git-root-dir) + (not (string= (helm-ls-git-status ignore-untracked) "")))) + +(defun helm-ls-git-branches-merge (candidate) + (with-helm-default-directory (helm-ls-git-root-dir) + (let ((branch (replace-regexp-in-string "[ ]" "" candidate)) + (current (helm-ls-git--branch))) + (when (y-or-n-p (format "Merge branch %s into %s?" branch current)) + (if (= (process-file "git" nil nil nil "merge" branch) 0) + (progn (message "Branch %s merged successfully into %s" branch current) + (helm-ls-git-revert-buffers-in-project)) + (message "failed to merge branch %s" branch)))))) + +(defvar helm-ls-git-create-branch-source + (helm-build-dummy-source "Create branch" + :filtered-candidate-transformer + (lambda (_candidates _source) + (list (or (and (not (string= helm-pattern "")) + helm-pattern) + "Enter new branch name"))) + :action 'helm-ls-git-branches-create)) + +(defun helm-ls-git-oneline-log (branch) + (let ((output (with-output-to-string + (with-current-buffer standard-output + (process-file + "git" nil t nil + "log" (car (split-string branch "->")) + "-n" "1" "--oneline"))))) + (replace-regexp-in-string "\n" "" output))) + +(defun helm-ls-git-branches-transformer (candidates) + (cl-loop for c in candidates + for maxlen = (cl-loop for i in candidates maximize (length i)) + for name = (replace-regexp-in-string "[ *]" "" c) + for log = (helm-ls-git-oneline-log name) + for disp = (if (string-match "\\`\\([*]\\)\\(.*\\)" c) + (format "%s%s: %s%s" + (propertize (match-string 1 c) + 'face 'helm-ls-git-branches-current) + (propertize (match-string 2 c) + 'face 'helm-ls-git-branches-name-current) + (make-string (- maxlen (length c)) ? ) + log) + (format "%s: %s%s" + (propertize c 'face 'helm-ls-git-branches-name) + (make-string (- maxlen (length c)) ? ) + log)) + collect (cons disp c))) + +(defun helm-ls-git-push (_candidate) + (with-helm-default-directory (helm-default-directory) + (let ((branch (helm-ls-git--branch)) + pr tm) + (when (y-or-n-p (format "Really push branch `%s' on remote ?" branch)) + (setq pr (make-progress-reporter + (format "Pushing branch `%s' on remote..." branch)) + tm (run-at-time 1 0.1 #'progress-reporter-update pr)) + (let ((proc (start-file-process + "git" "*helm-ls-git push*" "git" "push" "origin" "HEAD"))) + (set-process-sentinel + proc (lambda (_process event) + (if (string= event "finished\n") + (progress-reporter-done pr) + (message "Failed to push branch `%s' on remote" branch)) + (cancel-timer tm)))))))) + +(defun helm-ls-git-run-push () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action #'helm-ls-git-push))) +(put 'helm-ls-git-run-push 'no-helm-mx t) + +(defun helm-ls-git-remotes () + (with-helm-default-directory (helm-default-directory) + (with-output-to-string + (with-current-buffer standard-output + (process-file "git" nil t nil "remote"))))) + +(defun helm-ls-git--pull-or-fetch (command &rest args) + (with-helm-default-directory (helm-default-directory) + (let* ((remote "origin") + (pcommand (capitalize command)) + (branch (helm-ls-git--branch)) + ;; A `C-g' in helm-comp-read will quit function as well. + (switches (if current-prefix-arg + (append (list command) + args + (list (setq remote + (helm-comp-read + (format "%s from: " pcommand) + (split-string + (helm-ls-git-remotes) + "\n") + :allow-nest t))) + (list branch)) + (append (list command) args))) + (pr (make-progress-reporter + (format "%sing from `%s/%s'..." pcommand remote branch))) + (tm (run-at-time 1 0.1 (lambda () (progress-reporter-update pr)))) + process-connection-type + proc) + (setq proc (apply #'helm-ls-git-with-editor switches)) + (with-current-buffer (process-buffer proc) (erase-buffer)) + (set-process-filter proc 'helm-ls-git--filter-process) + (save-selected-window + (display-buffer (process-buffer proc))) + (set-process-sentinel + proc (lambda (process event) + (let ((status (process-exit-status process))) + (if (string= event "finished\n") + (progn (progress-reporter-done pr) + (when helm-alive-p + (with-helm-window (helm-force-update "^\\*")))) + (message "Failed %sing from %s: Process exited with code %s" + command remote status)) + (and tm (cancel-timer tm)))))))) + +(defun helm-ls-git--filter-process (proc string) + (when (buffer-live-p (process-buffer proc)) + (with-current-buffer (process-buffer proc) + (let ((moving (= (point) (process-mark proc)))) + (save-excursion + ;; Insert the text, advancing the process marker. + (goto-char (process-mark proc)) + ;; Ignore git progress reporter lines. + (unless (string-match-p " \\'" string) + (insert string)) + (set-marker (process-mark proc) (point))) + (when moving + (goto-char (process-mark proc))))))) + +(defun helm-ls-git-pull (_candidate) + (helm-ls-git--pull-or-fetch "pull" "--stat")) + +(defun helm-ls-git-fetch (_candidate) + (helm-ls-git--pull-or-fetch "fetch")) + +(defun helm-ls-git-run-pull () + (interactive) + (with-helm-alive-p + (helm-set-attr 'pull 'helm-ls-git-pull) + (helm-execute-persistent-action 'pull))) +(put 'helm-ls-git-run-pull 'no-helm-mx t) + +(defun helm-ls-git-run-fetch () + (interactive) + (with-helm-alive-p + (helm-set-attr 'fetch '(helm-ls-git-fetch . never-split)) + (helm-execute-persistent-action 'fetch))) +(put 'helm-ls-git-run-fetch 'no-helm-mx t) + +(defun helm-ls-git-branch-rebase (candidate) + "Rebase CANDIDATE branch on current branch." + (if (helm-ls-git-rebase-running-p) + (if (y-or-n-p "A rebase is already running, continue ?") + (helm-ls-git-rebase-continue nil) + (helm-ls-git-rebase-abort nil)) + (let ((branch (helm-ls-git-normalize-branch-name candidate)) + (current (helm-ls-git--branch))) + (when (y-or-n-p (format "Rebase branch %s from %s?" current branch)) + (if (= (process-file "git" nil nil nil "rebase" branch) 0) + (progn (message "Branch %s rebased successfully from %s" current branch) + (helm-ls-git-revert-buffers-in-project)) + (message "failed to rebase from branch %s, try to abort rebasing or resolve conflicts" branch)))))) + +(defvar helm-ls-git-branches-source + (helm-build-in-buffer-source "Git branches" + :init (lambda () + (let ((data (helm-ls-git-collect-branches + helm-ls-git-branches-show-all))) + (helm-init-candidates-in-buffer 'global data))) + :candidate-transformer 'helm-ls-git-branches-transformer + :action-transformer (lambda (actions candidate) + (cond ((string-match "\\`[*] detached" candidate) + (append + actions + '(("Git rebase continue" . + helm-ls-git-rebase-continue) + ("Git cherry-pick continue" . + helm-ls-git-cherry-pick-continue) + ("Git AM continue" . + helm-ls-git-am-continue) + ("Git merge continue" . + helm-ls-git-merge-continue) + ("Git revert continue" . + helm-ls-git-log-revert-continue) + ("Git cherry-pick abort" . + helm-ls-git-cherry-pick-abort) + ("Git rebase abort" . + helm-ls-git-rebase-abort) + ("Git AM abort" . + helm-ls-git-am-abort) + ("Git merge abort" . + helm-ls-git-merge-abort) + ("Git revert abort" . + helm-ls-git-log-revert-abort)))) + ((not (string-match "\\`[*]" candidate)) + (append + '(("Checkout" . helm-ls-git-checkout) + ("Delete branche(s)" . helm-ls-git-delete-marked-branches) + ("Merge in current" . + helm-ls-git-branches-merge) + ("Rebase in current" . + helm-ls-git-branch-rebase)) + actions)) + (t (helm-append-at-nth + actions + '(("Git amend" . helm-ls-git-amend-commit) + ("Git push (C-c P)" . helm-ls-git-push)) + 2)))) + :help-message 'helm-ls-git-help-message + :cleanup (lambda () (setq helm-ls-git-branches-show-all nil)) + :persistent-help "Checkout" + :persistent-action (lambda (candidate) + (helm-ls-git-checkout candidate) + (helm-force-update "^\\*")) + :action (helm-make-actions + (lambda () + (and helm-ls-git-status-command "Git status")) + (lambda (_candidate) + (funcall helm-ls-git-status-command + (helm-default-directory))) + "Switch to shell" #'helm-ls-git-switch-to-shell + "Git log (M-L)" #'helm-ls-git-show-log) + :keymap 'helm-ls-git-branches-map + :group 'helm-ls-git)) + + +;;; Stashing +;; +(defun helm-ls-git-list-stashes () + (helm-aif (helm-ls-git-root-dir) + (with-helm-default-directory it + (with-output-to-string + (with-current-buffer standard-output + (apply #'process-file + "git" + nil (list t helm-ls-git-log-file) nil + (list "stash" "list"))))))) + +(defun helm-ls-git-get-stash-name (candidate) + (when (string-match "stash@[{][0-9]+[}]" candidate) + (match-string 0 candidate))) + +(defun helm-ls-git-stash-show (candidate) + (if (and (eq last-command 'helm-execute-persistent-action) + (get-buffer-window "*stash diff*" 'visible)) + (kill-buffer "*stash diff*") + (let ((stash (helm-ls-git-get-stash-name candidate))) + (with-current-buffer (get-buffer-create "*stash diff*") + (let ((inhibit-read-only t)) + (erase-buffer) + (insert (with-helm-default-directory (helm-ls-git-root-dir) + (with-output-to-string + (with-current-buffer standard-output + (process-file + "git" nil (list t helm-ls-git-log-file) nil + "stash" "show" "-p" stash))))) + (diff-mode)) + (display-buffer (current-buffer)))))) + +(defun helm-ls-git-stash-apply (candidate) + (let ((num (helm-ls-git-get-stash-name candidate))) + (if (eq (process-file "git" nil nil nil "stash" "apply" num) 0) + (progn + (helm-ls-git-revert-buffers-in-project) + (message "Stash <%s> applied" candidate)) + (error "Couldn't apply stash <%s>" candidate)))) + +(defun helm-ls-git-stash-pop (candidate) + (let ((num (helm-ls-git-get-stash-name candidate))) + (if (eq (process-file "git" nil nil nil "stash" "pop" num) 0) + (progn + (helm-ls-git-revert-buffers-in-project) + (message "Stashed pop <%s>" candidate)) + (error "Couldn't stash pop <%s>" candidate)))) + +(defun helm-ls-git-stash-1 (name) + (with-helm-default-directory (helm-ls-git-root-dir) + (apply #'process-file "git" nil nil nil `("stash" "push" "-m" ,name)) + (helm-ls-git-revert-buffers-in-project))) + +(defun helm-ls-git-stash (_candidate) + (let ((name (read-string "Stash name: "))) + (helm-ls-git-stash-1 name))) + +(defun helm-ls-git-run-stash () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ls-git-stash))) +(put 'helm-ls-git-run-stash 'no-helm-mx t) + +(defun helm-ls-git-stash-snapshot (_candidate) + (vc-git-stash-snapshot)) + +(defun helm-ls-git-run-stash-snapshot () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ls-git-stash-snapshot))) +(put 'helm-ls-git-run-stash-snapshot 'no-helm-mx t) + +(defun helm-ls-git-stash-drop (candidate) + (let ((num (helm-ls-git-get-stash-name candidate))) + (if (eq (process-file "git" nil nil nil "stash" "drop" num) 0) + (message "Stash <%s> deleted" candidate) + (error "Couldn't delete <%s>" candidate)))) + +(defun helm-ls-git-stash-drop-marked (_candidate) + (let ((mkd (helm-marked-candidates))) + (cl-loop with sorted = + (sort mkd (lambda (s1 s2) + (let ((n1 (and (string-match + "^stash@[{]\\([0-9]+\\)[}]" s1) + (match-string 1 s1))) + (n2 (and (string-match + "^stash@[{]\\([0-9]+\\)[}]" s2) + (match-string 1 s2)))) + (string-greaterp n1 n2)))) + for c in sorted do (helm-ls-git-stash-drop c)))) + +(defun helm-ls-git-apply-patch (_candidate) + (with-helm-default-directory (helm-default-directory) + (let ((patchs (helm-marked-candidates))) + (with-current-buffer-window "*git apply*" '(display-buffer-below-selected + (window-height . fit-window-to-buffer) + (preserve-size . (nil . t))) + nil + (apply #'process-file "git" nil t t "apply" patchs) + (helm-ls-git-revert-buffers-in-project))))) + +(defvar helm-ls-git-stashes-source + (helm-build-in-buffer-source "Stashes" + :data 'helm-ls-git-list-stashes + :persistent-action 'helm-ls-git-stash-show + :action '(("Apply stash" . helm-ls-git-stash-apply) + ("Pop stash" . helm-ls-git-stash-pop) + ("Drop stashe(s)" . helm-ls-git-stash-drop-marked)) + :group 'helm-ls-git)) + +;;; Git status +(defun helm-ls-git-status (&optional ignore-untracked) + (when (and helm-ls-git-log-file + (file-exists-p helm-ls-git-log-file)) + (delete-file helm-ls-git-log-file)) + (let ((process-environment + (cons "GIT_OPTIONAL_LOCKS=0" process-environment))) + (helm-aif (helm-ls-git-root-dir) + (with-helm-default-directory it + (with-output-to-string + (with-current-buffer standard-output + (apply #'process-file + "git" + nil (list t helm-ls-git-log-file) nil + (if ignore-untracked + (list "status" "-uno" "--porcelain") + (list "status" "--porcelain"))))))))) + +(defun helm-ls-git-status-transformer (candidates _source) + (cl-loop with root = (helm-ls-git-root-dir) + for i in candidates + collect + (cond ((string-match "^\\( M \\)\\(.*\\)" i) ; modified. + (cons (propertize i 'face 'helm-ls-git-modified-not-staged-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\(M+ *\\)\\(.*\\)" i) ; modified and staged. + (cons (propertize i 'face 'helm-ls-git-modified-and-staged-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\([?]\\{2\\} \\)\\(.*\\)" i) + (cons (propertize i 'face 'helm-ls-git-untracked-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\([AC] +\\)\\(.*\\)" i) + (cons (propertize i 'face 'helm-ls-git-added-copied-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\( [D] \\)\\(.*\\)" i) + (cons (propertize i 'face 'helm-ls-git-deleted-not-staged-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\(RM?\\).* -> \\(.*\\)" i) + (cons (propertize i 'face 'helm-ls-git-renamed-modified-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\([D] +\\)\\(.*\\)" i) + (cons (propertize i 'face 'helm-ls-git-deleted-and-staged-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\(UU \\)\\(.*\\)" i) + (cons (propertize i 'face 'helm-ls-git-conflict-face) + (expand-file-name (match-string 2 i) root))) + ((string-match "^\\(AM \\)\\(.*\\)" i) + (cons (propertize i 'face 'helm-ls-git-added-modified-face) + (expand-file-name (match-string 2 i) root))) + (t i)))) + +(defun helm-ls-git-status-action-transformer (actions _candidate) + (let ((disp (helm-get-selection nil t)) + (mofified-actions + (helm-make-actions "Diff file" 'helm-ls-git-diff + "Revert file(s)" 'helm-ls-git-status-revert-files + "Copy file(s) `C-u to follow'" 'helm-find-files-copy + "Rename file(s) `C-u to follow'" 'helm-find-files-rename))) + ;; Unregistered files + (cond ((string-match "^[?]\\{2\\}" disp) + (append actions + (helm-make-actions "Add file(s)" + (lambda (candidate) + (let ((default-directory + (file-name-directory candidate)) + (marked (helm-marked-candidates))) + (vc-call-backend 'Git 'register marked))) + "Delete file(s)" + 'helm-ff-delete-files + (lambda () + (and (string-match "\\`[?]\\{2\\}.*\\.patch\\|diff" disp) + "Apply patch")) + 'helm-ls-git-apply-patch + (lambda () + (and (string-match "\\`[?]\\{2\\}.*\\.patch" disp) + "Git AM patches")) + 'helm-ls-git-am-files + "Copy bnames to .gitignore" + (lambda (candidate) + (let ((default-directory + (file-name-directory candidate)) + (marked (helm-marked-candidates))) + (with-current-buffer (find-file-noselect + (expand-file-name + ".gitignore" + (helm-ls-git-root-dir))) + (goto-char (point-max)) + (cl-loop with last-bname + for f in marked + for bname = (helm-basename f) + unless (string= bname last-bname) + do (insert (concat bname "\n")) + do (setq last-bname bname)) + (save-buffer))))))) + ((string-match "^A " disp) + (append actions '(("Commit staged file(s)" + . helm-ls-git-commit) + ("Extend commit" + . helm-ls-git-extend-commit) + ("Amend commit" + . helm-ls-git-amend-commit) + ("Unstage file(s)" + . helm-ls-git-unstage-files)))) + ;; Modified but not staged + ((string-match "^ M+ *" disp) + (append actions (helm-append-at-nth + mofified-actions + '(("Stage file(s) (C-c s)" + . helm-ls-git-stage-files) + ("Stage marked file(s) and commit (C-c c)" + . helm-ls-git-stage-marked-and-commit) + ("Stage marked file(s) and extend commit (C-c e)" + . helm-ls-git-stage-marked-and-extend-commit) + ("Stage marked file(s) and amend commit (C-c a)" + . helm-ls-git-stage-marked-and-amend-commit) + ("Stash (C-c z)" . helm-ls-git-stash) + ("Stash snapshot (C-c Z)" . helm-ls-git-stash-snapshot)) + 1))) + ;; Modified and staged + ((string-match "^M+ +" disp) + (append actions (helm-append-at-nth + mofified-actions + '(("Commit staged file(s)" + . helm-ls-git-commit) + ("Extend commit" + . helm-ls-git-extend-commit) + ("Amend commit" + . helm-ls-git-amend-commit) + ("Unstage file(s)" + . helm-ls-git-unstage-files) + ("Git rebase continue" . + helm-ls-git-rebase-continue) + ("Git cherry-pick continue" . + helm-ls-git-cherry-pick-continue) + ("Git AM continue" . + helm-ls-git-am-continue) + ("Git merge continue" . + helm-ls-git-merge-continue) + ("Git revert continue" . + helm-ls-git-log-revert-continue)) + 1))) + ;; Deleted + ((string-match "^ D " disp) + (append actions (list '("Git delete" . (lambda (_candidate) + (let ((mkd (helm-marked-candidates))) + (cl-loop for c in mkd + do (helm-ls-git-rm c))))) + '("Stage file(s)" + . helm-ls-git-stage-files)))) + ;; Deleted and staged + ((string-match "^D +" disp) + (append actions (list '("Commit staged file(s)" + . helm-ls-git-commit) + '("Stage marked file(s) and commit" + . helm-ls-git-stage-marked-and-commit)))) + ;; Conflict + ((string-match "^U+ +" disp) + (append actions (list '("Git cherry-pick abort" . helm-ls-git-cherry-pick-abort) + '("Git rebase abort" . helm-ls-git-rebase-abort) + '("Git AM abort" . helm-ls-git-am-abort) + '("Git merge abort" . helm-ls-git-merge-abort) + '("Git revert abort" . helm-ls-git-log-revert-abort)))) + (t actions)))) + +(defun helm-ls-git-am-files (_candidate) + (let ((files (helm-marked-candidates))) + (cl-assert (cl-loop for f in files + for ext = (file-name-extension f) + always (and ext (string= ext "patch")))) + (with-current-buffer-window "*git am*" '(display-buffer-below-selected + (window-height . fit-window-to-buffer) + (preserve-size . (nil . t))) + nil + (apply #'process-file "git" nil t nil "am" files)))) + +(defun helm-ls-git-am-abort (_candidate) + (with-helm-default-directory (helm-default-directory) + (process-file "git" nil nil nil "am" "--abort"))) + +(defun helm-ls-git-rm (_candidate) + (with-helm-default-directory (helm-default-directory) + (let ((files (helm-marked-candidates))) + (apply #'process-file "git" nil nil nil "rm" files)))) + +(defun helm-ls-git-switch-to-shell (_candidate) + (let ((helm-ff-default-directory + (helm-ls-git-root-dir))) + (helm-ff-switch-to-shell nil))) + +(defun helm-ls-git-run-switch-to-shell () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ls-git-switch-to-shell))) +(put 'helm-ls-git-run-switch-to-shell 'no-helm-mx t) + +(defun helm-ls-git-status-revert-files (_candidate) + (let ((marked (helm-marked-candidates))) + (cl-loop for f in marked do + (progn + (vc-git-revert f) + (helm-aif (get-file-buffer f) + (with-current-buffer it + (revert-buffer t t))))) + (when helm-in-persistent-action (helm-force-update)))) + +(defun helm-ls-git-run-status-revert-files () + (interactive) + (with-helm-alive-p + (helm-set-attr 'revert 'helm-ls-git-status-revert-files) + (helm-execute-persistent-action 'revert))) +(put 'helm-ls-git-run-status-revert-files 'no-helm-mx t) + + +;;; Stage and commit +;; +(defun helm-ls-git-stage-files (_candidate) + "Stage marked files." + (let* ((files (helm-marked-candidates)) + (default-directory (helm-default-directory))) + (apply #'process-file "git" nil nil nil "stage" files))) + +(defun helm-ls-git-run-stage-files (arg) + (interactive "P") + (with-helm-alive-p + (helm-exit-and-execute-action (if arg + 'helm-ls-git-unstage-files + 'helm-ls-git-stage-files)))) +(put 'helm-ls-git-run-stage-files 'no-helm-mx t) + +(defun helm-ls-git-unstage-files (_candidate) + "Unstage marked files." + (let* ((files (helm-marked-candidates)) + (default-directory (file-name-directory (car files)))) + (apply #'process-file "git" nil nil nil "reset" "HEAD" "--" files))) + +(defun helm-ls-git-stage-marked-and-commit (_candidate) + "Stage marked files and commit." + (helm-ls-git-stage-files nil) + (let ((proc (helm-ls-git-with-editor "commit" "-v"))) + (set-process-sentinel proc 'helm-ls-git-commit-sentinel))) + +(defun helm-ls-git-commit-sentinel (process event) + (let ((default-directory (with-current-buffer (process-buffer process) + default-directory))) + (when (string= event "finished\n") + (let ((commit (helm-ls-git-oneline-log (helm-ls-git--branch)))) + (when (string-match "\\`\\([^ ]+\\)+ +\\(.*\\)" commit) + (add-face-text-property 0 (match-end 1) + 'font-lock-type-face nil commit) + (add-face-text-property (1+ (match-end 1)) + (match-end 2) + 'font-lock-function-name-face nil commit)) + (message "Commit done, now at `%s'" commit))))) + +(defun helm-ls-git-run-stage-marked-and-commit () + (interactive) + (with-helm-alive-p + (condition-case _err + ;; Fail when helm-ls-git-stage-marked-and-commit is not in + ;; action list because file is already staged. + (helm-exit-and-execute-action 'helm-ls-git-stage-marked-and-commit) + (error (helm-exit-and-execute-action 'helm-ls-git-commit) nil)))) +(put 'helm-ls-git-run-stage-marked-and-commit 'no-helm-mx t) + +(defun helm-ls-git-stage-marked-and-extend-commit (candidate) + "Stage marked files and extend these changes to last commit" + (helm-ls-git-stage-files nil) + (helm-ls-git-extend-commit candidate)) + +(defun helm-ls-git-run-stage-marked-and-extend-commit () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ls-git-stage-marked-and-extend-commit))) +(put 'helm-ls-git-run-stage-marked-and-extend-commit 'no-helm-mx t) + +(defun helm-ls-git-stage-marked-and-amend-commit (candidate) + "Stage marked files and amend last commit." + (helm-ls-git-stage-files nil) + (helm-ls-git-amend-commit candidate)) + +(defun helm-ls-git-run-stage-marked-and-amend-commit () + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-ls-git-stage-marked-and-amend-commit))) +(put 'helm-ls-git-run-stage-marked-and-amend-commit 'no-helm-mx t) + +(defun helm-ls-git-extend-commit (candidate) + (let ((default-directory (file-name-directory candidate))) + (process-file "git" nil nil nil "commit" "--amend" "--no-edit"))) + +(defun helm-ls-git-amend-commit (_candidate) + "Amend last commit." + (let ((proc (helm-ls-git-with-editor "commit" "-v" "--amend"))) + (set-process-sentinel proc 'helm-ls-git-commit-sentinel))) + +(defun helm-ls-git-commit (_candidate) + "Commit already staged files." + (let ((proc (helm-ls-git-with-editor "commit" "-v"))) + (set-process-sentinel proc 'helm-ls-git-commit-sentinel))) + + +;;; Emacsclient as git editor +;; +;;;###autoload +(add-to-list 'auto-mode-alist '("/COMMIT_EDITMSG$" . helm-ls-git-commit-mode)) + +(defvar helm-ls-git-author-name-history nil) +(defvar helm-ls-git-author-email-history nil) + +(defun helm-ls-git-with-editor (&rest args) + "Binds GIT_EDITOR env var to emacsclient and run git with ARGS. +Bound `default-directory' to the root dir of project determining value +from the helm-buffer, so don't use this function outside of helm +context i.e. use it in helm actions." + (require 'server) + (let ((default-directory (expand-file-name + (helm-ls-git-root-dir + (helm-default-directory)))) + (process-environment process-environment) + (bname (format "*helm-ls-git %s*" (car args))) + (alt-auth (and helm-current-prefix-arg + (string= (car args) "commit") + (list (read-string "Author name: " + nil 'helm-ls-git-author-name-history) + (read-string "Author email: " + nil 'helm-ls-git-author-email-history))))) + ;; It seems git once it knows GIT_EDITOR reuse the same value + ;; along its whole process e.g. when squashing in a rebase + ;; process, so even if the env setting goes away after initial + ;; process, git should reuse same GIT_EDITOR in subsequent + ;; commits. + (when (get-buffer bname) (kill-buffer bname)) + (push "GIT_EDITOR=emacsclient $@" process-environment) + (when (and alt-auth (not (member "" alt-auth))) + (push (format "GIT_AUTHOR_NAME=%s" (car alt-auth)) process-environment) + (push (format "GIT_AUTHOR_EMAIL=%s" (cadr alt-auth)) process-environment)) + (unless (server-running-p) + (server-start)) + (apply #'start-file-process "git" bname "git" args))) + +(defun helm-ls-git-server-edit () + (interactive) + (cl-assert server-clients nil "No server editing buffers exist") + ;; Prevent server asking to save file when done. + (helm-aif buffer-file-name + (save-buffer it)) + (server-edit)) + +;; Same as `server-edit-abort' from emacs-28 but kill edit buffer as well. +(defun helm-ls-git-server-edit-abort () + "Abort editing the current client buffer." + (interactive) + (if server-clients + (progn + (mapc (lambda (proc) + (server-send-string + proc (concat "-error " + (server-quote-arg "Aborted by the user")))) + server-clients) + (kill-buffer)) + (message "This buffer has no clients"))) + +(defvar helm-ls-git-commit-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-c") 'helm-ls-git-server-edit) + (define-key map (kbd "C-c C-k") 'helm-ls-git-server-edit-abort) + map)) + +;;;###autoload +(define-derived-mode helm-ls-git-commit-mode diff-mode "helm-ls-git-commit" + "Mode to edit COMMIT_EDITMSG files. + +Commands: +\\{helm-ls-git-commit-mode-map} +" + (helm-ls-git-with-editor-setup)) + +(defun helm-ls-git-with-editor-setup () + (setq fill-column 70) + ;; For some reasons, using (setq buffer-read-only nil) in emacs-29 + ;; doesn't work anymore. + (read-only-mode -1) + (set (make-local-variable 'comment-start) "#") + (set (make-local-variable 'comment-end) "") + (auto-fill-mode 1) + (run-at-time + 0.1 nil + (lambda () + (message + "When done with a buffer, type `C-c C-c', to abort type `C-c C-k'")))) + +;;; Git rebase +;; +;;;###autoload +(add-to-list 'auto-mode-alist '("/git-rebase-todo$" . helm-ls-git-rebase-todo-mode)) + +(defconst helm-ls-git-rebase-actions + '(("p" . "pick") + ("r" . "reword") + ("e" . "edit") + ("s" . "squash") + ("f" . "fixup") + ("x" . "exec") + ("d" . "drop"))) + +(defvar helm-ls-git-rebase-todo-font-lock-keywords + '(("^\\([a-z]+\\) \\([0-9a-f]+\\) \\(.*\\)$" + (1 'font-lock-keyword-face) + (2 'font-lock-function-name-face)) + ("^#.*$" . 'font-lock-comment-face)) + "Keywords in `helm-ls-git-rebase-todo' mode.") + +(defvar helm-ls-git-rebase-todo-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "M-n") 'helm-ls-git-rebase-todo-move-down) + (define-key map (kbd "M-p") 'helm-ls-git-rebase-todo-move-up) + (define-key map (kbd "C-c C-c") 'helm-ls-git-server-edit) + (define-key map (kbd "C-c C-k") 'helm-ls-git-server-edit-abort) + map) + "Keymap used in `helm-ls-git-rebase-todo-mode' buffers.") + +(defun helm-ls-git-rebase-todo-move-down () + "Move commit line one line down." + (interactive) + (beginning-of-line) + (let* ((next (+ 1 (line-end-position))) + (line (buffer-substring (point) next))) + (delete-region (point) next) + (forward-line 1) + (insert line) + (forward-line -1))) + +(defun helm-ls-git-rebase-todo-move-up () + "Move commit line on line up." + (interactive) + (beginning-of-line) + (let* ((next (+ 1 (line-end-position))) + (line (buffer-substring (point) next))) + (delete-region (point) next) + (forward-line -1) + (insert line) + (forward-line -1))) + +(defun helm-ls-git-rebase-action (action) + "Replace the current rebase command at bol by ACTION. +ACTION is the cdr entry of one of `helm-ls-git-rebase-actions'." + (let* ((assocs helm-ls-git-rebase-actions) + (regexp (cl-loop with len = (length assocs) + for (_k . v) in assocs + for count from 1 to len + concat (concat v (if (= count len) "" "\\|")) into str + finally return (concat "^\\(" str "\\) +"))) + (inhibit-read-only t)) + (goto-char (point-at-bol)) + (save-excursion + (when (re-search-forward regexp (point-at-eol) t) + (delete-region (point-at-bol) (match-end 1)))) + (insert (cdr (rassoc action assocs))) + (forward-line 1))) + +(cl-defun helm-ls-git-rebase-build-commands () + "build a function for each `helm-ls-git-rebase-actions' entry. +Bind it to the car of each entry of `helm-ls-git-rebase-actions'." + (cl-loop for (k . v) in helm-ls-git-rebase-actions + for sym = (intern (concat "helm-ls-git-rebase-" v)) + for doc = (format "Replace current rebase command at bol by `%s'." v) + do (progn + (defalias sym `(lambda () (interactive) + (helm-ls-git-rebase-action ,v)) + doc) + (define-key helm-ls-git-rebase-todo-mode-map (kbd k) sym)))) + +;;;###autoload +(define-derived-mode helm-ls-git-rebase-todo-mode fundamental-mode "helm-ls-git-rebase-todo" + "Major Mode to edit git-rebase-todo files when using git rebase -i. + +Commands: +\\{helm-ls-git-rebase-todo-mode-map} +" + (set (make-local-variable 'font-lock-defaults) + '(helm-ls-git-rebase-todo-font-lock-keywords t)) + (helm-ls-git-rebase-build-commands) + (set (make-local-variable 'comment-start) "#") + (set (make-local-variable 'comment-end) "") + (run-at-time + 0.1 nil + (lambda () + (message + "When done with a buffer, type `C-c C-c', to abort type `C-c C-k'")))) + + +;;; Build sources +;; +;; Overhide the actions of helm-type-buffer. +(cl-defmethod helm--setup-source :after ((source helm-source-buffers)) + (let ((name (slot-value source 'name))) + (when (string= name "Buffers in git project") + (setf (slot-value source 'action) + (helm-append-at-nth + helm-type-buffer-actions + (helm-make-actions (lambda () + (and helm-ls-git-status-command "Git status")) + (lambda (_candidate) + (funcall helm-ls-git-status-command + (helm-default-directory)))) + 1))))) + +(defun helm-ls-git-build-git-status-source () + "Build `helm-source-ls-git-status'. + +Do nothing when `helm-source-ls-git-status' is not member of +`helm-ls-git-default-sources'." + (and (memq 'helm-source-ls-git-status helm-ls-git-default-sources) + (helm-make-source "Git status" 'helm-ls-git-status-source + :fuzzy-match helm-ls-git-fuzzy-match + :group 'helm-ls-git))) + +(defun helm-ls-git-build-ls-git-source () + "Build `helm-source-ls-git'. + +Do nothing when `helm-source-ls-git' is not member of +`helm-ls-git-default-sources'." + (and (memq 'helm-source-ls-git helm-ls-git-default-sources) + (helm-make-source "Git files" 'helm-ls-git-source + :fuzzy-match helm-ls-git-fuzzy-match + :action (helm-ls-git-actions-list helm-type-file-actions) + :group 'helm-ls-git))) + +(defun helm-ls-git-build-buffers-source () + "Build `helm-source-ls-git-buffers'. + +Do nothing when `helm-source-ls-git-buffers' is not member of +`helm-ls-git-default-sources'." + (and (memq 'helm-source-ls-git-buffers helm-ls-git-default-sources) + (helm-make-source "Buffers in git project" 'helm-source-buffers + :header-name #'helm-ls-git-header-name + :buffer-list (lambda () (helm-browse-project-get-buffers + (helm-ls-git-root-dir))) + :keymap 'helm-ls-git-buffer-map))) + + +;;;###autoload +(defun helm-ls-git (&optional arg) + (interactive "p") + (let ((helm-ff-default-directory + (or helm-ff-default-directory + default-directory))) + (when (and arg (helm-ls-git-not-inside-git-repo)) + (error "Not inside a Git repository")) + (unless (cl-loop for s in helm-ls-git-default-sources + always (symbol-value s)) + (setq helm-source-ls-git-status + (helm-ls-git-build-git-status-source) + helm-source-ls-git + (helm-ls-git-build-ls-git-source) + helm-source-ls-git-buffers + (helm-ls-git-build-buffers-source))) + (helm-set-local-variable 'helm-ls-git--current-branch (helm-ls-git--branch)) + (helm :sources helm-ls-git-default-sources + :ff-transformer-show-only-basename nil + :truncate-lines helm-buffers-truncate-lines + :buffer "*helm lsgit*"))) + +(defalias 'helm-ls-git-ls 'helm-ls-git) +(make-obsolete 'helm-ls-git-ls 'helm-ls-git "1.9.2") +(put 'helm-ls-git-ls 'no-helm-mx t) + + +(provide 'helm-ls-git) + +;;; helm-ls-git.el ends here diff --git a/code/elpa/helm-projectile-20220820.826/helm-projectile-autoloads.el b/code/elpa/helm-projectile-20220820.826/helm-projectile-autoloads.el new file mode 100644 index 0000000..b73fa43 --- /dev/null +++ b/code/elpa/helm-projectile-20220820.826/helm-projectile-autoloads.el @@ -0,0 +1,79 @@ +;;; helm-projectile-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "helm-projectile" "helm-projectile.el" (0 0 +;;;;;; 0 0)) +;;; Generated autoloads from helm-projectile.el + +(defvar helm-projectile-fuzzy-match t "\ +Enable fuzzy matching for Helm Projectile commands. +This needs to be set before loading helm-projectile.el.") + +(custom-autoload 'helm-projectile-fuzzy-match "helm-projectile" t) + +(autoload 'helm-projectile-find-file-dwim "helm-projectile" "\ +Find file at point based on context." t nil) + +(autoload 'helm-projectile-find-other-file "helm-projectile" "\ +Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of current file. +Other file extensions can be customized with the variable `projectile-other-file-alist'. + +\(fn &optional FLEX-MATCHING)" t nil) + +(autoload 'helm-projectile-on "helm-projectile" "\ +Turn on `helm-projectile' key bindings." t nil) + +(autoload 'helm-projectile-off "helm-projectile" "\ +Turn off `helm-projectile' key bindings." t nil) + +(autoload 'helm-projectile-grep "helm-projectile" "\ +Helm version of `projectile-grep'. +DIR is the project root, if not set then current directory is used + +\(fn &optional DIR)" t nil) + +(autoload 'helm-projectile-ack "helm-projectile" "\ +Helm version of projectile-ack. + +\(fn &optional DIR)" t nil) + +(autoload 'helm-projectile-ag "helm-projectile" "\ +Helm version of `projectile-ag'. + +\(fn &optional OPTIONS)" t nil) + +(autoload 'helm-projectile-rg "helm-projectile" "\ +Projectile version of `helm-rg'." t nil) + +(autoload 'helm-projectile-toggle "helm-projectile" "\ +Toggle Helm version of Projectile commands. + +\(fn TOGGLE)" nil nil) + +(autoload 'helm-projectile "helm-projectile" "\ +Use projectile with Helm instead of ido. + +With a prefix ARG invalidates the cache first. +If invoked outside of a project, displays a list of known projects to jump. + +\(fn &optional ARG)" t nil) + +(eval-after-load 'projectile '(progn (define-key projectile-command-map (kbd "h") #'helm-projectile))) + +(register-definition-prefixes "helm-projectile" '("glob-quote" "helm-")) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; helm-projectile-autoloads.el ends here diff --git a/code/elpa/helm-projectile-20220820.826/helm-projectile-pkg.el b/code/elpa/helm-projectile-20220820.826/helm-projectile-pkg.el new file mode 100644 index 0000000..f37dffc --- /dev/null +++ b/code/elpa/helm-projectile-20220820.826/helm-projectile-pkg.el @@ -0,0 +1,2 @@ +;;; Generated package description from helm-projectile.el -*- no-byte-compile: t -*- +(define-package "helm-projectile" "20220820.826" "Helm integration for Projectile" '((helm "1.9.9") (projectile "2.2.0") (cl-lib "0.3")) :commit "5813f7286533990783546c9c39c184faa034d1f1" :authors '(("Bozhidar Batsov")) :maintainer '("Bozhidar Batsov") :keywords '("project" "convenience") :url "https://github.com/bbatsov/helm-projectile") diff --git a/code/elpa/helm-projectile-20220820.826/helm-projectile.el b/code/elpa/helm-projectile-20220820.826/helm-projectile.el new file mode 100644 index 0000000..ecb53a1 --- /dev/null +++ b/code/elpa/helm-projectile-20220820.826/helm-projectile.el @@ -0,0 +1,1126 @@ +;;; helm-projectile.el --- Helm integration for Projectile -*- lexical-binding: t; -*- + +;; Copyright (C) 2011-2020 Bozhidar Batsov + +;; Author: Bozhidar Batsov +;; URL: https://github.com/bbatsov/helm-projectile +;; Package-Version: 20220820.826 +;; Package-Commit: 5813f7286533990783546c9c39c184faa034d1f1 +;; Created: 2011-31-07 +;; Keywords: project, convenience +;; Version: 1.1.0-snapshot +;; Package-Requires: ((helm "1.9.9") (projectile "2.2.0") (cl-lib "0.3")) + +;; This file is NOT part of GNU Emacs. + +;;; License: + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; This library provides easy project management and navigation. The +;; concept of a project is pretty basic - just a folder containing +;; special file. Currently git, mercurial and bazaar repos are +;; considered projects by default. If you want to mark a folder +;; manually as a project just create an empty .projectile file in +;; it. See the README for more details. +;; +;;; Code: + +(require 'subr-x) +(require 'projectile) +(require 'cl-lib) +(require 'grep) +(require 'helm) +(require 'helm-types) +(require 'helm-locate) +(require 'helm-buffers) +(require 'helm-files) + +(declare-function eshell "eshell") +(declare-function helm-do-ag "ext:helm-ag") +(declare-function dired-get-filename "dired") +(defvar helm-ag-base-command) + +(defvar grep-find-ignored-directories) +(defvar grep-find-ignored-files) + +(defgroup helm-projectile nil + "Helm support for projectile." + :prefix "helm-projectile-" + :group 'projectile + :group 'helm + :link `(url-link :tag "GitHub" "https://github.com/bbatsov/helm-projectile")) + +(defvar helm-projectile-current-project-root) + +(defcustom helm-projectile-truncate-lines nil + "Truncate lines in helm projectile commands when non--nil. + +Some `helm-projectile' commands have similar behavior with existing +Helms. In these cases their respective custom var for truncation +of lines will be honored. E.g. `helm-buffers-truncate-lines' +dictates the truncation in `helm-projectile-switch-to-buffer'." + :group 'helm-projectile + :type 'boolean) + +;;;###autoload +(defcustom helm-projectile-fuzzy-match t + "Enable fuzzy matching for Helm Projectile commands. +This needs to be set before loading helm-projectile.el." + :group 'helm-projectile + :type 'boolean) + +(defmacro helm-projectile-define-key (keymap key def &rest bindings) + "In KEYMAP, define KEY - DEF sequence KEY1 as DEF1, KEY2 as DEF2 ..." + (declare (indent defun)) + (let ((ret '(progn))) + (while key + (push + `(define-key ,keymap ,key + (lambda () + (interactive) + (helm-exit-and-execute-action ,def))) + ret) + (setq key (pop bindings) + def (pop bindings))) + (reverse ret))) + +(defun helm-projectile-hack-actions (actions &rest prescription) + "Given a Helm action list and a prescription, return a hacked Helm action list, after applying the PRESCRIPTION. + +The Helm action list ACTIONS is of the form: + +\(\(DESCRIPTION1 . FUNCTION1\) + \(DESCRIPTION2 . FUNCTION2\) + ... + \(DESCRIPTIONn . FUNCTIONn\)\) + +PRESCRIPTION is in the form: + +\(INSTRUCTION1 INSTRUCTION2 ... INSTRUCTIONn\) + +If an INSTRUCTION is a symbol, the action with function name +INSTRUCTION is deleted. + +If an INSTRUCTION is of the form \(FUNCTION1 . FUNCTION2\), the +action with function name FUNCTION1 will change it's function to +FUNCTION2. + +If an INSTRUCTION is of the form \(FUNCTION . DESCRIPTION\), and +if an action with function name FUNCTION exists in the original +Helm action list, the action in the Helm action list, with +function name FUNCTION will change it's description to +DESCRIPTION. Otherwise, (FUNCTION . DESCRIPTION) will be added to +the action list. + +Please check out how `helm-projectile-file-actions' is defined +for an example of how this function is being used." + (let* ((to-delete (cl-remove-if (lambda (entry) (listp entry)) prescription)) + (actions (cl-delete-if (lambda (action) (memq (cdr action) to-delete)) + (copy-alist actions))) + new) + (cl-dolist (action actions) + (when (setq new (cdr (assq (cdr action) prescription))) + (if (stringp new) + (setcar action new) + (setcdr action new)))) + ;; Add new actions from PRESCRIPTION + (setq new nil) + (cl-dolist (instruction prescription) + (when (and (listp instruction) + (null (rassq (car instruction) actions)) + (symbolp (car instruction)) (stringp (cdr instruction))) + (push (cons (cdr instruction) (car instruction)) new))) + (append actions (nreverse new)))) + +(defun helm-projectile-vc (dir) + "A Helm action for jumping to project root using `vc-dir' or Magit. +DIR is a directory to be switched" + (let ((projectile-require-project-root nil)) + (projectile-vc dir))) + +(defun helm-projectile-compile-project (dir) + "A Helm action for compile a project. +DIR is the project root." + (let ((helm--reading-passwd-or-string t) + (default-directory dir)) + (projectile-compile-project helm-current-prefix-arg))) + +(defun helm-projectile-test-project (dir) + "A Helm action for test a project. +DIR is the project root." + (let ((helm--reading-passwd-or-string t) + (default-directory dir)) + (projectile-test-project helm-current-prefix-arg))) + +(defun helm-projectile-run-project (dir) + "A Helm action for run a project. +DIR is the project root." + (let ((helm--reading-passwd-or-string t) + (default-directory dir)) + (projectile-run-project helm-current-prefix-arg))) + +(defun helm-projectile-remove-known-project (_ignore) + "Remove selected projects from projectile project list. +_IGNORE means the argument does not matter. +It is there because Helm requires it." + (let* ((projects (helm-marked-candidates :with-wildcard t)) + (len (length projects))) + (with-helm-display-marked-candidates + helm-marked-buffer-name + projects + (if (not (y-or-n-p (format "Remove *%s projects(s)? " len))) + (message "(No removal performed)") + (progn + (mapc (lambda (p) + (setq projectile-known-projects (delete p projectile-known-projects))) + projects) + (projectile-save-known-projects)) + (message "%s projects(s) removed" len))))) + +(defvar helm-projectile-projects-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (helm-projectile-define-key map + (kbd "C-d") #'dired + (kbd "M-g") #'helm-projectile-vc + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "C-s") #'helm-projectile-grep + (kbd "M-c") #'helm-projectile-compile-project + (kbd "M-t") #'helm-projectile-test-project + (kbd "M-r") #'helm-projectile-run-project + (kbd "M-D") #'helm-projectile-remove-known-project) + map) + "Mapping for known projectile projects.") + +(defcustom helm-source-projectile-projects-actions + (helm-make-actions + "Switch to project" (lambda (project) + (let ((projectile-completion-system 'helm)) + (projectile-switch-project-by-name project))) + "Open Dired in project's directory `C-d'" #'dired + "Open project root in vc-dir or magit `M-g'" #'helm-projectile-vc + "Switch to Eshell `M-e'" #'helm-projectile-switch-to-shell + "Grep in projects `C-s'" #'helm-projectile-grep + "Compile project `M-c'. With C-u, new compile command" #'helm-projectile-compile-project + "Remove project(s) from project list `M-D'" #'helm-projectile-remove-known-project) + "Actions for `helm-source-projectile-projects'." + :group 'helm-projectile + :type '(alist :key-type string :value-type function)) + +(defvar helm-source-projectile-projects + (helm-build-sync-source "Projectile projects" + :candidates (lambda () (with-helm-current-buffer projectile-known-projects)) + :fuzzy-match helm-projectile-fuzzy-match + :keymap helm-projectile-projects-map + :mode-line helm-read-file-name-mode-line-string + :action 'helm-source-projectile-projects-actions) + "Helm source for known projectile projects.") + +(defvar helm-projectile-dirty-projects-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (helm-projectile-define-key map + (kbd "C-d") #'dired + (kbd "M-o") #'(lambda (project) + (let ((projectile-completion-system 'helm)) + (projectile-switch-project-by-name project))) + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "C-s") #'helm-projectile-grep + (kbd "M-c") #'helm-projectile-compile-project + (kbd "M-t") #'helm-projectile-test-project + (kbd "M-r") #'helm-projectile-run-project + (kbd "M-D") #'helm-projectile-remove-known-project) + map) + "Mapping for dirty projectile projects.") + +(defvar helm-source-projectile-dirty-projects + (helm-build-sync-source "Projectile dirty projects" + :candidates (lambda () (with-helm-current-buffer (helm-projectile-get-dirty-projects))) + :fuzzy-match helm-projectile-fuzzy-match + :keymap helm-projectile-dirty-projects-map + :mode-line helm-read-file-name-mode-line-string + :action '(("Open project root in vc-dir or magit" . helm-projectile-vc) + ("Switch to project `M-o'" . + (lambda (project) + (let ((projectile-completion-system 'helm)) + (projectile-switch-project-by-name project)))) + ("Open Dired in project's directory `C-d'" . dired) + ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell) + ("Grep in projects `C-s'" . helm-projectile-grep) + ("Compile project `M-c'. With C-u, new compile command" + . helm-projectile-compile-project))) + "Helm source for dirty version controlled projectile projects.") + +(defun helm-projectile-get-dirty-projects () + "Return dirty version controlled known projects as an alist to +have a nice display in Helm." + (message "Checking for dirty known projects...") + (let* ((status (projectile-check-vcs-status-of-known-projects)) + (proj-dir (cl-loop for stat in status + collect (car stat))) + (status-display (cl-loop for stat in status collect + (propertize (format "[%s]" (mapconcat 'identity (car (cdr stat)) ", ")) 'face 'helm-match))) + (max-status-display-length (cl-loop for sd in status-display + maximize (length sd))) + (status-display (cl-loop for sd in status-display collect + (format "%s%s " sd (make-string (- max-status-display-length (length sd)) ? )))) + (full-display (cl-mapcar 'concat status-display proj-dir)) + (helm-list (cl-pairlis full-display proj-dir))) + helm-list)) + +(define-key helm-etags-map (kbd "C-c p f") + (lambda () + (interactive) + (helm-run-after-exit 'helm-projectile-find-file nil))) + +(defun helm-projectile-file-persistent-action (candidate) + "Persistent action for file-related functionality. + +Previews the contents of a file in a temporary buffer." + (let ((buf (get-buffer-create " *helm-projectile persistent*"))) + (cl-flet ((preview (candidate) + (switch-to-buffer buf) + (setq inhibit-read-only t) + (erase-buffer) + (insert-file-contents candidate) + (let ((buffer-file-name candidate)) + (set-auto-mode)) + (font-lock-ensure) + (setq inhibit-read-only nil))) + (if (and (helm-attr 'previewp) + (string= candidate (helm-attr 'current-candidate))) + (progn + (kill-buffer buf) + (helm-attrset 'previewp nil)) + (preview candidate) + (helm-attrset 'previewp t))) + (helm-attrset 'current-candidate candidate))) + +(defun helm-projectile-find-files-eshell-command-on-file-action (candidate) + (interactive) + (let* ((helm-ff-default-directory (file-name-directory candidate))) + (helm-find-files-eshell-command-on-file candidate))) + +(defun helm-projectile-ff-etags-select-action (candidate) + (interactive) + (let* ((helm-ff-default-directory (file-name-directory candidate))) + (helm-ff-etags-select candidate))) + +(defun helm-projectile-switch-to-shell (dir) + "Within DIR, switch to a shell corresponding to `helm-ff-preferred-shell-mode'." + (interactive) + (let* ((projectile-require-project-root nil) + (helm-ff-default-directory (file-name-directory (projectile-expand-root dir)))) + (helm-ff-switch-to-shell dir))) + +(defun helm-projectile-files-in-current-dired-buffer () + "Return a list of files (only) in the current dired buffer." + (let (flist) + (cl-flet ((fpush (fname) (push fname flist))) + (save-excursion + (let (file buffer-read-only) + (goto-char (point-min)) + (while (not (eobp)) + (save-excursion + (and (not (eolp)) + (setq file (dired-get-filename t t)) ; nil on non-file + (progn (end-of-line) + (funcall #'fpush file)))) + (forward-line 1))))) + (mapcar 'file-truename (nreverse flist)))) + +(defun helm-projectile-all-dired-buffers () + "Get all current Dired buffers." + (mapcar (lambda (b) + (with-current-buffer b (buffer-name))) + (cl-remove-if-not + (lambda (b) + (with-current-buffer b + (and (eq major-mode 'dired-mode) + (buffer-name)))) + (buffer-list)))) + +(defvar helm-projectile-virtual-dired-remote-enable nil + "Enable virtual Dired manager on remote host. +Disabled by default.") + +(defun helm-projectile-dired-files-new-action (candidate) + "Create a Dired buffer from chosen files. +CANDIDATE is the selected file, but choose the marked files if available." + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + (message "Virtual Dired manager is disabled in remote host. Enable with %s." + (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) + (let ((files (cl-remove-if-not + (lambda (f) + (not (string= f ""))) + (mapcar (lambda (file) + (replace-regexp-in-string (projectile-project-root) "" file)) + (helm-marked-candidates :with-wildcard t)))) + (new-name (completing-read "Select or enter a new buffer name: " + (helm-projectile-all-dired-buffers))) + (helm--reading-passwd-or-string t) + (default-directory (projectile-project-root))) + ;; create a unique buffer that is unique to any directory in default-directory + ;; or opened buffer; when Dired is passed with a non-existence directory name, + ;; it only creates a buffer and insert everything. If a new name user supplied + ;; exists as default-directory, Dired throws error when insert anything that + ;; does not exist in current directory. + (with-current-buffer (dired (cons (make-temp-name new-name) + (if files + files + (list candidate)))) + (when (get-buffer new-name) + (kill-buffer new-name)) + (rename-buffer new-name))))) + +(defun helm-projectile-dired-files-add-action (candidate) + "Add files to a Dired buffer. +CANDIDATE is the selected file. Used when no file is explicitly marked." + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + (message "Virtual Dired manager is disabled in remote host. Enable with %s." + (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) + (if (eq (with-helm-current-buffer major-mode) 'dired-mode) + (let* ((marked-files (helm-marked-candidates :with-wildcard t)) + (helm--reading-passwd-or-string t) + (root (projectile-project-root)) ; store root for later use + (dired-buffer-name (or (and (eq major-mode 'dired-mode) (buffer-name)) + (completing-read "Select a Dired buffer:" + (helm-projectile-all-dired-buffers)))) + (dired-files (with-current-buffer dired-buffer-name + (helm-projectile-files-in-current-dired-buffer))) + (files (sort (mapcar (lambda (file) + (replace-regexp-in-string (projectile-project-root) "" file)) + (cl-nunion (if marked-files + marked-files + (list candidate)) + dired-files + :test #'string-equal)) + 'string-lessp))) + (kill-buffer dired-buffer-name) + ;; Rebind default-directory because after killing a buffer, we + ;; could be in any buffer and default-directory is set to that + ;; random buffer + ;; + ;; Also use saved root directory, because after killing a buffer, + ;; we could be outside of current project + (let ((default-directory root)) + (with-current-buffer (dired (cons (make-temp-name dired-buffer-name) + (if files + (mapcar (lambda (file) + (replace-regexp-in-string root "" file)) + files) + (list candidate)))) + (rename-buffer dired-buffer-name)))) + (error "You're not in a Dired buffer to add")))) + +(defun helm-projectile-dired-files-delete-action (candidate) + "Delete selected entries from a Dired buffer. +CANDIDATE is the selected file. Used when no file is explicitly marked." + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + (message "Virtual Dired manager is disabled in remote host. Enable with %s." + (propertize "helm-projectile-virtual-dired-remote-enable" 'face 'font-lock-keyword-face)) + (let* ((helm--reading-passwd-or-string t) + (root (projectile-project-root)) + (dired-buffer-name (with-helm-current-buffer (buffer-name))) + (dired-files (with-current-buffer dired-buffer-name + (helm-projectile-files-in-current-dired-buffer))) + (files (sort (cl-set-exclusive-or (helm-marked-candidates :with-wildcard t) + dired-files + :test #'string-equal) #'string-lessp))) + (kill-buffer dired-buffer-name) + ;; similar reason to `helm-projectile-dired-files-add-action' + (let ((default-directory root)) + (with-current-buffer (dired (cons (make-temp-name dired-buffer-name) + (if files + (mapcar (lambda (file) + (replace-regexp-in-string root "" file)) + files) + (list candidate)))) + (rename-buffer dired-buffer-name)))))) + +(defun helm-projectile-run-projectile-hooks-after-find-file (_orig-fun &rest _args) + "Run `projectile-find-file-hook' if using projectile." + (when (and projectile-mode (projectile-project-p)) + (run-hooks 'projectile-find-file-hook))) + +(advice-add 'helm-find-file-or-marked + :after #'helm-projectile-run-projectile-hooks-after-find-file) + +(defvar helm-projectile-find-file-map + (let ((map (copy-keymap helm-find-files-map))) + (helm-projectile-define-key map + (kbd "C-c f") #'helm-projectile-dired-files-new-action + (kbd "C-c a") #'helm-projectile-dired-files-add-action + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "M-.") #'helm-projectile-ff-etags-select-action + (kbd "M-!") #'helm-projectile-find-files-eshell-command-on-file-action) + (define-key map (kbd "") #'helm-previous-source) + (define-key map (kbd "") #'helm-next-source) + (dolist (cmd '(helm-find-files-up-one-level + helm-find-files-down-last-level)) + (substitute-key-definition cmd nil map)) + map) + "Mapping for file commands in Helm Projectile.") + +(defvar helm-projectile-file-actions + (helm-projectile-hack-actions + helm-find-files-actions + ;; Delete these actions + 'helm-ff-browse-project + 'helm-insert-file-name-completion-at-point + 'helm-ff-find-sh-command + 'helm-ff-cache-add-file + ;; Substitute these actions + '(helm-ff-switch-to-shell . helm-projectile-switch-to-shell) + '(helm-ff-etags-select . helm-projectile-ff-etags-select-action) + '(helm-find-files-eshell-command-on-file + . helm-projectile-find-files-eshell-command-on-file-action) + ;; Change action descriptions + '(helm-find-file-as-root . "Find file as root `C-c r'") + ;; New actions + '(helm-projectile-dired-files-new-action + . "Create Dired buffer from files `C-c f'") + '(helm-projectile-dired-files-add-action + . "Add files to Dired buffer `C-c a'")) + "Action for files.") + +(defun helm-projectile--move-selection-p (selection) + "Return non-nil if should move Helm selector after SELECTION. + +SELECTION should be moved unless it's one of: + +- Non-string +- Existing file +- Non-remote file that matches `helm-tramp-file-name-regexp'" + (not (or (not (stringp selection)) + (file-exists-p selection) + (and (string-match helm-tramp-file-name-regexp selection) + (not (file-remote-p selection nil t)))))) + +(defun helm-projectile--move-to-real () + "Move to first real candidate. + +Similar to `helm-ff--move-to-first-real-candidate', but without +unnecessary complexity." + (while (let* ((src (helm-get-current-source)) + (selection (and (not (helm-empty-source-p)) + (helm-get-selection nil nil src)))) + (and (not (helm-end-of-source-p)) + (helm-projectile--move-selection-p selection))) + (helm-next-line))) + +(defun helm-projectile--remove-move-to-real () + "Hook function to remove `helm-projectile--move-to-real'. + +Meant to be added to `helm-cleanup-hook', from which it removes + itself at the end." + (remove-hook 'helm-after-update-hook #'helm-projectile--move-to-real) + (remove-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real)) + +(defvar helm-source-projectile-files-list-before-init-hook + (lambda () + (add-hook 'helm-after-update-hook #'helm-projectile--move-to-real) + (add-hook 'helm-cleanup-hook #'helm-projectile--remove-move-to-real))) + +(defvar helm-source-projectile-files-list + (helm-build-sync-source "Projectile files" + :before-init-hook 'helm-source-projectile-files-list-before-init-hook + :candidates (lambda () + (when (projectile-project-p) + (with-helm-current-buffer + (cl-loop with root = (projectile-project-root) + for display in (projectile-current-project-files) + collect (cons display (expand-file-name display root)))))) + :filtered-candidate-transformer + (lambda (files _source) + (with-helm-current-buffer + (let* ((root (projectile-project-root)) + (file-at-root (file-relative-name (expand-file-name helm-pattern root)))) + (if (or (string-empty-p helm-pattern) + (assoc helm-pattern files)) + files + (if (equal helm-pattern file-at-root) + (cl-acons (helm-ff-prefix-filename helm-pattern nil t) + (expand-file-name helm-pattern) + files) + (cl-pairlis (list (helm-ff-prefix-filename helm-pattern nil t) + (helm-ff-prefix-filename file-at-root nil t)) + (list (expand-file-name helm-pattern) + (expand-file-name helm-pattern root)) + files)))))) + :fuzzy-match helm-projectile-fuzzy-match + :keymap helm-projectile-find-file-map + :help-message 'helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-file-actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + "Helm source definition for Projectile files.") + +(defvar helm-source-projectile-files-in-all-projects-list + (helm-build-sync-source "Projectile files in all Projects" + :candidates (lambda () + (with-helm-current-buffer + (let ((projectile-require-project-root nil)) + (projectile-all-project-files)))) + :keymap helm-projectile-find-file-map + :help-message 'helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-file-actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + "Helm source definition for all Projectile files in all projects.") + +(defvar helm-projectile-dired-file-actions + (helm-projectile-hack-actions + helm-projectile-file-actions + ;; New actions + '(helm-projectile-dired-files-delete-action . "Remove entry(s) from Dired buffer `C-c d'"))) + +(defvar helm-source-projectile-dired-files-list + (helm-build-in-buffer-source "Projectile files in current Dired buffer" + :data (lambda () + (if (and (file-remote-p (projectile-project-root)) + (not helm-projectile-virtual-dired-remote-enable)) + nil + (when (eq major-mode 'dired-mode) + (helm-projectile-files-in-current-dired-buffer)))) + :filter-one-by-one (lambda (file) + (let ((helm-ff-transformer-show-only-basename t)) + (helm-ff-filter-candidate-one-by-one file))) + :action-transformer 'helm-find-files-action-transformer + :keymap (let ((map (copy-keymap helm-projectile-find-file-map))) + (helm-projectile-define-key map + (kbd "C-c d") 'helm-projectile-dired-files-delete-action) + map) + :help-message 'helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-dired-file-actions) + "Helm source definition for Projectile delete files.") + +(defun helm-projectile-dired-find-dir (dir) + "Jump to a selected directory DIR from `helm-projectile'." + (dired (expand-file-name dir (projectile-project-root))) + (run-hooks 'projectile-find-dir-hook)) + +(defun helm-projectile-dired-find-dir-other-window (dir) + "Jump to a selected directory DIR from `helm-projectile'." + (dired-other-window (expand-file-name dir (projectile-project-root))) + (run-hooks 'projectile-find-dir-hook)) + +(defvar helm-source-projectile-directories-list + (helm-build-sync-source "Projectile directories" + :candidates (lambda () + (when (projectile-project-p) + (with-helm-current-buffer + (let ((dirs (if projectile-find-dir-includes-top-level + (append '("./") (projectile-current-project-dirs)) + (projectile-current-project-dirs)))) + (helm-projectile--files-display-real dirs (projectile-project-root)))))) + :fuzzy-match helm-projectile-fuzzy-match + :action-transformer 'helm-find-files-action-transformer + :keymap (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (helm-projectile-define-key map + (kbd "") #'helm-previous-source + (kbd "") #'helm-next-source + (kbd "C-c o") #'helm-projectile-dired-find-dir-other-window + (kbd "M-e") #'helm-projectile-switch-to-shell + (kbd "C-c f") #'helm-projectile-dired-files-new-action + (kbd "C-c a") #'helm-projectile-dired-files-add-action + (kbd "C-s") #'helm-projectile-grep) + map) + :help-message 'helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action '(("Open Dired" . helm-projectile-dired-find-dir) + ("Open Dired in other window `C-c o'" . helm-projectile-dired-find-dir) + ("Switch to Eshell `M-e'" . helm-projectile-switch-to-shell) + ("Grep in projects `C-s'" . helm-projectile-grep) + ("Create Dired buffer from files `C-c f'" . helm-projectile-dired-files-new-action) + ("Add files to Dired buffer `C-c a'" . helm-projectile-dired-files-add-action))) + "Helm source for listing project directories.") + +(defvar helm-projectile-buffers-list-cache nil) + +(defclass helm-source-projectile-buffer (helm-source-sync helm-type-buffer) + ((init :initform (lambda () + ;; Issue #51 Create the list before `helm-buffer' creation. + (setq helm-projectile-buffers-list-cache + (ignore-errors (cdr (projectile-project-buffer-names)))) + (let ((result (cl-loop for b in helm-projectile-buffers-list-cache + maximize (length b) into len-buf + maximize (length (with-current-buffer b + (symbol-name major-mode))) + into len-mode + finally return (cons len-buf len-mode)))) + (unless helm-buffer-max-length + (setq helm-buffer-max-length (car result))) + (unless helm-buffer-max-len-mode + ;; If a new buffer is longer that this value + ;; this value will be updated + (setq helm-buffer-max-len-mode (cdr result)))))) + (candidates :initform helm-projectile-buffers-list-cache) + (matchplugin :initform nil) + (match :initform 'helm-buffers-match-function) + (persistent-action :initform 'helm-buffers-list-persistent-action) + (keymap :initform helm-buffer-map) + (volatile :initform t) + (persistent-help + :initform + "Show this buffer / C-u \\[helm-execute-persistent-action]: Kill this buffer"))) + +(defvar helm-source-projectile-buffers-list (helm-make-source "Project buffers" 'helm-source-projectile-buffer)) + +(defvar helm-source-projectile-recentf-list + (helm-build-sync-source "Projectile recent files" + :candidates (lambda () + (when (projectile-project-p) + (with-helm-current-buffer + (helm-projectile--files-display-real (projectile-recentf-files) + (projectile-project-root))))) + :fuzzy-match helm-projectile-fuzzy-match + :keymap helm-projectile-find-file-map + :help-message 'helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-file-actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + "Helm source definition for recent files in current project.") + +(defvar helm-source-projectile-files-and-dired-list + '(helm-source-projectile-dired-files-list + helm-source-projectile-files-list)) + +(defvar helm-source-projectile-directories-and-dired-list + '(helm-source-projectile-dired-files-list + helm-source-projectile-directories-list)) + +(defcustom helm-projectile-git-grep-command + "git --no-pager grep --no-color -n%c -e %p -- %f" + "Command to execute when performing `helm-grep' inside a projectile git project. +See documentation of `helm-grep-default-command' for the format." + :type 'string + :group 'helm-projectile + ) + +(defcustom helm-projectile-grep-command + "grep -a -r %e -n%cH -e %p %f ." + "Command to execute when performing `helm-grep' outside a projectile git project. +See documentation of `helm-grep-default-command' for the format." + :type 'string + :group 'helm-projectile + ) + + +(defcustom helm-projectile-sources-list + '(helm-source-projectile-buffers-list + helm-source-projectile-files-list + helm-source-projectile-projects) + "Default sources for `helm-projectile'." + :type '(repeat symbol) + :group 'helm-projectile) + +(defmacro helm-projectile-command (command source prompt &optional not-require-root truncate-lines-var) + "Template for generic `helm-projectile' commands. +COMMAND is a command name to be appended with \"helm-projectile\" prefix. +SOURCE is a Helm source that should be Projectile specific. +PROMPT is a string for displaying as a prompt. +NOT-REQUIRE-ROOT specifies the command doesn't need to be used in a +project root. +TRUNCATE-LINES-VAR is the symbol used dictate truncation of lines. +Defaults is `helm-projectile-truncate-lines'." + (unless truncate-lines-var (setq truncate-lines-var 'helm-projectile-truncate-lines)) + `(defun ,(intern (concat "helm-projectile-" command)) (&optional arg) + "Use projectile with Helm for finding files in project + +With a prefix ARG invalidates the cache first." + (interactive "P") + (if (projectile-project-p) + (projectile-maybe-invalidate-cache arg) + (unless ,not-require-root + (error "You're not in a project"))) + (let ((helm-ff-transformer-show-only-basename nil) + ;; for consistency, we should just let Projectile take care of ignored files + (helm-boring-file-regexp-list nil)) + (helm :sources ,source + :buffer (concat "*helm projectile: " (projectile-project-name) "*") + :truncate-lines ,truncate-lines-var + :prompt (projectile-prepend-project-name ,prompt))))) + +(helm-projectile-command "switch-project" 'helm-source-projectile-projects "Switch to project: " t) +(helm-projectile-command "find-file" helm-source-projectile-files-and-dired-list "Find file: ") +(helm-projectile-command "find-file-in-known-projects" 'helm-source-projectile-files-in-all-projects-list "Find file in projects: " t) +(helm-projectile-command "find-dir" helm-source-projectile-directories-and-dired-list "Find dir: ") +(helm-projectile-command "recentf" 'helm-source-projectile-recentf-list "Recently visited file: ") +(helm-projectile-command "switch-to-buffer" 'helm-source-projectile-buffers-list "Switch to buffer: " nil helm-buffers-truncate-lines) +(helm-projectile-command "browse-dirty-projects" 'helm-source-projectile-dirty-projects "Select a project: " t) + +(defun helm-projectile--files-display-real (files root) + "Create (DISPLAY . REAL) pairs with FILES and ROOT. + + DISPLAY is the short file name. REAL is the full path." + (cl-loop for display in files + collect (cons display (expand-file-name display root)))) + +;;;###autoload +(defun helm-projectile-find-file-dwim () + "Find file at point based on context." + (interactive) + (let* ((project-root (projectile-project-root)) + (project-files (projectile-current-project-files)) + (files (projectile-select-files project-files))) + (if (= (length files) 1) + (find-file (expand-file-name (car files) (projectile-project-root))) + (helm :sources (helm-build-sync-source "Projectile files" + :candidates (if (> (length files) 1) + (helm-projectile--files-display-real files project-root) + (helm-projectile--files-display-real project-files project-root)) + :fuzzy-match helm-projectile-fuzzy-match + :action-transformer 'helm-find-files-action-transformer + :keymap helm-projectile-find-file-map + :help-message helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-file-actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + :buffer "*helm projectile*" + :truncate-lines helm-projectile-truncate-lines + :prompt (projectile-prepend-project-name "Find file: "))))) + +;;;###autoload +(defun helm-projectile-find-other-file (&optional flex-matching) + "Switch between files with the same name but different extensions using Helm. +With FLEX-MATCHING, match any file that contains the base name of current file. +Other file extensions can be customized with the variable `projectile-other-file-alist'." + (interactive "P") + (let* ((project-root (projectile-project-root)) + (other-files (projectile-get-other-files (buffer-file-name) + flex-matching))) + (if other-files + (if (= (length other-files) 1) + (find-file (expand-file-name (car other-files) project-root)) + (progn + (let* ((helm-ff-transformer-show-only-basename nil)) + (helm :sources (helm-build-sync-source "Projectile other files" + :candidates (helm-projectile--files-display-real other-files project-root) + :keymap helm-projectile-find-file-map + :help-message helm-ff-help-message + :mode-line helm-read-file-name-mode-line-string + :action helm-projectile-file-actions + :persistent-action #'helm-projectile-file-persistent-action + :persistent-help "Preview file") + :buffer "*helm projectile*" + :truncate-lines helm-projectile-truncate-lines + :prompt (projectile-prepend-project-name "Find other file: "))))) + (error "No other file found")))) + +(defcustom helm-projectile-grep-or-ack-actions + '("Find file" helm-grep-action + "Find file other frame" helm-grep-other-frame + (lambda () (and (locate-library "elscreen") + "Find file in Elscreen")) + helm-grep-jump-elscreen + "Save results in grep buffer" helm-grep-save-results + "Find file other window" helm-grep-other-window) + "Available actions for `helm-projectile-grep-or-ack'. +The contents of this list are passed as the arguments to `helm-make-actions'." + :type 'symbol + :group 'helm-projectile) + +(defcustom helm-projectile-set-input-automatically t + "If non-nil, attempt to set search input automatically. +Automatic input selection uses the region (if there is an active +region), otherwise it uses the current symbol at point (if there +is one). Applies to `helm-projectile-grep' and +`helm-projectile-ack' only. If the `helm-ag' package is +installed, then automatic input behavior for `helm-projectile-ag' +can be customized using `helm-ag-insert-at-point'." + :group 'helm-projectile + :type 'boolean) + +(defun helm-projectile-grep-or-ack (&optional dir use-ack-p ack-ignored-pattern ack-executable) + "Perform helm-grep at project root. +DIR directory where to search +USE-ACK-P indicates whether to use ack or not. +ACK-IGNORED-PATTERN is a file regex to exclude from searching. +ACK-EXECUTABLE is the actual ack binary name. +It is usually \"ack\" or \"ack-grep\". +If it is nil, or ack/ack-grep not found then use default grep command." + (let* ((default-directory (or dir (projectile-project-root))) + (helm-ff-default-directory default-directory) + (helm-grep-in-recurse t) + (helm-grep-ignored-files (cl-union (projectile-ignored-files-rel) grep-find-ignored-files)) + (helm-grep-ignored-directories + (cl-union (mapcar 'directory-file-name (projectile-ignored-directories-rel)) + grep-find-ignored-directories)) + (helm-grep-default-command (if use-ack-p + (concat ack-executable " -H --no-group --no-color " ack-ignored-pattern " %p %f") + (if (and projectile-use-git-grep (eq (projectile-project-vcs) 'git)) + helm-projectile-git-grep-command + helm-projectile-grep-command))) + (helm-grep-default-recurse-command helm-grep-default-command)) + + (setq helm-source-grep + (helm-build-async-source + (capitalize (helm-grep-command t)) + :header-name (lambda (_name) + (let ((name (if use-ack-p + "Helm Projectile Ack" + "Helm Projectile Grep"))) + (concat name " " "(C-c ? Help)"))) + :candidates-process 'helm-grep-collect-candidates + :filter-one-by-one 'helm-grep-filter-one-by-one + :candidate-number-limit 9999 + :nohighlight t + ;; We need to specify keymap here and as :keymap arg [1] + ;; to make it available in further resuming. + :keymap helm-grep-map + :history 'helm-grep-history + :action (apply #'helm-make-actions helm-projectile-grep-or-ack-actions) + :persistent-action 'helm-grep-persistent-action + :persistent-help "Jump to line (`C-u' Record in mark ring)" + :requires-pattern 2)) + (helm + :sources 'helm-source-grep + :input (when helm-projectile-set-input-automatically + (if (region-active-p) + (buffer-substring-no-properties (region-beginning) (region-end)) + (thing-at-point 'symbol))) + :buffer (format "*helm %s*" (if use-ack-p + "ack" + "grep")) + :default-directory default-directory + :keymap helm-grep-map + :history 'helm-grep-history + :truncate-lines helm-grep-truncate-lines))) + +;;;###autoload +(defun helm-projectile-on () + "Turn on `helm-projectile' key bindings." + (interactive) + (message "Turn on helm-projectile key bindings") + (helm-projectile-toggle 1)) + +;;;###autoload +(defun helm-projectile-off () + "Turn off `helm-projectile' key bindings." + (interactive) + (message "Turn off helm-projectile key bindings") + (helm-projectile-toggle -1)) + +;;;###autoload +(defun helm-projectile-grep (&optional dir) + "Helm version of `projectile-grep'. +DIR is the project root, if not set then current directory is used" + (interactive) + (let ((project-root (or dir (projectile-project-root) (error "You're not in a project")))) + (funcall 'run-with-timer 0.01 nil + #'helm-projectile-grep-or-ack project-root nil))) + +;;;###autoload +(defun helm-projectile-ack (&optional dir) + "Helm version of projectile-ack." + (interactive) + (let ((project-root (or dir (projectile-project-root) (error "You're not in a project")))) + (let ((ack-ignored (mapconcat + 'identity + (cl-union (mapcar (lambda (path) + (concat "--ignore-dir=" (file-name-nondirectory (directory-file-name path)))) + (projectile-ignored-directories)) + (mapcar (lambda (path) + (concat "--ignore-file=match:" (shell-quote-argument path))) + (append (projectile-ignored-files) (projectile-patterns-to-ignore)))) " ")) + (helm-ack-grep-executable (cond + ((executable-find "ack") "ack") + ((executable-find "ack-grep") "ack-grep") + (t (error "ack or ack-grep is not available"))))) + (funcall 'run-with-timer 0.01 nil + #'helm-projectile-grep-or-ack project-root t ack-ignored helm-ack-grep-executable)))) + +;;;###autoload +(defun helm-projectile-ag (&optional options) + "Helm version of `projectile-ag'." + (interactive (if current-prefix-arg (list (helm-read-string "option: " "" 'helm-ag--extra-options-history)))) + (if (require 'helm-ag nil t) + (if (projectile-project-p) + (let* ((grep-find-ignored-files (cl-union (projectile-ignored-files-rel) grep-find-ignored-files)) + (grep-find-ignored-directories (cl-union (projectile-ignored-directories-rel) grep-find-ignored-directories)) + (ignored (mapconcat (lambda (i) + (concat "--ignore " i)) + (append grep-find-ignored-files grep-find-ignored-directories (cadr (projectile-parse-dirconfig-file))) + " ")) + (helm-ag-base-command (concat helm-ag-base-command " " ignored " " options)) + (current-prefix-arg nil)) + (helm-do-ag (projectile-project-root) (car (projectile-parse-dirconfig-file)))) + (error "You're not in a project")) + (when (yes-or-no-p "`helm-ag' is not installed. Install? ") + (condition-case nil + (progn + (package-install 'helm-ag) + (helm-projectile-ag options)) + (error (error "`helm-ag' is not available. Is MELPA in your `package-archives'?")))))) + +;; Declare/define these to satisfy the byte compiler +(defvar helm-rg-prepend-file-name-line-at-top-of-matches) +(defvar helm-rg-include-file-on-every-match-line) +(declare-function helm-rg "ext:helm-rg") +(declare-function helm-rg--get-thing-at-pt "ext:helm-rg") + +(defun helm-projectile-rg--region-selection () + (when helm-projectile-set-input-automatically + (if (region-active-p) + (buffer-substring-no-properties (region-beginning) (region-end)) + (helm-rg--get-thing-at-pt)))) + +(defun glob-quote (string) + "Quote the special glob characters: *, ?, [, and ]. +STRING the string in which to escape special characters." + (replace-regexp-in-string "[]*?[]" "\\\\\\&" string)) + +;;;###autoload +(defun helm-projectile-rg () + "Projectile version of `helm-rg'." + (interactive) + (if (require 'helm-rg nil t) + (if (projectile-project-p) + (let* ((helm-rg-prepend-file-name-line-at-top-of-matches nil) + (helm-rg-include-file-on-every-match-line t) + (ignored-files (mapcan (lambda (path) + (list "--glob" (concat "!" (glob-quote path)))) + (cl-union (projectile-ignored-files-rel) grep-find-ignored-files))) + (ignored-directories (mapcan (lambda (path) + (list "--glob" (concat "!" (glob-quote path) "/**"))) + (cl-union (mapcar 'directory-file-name (projectile-ignored-directories-rel)) + grep-find-ignored-directories))) + (helm-rg--extra-args `(,@ignored-files ,@ignored-directories))) + (let ((default-directory (projectile-project-root))) + (helm-rg (helm-projectile-rg--region-selection) + nil))) + (error "You're not in a project")) + (when (yes-or-no-p "`helm-rg' is not installed. Install? ") + (condition-case nil + (progn + (package-install 'helm-rg) + (helm-projectile-rg)) + (error "`helm-rg' is not available. Is MELPA in your `package-archives'?"))))) + +(defun helm-projectile-commander-bindings () + (def-projectile-commander-method ?a + "Run ack on project." + (call-interactively 'helm-projectile-ack)) + + (def-projectile-commander-method ?A + "Find ag on project." + (call-interactively 'helm-projectile-ag)) + + (def-projectile-commander-method ?f + "Find file in project." + (helm-projectile-find-file)) + + (def-projectile-commander-method ?b + "Switch to project buffer." + (helm-projectile-switch-to-buffer)) + + (def-projectile-commander-method ?d + "Find directory in project." + (helm-projectile-find-dir)) + + (def-projectile-commander-method ?g + "Run grep on project." + (helm-projectile-grep)) + + (def-projectile-commander-method ?s + "Switch project." + (helm-projectile-switch-project)) + + (def-projectile-commander-method ?e + "Find recently visited file in project." + (helm-projectile-recentf)) + + (def-projectile-commander-method ?V + "Find dirty projects." + (helm-projectile-browse-dirty-projects))) + +;;;###autoload +(defun helm-projectile-toggle (toggle) + "Toggle Helm version of Projectile commands." + (if (> toggle 0) + (progn + (when (eq projectile-switch-project-action #'projectile-find-file) + (setq projectile-switch-project-action #'helm-projectile-find-file)) + (define-key projectile-mode-map [remap projectile-find-other-file] #'helm-projectile-find-other-file) + (define-key projectile-mode-map [remap projectile-find-file] #'helm-projectile-find-file) + (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] #'helm-projectile-find-file-in-known-projects) + (define-key projectile-mode-map [remap projectile-find-file-dwim] #'helm-projectile-find-file-dwim) + (define-key projectile-mode-map [remap projectile-find-dir] #'helm-projectile-find-dir) + (define-key projectile-mode-map [remap projectile-switch-project] #'helm-projectile-switch-project) + (define-key projectile-mode-map [remap projectile-recentf] #'helm-projectile-recentf) + (define-key projectile-mode-map [remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer) + (define-key projectile-mode-map [remap projectile-grep] #'helm-projectile-grep) + (define-key projectile-mode-map [remap projectile-ack] #'helm-projectile-ack) + (define-key projectile-mode-map [remap projectile-ag] #'helm-projectile-ag) + (define-key projectile-mode-map [remap projectile-ripgrep] #'helm-projectile-rg) + (define-key projectile-mode-map [remap projectile-browse-dirty-projects] #'helm-projectile-browse-dirty-projects) + (helm-projectile-commander-bindings)) + (progn + (when (eq projectile-switch-project-action #'helm-projectile-find-file) + (setq projectile-switch-project-action #'projectile-find-file)) + (define-key projectile-mode-map [remap projectile-find-other-file] nil) + (define-key projectile-mode-map [remap projectile-find-file] nil) + (define-key projectile-mode-map [remap projectile-find-file-in-known-projects] nil) + (define-key projectile-mode-map [remap projectile-find-file-dwim] nil) + (define-key projectile-mode-map [remap projectile-find-dir] nil) + (define-key projectile-mode-map [remap projectile-switch-project] nil) + (define-key projectile-mode-map [remap projectile-recentf] nil) + (define-key projectile-mode-map [remap projectile-switch-to-buffer] nil) + (define-key projectile-mode-map [remap projectile-grep] nil) + (define-key projectile-mode-map [remap projectile-ag] nil) + (define-key projectile-mode-map [remap projectile-ripgrep] nil) + (define-key projectile-mode-map [remap projectile-browse-dirty-projects] nil) + (projectile-commander-bindings)))) + +;;;###autoload +(defun helm-projectile (&optional arg) + "Use projectile with Helm instead of ido. + +With a prefix ARG invalidates the cache first. +If invoked outside of a project, displays a list of known projects to jump." + (interactive "P") + (if (not (projectile-project-p)) + (helm-projectile-switch-project arg) + (projectile-maybe-invalidate-cache arg) + (let ((helm-ff-transformer-show-only-basename nil)) + (helm :sources helm-projectile-sources-list + :buffer "*helm projectile*" + :truncate-lines helm-projectile-truncate-lines + :prompt (projectile-prepend-project-name (if (projectile-project-p) + "pattern: " + "Switch to project: ")))))) + +;;;###autoload +(eval-after-load 'projectile + '(progn + (define-key projectile-command-map (kbd "h") #'helm-projectile))) + +(provide 'helm-projectile) + +;;; helm-projectile.el ends here diff --git a/code/elpa/magit-20220821.1819/AUTHORS.md b/code/elpa/magit-20220821.1819/AUTHORS.md new file mode 100644 index 0000000..5eba575 --- /dev/null +++ b/code/elpa/magit-20220821.1819/AUTHORS.md @@ -0,0 +1,386 @@ +The following people have contributed to Magit, including the +libraries `git-commit.el`, `magit-popup.el`, and `with-editor.el` +which are distributed as separate Elpa packages. + +For statistics see https://magit.vc/stats/magit/authors.html. + +Authors +------- + +- Marius Vollmer +- Jonas Bernoulli + +Active Maintainers +------------------ + +- Jonas Bernoulli +- Kyle Meyer + +Former Maintainers +------------------ + +- Nicolas Dudebout +- Noam Postavsky +- Peter J. Weisberg +- Phil Jackson +- Rémi Vanicat +- Yann Hodique + +All Contributors +---------------- + +- Aaron Culich +- Aaron L. Zeng +- Aaron Madlon-Kay +- Abdo Roig-Maranges +- Adam Benanti +- Adam Kruszewski +- Adam Porter +- Adam Spiers +- Adeodato Simó +- Ævar Arnfjörð Bjarmason +- Alan Falloon +- Alban Gruin +- Aleksey Uimanov +- Alexander Gramiak +- Alexander Miller +- Alex Branham +- Alex Dunn +- Alexey Voinov +- Alex Kost +- Alex Ott +- Allen Li +- Andreas Fuchs +- Andreas Liljeqvist +- Andreas Rottmann +- Andrei Chițu +- Andrew Eggenberger +- Andrew Kirkpatrick +- Andrew Psaltis +- Andrew Schwartzmeyer +- Andrey Smirnov +- Andriy Kmit' +- Andy Sawyer +- Angel de Vicente +- Aria Edmonds +- Arialdo Martini +- Arnau Roig Ninerola +- Ashlynn Anderson +- Barak A. Pearlmutter +- Bar Magal +- Bart Bakker +- Basil L. Contovounesios +- Bastian Beischer +- Bastian Beranek +- Benjamin Motz +- Ben North +- Ben Walton +- Bob Uhl +- Boruch Baum +- Bradley Wright +- Brandon W Maister +- Brennan Vincent +- Brian Leung +- Brian Warner +- Bryan Shell +- Buster Copley +- Cameron Chaparro +- Carl Lieberman +- Chillar Anand +- Chris Bernard +- Chris Done +- Chris LaRose +- Chris Moore +- Chris Ring +- Chris Shoemaker +- Christian Dietrich +- Christian Kluge +- Christophe Junke +- Christopher Monsanto +- Clément Pit-Claudel +- Cornelius Mika +- Craig Andera +- Dale Hagglund +- Damien Cassou +- Dan Davison +- Dan Erikson +- Daniel Brockman +- Daniel Farina +- Daniel Fleischer +- Daniel Gröber +- Daniel Hackney +- Daniel Kraus +- Daniel Mai +- Daniel Martín +- Daniel Nagy +- Dan Kessler +- Dan LaManna +- Danny Zhu +- Dato Simó +- David Abrahams +- David Ellison +- David Hull +- David L. Rager +- David Wallin +- Dean Kariniemi +- Dennis Paskorz +- Divye Kapoor +- Dominique Quatravaux +- Duianto Vebotci +- Eli Barzilay +- Eric +- Eric Davis +- Eric Prud'hommeaux +- Eric Schulte +- Erik Anderson +- Evan Torrie +- Evgkeni Sampelnikof +- Eyal Lotem +- Fabian Wiget +- Felix Geller +- Felix Yan +- Feng Li +- Florian Ragwitz +- Franklin Delehelle +- Frédéric Giquel +- Fritz Grabo +- Fritz Stelzer +- Geoff Shannon +- George Kadianakis +- Géza Herman +- Graham Clark +- Graham Dobbins +- Greg A. Woods +- Greg Lucas +- Gregory Heytings +- Greg Sexton +- Greg Steuck +- Guillaume Martres +- Hannu Koivisto +- Hans-Peter Deifel +- Hussein Ait-Lahcen +- Ian Eure +- Ian Milligan +- Ilya Grigoriev +- Ingmar Sittl +- Ingo Lohmar +- Ioan-Adrian Ratiu +- Ivan Brennan +- Jan Tatarik +- Jasper St. Pierre +- Jean-Louis Giordano +- Jeff Bellegarde +- Jeff Dairiki +- Jeremy Meng +- Jesse Alama +- Jim Blandy +- Joakim Jalap +- Johannes Altmanninger +- Johann Klähn +- John Mastro +- John Morris +- John Wiegley +- Jonas Bernoulli +- Jonas Galvão Xavier +- Jonathan Arnett +- Jonathan del Strother +- Jonathan Leech-Pepin +- Jonathan Roes +- Jonathon McKitrick +- Jon Vanderwijk +- Jordan Galby +- Jordan Greenberg +- Jorge Israel Peña +- Josh Elsasser +- Josiah Schwab +- Julien Danjou +- Justin Burkett +- Justin Caratzas +- Justin Guenther +- Justin Thomas +- Kan-Ru Chen +- Kenny Ballou +- Keshav Kini +- Kevin Brubeck Unhammer +- Kevin J. Foley +- Kévin Le Gouguec +- Kimberly Wolk +- Knut Olav Bøhmer +- Kyle Meyer +- Laurent Laffont +- Laverne Schrock +- Leandro Facchinetti +- Lele Gaifax +- Leo Liu +- Leonardo Etcheverry +- Leo Vivier +- Lingchao Xin +- Lin Sun +- Li-Yun Chang +- Lluís Vilanova +- Loic Dachary +- Louis Roché +- Luís Oliveira +- Luke Amdor +- Magnus Malm +- Mak Kolybabi +- Manuel Vázquez Acosta +- Marcel Wolf +- Marc Herbert +- Marcin Bachry +- Marco Craveiro +- Marco Wahl +- Marc Sherry +- Marian Schubert +- Mario Rodas +- Marius Vollmer +- Mark Hepburn +- Mark Karpov +- Mark Oteiza +- Martin Joerg +- Martin Polden +- Matthew Fluet +- Matthew Kraai +- Matthieu Hauglustaine +- Matus Goljer +- Maxim Cournoyer +- Michael Fogleman +- Michael Griffiths +- Michael Heerdegen +- Michal Sojka +- Miciah Masters +- Miles Bader +- Miloš Mošić +- Mitchel Humpherys +- Moritz Bunkus +- Naoya Yamashita +- Natalie Weizenbaum +- Nguyễn Tuấn Anh +- Nic Ferier +- Nick Alcock +- Nick Alexander +- Nick Dimiduk +- Nicklas Lindgren +- Nicolas Dudebout +- Nicolas Petton +- Nicolas Richard +- Nikolay Martynov +- Noam Postavsky +- N. Troy de Freitas +- Ola x Nilsson +- Ole Arndt +- Oleh Krehel +- Orivej Desh +- Óscar Fuentes +- Pancho Horrillo +- Paul Stadig +- Pavel Holejsovsky +- Pekka Pessi +- Peter Eisentraut +- Peter Jaros +- Peter J. Weisberg +- Peter Vasil +- Philippe Cavalaria +- Philippe Vaucher +- Philipp Fehre +- Philipp Haselwarter +- Philipp Stephani +- Philip Weaver +- Phil Jackson +- Phil Sainty +- Pierre Neidhardt +- Pieter Praet +- Prathamesh Sonpatki +- Pritam Baral +- rabio +- Radon Rosborough +- Rafael Laboissiere +- Raimon Grau +- Ramkumar Ramachandra +- Remco van 't Veer +- Rémi Vanicat +- René Stadler +- Richard Kim +- Robert Boone +- Robert Irelan +- Robin Green +- Roey Darwish Dror +- Roger Crew +- Romain Francoise +- Ron Parker +- Roy Crihfield +- Rüdiger Sonderfeld +- Russell Black +- Ryan C. Thompson +- Sam Cedarbaum +- Samuel Bronson +- Samuel W. Flint +- Sanjoy Das +- Sean Allred +- Sean Bryant +- Sean Whitton +- Sebastian Wiesner +- Sébastien Gross +- Seong-Kook Shin +- Sergey Pashinin +- Sergey Vinokurov +- Servilio Afre Puentes +- Shuguang Sun +- Siavash Askari Nasr +- Silent Sphere +- Simon Pintarelli +- Stefan Kangas +- Štěpán Němec +- Steven Chow +- Steven E. Harris +- Steven Thomas +- Steven Vancoillie +- Steve Purcell +- Suhail Shergill +- Sylvain Rousseau +- Syohei Yoshida +- Szunti +- Takafumi Arakaki +- Tassilo Horn +- TEC +- Teemu Likonen +- Teruki Shigitani +- Thierry Volpiatto +- Thomas A Caswell +- Thomas Fini Hansen +- Thomas Frössman +- Thomas Jost +- Thomas Riccardi +- Tibor Simko +- Timo Juhani Lindfors +- Tim Perkins +- Tim Wraight +- Ting-Yu Lin +- Tom Feist +- Toon Claes +- Topi Miettinen +- Troy Hinckley +- Tsuyoshi Kitamoto +- Tunc Uzlu +- Vineet Naik +- Vitaly Ostashov +- Vladimir Ivanov +- Vladimir Panteleev +- Vladimir Sedach +- Wei Huang +- Wilfred Hughes +- Win Treese +- Wojciech Siewierski +- Wouter Bolsterlee +- Xavier Noria +- Xu Chunyang +- Yann Herklotz +- Yann Hodique +- Ynilu +- York Zhao +- Yuichi Higashi +- Yuri Khan +- Zach Latta +- zakora +- Zhu Zihao +- zilongshanren diff --git a/code/elpa/magit-20220821.1819/LICENSE b/code/elpa/magit-20220821.1819/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/code/elpa/magit-20220821.1819/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/code/elpa/magit-20220821.1819/dir b/code/elpa/magit-20220821.1819/dir new file mode 100644 index 0000000..dfdbd71 --- /dev/null +++ b/code/elpa/magit-20220821.1819/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* Magit: (magit). Using Git from Emacs with Magit. diff --git a/code/elpa/magit-20220821.1819/git-rebase.el b/code/elpa/magit-20220821.1819/git-rebase.el new file mode 100644 index 0000000..61cdf37 --- /dev/null +++ b/code/elpa/magit-20220821.1819/git-rebase.el @@ -0,0 +1,861 @@ +;;; git-rebase.el --- Edit Git rebase files -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Phil Jackson +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This package assists the user in editing the list of commits to be +;; rewritten during an interactive rebase. + +;; When the user initiates an interactive rebase, e.g. using "r e" in +;; a Magit buffer or on the command line using "git rebase -i REV", +;; Git invokes the `$GIT_SEQUENCE_EDITOR' (or if that is undefined +;; `$GIT_EDITOR' or even `$EDITOR') letting the user rearrange, drop, +;; reword, edit, and squash commits. + +;; This package provides the major-mode `git-rebase-mode' which makes +;; doing so much more fun, by making the buffer more colorful and +;; providing the following commands: +;; +;; C-c C-c Tell Git to make it happen. +;; C-c C-k Tell Git that you changed your mind, i.e. abort. +;; +;; p Move point to previous line. +;; n Move point to next line. +;; +;; M-p Move the commit at point up. +;; M-n Move the commit at point down. +;; +;; k Drop the commit at point. +;; c Don't drop the commit at point. +;; r Change the message of the commit at point. +;; e Edit the commit at point. +;; s Squash the commit at point, into the one above. +;; f Like "s" but don't also edit the commit message. +;; b Break for editing at this point in the sequence. +;; x Add a script to be run with the commit at point +;; being checked out. +;; z Add noop action at point. +;; +;; SPC Show the commit at point in another buffer. +;; RET Show the commit at point in another buffer and +;; select its window. +;; C-/ Undo last change. +;; +;; Commands for --rebase-merges: +;; l Associate label with current HEAD in sequence. +;; MM Merge specified revisions into HEAD. +;; Mt Toggle whether the merge will invoke an editor +;; before committing. +;; t Reset HEAD to the specified label. + +;; You should probably also read the `git-rebase' manpage. + +;;; Code: + +(require 'magit) + +(require 'easymenu) +(require 'server) +(require 'with-editor) + +(defvar recentf-exclude) + +;;; Options +;;;; Variables + +(defgroup git-rebase nil + "Edit Git rebase sequences." + :link '(info-link "(magit)Editing Rebase Sequences") + :group 'tools) + +(defcustom git-rebase-auto-advance t + "Whether to move to next line after changing a line." + :group 'git-rebase + :type 'boolean) + +(defcustom git-rebase-show-instructions t + "Whether to show usage instructions inside the rebase buffer." + :group 'git-rebase + :type 'boolean) + +(defcustom git-rebase-confirm-cancel t + "Whether confirmation is required to cancel." + :group 'git-rebase + :type 'boolean) + +;;;; Faces + +(defgroup git-rebase-faces nil + "Faces used by Git-Rebase mode." + :group 'faces + :group 'git-rebase) + +(defface git-rebase-hash '((t :inherit magit-hash)) + "Face for commit hashes." + :group 'git-rebase-faces) + +(defface git-rebase-label '((t :inherit magit-refname)) + "Face for labels in label, merge, and reset lines." + :group 'git-rebase-faces) + +(defface git-rebase-description '((t nil)) + "Face for commit descriptions." + :group 'git-rebase-faces) + +(defface git-rebase-action + '((t :inherit font-lock-keyword-face)) + "Face for action keywords." + :group 'git-rebase-faces) + +(defface git-rebase-killed-action + '((t :inherit font-lock-comment-face :strike-through t)) + "Face for commented commit action lines." + :group 'git-rebase-faces) + +(defface git-rebase-comment-hash + '((t :inherit git-rebase-hash :weight bold)) + "Face for commit hashes in commit message comments." + :group 'git-rebase-faces) + +(defface git-rebase-comment-heading + '((t :inherit font-lock-keyword-face)) + "Face for headings in rebase message comments." + :group 'git-rebase-faces) + +;;; Keymaps + +(defvar git-rebase-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map special-mode-map) + (define-key map (kbd "C-m") #'git-rebase-show-commit) + (define-key map (kbd "p") #'git-rebase-backward-line) + (define-key map (kbd "n") #'forward-line) + (define-key map (kbd "M-p") #'git-rebase-move-line-up) + (define-key map (kbd "M-n") #'git-rebase-move-line-down) + (define-key map (kbd "c") #'git-rebase-pick) + (define-key map (kbd "k") #'git-rebase-kill-line) + (define-key map (kbd "C-k") #'git-rebase-kill-line) + (define-key map (kbd "b") #'git-rebase-break) + (define-key map (kbd "e") #'git-rebase-edit) + (define-key map (kbd "l") #'git-rebase-label) + (define-key map (kbd "M M") #'git-rebase-merge) + (define-key map (kbd "M t") #'git-rebase-merge-toggle-editmsg) + (define-key map (kbd "m") #'git-rebase-edit) + (define-key map (kbd "f") #'git-rebase-fixup) + (define-key map (kbd "q") #'undefined) + (define-key map (kbd "r") #'git-rebase-reword) + (define-key map (kbd "w") #'git-rebase-reword) + (define-key map (kbd "s") #'git-rebase-squash) + (define-key map (kbd "t") #'git-rebase-reset) + (define-key map (kbd "x") #'git-rebase-exec) + (define-key map (kbd "y") #'git-rebase-insert) + (define-key map (kbd "z") #'git-rebase-noop) + (define-key map (kbd "SPC") #'git-rebase-show-or-scroll-up) + (define-key map (kbd "DEL") #'git-rebase-show-or-scroll-down) + (define-key map (kbd "C-x C-t") #'git-rebase-move-line-up) + (define-key map [M-up] #'git-rebase-move-line-up) + (define-key map [M-down] #'git-rebase-move-line-down) + (define-key map [remap undo] #'git-rebase-undo) + map) + "Keymap for Git-Rebase mode.") + +(put 'git-rebase-reword :advertised-binding (kbd "r")) +(put 'git-rebase-move-line-up :advertised-binding (kbd "M-p")) +(put 'git-rebase-kill-line :advertised-binding (kbd "k")) + +(easy-menu-define git-rebase-mode-menu git-rebase-mode-map + "Git-Rebase mode menu" + '("Rebase" + ["Pick" git-rebase-pick t] + ["Reword" git-rebase-reword t] + ["Edit" git-rebase-edit t] + ["Squash" git-rebase-squash t] + ["Fixup" git-rebase-fixup t] + ["Kill" git-rebase-kill-line t] + ["Noop" git-rebase-noop t] + ["Execute" git-rebase-exec t] + ["Move Down" git-rebase-move-line-down t] + ["Move Up" git-rebase-move-line-up t] + "---" + ["Cancel" with-editor-cancel t] + ["Finish" with-editor-finish t])) + +(defvar git-rebase-command-descriptions + '((with-editor-finish . "tell Git to make it happen") + (with-editor-cancel . "tell Git that you changed your mind, i.e. abort") + (git-rebase-backward-line . "move point to previous line") + (forward-line . "move point to next line") + (git-rebase-move-line-up . "move the commit at point up") + (git-rebase-move-line-down . "move the commit at point down") + (git-rebase-show-or-scroll-up . "show the commit at point in another buffer") + (git-rebase-show-commit + . "show the commit at point in another buffer and select its window") + (undo . "undo last change") + (git-rebase-kill-line . "drop the commit at point") + (git-rebase-insert . "insert a line for an arbitrary commit") + (git-rebase-noop . "add noop action at point"))) + +;;; Commands + +(defun git-rebase-pick () + "Use commit on current line. +If the region is active, act on all lines touched by the region." + (interactive) + (git-rebase-set-action "pick")) + +(defun git-rebase-reword () + "Edit message of commit on current line. +If the region is active, act on all lines touched by the region." + (interactive) + (git-rebase-set-action "reword")) + +(defun git-rebase-edit () + "Stop at the commit on the current line. +If the region is active, act on all lines touched by the region." + (interactive) + (git-rebase-set-action "edit")) + +(defun git-rebase-squash () + "Meld commit on current line into previous commit, edit message. +If the region is active, act on all lines touched by the region." + (interactive) + (git-rebase-set-action "squash")) + +(defun git-rebase-fixup () + "Meld commit on current line into previous commit, discard its message. +If the region is active, act on all lines touched by the region." + (interactive) + (git-rebase-set-action "fixup")) + +(defvar-local git-rebase-comment-re nil) + +(defvar git-rebase-short-options + '((?b . "break") + (?e . "edit") + (?f . "fixup") + (?l . "label") + (?m . "merge") + (?p . "pick") + (?r . "reword") + (?s . "squash") + (?t . "reset") + (?x . "exec")) + "Alist mapping single key of an action to the full name.") + +(defclass git-rebase-action () + (;; action-type: commit, exec, bare, label, merge + (action-type :initarg :action-type :initform nil) + ;; Examples for each action type: + ;; | action | action options | target | trailer | + ;; |--------+----------------+---------+---------| + ;; | pick | | hash | subject | + ;; | exec | | command | | + ;; | noop | | | | + ;; | reset | | name | subject | + ;; | merge | -C hash | name | subject | + (action :initarg :action :initform nil) + (action-options :initarg :action-options :initform nil) + (target :initarg :target :initform nil) + (trailer :initarg :trailer :initform nil) + (comment-p :initarg :comment-p :initform nil))) + +(defvar git-rebase-line-regexps + `((commit . ,(concat + (regexp-opt '("e" "edit" + "f" "fixup" + "p" "pick" + "r" "reword" + "s" "squash") + "\\(?1:") + " \\(?3:[^ \n]+\\) ?\\(?4:.*\\)")) + (exec . "\\(?1:x\\|exec\\) \\(?3:.*\\)") + (bare . ,(concat (regexp-opt '("b" "break" "noop") "\\(?1:") + " *$")) + (label . ,(concat (regexp-opt '("l" "label" + "t" "reset") + "\\(?1:") + " \\(?3:[^ \n]+\\) ?\\(?4:.*\\)")) + (merge . ,(concat "\\(?1:m\\|merge\\) " + "\\(?:\\(?2:-[cC] [^ \n]+\\) \\)?" + "\\(?3:[^ \n]+\\)" + " ?\\(?4:.*\\)")))) + +;;;###autoload +(defun git-rebase-current-line () + "Parse current line into a `git-rebase-action' instance. +If the current line isn't recognized as a rebase line, an +instance with all nil values is returned." + (save-excursion + (goto-char (line-beginning-position)) + (if-let ((re-start (concat "^\\(?5:" (regexp-quote comment-start) + "\\)? *")) + (type (seq-some (lambda (arg) + (let ((case-fold-search nil)) + (and (looking-at (concat re-start (cdr arg))) + (car arg)))) + git-rebase-line-regexps))) + (git-rebase-action + :action-type type + :action (and-let* ((action (match-string-no-properties 1))) + (or (cdr (assoc action git-rebase-short-options)) + action)) + :action-options (match-string-no-properties 2) + :target (match-string-no-properties 3) + :trailer (match-string-no-properties 4) + :comment-p (and (match-string 5) t)) + ;; Use default empty class rather than nil to ease handling. + (git-rebase-action)))) + +(defun git-rebase-set-action (action) + "Set action of commit line to ACTION. +If the region is active, operate on all lines that it touches. +Otherwise, operate on the current line. As a special case, an +ACTION of nil comments the rebase line, regardless of its action +type." + (pcase (git-rebase-region-bounds t) + (`(,beg ,end) + (let ((end-marker (copy-marker end)) + (pt-below-p (and mark-active (< (mark) (point))))) + (set-marker-insertion-type end-marker t) + (goto-char beg) + (while (< (point) end-marker) + (with-slots (action-type target trailer comment-p) + (git-rebase-current-line) + (cond + ((and action (eq action-type 'commit)) + (let ((inhibit-read-only t)) + (magit-delete-line) + (insert (concat action " " target " " trailer "\n")))) + ((and action-type (not (or action comment-p))) + (let ((inhibit-read-only t)) + (insert comment-start " ")) + (forward-line)) + (t + ;; In the case of --rebase-merges, commit lines may have + ;; other lines with other action types, empty lines, and + ;; "Branch" comments interspersed. Move along. + (forward-line))))) + (goto-char + (if git-rebase-auto-advance + end-marker + (if pt-below-p (1- end-marker) beg))) + (goto-char (line-beginning-position)))) + (_ (ding)))) + +(defun git-rebase-line-p (&optional pos) + (save-excursion + (when pos (goto-char pos)) + (and (oref (git-rebase-current-line) action-type) + t))) + +(defun git-rebase-region-bounds (&optional fallback) + "Return region bounds if both ends touch rebase lines. +Each bound is extended to include the entire line touched by the +point or mark. If the region isn't active and FALLBACK is +non-nil, return the beginning and end of the current rebase line, +if any." + (cond + ((use-region-p) + (let ((beg (save-excursion (goto-char (region-beginning)) + (line-beginning-position))) + (end (save-excursion (goto-char (region-end)) + (line-end-position)))) + (when (and (git-rebase-line-p beg) + (git-rebase-line-p end)) + (list beg (1+ end))))) + ((and fallback (git-rebase-line-p)) + (list (line-beginning-position) + (1+ (line-end-position)))))) + +(defun git-rebase-move-line-down (n) + "Move the current commit (or command) N lines down. +If N is negative, move the commit up instead. With an active +region, move all the lines that the region touches, not just the +current line." + (interactive "p") + (pcase-let* ((`(,beg ,end) + (or (git-rebase-region-bounds) + (list (line-beginning-position) + (1+ (line-end-position))))) + (pt-offset (- (point) beg)) + (mark-offset (and mark-active (- (mark) beg)))) + (save-restriction + (narrow-to-region + (point-min) + (1- + (if git-rebase-show-instructions + (save-excursion + (goto-char (point-min)) + (while (or (git-rebase-line-p) + ;; The output for --rebase-merges has empty + ;; lines and "Branch" comments interspersed. + (looking-at-p "^$") + (looking-at-p (concat git-rebase-comment-re + " Branch"))) + (forward-line)) + (line-beginning-position)) + (point-max)))) + (if (or (and (< n 0) (= beg (point-min))) + (and (> n 0) (= end (point-max))) + (> end (point-max))) + (ding) + (goto-char (if (< n 0) beg end)) + (forward-line n) + (atomic-change-group + (let ((inhibit-read-only t)) + (insert (delete-and-extract-region beg end))) + (let ((new-beg (- (point) (- end beg)))) + (when (use-region-p) + (setq deactivate-mark nil) + (set-mark (+ new-beg mark-offset))) + (goto-char (+ new-beg pt-offset)))))))) + +(defun git-rebase-move-line-up (n) + "Move the current commit (or command) N lines up. +If N is negative, move the commit down instead. With an active +region, move all the lines that the region touches, not just the +current line." + (interactive "p") + (git-rebase-move-line-down (- n))) + +(defun git-rebase-highlight-region (start end window rol) + (let ((inhibit-read-only t) + (deactivate-mark nil) + (bounds (git-rebase-region-bounds))) + (mapc #'delete-overlay magit-section-highlight-overlays) + (when bounds + (magit-section-make-overlay (car bounds) (cadr bounds) + 'magit-section-heading-selection)) + (if (and bounds (not magit-section-keep-region-overlay)) + (funcall (default-value 'redisplay-unhighlight-region-function) rol) + (funcall (default-value 'redisplay-highlight-region-function) + start end window rol)))) + +(defun git-rebase-unhighlight-region (rol) + (mapc #'delete-overlay magit-section-highlight-overlays) + (funcall (default-value 'redisplay-unhighlight-region-function) rol)) + +(defun git-rebase-kill-line () + "Kill the current action line. +If the region is active, act on all lines touched by the region." + (interactive) + (git-rebase-set-action nil)) + +(defun git-rebase-insert (rev) + "Read an arbitrary commit and insert it below current line." + (interactive (list (magit-read-branch-or-commit "Insert revision"))) + (forward-line) + (--if-let (magit-rev-format "%h %s" rev) + (let ((inhibit-read-only t)) + (insert "pick " it ?\n)) + (user-error "Unknown revision"))) + +(defun git-rebase-set-noncommit-action (action value-fn arg) + (goto-char (line-beginning-position)) + (pcase-let* ((inhibit-read-only t) + (`(,initial ,trailer ,comment-p) + (and (not arg) + (with-slots ((ln-action action) + target trailer comment-p) + (git-rebase-current-line) + (and (equal ln-action action) + (list target trailer comment-p))))) + (value (funcall value-fn initial))) + (pcase (list value initial comment-p) + (`("" nil ,_) + (ding)) + (`("" ,_ ,_) + (magit-delete-line)) + (_ + (if initial + (magit-delete-line) + (forward-line)) + (insert (concat action " " value + (and (equal value initial) + trailer + (concat " " trailer)) + "\n")) + (unless git-rebase-auto-advance + (forward-line -1)))))) + +(defun git-rebase-exec (arg) + "Insert a shell command to be run after the current commit. + +If there already is such a command on the current line, then edit +that instead. With a prefix argument insert a new command even +when there already is one on the current line. With empty input +remove the command on the current line, if any." + (interactive "P") + (git-rebase-set-noncommit-action + "exec" + (lambda (initial) (read-shell-command "Execute: " initial)) + arg)) + +(defun git-rebase-label (arg) + "Add a label after the current commit. +If there already is a label on the current line, then edit that +instead. With a prefix argument, insert a new label even when +there is already a label on the current line. With empty input, +remove the label on the current line, if any." + (interactive "P") + (git-rebase-set-noncommit-action + "label" + (lambda (initial) + (read-from-minibuffer + "Label: " initial magit-minibuffer-local-ns-map)) + arg)) + +(defun git-rebase-buffer-labels () + (let (labels) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "^\\(?:l\\|label\\) \\([^ \n]+\\)" nil t) + (push (match-string-no-properties 1) labels))) + (nreverse labels))) + +(defun git-rebase-reset (arg) + "Reset the current HEAD to a label. +If there already is a reset command on the current line, then +edit that instead. With a prefix argument, insert a new reset +line even when point is already on a reset line. With empty +input, remove the reset command on the current line, if any." + (interactive "P") + (git-rebase-set-noncommit-action + "reset" + (lambda (initial) + (or (magit-completing-read "Label" (git-rebase-buffer-labels) + nil t initial) + "")) + arg)) + +(defun git-rebase-merge (arg) + "Add a merge command after the current commit. +If there is already a merge command on the current line, then +replace that command instead. With a prefix argument, insert a +new merge command even when there is already one on the current +line. With empty input, remove the merge command on the current +line, if any." + (interactive "P") + (git-rebase-set-noncommit-action + "merge" + (lambda (_) + (or (magit-completing-read "Merge" (git-rebase-buffer-labels)) + "")) + arg)) + +(defun git-rebase-merge-toggle-editmsg () + "Toggle whether an editor is invoked when performing the merge at point. +When a merge command uses a lower-case -c, the message for the +specified commit will be opened in an editor before creating the +commit. For an upper-case -C, the message will be used as is." + (interactive) + (with-slots (action-type target action-options trailer) + (git-rebase-current-line) + (if (eq action-type 'merge) + (let ((inhibit-read-only t)) + (magit-delete-line) + (insert + (format "merge %s %s %s\n" + (replace-regexp-in-string + "-[cC]" (lambda (c) + (if (equal c "-c") "-C" "-c")) + action-options t t) + target + trailer))) + (ding)))) + +(defun git-rebase-set-bare-action (action arg) + (goto-char (line-beginning-position)) + (with-slots ((ln-action action) comment-p) + (git-rebase-current-line) + (let ((same-action-p (equal action ln-action)) + (inhibit-read-only t)) + (when (or arg + (not ln-action) + (not same-action-p) + (and same-action-p comment-p)) + (unless (or arg (not same-action-p)) + (magit-delete-line)) + (insert action ?\n) + (unless git-rebase-auto-advance + (forward-line -1)))))) + +(defun git-rebase-noop (&optional arg) + "Add noop action at point. + +If the current line already contains a noop action, leave it +unchanged. If there is a commented noop action present, remove +the comment. Otherwise add a new noop action. With a prefix +argument insert a new noop action regardless of what is already +present on the current line. + +A noop action can be used to make git perform a rebase even if +no commits are selected. Without the noop action present, git +would see an empty file and therefore do nothing." + (interactive "P") + (git-rebase-set-bare-action "noop" arg)) + +(defun git-rebase-break (&optional arg) + "Add break action at point. + +If there is a commented break action present, remove the comment. +If the current line already contains a break action, add another +break action only if a prefix argument is given. + +A break action can be used to interrupt the rebase at the +specified point. It is particularly useful for pausing before +the first commit in the sequence. For other cases, the +equivalent behavior can be achieved with `git-rebase-edit'." + (interactive "P") + (git-rebase-set-bare-action "break" arg)) + +(defun git-rebase-undo (&optional arg) + "Undo some previous changes. +Like `undo' but works in read-only buffers." + (interactive "P") + (let ((inhibit-read-only t)) + (undo arg))) + +(defun git-rebase--show-commit (&optional scroll) + (let ((magit--disable-save-buffers t)) + (save-excursion + (goto-char (line-beginning-position)) + (--if-let (with-slots (action-type target) (git-rebase-current-line) + (and (eq action-type 'commit) + target)) + (pcase scroll + ('up (magit-diff-show-or-scroll-up)) + ('down (magit-diff-show-or-scroll-down)) + (_ (apply #'magit-show-commit it + (magit-diff-arguments 'magit-revision-mode)))) + (ding))))) + +(defun git-rebase-show-commit () + "Show the commit on the current line if any." + (interactive) + (git-rebase--show-commit)) + +(defun git-rebase-show-or-scroll-up () + "Update the commit buffer for commit on current line. + +Either show the commit at point in the appropriate buffer, or if +that buffer is already being displayed in the current frame and +contains information about that commit, then instead scroll the +buffer up." + (interactive) + (git-rebase--show-commit 'up)) + +(defun git-rebase-show-or-scroll-down () + "Update the commit buffer for commit on current line. + +Either show the commit at point in the appropriate buffer, or if +that buffer is already being displayed in the current frame and +contains information about that commit, then instead scroll the +buffer down." + (interactive) + (git-rebase--show-commit 'down)) + +(defun git-rebase-backward-line (&optional n) + "Move N lines backward (forward if N is negative). +Like `forward-line' but go into the opposite direction." + (interactive "p") + (forward-line (- (or n 1)))) + +;;; Mode + +;;;###autoload +(define-derived-mode git-rebase-mode special-mode "Git Rebase" + "Major mode for editing of a Git rebase file. + +Rebase files are generated when you run \"git rebase -i\" or run +`magit-interactive-rebase'. They describe how Git should perform +the rebase. See the documentation for git-rebase (e.g., by +running \"man git-rebase\" at the command line) for details." + :group 'git-rebase + (setq comment-start (or (magit-get "core.commentChar") "#")) + (setq git-rebase-comment-re (concat "^" (regexp-quote comment-start))) + (setq font-lock-defaults (list (git-rebase-mode-font-lock-keywords) t t)) + (unless git-rebase-show-instructions + (let ((inhibit-read-only t)) + (flush-lines git-rebase-comment-re))) + (unless with-editor-mode + ;; Maybe already enabled when using `shell-command' or an Emacs shell. + (with-editor-mode 1)) + (when git-rebase-confirm-cancel + (add-hook 'with-editor-cancel-query-functions + #'git-rebase-cancel-confirm nil t)) + (setq-local redisplay-highlight-region-function #'git-rebase-highlight-region) + (setq-local redisplay-unhighlight-region-function #'git-rebase-unhighlight-region) + (add-hook 'with-editor-pre-cancel-hook #'git-rebase-autostash-save nil t) + (add-hook 'with-editor-post-cancel-hook #'git-rebase-autostash-apply nil t) + (setq imenu-prev-index-position-function + #'magit-imenu--rebase-prev-index-position-function) + (setq imenu-extract-index-name-function + #'magit-imenu--rebase-extract-index-name-function) + (when (boundp 'save-place) + (setq save-place nil))) + +(defun git-rebase-cancel-confirm (force) + (or (not (buffer-modified-p)) + force + (magit-confirm 'abort-rebase "Abort this rebase" nil 'noabort))) + +(defun git-rebase-autostash-save () + (--when-let (magit-file-line (magit-git-dir "rebase-merge/autostash")) + (push (cons 'stash it) with-editor-cancel-alist))) + +(defun git-rebase-autostash-apply () + (--when-let (cdr (assq 'stash with-editor-cancel-alist)) + (magit-stash-apply it))) + +(defun git-rebase-match-comment-line (limit) + (re-search-forward (concat git-rebase-comment-re ".*") limit t)) + +(defun git-rebase-mode-font-lock-keywords () + "Font lock keywords for Git-Rebase mode." + `((,(concat "^" (cdr (assq 'commit git-rebase-line-regexps))) + (1 'git-rebase-action) + (3 'git-rebase-hash) + (4 'git-rebase-description)) + (,(concat "^" (cdr (assq 'exec git-rebase-line-regexps))) + (1 'git-rebase-action) + (3 'git-rebase-description)) + (,(concat "^" (cdr (assq 'bare git-rebase-line-regexps))) + (1 'git-rebase-action)) + (,(concat "^" (cdr (assq 'label git-rebase-line-regexps))) + (1 'git-rebase-action) + (3 'git-rebase-label) + (4 'font-lock-comment-face)) + ("^\\(m\\(?:erge\\)?\\) -[Cc] \\([^ \n]+\\) \\([^ \n]+\\)\\( #.*\\)?" + (1 'git-rebase-action) + (2 'git-rebase-hash) + (3 'git-rebase-label) + (4 'font-lock-comment-face)) + ("^\\(m\\(?:erge\\)?\\) \\([^ \n]+\\)" + (1 'git-rebase-action) + (2 'git-rebase-label)) + (,(concat git-rebase-comment-re " *" + (cdr (assq 'commit git-rebase-line-regexps))) + 0 'git-rebase-killed-action t) + (git-rebase-match-comment-line 0 'font-lock-comment-face) + ("\\[[^[]*\\]" + 0 'magit-keyword t) + ("\\(?:fixup!\\|squash!\\)" + 0 'magit-keyword-squash t) + (,(format "^%s Rebase \\([^ ]*\\) onto \\([^ ]*\\)" comment-start) + (1 'git-rebase-comment-hash t) + (2 'git-rebase-comment-hash t)) + (,(format "^%s \\(Commands:\\)" comment-start) + (1 'git-rebase-comment-heading t)) + (,(format "^%s Branch \\(.*\\)" comment-start) + (1 'git-rebase-label t)))) + +(defun git-rebase-mode-show-keybindings () + "Modify the \"Commands:\" section of the comment Git generates +at the bottom of the file so that in place of the one-letter +abbreviation for the command, it shows the command's keybinding. +By default, this is the same except for the \"pick\" command." + (let ((inhibit-read-only t)) + (save-excursion + (goto-char (point-min)) + (when (and git-rebase-show-instructions + (re-search-forward + (concat git-rebase-comment-re "\\s-+p, pick") + nil t)) + (goto-char (line-beginning-position)) + (pcase-dolist (`(,cmd . ,desc) git-rebase-command-descriptions) + (insert (format (propertize "%s %s %s\n" + 'font-lock-face 'font-lock-comment-face) + comment-start + (string-pad + (substitute-command-keys (format "\\[%s]" cmd)) 8) + desc))) + (while (re-search-forward + (concat git-rebase-comment-re "\\(?:" + "\\( \\.? *\\)\\|" + "\\( +\\)\\([^\n,],\\) \\([^\n ]+\\) \\)") + nil t) + (if (match-string 1) + (replace-match (make-string 10 ?\s) t t nil 1) + (let ((cmd (intern (concat "git-rebase-" (match-string 4))))) + (if (not (fboundp cmd)) + (delete-region (line-beginning-position) + (1+ (line-end-position))) + (add-text-properties (line-beginning-position) + (1+ (line-end-position)) + '(font-lock-face font-lock-comment-face)) + (replace-match " " t t nil 2) + (replace-match + (string-pad + (mapconcat (lambda (key) + (save-match-data + (substitute-command-keys + (format "\\`%s'" (key-description key))))) + (cl-remove-if (lambda (key) (eq (elt key 0) 'menu-bar)) + (reverse (where-is-internal + cmd git-rebase-mode-map))) + ", ") + 8) + t t nil 3))))))))) + +(add-hook 'git-rebase-mode-hook #'git-rebase-mode-show-keybindings t) + +(defun git-rebase-mode-disable-before-save-hook () + (set (make-local-variable 'before-save-hook) nil)) + +(add-hook 'git-rebase-mode-hook #'git-rebase-mode-disable-before-save-hook) + +;;;###autoload +(defconst git-rebase-filename-regexp "/git-rebase-todo\\'") +;;;###autoload +(add-to-list 'auto-mode-alist + (cons git-rebase-filename-regexp #'git-rebase-mode)) + +(add-to-list 'with-editor-server-window-alist + (cons git-rebase-filename-regexp #'switch-to-buffer)) + +(with-eval-after-load 'recentf + (add-to-list 'recentf-exclude git-rebase-filename-regexp)) + +(add-to-list 'with-editor-file-name-history-exclude git-rebase-filename-regexp) + +;;; Imenu Support + +(defun magit-imenu--rebase-prev-index-position-function () + "Move point to previous commit in git-rebase buffer. +Used as a value for `imenu-prev-index-position-function'." + (catch 'found + (while (not (bobp)) + (git-rebase-backward-line) + (when (git-rebase-line-p) + (throw 'found t))))) + +(defun magit-imenu--rebase-extract-index-name-function () + "Return imenu name for line at point. +Point should be at the beginning of the line. This function +is used as a value for `imenu-extract-index-name-function'." + (buffer-substring-no-properties (line-beginning-position) + (line-end-position))) + +;;; _ +(provide 'git-rebase) +;;; git-rebase.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-apply.el b/code/elpa/magit-20220821.1819/magit-apply.el new file mode 100644 index 0000000..bd18f78 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-apply.el @@ -0,0 +1,813 @@ +;;; magit-apply.el --- Apply Git diffs -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements commands for applying Git diffs or parts +;; of such a diff. The supported "apply variants" are apply, stage, +;; unstage, discard, and reverse - more than Git itself knows about, +;; at least at the porcelain level. + +;;; Code: + +(require 'magit-core) +(require 'magit-diff) +(require 'magit-wip) + +(require 'transient) ; See #3732. + +;; For `magit-apply' +(declare-function magit-am "magit-sequence" () t) +(declare-function magit-patch-apply "magit-patch" () t) +;; For `magit-discard-files' +(declare-function magit-checkout-stage "magit-merge" (file arg)) +(declare-function magit-checkout-read-stage "magit-merge" (file)) +(defvar auto-revert-verbose) +;; For `magit-stage-untracked' +(declare-function magit-submodule-add-1 "magit-submodule" + (url &optional path name args)) +(declare-function magit-submodule-read-name-for-path "magit-submodule" + (path &optional prefer-short)) +(defvar borg-user-emacs-directory) + +(cl-eval-when (compile load) + (when (< emacs-major-version 26) + (defalias 'smerge-keep-upper 'smerge-keep-mine) + (defalias 'smerge-keep-lower 'smerge-keep-other))) + +;;; Options + +(defcustom magit-delete-by-moving-to-trash t + "Whether Magit uses the system's trash can. + +You should absolutely not disable this and also remove `discard' +from `magit-no-confirm'. You shouldn't do that even if you have +all of the Magit-Wip modes enabled, because those modes do not +track any files that are not tracked in the proper branch." + :package-version '(magit . "2.1.0") + :group 'magit-essentials + :type 'boolean) + +(defcustom magit-unstage-committed t + "Whether unstaging a committed change reverts it instead. + +A committed change cannot be unstaged, because staging and +unstaging are actions that are concerned with the differences +between the index and the working tree, not with committed +changes. + +If this option is non-nil (the default), then typing \"u\" +\(`magit-unstage') on a committed change, causes it to be +reversed in the index but not the working tree. For more +information see command `magit-reverse-in-index'." + :package-version '(magit . "2.4.1") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-reverse-atomically nil + "Whether to reverse changes atomically. + +If some changes can be reversed while others cannot, then nothing +is reversed if the value of this option is non-nil. But when it +is nil, then the changes that can be reversed are reversed and +for the other changes diff files are created that contain the +rejected reversals." + :package-version '(magit . "2.7.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-post-stage-hook nil + "Hook run after staging changes. +This hook is run by `magit-refresh' if `this-command' +is a member of `magit-post-stage-hook-commands'." + :package-version '(magit . "2.90.0") + :group 'magit-commands + :type 'hook) + +(defcustom magit-post-unstage-hook nil + "Hook run after unstaging changes. +This hook is run by `magit-refresh' if `this-command' +is a member of `magit-post-unstage-hook-commands'." + :package-version '(magit . "2.90.0") + :group 'magit-commands + :type 'hook) + +;;; Commands +;;;; Apply + +(defun magit-apply (&rest args) + "Apply the change at point to the working tree. +With a prefix argument fallback to a 3-way merge. Doing +so causes the change to be applied to the index as well." + (interactive (and current-prefix-arg (list "--3way"))) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(,(or 'unstaged 'staged) ,_) + (user-error "Change is already in the working tree")) + (`(untracked ,(or 'file 'files)) + (call-interactively #'magit-am)) + (`(,_ region) (magit-apply-region it args)) + (`(,_ hunk) (magit-apply-hunk it args)) + (`(,_ hunks) (magit-apply-hunks it args)) + (`(rebase-sequence file) + (call-interactively #'magit-patch-apply)) + (`(,_ file) (magit-apply-diff it args)) + (`(,_ files) (magit-apply-diffs it args))))) + +(defun magit-apply--section-content (section) + (buffer-substring-no-properties (if (magit-hunk-section-p section) + (oref section start) + (oref section content)) + (oref section end))) + +(defun magit-apply-diffs (sections &rest args) + (setq sections (magit-apply--get-diffs sections)) + (magit-apply-patch sections args + (mapconcat + (lambda (s) + (concat (magit-diff-file-header s) + (magit-apply--section-content s))) + sections ""))) + +(defun magit-apply-diff (section &rest args) + (setq section (car (magit-apply--get-diffs (list section)))) + (magit-apply-patch section args + (concat (magit-diff-file-header section) + (magit-apply--section-content section)))) + +(defun magit-apply--adjust-hunk-new-starts (hunks) + "Adjust new line numbers in headers of HUNKS for partial application. +HUNKS should be a list of ordered, contiguous hunks to be applied +from a file. For example, if there is a sequence of hunks with +the headers + + @@ -2,6 +2,7 @@ + @@ -10,6 +11,7 @@ + @@ -18,6 +20,7 @@ + +and only the second and third are to be applied, they would be +adjusted as \"@@ -10,6 +10,7 @@\" and \"@@ -18,6 +19,7 @@\"." + (let* ((first-hunk (car hunks)) + (offset (if (string-match diff-hunk-header-re-unified first-hunk) + (- (string-to-number (match-string 3 first-hunk)) + (string-to-number (match-string 1 first-hunk))) + (error "Header hunks have to be applied individually")))) + (if (= offset 0) + hunks + (mapcar (lambda (hunk) + (if (string-match diff-hunk-header-re-unified hunk) + (replace-match (number-to-string + (- (string-to-number (match-string 3 hunk)) + offset)) + t t hunk 3) + (error "Hunk does not have expected header"))) + hunks)))) + +(defun magit-apply--adjust-hunk-new-start (hunk) + (car (magit-apply--adjust-hunk-new-starts (list hunk)))) + +(defun magit-apply-hunks (sections &rest args) + (let ((section (oref (car sections) parent))) + (when (string-match "^diff --cc" (oref section value)) + (user-error "Cannot un-/stage resolution hunks. Stage the whole file")) + (magit-apply-patch + section args + (concat (oref section header) + (mapconcat #'identity + (magit-apply--adjust-hunk-new-starts + (mapcar #'magit-apply--section-content sections)) + ""))))) + +(defun magit-apply-hunk (section &rest args) + (when (string-match "^diff --cc" (magit-section-parent-value section)) + (user-error "Cannot un-/stage resolution hunks. Stage the whole file")) + (let* ((header (car (oref section value))) + (header (and (symbolp header) header)) + (content (magit-apply--section-content section))) + (magit-apply-patch + (oref section parent) args + (concat (magit-diff-file-header section (not (eq header 'rename))) + (if header + content + (magit-apply--adjust-hunk-new-start content)))))) + +(defun magit-apply-region (section &rest args) + (when (string-match "^diff --cc" (magit-section-parent-value section)) + (user-error "Cannot un-/stage resolution hunks. Stage the whole file")) + (magit-apply-patch (oref section parent) args + (concat (magit-diff-file-header section) + (magit-apply--adjust-hunk-new-start + (magit-diff-hunk-region-patch section args))))) + +(defun magit-apply-patch (section:s args patch) + (let* ((files (if (atom section:s) + (list (oref section:s value)) + (--map (oref it value) section:s))) + (command (symbol-name this-command)) + (command (if (and command (string-match "^magit-\\([^-]+\\)" command)) + (match-string 1 command) + "apply")) + (ignore-context (magit-diff-ignore-any-space-p))) + (unless (magit-diff-context-p) + (user-error "Not enough context to apply patch. Increase the context")) + (when (and magit-wip-before-change-mode (not magit-inhibit-refresh)) + (magit-wip-commit-before-change files (concat " before " command))) + (with-temp-buffer + (insert patch) + (magit-run-git-with-input + "apply" args "-p0" + (and ignore-context "-C0") + "--ignore-space-change" "-")) + (unless magit-inhibit-refresh + (when magit-wip-after-apply-mode + (magit-wip-commit-after-apply files (concat " after " command))) + (magit-refresh)))) + +(defun magit-apply--get-selection () + (or (magit-region-sections '(hunk file module) t) + (let ((section (magit-current-section))) + (pcase (oref section type) + ((or 'hunk 'file 'module) section) + ((or 'staged 'unstaged 'untracked + 'stashed-index 'stashed-worktree 'stashed-untracked) + (oref section children)) + (_ (user-error "Cannot apply this, it's not a change")))))) + +(defun magit-apply--get-diffs (sections) + (magit-section-case + ([file diffstat] + (--map (or (magit-get-section + (append `((file . ,(oref it value))) + (magit-section-ident magit-root-section))) + (error "Cannot get required diff headers")) + sections)) + (t sections))) + +(defun magit-apply--diff-ignores-whitespace-p () + (and (cl-intersection magit-buffer-diff-args + '("--ignore-space-at-eol" + "--ignore-space-change" + "--ignore-all-space" + "--ignore-blank-lines") + :test #'equal) + t)) + +;;;; Stage + +(defun magit-stage (&optional intent) + "Add the change at point to the staging area. +With a prefix argument, INTENT, and an untracked file (or files) +at point, stage the file but not its content." + (interactive "P") + (--if-let (and (derived-mode-p 'magit-mode) (magit-apply--get-selection)) + (pcase (list (magit-diff-type) + (magit-diff-scope) + (magit-apply--diff-ignores-whitespace-p)) + (`(untracked ,_ ,_) (magit-stage-untracked intent)) + (`(unstaged region ,_) (magit-apply-region it "--cached")) + (`(unstaged hunk ,_) (magit-apply-hunk it "--cached")) + (`(unstaged hunks ,_) (magit-apply-hunks it "--cached")) + ('(unstaged file t) (magit-apply-diff it "--cached")) + ('(unstaged files t) (magit-apply-diffs it "--cached")) + ('(unstaged list t) (magit-apply-diffs it "--cached")) + ('(unstaged file nil) (magit-stage-1 "-u" (list (oref it value)))) + ('(unstaged files nil) (magit-stage-1 "-u" (magit-region-values nil t))) + ('(unstaged list nil) (magit-stage-modified)) + (`(staged ,_ ,_) (user-error "Already staged")) + (`(committed ,_ ,_) (user-error "Cannot stage committed changes")) + (`(undefined ,_ ,_) (user-error "Cannot stage this change"))) + (call-interactively #'magit-stage-file))) + +;;;###autoload +(defun magit-stage-file (file) + "Stage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be staged. Otherwise stage the file at point without +requiring confirmation." + (interactive + (let* ((atpoint (magit-section-value-if 'file)) + (current (magit-file-relative-name)) + (choices (nconc (magit-unstaged-files) + (magit-untracked-files))) + (default (car (member (or atpoint current) choices)))) + (list (if (or current-prefix-arg (not default)) + (magit-completing-read "Stage file" choices + nil t nil nil default) + default)))) + (magit-with-toplevel + (magit-stage-1 nil (list file)))) + +;;;###autoload +(defun magit-stage-modified (&optional all) + "Stage all changes to files modified in the worktree. +Stage all new content of tracked files and remove tracked files +that no longer exist in the working tree from the index also. +With a prefix argument also stage previously untracked (but not +ignored) files." + (interactive "P") + (when (magit-anything-staged-p) + (magit-confirm 'stage-all-changes)) + (magit-with-toplevel + (magit-stage-1 (if all "--all" "-u") magit-buffer-diff-files))) + +(defun magit-stage-1 (arg &optional files) + (magit-wip-commit-before-change files " before stage") + (magit-run-git "add" arg (if files (cons "--" files) ".")) + (when magit-auto-revert-mode + (mapc #'magit-turn-on-auto-revert-mode-if-desired files)) + (magit-wip-commit-after-apply files " after stage")) + +(defun magit-stage-untracked (&optional intent) + (let* ((section (magit-current-section)) + (files (pcase (magit-diff-scope) + ('file (list (oref section value))) + ('files (magit-region-values nil t)) + ('list (magit-untracked-files)))) + plain repos) + (dolist (file files) + (if (and (not (file-symlink-p file)) + (magit-git-repo-p file t)) + (push file repos) + (push file plain))) + (magit-wip-commit-before-change files " before stage") + (when plain + (magit-run-git "add" (and intent "--intent-to-add") + "--" plain) + (when magit-auto-revert-mode + (mapc #'magit-turn-on-auto-revert-mode-if-desired plain))) + (dolist (repo repos) + (save-excursion + (goto-char (oref (magit-get-section + `((file . ,repo) (untracked) (status))) + start)) + (when (and (fboundp 'borg-assimilate) + (fboundp 'borg--maybe-absorb-gitdir) + (fboundp 'borg--sort-submodule-sections)) + (let* ((topdir (magit-toplevel)) + (url (let ((default-directory + (file-name-as-directory (expand-file-name repo)))) + (or (magit-get "remote" (magit-get-some-remote) "url") + (concat (file-name-as-directory ".") repo)))) + (package + (and (equal borg-user-emacs-directory topdir) + (file-name-nondirectory (directory-file-name repo))))) + (if (and package + (y-or-n-p (format "Also assimilate `%s' drone?" package))) + (borg-assimilate package url) + (magit-submodule-add-1 + url repo (magit-submodule-read-name-for-path repo package)) + (when package + (borg--sort-submodule-sections + (expand-file-name ".gitmodules" topdir)) + (let ((default-directory borg-user-emacs-directory)) + (borg--maybe-absorb-gitdir package)))))))) + (magit-wip-commit-after-apply files " after stage"))) + +(defvar magit-post-stage-hook-commands + '(magit-stage magit-stage-file magit-stage-modified)) + +(defun magit-run-post-stage-hook () + (when (memq this-command magit-post-stage-hook-commands) + (magit-run-hook-with-benchmark 'magit-post-stage-hook))) + +;;;; Unstage + +(defun magit-unstage () + "Remove the change at point from the staging area." + (interactive) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) + (magit-diff-scope) + (magit-apply--diff-ignores-whitespace-p)) + (`(untracked ,_ ,_) (user-error "Cannot unstage untracked changes")) + (`(unstaged file ,_) (magit-unstage-intent (list (oref it value)))) + (`(unstaged files ,_) (magit-unstage-intent (magit-region-values nil t))) + (`(unstaged ,_ ,_) (user-error "Already unstaged")) + (`(staged region ,_) (magit-apply-region it "--reverse" "--cached")) + (`(staged hunk ,_) (magit-apply-hunk it "--reverse" "--cached")) + (`(staged hunks ,_) (magit-apply-hunks it "--reverse" "--cached")) + ('(staged file t) (magit-apply-diff it "--reverse" "--cached")) + ('(staged files t) (magit-apply-diffs it "--reverse" "--cached")) + ('(staged list t) (magit-apply-diffs it "--reverse" "--cached")) + ('(staged file nil) (magit-unstage-1 (list (oref it value)))) + ('(staged files nil) (magit-unstage-1 (magit-region-values nil t))) + ('(staged list nil) (magit-unstage-all)) + (`(committed ,_ ,_) (if magit-unstage-committed + (magit-reverse-in-index) + (user-error "Cannot unstage committed changes"))) + (`(undefined ,_ ,_) (user-error "Cannot unstage this change"))))) + +;;;###autoload +(defun magit-unstage-file (file) + "Unstage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be unstaged. Otherwise unstage the file at point +without requiring confirmation." + (interactive + (let* ((atpoint (magit-section-value-if 'file)) + (current (magit-file-relative-name)) + (choices (magit-staged-files)) + (default (car (member (or atpoint current) choices)))) + (list (if (or current-prefix-arg (not default)) + (magit-completing-read "Unstage file" choices + nil t nil nil default) + default)))) + (magit-with-toplevel + (magit-unstage-1 (list file)))) + +(defun magit-unstage-1 (files) + (magit-wip-commit-before-change files " before unstage") + (if (magit-no-commit-p) + (magit-run-git "rm" "--cached" "--" files) + (magit-run-git "reset" "HEAD" "--" files)) + (magit-wip-commit-after-apply files " after unstage")) + +(defun magit-unstage-intent (files) + (if-let ((staged (magit-staged-files)) + (intent (--filter (member it staged) files))) + (magit-unstage-1 intent) + (user-error "Already unstaged"))) + +;;;###autoload +(defun magit-unstage-all () + "Remove all changes from the staging area." + (interactive) + (unless (magit-anything-staged-p) + (user-error "Nothing to unstage")) + (when (or (magit-anything-unstaged-p) + (magit-untracked-files)) + (magit-confirm 'unstage-all-changes)) + (magit-wip-commit-before-change nil " before unstage") + (magit-run-git "reset" "HEAD" "--" magit-buffer-diff-files) + (magit-wip-commit-after-apply nil " after unstage")) + +(defvar magit-post-unstage-hook-commands + '(magit-unstage magit-unstage-file magit-unstage-all)) + +(defun magit-run-post-unstage-hook () + (when (memq this-command magit-post-unstage-hook-commands) + (magit-run-hook-with-benchmark 'magit-post-unstage-hook))) + +;;;; Discard + +(defun magit-discard () + "Remove the change at point. + +On a hunk or file with unresolved conflicts prompt which side to +keep (while discarding the other). If point is within the text +of a side, then keep that side without prompting." + (interactive) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(committed ,_) (user-error "Cannot discard committed changes")) + (`(undefined ,_) (user-error "Cannot discard this change")) + (`(,_ region) (magit-discard-region it)) + (`(,_ hunk) (magit-discard-hunk it)) + (`(,_ hunks) (magit-discard-hunks it)) + (`(,_ file) (magit-discard-file it)) + (`(,_ files) (magit-discard-files it)) + (`(,_ list) (magit-discard-files it))))) + +(defun magit-discard-region (section) + (magit-confirm 'discard "Discard region") + (magit-discard-apply section 'magit-apply-region)) + +(defun magit-discard-hunk (section) + (magit-confirm 'discard "Discard hunk") + (let ((file (magit-section-parent-value section))) + (pcase (cddr (car (magit-file-status file))) + ('(?U ?U) (magit-smerge-keep-current)) + (_ (magit-discard-apply section #'magit-apply-hunk))))) + +(defun magit-discard-apply (section apply) + (if (eq (magit-diff-type section) 'unstaged) + (funcall apply section "--reverse") + (if (magit-anything-unstaged-p + nil (if (magit-file-section-p section) + (oref section value) + (magit-section-parent-value section))) + (progn (let ((magit-inhibit-refresh t)) + (funcall apply section "--reverse" "--cached") + (funcall apply section "--reverse" "--reject")) + (magit-refresh)) + (funcall apply section "--reverse" "--index")))) + +(defun magit-discard-hunks (sections) + (magit-confirm 'discard (format "Discard %s hunks from %s" + (length sections) + (magit-section-parent-value (car sections)))) + (magit-discard-apply-n sections #'magit-apply-hunks)) + +(defun magit-discard-apply-n (sections apply) + (let ((section (car sections))) + (if (eq (magit-diff-type section) 'unstaged) + (funcall apply sections "--reverse") + (if (magit-anything-unstaged-p + nil (if (magit-file-section-p section) + (oref section value) + (magit-section-parent-value section))) + (progn (let ((magit-inhibit-refresh t)) + (funcall apply sections "--reverse" "--cached") + (funcall apply sections "--reverse" "--reject")) + (magit-refresh)) + (funcall apply sections "--reverse" "--index"))))) + +(defun magit-discard-file (section) + (magit-discard-files (list section))) + +(defun magit-discard-files (sections) + (let ((auto-revert-verbose nil) + (type (magit-diff-type (car sections))) + (status (magit-file-status)) + files delete resurrect rename discard discard-new resolve) + (dolist (section sections) + (let ((file (oref section value))) + (push file files) + (pcase (cons (pcase type + (`staged ?X) + (`unstaged ?Y) + (`untracked ?Z)) + (cddr (assoc file status))) + ('(?Z) (dolist (f (magit-untracked-files nil file)) + (push f delete))) + ((or '(?Z ?? ??) '(?Z ?! ?!)) (push file delete)) + ('(?Z ?D ? ) (push file delete)) + (`(,_ ?D ?D) (push file resolve)) + ((or `(,_ ?U ,_) `(,_ ,_ ?U)) (push file resolve)) + (`(,_ ?A ?A) (push file resolve)) + (`(?X ?M ,(or ? ?M ?D)) (push section discard)) + (`(?Y ,_ ?M ) (push section discard)) + ('(?X ?A ?M ) (push file discard-new)) + ('(?X ?C ?M ) (push file discard-new)) + (`(?X ?A ,(or ? ?D)) (push file delete)) + (`(?X ?C ,(or ? ?D)) (push file delete)) + (`(?X ?D ,(or ? ?M )) (push file resurrect)) + (`(?Y ,_ ?D ) (push file resurrect)) + (`(?X ?R ,(or ? ?M ?D)) (push file rename))))) + (unwind-protect + (let ((magit-inhibit-refresh t)) + (magit-wip-commit-before-change files " before discard") + (when resolve + (magit-discard-files--resolve (nreverse resolve))) + (when resurrect + (magit-discard-files--resurrect (nreverse resurrect))) + (when delete + (magit-discard-files--delete (nreverse delete) status)) + (when rename + (magit-discard-files--rename (nreverse rename) status)) + (when (or discard discard-new) + (magit-discard-files--discard (nreverse discard) + (nreverse discard-new))) + (magit-wip-commit-after-apply files " after discard")) + (magit-refresh)))) + +(defun magit-discard-files--resolve (files) + (if-let ((arg (and (cdr files) + (magit-read-char-case + (format "For these %i files\n%s\ncheckout:\n" + (length files) + (mapconcat (lambda (file) + (concat " " file)) + files "\n")) + t + (?o "[o]ur stage" "--ours") + (?t "[t]heir stage" "--theirs") + (?c "[c]onflict" "--merge") + (?i "decide [i]ndividually" nil))))) + (dolist (file files) + (magit-checkout-stage file arg)) + (dolist (file files) + (magit-checkout-stage file (magit-checkout-read-stage file))))) + +(defun magit-discard-files--resurrect (files) + (magit-confirm-files 'resurrect files) + (if (eq (magit-diff-type) 'staged) + (magit-call-git "reset" "--" files) + (magit-call-git "checkout" "--" files))) + +(defun magit-discard-files--delete (files status) + (magit-confirm-files (if magit-delete-by-moving-to-trash 'trash 'delete) + files) + (let ((delete-by-moving-to-trash magit-delete-by-moving-to-trash)) + (dolist (file files) + (when (string-match-p "\\`\\\\?~" file) + (error "Refusing to delete %S, too dangerous" file)) + (pcase (nth 3 (assoc file status)) + ((guard (memq (magit-diff-type) '(unstaged untracked))) + (dired-delete-file file dired-recursive-deletes + magit-delete-by-moving-to-trash) + (dired-clean-up-after-deletion file)) + (?\s (delete-file file t) + (magit-call-git "rm" "--cached" "--" file)) + (?M (let ((temp (magit-git-string "checkout-index" "--temp" file))) + (string-match + (format "\\(.+?\\)\t%s" (regexp-quote file)) temp) + (rename-file (match-string 1 temp) + (setq temp (concat file ".~{index}~"))) + (delete-file temp t)) + (magit-call-git "rm" "--cached" "--force" "--" file)) + (?D (magit-call-git "checkout" "--" file) + (delete-file file t) + (magit-call-git "rm" "--cached" "--force" "--" file)))))) + +(defun magit-discard-files--rename (files status) + (magit-confirm 'rename "Undo rename %s" "Undo %i renames" nil + (mapcar (lambda (file) + (setq file (assoc file status)) + (format "%s -> %s" (cadr file) (car file))) + files)) + (dolist (file files) + (let ((orig (cadr (assoc file status)))) + (if (file-exists-p file) + (progn + (--when-let (file-name-directory orig) + (make-directory it t)) + (magit-call-git "mv" file orig)) + (magit-call-git "rm" "--cached" "--" file) + (magit-call-git "reset" "--" orig))))) + +(defun magit-discard-files--discard (sections new-files) + (let ((files (--map (oref it value) sections))) + (magit-confirm-files 'discard (append files new-files) + (format "Discard %s changes in" (magit-diff-type))) + (if (eq (magit-diff-type (car sections)) 'unstaged) + (magit-call-git "checkout" "--" files) + (when new-files + (magit-call-git "add" "--" new-files) + (magit-call-git "reset" "--" new-files)) + (let ((binaries (magit-binary-files "--cached"))) + (when binaries + (setq sections + (--remove (member (oref it value) binaries) + sections))) + (cond ((length= sections 1) + (magit-discard-apply (car sections) 'magit-apply-diff)) + (sections + (magit-discard-apply-n sections #'magit-apply-diffs))) + (when binaries + (let ((modified (magit-unstaged-files t))) + (setq binaries (--separate (member it modified) binaries))) + (when (cadr binaries) + (magit-call-git "reset" "--" (cadr binaries))) + (when (car binaries) + (user-error + (concat + "Cannot discard staged changes to binary files, " + "which also have unstaged changes. Unstage instead.")))))))) + +;;;; Reverse + +(defun magit-reverse (&rest args) + "Reverse the change at point in the working tree. +With a prefix argument fallback to a 3-way merge. Doing +so causes the change to be applied to the index as well." + (interactive (and current-prefix-arg (list "--3way"))) + (--when-let (magit-apply--get-selection) + (pcase (list (magit-diff-type) (magit-diff-scope)) + (`(untracked ,_) (user-error "Cannot reverse untracked changes")) + (`(unstaged ,_) (user-error "Cannot reverse unstaged changes")) + (`(,_ region) (magit-reverse-region it args)) + (`(,_ hunk) (magit-reverse-hunk it args)) + (`(,_ hunks) (magit-reverse-hunks it args)) + (`(,_ file) (magit-reverse-file it args)) + (`(,_ files) (magit-reverse-files it args)) + (`(,_ list) (magit-reverse-files it args))))) + +(defun magit-reverse-region (section args) + (magit-confirm 'reverse "Reverse region") + (magit-reverse-apply section #'magit-apply-region args)) + +(defun magit-reverse-hunk (section args) + (magit-confirm 'reverse "Reverse hunk") + (magit-reverse-apply section #'magit-apply-hunk args)) + +(defun magit-reverse-hunks (sections args) + (magit-confirm 'reverse + (format "Reverse %s hunks from %s" + (length sections) + (magit-section-parent-value (car sections)))) + (magit-reverse-apply sections #'magit-apply-hunks args)) + +(defun magit-reverse-file (section args) + (magit-reverse-files (list section) args)) + +(defun magit-reverse-files (sections args) + (pcase-let ((`(,binaries ,sections) + (let ((bs (magit-binary-files + (cond ((derived-mode-p 'magit-revision-mode) + magit-buffer-range) + ((derived-mode-p 'magit-diff-mode) + magit-buffer-range) + (t + "--cached"))))) + (--separate (member (oref it value) bs) + sections)))) + (magit-confirm-files 'reverse (--map (oref it value) sections)) + (cond ((length= sections 1) + (magit-reverse-apply (car sections) #'magit-apply-diff args)) + (sections + (magit-reverse-apply sections #'magit-apply-diffs args))) + (when binaries + (user-error "Cannot reverse binary files")))) + +(defun magit-reverse-apply (section:s apply args) + (funcall apply section:s "--reverse" args + (and (not magit-reverse-atomically) + (not (member "--3way" args)) + "--reject"))) + +(defun magit-reverse-in-index (&rest args) + "Reverse the change at point in the index but not the working tree. + +Use this command to extract a change from `HEAD', while leaving +it in the working tree, so that it can later be committed using +a separate commit. A typical workflow would be: + +0. Optionally make sure that there are no uncommitted changes. +1. Visit the `HEAD' commit and navigate to the change that should + not have been included in that commit. +2. Type \"u\" (`magit-unstage') to reverse it in the index. + This assumes that `magit-unstage-committed-changes' is non-nil. +3. Type \"c e\" to extend `HEAD' with the staged changes, + including those that were already staged before. +4. Optionally stage the remaining changes using \"s\" or \"S\" + and then type \"c c\" to create a new commit." + (interactive) + (magit-reverse (cons "--cached" args))) + +;;; Smerge Support + +(defun magit-smerge-keep-current () + "Keep the current version of the conflict at point." + (interactive) + (magit-call-smerge #'smerge-keep-current)) + +(defun magit-smerge-keep-upper () + "Keep the upper/our version of the conflict at point." + (interactive) + (magit-call-smerge #'smerge-keep-upper)) + +(defun magit-smerge-keep-base () + "Keep the base version of the conflict at point." + (interactive) + (magit-call-smerge #'smerge-keep-base)) + +(defun magit-smerge-keep-lower () + "Keep the lower/their version of the conflict at point." + (interactive) + (magit-call-smerge #'smerge-keep-lower)) + +(defun magit-call-smerge (fn) + (pcase-let* ((file (magit-file-at-point t t)) + (keep (get-file-buffer file)) + (`(,buf ,pos) + (let ((magit-diff-visit-jump-to-change nil)) + (magit-diff-visit-file--noselect file)))) + (with-current-buffer buf + (save-excursion + (save-restriction + (unless (<= (point-min) pos (point-max)) + (widen)) + (goto-char pos) + (condition-case nil + (smerge-match-conflict) + (error + (if (eq fn #'smerge-keep-current) + (when (eq this-command #'magit-discard) + (re-search-forward smerge-begin-re nil t) + (setq fn + (magit-read-char-case "Keep side: " t + (?o "[o]urs/upper" #'smerge-keep-upper) + (?b "[b]ase" #'smerge-keep-base) + (?t "[t]heirs/lower" #'smerge-keep-lower)))) + (re-search-forward smerge-begin-re nil t)))) + (funcall fn))) + (when (and keep (magit-anything-unmerged-p file)) + (smerge-start-session)) + (save-buffer)) + (unless keep + (kill-buffer buf)) + (magit-refresh))) + +;;; _ +(provide 'magit-apply) +;;; magit-apply.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-autoloads.el b/code/elpa/magit-20220821.1819/magit-autoloads.el new file mode 100644 index 0000000..1d69161 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-autoloads.el @@ -0,0 +1,2583 @@ +;;; magit-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "git-rebase" "git-rebase.el" (0 0 0 0)) +;;; Generated autoloads from git-rebase.el + +(autoload 'git-rebase-current-line "git-rebase" "\ +Parse current line into a `git-rebase-action' instance. +If the current line isn't recognized as a rebase line, an +instance with all nil values is returned." nil nil) + +(autoload 'git-rebase-mode "git-rebase" "\ +Major mode for editing of a Git rebase file. + +Rebase files are generated when you run \"git rebase -i\" or run +`magit-interactive-rebase'. They describe how Git should perform +the rebase. See the documentation for git-rebase (e.g., by +running \"man git-rebase\" at the command line) for details. + +\(fn)" t nil) + +(defconst git-rebase-filename-regexp "/git-rebase-todo\\'") + +(add-to-list 'auto-mode-alist (cons git-rebase-filename-regexp #'git-rebase-mode)) + +(register-definition-prefixes "git-rebase" '("git-rebase-" "magit-imenu--rebase-")) + +;;;*** + +;;;### (autoloads nil "magit" "magit.el" (0 0 0 0)) +;;; Generated autoloads from magit.el + +(define-obsolete-variable-alias 'global-magit-file-mode 'magit-define-global-key-bindings "Magit 3.0.0") + +(defvar magit-define-global-key-bindings t "\ +Whether to bind some Magit commands in the global keymap. + +If this variable is non-nil, then the following bindings may +be added to the global keymap. The default is t. + +key binding +--- ------- +C-x g magit-status +C-x M-g magit-dispatch +C-c M-g magit-file-dispatch + +These bindings may be added when `after-init-hook' is run. +Each binding is added if and only if at that time no other key +is bound to the same command and no other command is bound to +the same key. In other words we try to avoid adding bindings +that are unnecessary, as well as bindings that conflict with +other bindings. + +Adding the above bindings is delayed until `after-init-hook' +is called to allow users to set the variable anywhere in their +init file (without having to make sure to do so before `magit' +is loaded or autoloaded) and to increase the likelihood that +all the potentially conflicting user bindings have already +been added. + +To set this variable use either `setq' or the Custom interface. +Do not use the function `customize-set-variable' because doing +that would cause Magit to be loaded immediately when that form +is evaluated (this differs from `custom-set-variables', which +doesn't load the libraries that define the customized variables). + +Setting this variable to nil has no effect if that is done after +the key bindings have already been added. + +We recommend that you bind \"C-c g\" instead of \"C-c M-g\" to +`magit-file-dispatch'. The former is a much better binding +but the \"C-c \" namespace is strictly reserved for +users; preventing Magit from using it by default. + +Also see info node `(magit)Commands for Buffers Visiting Files'.") + +(custom-autoload 'magit-define-global-key-bindings "magit" t) + +(defun magit-maybe-define-global-key-bindings (&optional force) (when magit-define-global-key-bindings (let ((map (current-global-map))) (dolist (elt '(("C-x g" . magit-status) ("C-x M-g" . magit-dispatch) ("C-c M-g" . magit-file-dispatch))) (let ((key (kbd (car elt))) (def (cdr elt))) (when (or force (not (or (lookup-key map key) (where-is-internal def (make-sparse-keymap) t)))) (define-key map key def))))))) + +(if after-init-time (magit-maybe-define-global-key-bindings) (add-hook 'after-init-hook #'magit-maybe-define-global-key-bindings t)) + (autoload 'magit-dispatch "magit" nil t) + (autoload 'magit-run "magit" nil t) + +(autoload 'magit-git-command "magit" "\ +Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. \"git \" is +used as initial input, but can be deleted to run another command. + +With a prefix argument COMMAND is run in the top-level directory +of the current working tree, otherwise in `default-directory'. + +\(fn COMMAND)" t nil) + +(autoload 'magit-git-command-topdir "magit" "\ +Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. \"git \" is +used as initial input, but can be deleted to run another command. + +COMMAND is run in the top-level directory of the current +working tree. + +\(fn COMMAND)" t nil) + +(autoload 'magit-shell-command "magit" "\ +Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. With a +prefix argument COMMAND is run in the top-level directory of +the current working tree, otherwise in `default-directory'. + +\(fn COMMAND)" t nil) + +(autoload 'magit-shell-command-topdir "magit" "\ +Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. COMMAND +is run in the top-level directory of the current working tree. + +\(fn COMMAND)" t nil) + +(autoload 'magit-version "magit" "\ +Return the version of Magit currently in use. +If optional argument PRINT-DEST is non-nil, output +stream (interactively, the echo area, or the current buffer with +a prefix argument), also print the used versions of Magit, Git, +and Emacs to it. + +\(fn &optional PRINT-DEST)" t nil) + +(register-definition-prefixes "magit" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-apply" "magit-apply.el" (0 0 0 0)) +;;; Generated autoloads from magit-apply.el + +(autoload 'magit-stage-file "magit-apply" "\ +Stage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be staged. Otherwise stage the file at point without +requiring confirmation. + +\(fn FILE)" t nil) + +(autoload 'magit-stage-modified "magit-apply" "\ +Stage all changes to files modified in the worktree. +Stage all new content of tracked files and remove tracked files +that no longer exist in the working tree from the index also. +With a prefix argument also stage previously untracked (but not +ignored) files. + +\(fn &optional ALL)" t nil) + +(autoload 'magit-unstage-file "magit-apply" "\ +Unstage all changes to FILE. +With a prefix argument or when there is no file at point ask for +the file to be unstaged. Otherwise unstage the file at point +without requiring confirmation. + +\(fn FILE)" t nil) + +(autoload 'magit-unstage-all "magit-apply" "\ +Remove all changes from the staging area." t nil) + +(register-definition-prefixes "magit-apply" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-autorevert" "magit-autorevert.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from magit-autorevert.el + +(put 'magit-auto-revert-mode 'globalized-minor-mode t) + +(defvar magit-auto-revert-mode (not (or global-auto-revert-mode noninteractive)) "\ +Non-nil if Magit-Auto-Revert mode is enabled. +See the `magit-auto-revert-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `magit-auto-revert-mode'.") + +(custom-autoload 'magit-auto-revert-mode "magit-autorevert" nil) + +(autoload 'magit-auto-revert-mode "magit-autorevert" "\ +Toggle Auto-Revert mode in all buffers. +With prefix ARG, enable Magit-Auto-Revert mode if ARG is positive; +otherwise, disable it. + +If called from Lisp, toggle the mode if ARG is `toggle'. +Enable the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +Auto-Revert mode is enabled in all buffers where +`magit-turn-on-auto-revert-mode-if-desired' would do it. + +See `auto-revert-mode' for more information on Auto-Revert mode. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "magit-autorevert" '("auto-revert-buffer" "magit-")) + +;;;*** + +;;;### (autoloads nil "magit-base" "magit-base.el" (0 0 0 0)) +;;; Generated autoloads from magit-base.el + +(autoload 'magit-emacs-Q-command "magit-base" "\ +Show a shell command that runs an uncustomized Emacs with only Magit loaded. +See info node `(magit)Debugging Tools' for more information." t nil) + +(autoload 'Info-follow-nearest-node--magit-gitman "magit-base" "\ + + +\(fn FN &optional FORK)" nil nil) + +(advice-add 'Info-follow-nearest-node :around #'Info-follow-nearest-node--magit-gitman) + +(advice-add 'org-man-export :around #'org-man-export--magit-gitman) + +(autoload 'org-man-export--magit-gitman "magit-base" "\ + + +\(fn FN LINK DESCRIPTION FORMAT)" nil nil) + +(register-definition-prefixes "magit-base" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-bisect" "magit-bisect.el" (0 0 0 0)) +;;; Generated autoloads from magit-bisect.el + (autoload 'magit-bisect "magit-bisect" nil t) + +(autoload 'magit-bisect-start "magit-bisect" "\ +Start a bisect session. + +Bisecting a bug means to find the commit that introduced it. +This command starts such a bisect session by asking for a known +good and a known bad commit. To move the session forward use the +other actions from the bisect transient command (\\\\[magit-bisect]). + +\(fn BAD GOOD ARGS)" t nil) + +(autoload 'magit-bisect-reset "magit-bisect" "\ +After bisecting, cleanup bisection state and return to original `HEAD'." t nil) + +(autoload 'magit-bisect-good "magit-bisect" "\ +While bisecting, mark the current commit as good. +Use this after you have asserted that the commit does not contain +the bug in question." t nil) + +(autoload 'magit-bisect-bad "magit-bisect" "\ +While bisecting, mark the current commit as bad. +Use this after you have asserted that the commit does contain the +bug in question." t nil) + +(autoload 'magit-bisect-mark "magit-bisect" "\ +While bisecting, mark the current commit with a bisect term. +During a bisect using alternate terms, commits can still be +marked with `magit-bisect-good' and `magit-bisect-bad', as those +commands map to the correct term (\"good\" to --term-old's value +and \"bad\" to --term-new's). However, in some cases, it can be +difficult to keep that mapping straight in your head; this +command provides an interface that exposes the underlying terms." t nil) + +(autoload 'magit-bisect-skip "magit-bisect" "\ +While bisecting, skip the current commit. +Use this if for some reason the current commit is not a good one +to test. This command lets Git choose a different one." t nil) + +(autoload 'magit-bisect-run "magit-bisect" "\ +Bisect automatically by running commands after each step. + +Unlike `git bisect run' this can be used before bisecting has +begun. In that case it behaves like `git bisect start; git +bisect run'. + +\(fn CMDLINE &optional BAD GOOD ARGS)" t nil) + +(register-definition-prefixes "magit-bisect" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-blame" "magit-blame.el" (0 0 0 0)) +;;; Generated autoloads from magit-blame.el + (autoload 'magit-blame-echo "magit-blame" nil t) + (autoload 'magit-blame-addition "magit-blame" nil t) + (autoload 'magit-blame-removal "magit-blame" nil t) + (autoload 'magit-blame-reverse "magit-blame" nil t) + (autoload 'magit-blame "magit-blame" nil t) + +(register-definition-prefixes "magit-blame" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-branch" "magit-branch.el" (0 0 0 0)) +;;; Generated autoloads from magit-branch.el + (autoload 'magit-branch "magit" nil t) + +(autoload 'magit-checkout "magit-branch" "\ +Checkout REVISION, updating the index and the working tree. +If REVISION is a local branch, then that becomes the current +branch. If it is something else, then `HEAD' becomes detached. +Checkout fails if the working tree or the staging area contain +changes. + +\(git checkout REVISION). + +\(fn REVISION &optional ARGS)" t nil) + +(autoload 'magit-branch-create "magit-branch" "\ +Create BRANCH at branch or revision START-POINT. + +\(fn BRANCH START-POINT)" t nil) + +(autoload 'magit-branch-and-checkout "magit-branch" "\ +Create and checkout BRANCH at branch or revision START-POINT. + +\(fn BRANCH START-POINT &optional ARGS)" t nil) + +(autoload 'magit-branch-or-checkout "magit-branch" "\ +Hybrid between `magit-checkout' and `magit-branch-and-checkout'. + +Ask the user for an existing branch or revision. If the user +input actually can be resolved as a branch or revision, then +check that out, just like `magit-checkout' would. + +Otherwise create and checkout a new branch using the input as +its name. Before doing so read the starting-point for the new +branch. This is similar to what `magit-branch-and-checkout' +does. + +\(fn ARG &optional START-POINT)" t nil) + +(autoload 'magit-branch-checkout "magit-branch" "\ +Checkout an existing or new local branch. + +Read a branch name from the user offering all local branches and +a subset of remote branches as candidates. Omit remote branches +for which a local branch by the same name exists from the list +of candidates. The user can also enter a completely new branch +name. + +- If the user selects an existing local branch, then check that + out. + +- If the user selects a remote branch, then create and checkout + a new local branch with the same name. Configure the selected + remote branch as push target. + +- If the user enters a new branch name, then create and check + that out, after also reading the starting-point from the user. + +In the latter two cases the upstream is also set. Whether it is +set to the chosen START-POINT or something else depends on the +value of `magit-branch-adjust-remote-upstream-alist', just like +when using `magit-branch-and-checkout'. + +\(fn BRANCH &optional START-POINT)" t nil) + +(autoload 'magit-branch-orphan "magit-branch" "\ +Create and checkout an orphan BRANCH with contents from revision START-POINT. + +\(fn BRANCH START-POINT)" t nil) + +(autoload 'magit-branch-spinout "magit-branch" "\ +Create new branch from the unpushed commits. +Like `magit-branch-spinoff' but remain on the current branch. +If there are any uncommitted changes, then behave exactly like +`magit-branch-spinoff'. + +\(fn BRANCH &optional FROM)" t nil) + +(autoload 'magit-branch-spinoff "magit-branch" "\ +Create new branch from the unpushed commits. + +Create and checkout a new branch starting at and tracking the +current branch. That branch in turn is reset to the last commit +it shares with its upstream. If the current branch has no +upstream or no unpushed commits, then the new branch is created +anyway and the previously current branch is not touched. + +This is useful to create a feature branch after work has already +began on the old branch (likely but not necessarily \"master\"). + +If the current branch is a member of the value of option +`magit-branch-prefer-remote-upstream' (which see), then the +current branch will be used as the starting point as usual, but +the upstream of the starting-point may be used as the upstream +of the new branch, instead of the starting-point itself. + +If optional FROM is non-nil, then the source branch is reset +to `FROM~', instead of to the last commit it shares with its +upstream. Interactively, FROM is only ever non-nil, if the +region selects some commits, and among those commits, FROM is +the commit that is the fewest commits ahead of the source +branch. + +The commit at the other end of the selection actually does not +matter, all commits between FROM and `HEAD' are moved to the new +branch. If FROM is not reachable from `HEAD' or is reachable +from the source branch's upstream, then an error is raised. + +\(fn BRANCH &optional FROM)" t nil) + +(autoload 'magit-branch-reset "magit-branch" "\ +Reset a branch to the tip of another branch or any other commit. + +When the branch being reset is the current branch, then do a +hard reset. If there are any uncommitted changes, then the user +has to confirm the reset because those changes would be lost. + +This is useful when you have started work on a feature branch but +realize it's all crap and want to start over. + +When resetting to another branch and a prefix argument is used, +then also set the target branch as the upstream of the branch +that is being reset. + +\(fn BRANCH TO &optional SET-UPSTREAM)" t nil) + +(autoload 'magit-branch-delete "magit-branch" "\ +Delete one or multiple branches. +If the region marks multiple branches, then offer to delete +those, otherwise prompt for a single branch to be deleted, +defaulting to the branch at point. + +\(fn BRANCHES &optional FORCE)" t nil) + +(autoload 'magit-branch-rename "magit-branch" "\ +Rename the branch named OLD to NEW. + +With a prefix argument FORCE, rename even if a branch named NEW +already exists. + +If `branch.OLD.pushRemote' is set, then unset it. Depending on +the value of `magit-branch-rename-push-target' (which see) maybe +set `branch.NEW.pushRemote' and maybe rename the push-target on +the remote. + +\(fn OLD NEW &optional FORCE)" t nil) + +(autoload 'magit-branch-shelve "magit-branch" "\ +Shelve a BRANCH. +Rename \"refs/heads/BRANCH\" to \"refs/shelved/BRANCH\", +and also rename the respective reflog file. + +\(fn BRANCH)" t nil) + +(autoload 'magit-branch-unshelve "magit-branch" "\ +Unshelve a BRANCH +Rename \"refs/shelved/BRANCH\" to \"refs/heads/BRANCH\", +and also rename the respective reflog file. + +\(fn BRANCH)" t nil) + (autoload 'magit-branch-configure "magit-branch" nil t) + +(register-definition-prefixes "magit-branch" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-bundle" "magit-bundle.el" (0 0 0 0)) +;;; Generated autoloads from magit-bundle.el + (autoload 'magit-bundle "magit-bundle" nil t) + (autoload 'magit-bundle-import "magit-bundle" nil t) + +(autoload 'magit-bundle-create-tracked "magit-bundle" "\ +Create and track a new bundle. + +\(fn FILE TAG BRANCH REFS ARGS)" t nil) + +(autoload 'magit-bundle-update-tracked "magit-bundle" "\ +Update a bundle that is being tracked using TAG. + +\(fn TAG)" t nil) + +(autoload 'magit-bundle-verify "magit-bundle" "\ +Check whether FILE is valid and applies to the current repository. + +\(fn FILE)" t nil) + +(autoload 'magit-bundle-list-heads "magit-bundle" "\ +List the refs in FILE. + +\(fn FILE)" t nil) + +(register-definition-prefixes "magit-bundle" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-clone" "magit-clone.el" (0 0 0 0)) +;;; Generated autoloads from magit-clone.el + (autoload 'magit-clone "magit-clone" nil t) + +(autoload 'magit-clone-regular "magit-clone" "\ +Create a clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. + +\(fn REPOSITORY DIRECTORY ARGS)" t nil) + +(autoload 'magit-clone-shallow "magit-clone" "\ +Create a shallow clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. +With a prefix argument read the DEPTH of the clone; +otherwise use 1. + +\(fn REPOSITORY DIRECTORY ARGS DEPTH)" t nil) + +(autoload 'magit-clone-shallow-since "magit-clone" "\ +Create a shallow clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. +Exclude commits before DATE, which is read from the +user. + +\(fn REPOSITORY DIRECTORY ARGS DATE)" t nil) + +(autoload 'magit-clone-shallow-exclude "magit-clone" "\ +Create a shallow clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. +Exclude commits reachable from EXCLUDE, which is a +branch or tag read from the user. + +\(fn REPOSITORY DIRECTORY ARGS EXCLUDE)" t nil) + +(autoload 'magit-clone-bare "magit-clone" "\ +Create a bare clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. + +\(fn REPOSITORY DIRECTORY ARGS)" t nil) + +(autoload 'magit-clone-mirror "magit-clone" "\ +Create a mirror of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. + +\(fn REPOSITORY DIRECTORY ARGS)" t nil) + +(autoload 'magit-clone-sparse "magit-clone" "\ +Clone REPOSITORY into DIRECTORY and create a sparse checkout. + +\(fn REPOSITORY DIRECTORY ARGS)" t nil) + +(register-definition-prefixes "magit-clone" '("magit-clone-")) + +;;;*** + +;;;### (autoloads nil "magit-commit" "magit-commit.el" (0 0 0 0)) +;;; Generated autoloads from magit-commit.el + (autoload 'magit-commit "magit-commit" nil t) + +(autoload 'magit-commit-create "magit-commit" "\ +Create a new commit on `HEAD'. +With a prefix argument, amend to the commit at `HEAD' instead. + +\(git commit [--amend] ARGS) + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-commit-amend "magit-commit" "\ +Amend the last commit. + +\(git commit --amend ARGS) + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-commit-extend "magit-commit" "\ +Amend the last commit, without editing the message. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-extend-override-date' can be used +to inverse the meaning of the prefix argument. +\(git commit +--amend --no-edit) + +\(fn &optional ARGS OVERRIDE-DATE)" t nil) + +(autoload 'magit-commit-reword "magit-commit" "\ +Reword the last commit, ignoring staged changes. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-reword-override-date' can be used +to inverse the meaning of the prefix argument. + +Non-interactively respect the optional OVERRIDE-DATE argument +and ignore the option. + +\(git commit --amend --only) + +\(fn &optional ARGS OVERRIDE-DATE)" t nil) + +(autoload 'magit-commit-fixup "magit-commit" "\ +Create a fixup commit. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-squash "magit-commit" "\ +Create a squash commit, without editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'. + +If you want to immediately add a message to the squash commit, +then use `magit-commit-augment' instead of this command. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-augment "magit-commit" "\ +Create a squash commit, editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-instant-fixup "magit-commit" "\ +Create a fixup commit targeting COMMIT and instantly rebase. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-instant-squash "magit-commit" "\ +Create a squash commit targeting COMMIT and instantly rebase. + +\(fn &optional COMMIT ARGS)" t nil) + +(autoload 'magit-commit-reshelve "magit-commit" "\ +Change the committer date and possibly the author date of `HEAD'. + +The current time is used as the initial minibuffer input and the +original author or committer date is available as the previous +history element. + +Both the author and the committer dates are changes, unless one +of the following is true, in which case only the committer date +is updated: +- You are not the author of the commit that is being reshelved. +- The command was invoked with a prefix argument. +- Non-interactively if UPDATE-AUTHOR is nil. + +\(fn DATE UPDATE-AUTHOR &optional ARGS)" t nil) + +(autoload 'magit-commit-absorb-modules "magit-commit" "\ +Spread modified modules across recent commits. + +\(fn PHASE COMMIT)" t nil) + (autoload 'magit-commit-absorb "magit-commit" nil t) + (autoload 'magit-commit-autofixup "magit-commit" nil t) + +(register-definition-prefixes "magit-commit" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-diff" "magit-diff.el" (0 0 0 0)) +;;; Generated autoloads from magit-diff.el + (autoload 'magit-diff "magit-diff" nil t) + (autoload 'magit-diff-refresh "magit-diff" nil t) + +(autoload 'magit-diff-dwim "magit-diff" "\ +Show changes for the thing at point. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-range "magit-diff" "\ +Show differences between two commits. + +REV-OR-RANGE should be a range or a single revision. If it is a +revision, then show changes in the working tree relative to that +revision. If it is a range, but one side is omitted, then show +changes relative to `HEAD'. + +If the region is active, use the revisions on the first and last +line of the region as the two sides of the range. With a prefix +argument, instead of diffing the revisions, choose a revision to +view changes along, starting at the common ancestor of both +revisions (i.e., use a \"...\" range). + +\(fn REV-OR-RANGE &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-working-tree "magit-diff" "\ +Show changes between the current working tree and the `HEAD' commit. +With a prefix argument show changes between the working tree and +a commit read from the minibuffer. + +\(fn &optional REV ARGS FILES)" t nil) + +(autoload 'magit-diff-staged "magit-diff" "\ +Show changes between the index and the `HEAD' commit. +With a prefix argument show changes between the index and +a commit read from the minibuffer. + +\(fn &optional REV ARGS FILES)" t nil) + +(autoload 'magit-diff-unstaged "magit-diff" "\ +Show changes between the working tree and the index. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-unmerged "magit-diff" "\ +Show changes that are being merged. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-diff-while-committing "magit-diff" "\ +While committing, show the changes that are about to be committed. +While amending, invoking the command again toggles between +showing just the new changes or all the changes that will +be committed." t nil) + +(autoload 'magit-diff-buffer-file "magit-diff" "\ +Show diff for the blob or file visited in the current buffer. + +When the buffer visits a blob, then show the respective commit. +When the buffer visits a file, then show the differences between +`HEAD' and the working tree. In both cases limit the diff to +the file or blob." t nil) + +(autoload 'magit-diff-paths "magit-diff" "\ +Show changes between any two files on disk. + +\(fn A B)" t nil) + +(autoload 'magit-show-commit "magit-diff" "\ +Visit the revision at point in another buffer. +If there is no revision at point or with a prefix argument prompt +for a revision. + +\(fn REV &optional ARGS FILES MODULE)" t nil) + +(register-definition-prefixes "magit-diff" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-ediff" "magit-ediff.el" (0 0 0 0)) +;;; Generated autoloads from magit-ediff.el + (autoload 'magit-ediff "magit-ediff" nil) + +(autoload 'magit-ediff-resolve-all "magit-ediff" "\ +Resolve all conflicts in the FILE at point using Ediff. + +If there is no file at point or if it doesn't have any unmerged +changes, then prompt for a file. + +See info node `(magit) Ediffing' for more information about this +and alternative commands. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-resolve-rest "magit-ediff" "\ +Resolve outstanding conflicts in the FILE at point using Ediff. + +If there is no file at point or if it doesn't have any unmerged +changes, then prompt for a file. + +See info node `(magit) Ediffing' for more information about this +and alternative commands. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-stage "magit-ediff" "\ +Stage and unstage changes to FILE using Ediff. +FILE has to be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-compare "magit-ediff" "\ +Compare REVA:FILEA with REVB:FILEB using Ediff. + +FILEA and FILEB have to be relative to the top directory of the +repository. If REVA or REVB is nil, then this stands for the +working tree state. + +If the region is active, use the revisions on the first and last +line of the region. With a prefix argument, instead of diffing +the revisions, choose a revision to view changes along, starting +at the common ancestor of both revisions (i.e., use a \"...\" +range). + +\(fn REVA REVB FILEA FILEB)" t nil) + +(autoload 'magit-ediff-dwim "magit-ediff" "\ +Compare, stage, or resolve using Ediff. +This command tries to guess what file, and what commit or range +the user wants to compare, stage, or resolve using Ediff. It +might only be able to guess either the file, or range or commit, +in which case the user is asked about the other. It might not +always guess right, in which case the appropriate `magit-ediff-*' +command has to be used explicitly. If it cannot read the user's +mind at all, then it asks the user for a command to run." t nil) + +(autoload 'magit-ediff-show-staged "magit-ediff" "\ +Show staged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-show-unstaged "magit-ediff" "\ +Show unstaged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-show-working-tree "magit-ediff" "\ +Show changes between `HEAD' and working tree using Ediff. +FILE must be relative to the top directory of the repository. + +\(fn FILE)" t nil) + +(autoload 'magit-ediff-show-commit "magit-ediff" "\ +Show changes introduced by COMMIT using Ediff. + +\(fn COMMIT)" t nil) + +(autoload 'magit-ediff-show-stash "magit-ediff" "\ +Show changes introduced by STASH using Ediff. +`magit-ediff-show-stash-with-index' controls whether a +three-buffer Ediff is used in order to distinguish changes in the +stash that were staged. + +\(fn STASH)" t nil) + +(register-definition-prefixes "magit-ediff" '("magit-ediff-")) + +;;;*** + +;;;### (autoloads nil "magit-extras" "magit-extras.el" (0 0 0 0)) +;;; Generated autoloads from magit-extras.el + (autoload 'magit-git-mergetool "magit-extras" nil t) + +(autoload 'magit-run-git-gui-blame "magit-extras" "\ +Run `git gui blame' on the given FILENAME and COMMIT. +Interactively run it for the current file and the `HEAD', with a +prefix or when the current file cannot be determined let the user +choose. When the current buffer is visiting FILENAME instruct +blame to center around the line point is on. + +\(fn COMMIT FILENAME &optional LINENUM)" t nil) + +(autoload 'magit-run-git-gui "magit-extras" "\ +Run `git gui' for the current git repository." t nil) + +(autoload 'magit-run-gitk "magit-extras" "\ +Run `gitk' in the current repository." t nil) + +(autoload 'magit-run-gitk-branches "magit-extras" "\ +Run `gitk --branches' in the current repository." t nil) + +(autoload 'magit-run-gitk-all "magit-extras" "\ +Run `gitk --all' in the current repository." t nil) + +(autoload 'ido-enter-magit-status "magit-extras" "\ +Drop into `magit-status' from file switching. + +This command does not work in Emacs 26.1. +See https://github.com/magit/magit/issues/3634 +and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31707. + +To make this command available use something like: + + (add-hook \\='ido-setup-hook + (lambda () + (define-key ido-completion-map + (kbd \"C-x g\") \\='ido-enter-magit-status))) + +Starting with Emacs 25.1 the Ido keymaps are defined just once +instead of every time Ido is invoked, so now you can modify it +like pretty much every other keymap: + + (define-key ido-common-completion-map + (kbd \"C-x g\") \\='ido-enter-magit-status)" t nil) + +(autoload 'magit-project-status "magit-extras" "\ +Run `magit-status' in the current project's root." t nil) + +(autoload 'magit-dired-jump "magit-extras" "\ +Visit file at point using Dired. +With a prefix argument, visit in another window. If there +is no file at point, then instead visit `default-directory'. + +\(fn &optional OTHER-WINDOW)" t nil) + +(autoload 'magit-dired-log "magit-extras" "\ +Show log for all marked files, or the current file. + +\(fn &optional FOLLOW)" t nil) + +(autoload 'magit-dired-am-apply-patches "magit-extras" "\ +In Dired, apply the marked (or next ARG) files as patches. +If inside a repository, then apply in that. Otherwise prompt +for a repository. + +\(fn REPO &optional ARG)" t nil) + +(autoload 'magit-do-async-shell-command "magit-extras" "\ +Open FILE with `dired-do-async-shell-command'. +Interactively, open the file at point. + +\(fn FILE)" t nil) + +(autoload 'magit-previous-line "magit-extras" "\ +Like `previous-line' but with Magit-specific shift-selection. + +Magit's selection mechanism is based on the region but selects an +area that is larger than the region. This causes `previous-line' +when invoked while holding the shift key to move up one line and +thereby select two lines. When invoked inside a hunk body this +command does not move point on the first invocation and thereby +it only selects a single line. Which inconsistency you prefer +is a matter of preference. + +\(fn &optional ARG TRY-VSCROLL)" t nil) + +(function-put 'magit-previous-line 'interactive-only '"use `forward-line' with negative argument instead.") + +(autoload 'magit-next-line "magit-extras" "\ +Like `next-line' but with Magit-specific shift-selection. + +Magit's selection mechanism is based on the region but selects +an area that is larger than the region. This causes `next-line' +when invoked while holding the shift key to move down one line +and thereby select two lines. When invoked inside a hunk body +this command does not move point on the first invocation and +thereby it only selects a single line. Which inconsistency you +prefer is a matter of preference. + +\(fn &optional ARG TRY-VSCROLL)" t nil) + +(function-put 'magit-next-line 'interactive-only 'forward-line) + +(autoload 'magit-clean "magit-extras" "\ +Remove untracked files from the working tree. +With a prefix argument also remove ignored files, +with two prefix arguments remove ignored files only. + +\(git clean -f -d [-x|-X]) + +\(fn &optional ARG)" t nil) + +(autoload 'magit-generate-changelog "magit-extras" "\ +Insert ChangeLog entries into the current buffer. + +The entries are generated from the diff being committed. +If prefix argument, AMENDING, is non-nil, include changes +in HEAD as well as staged changes in the diff to check. + +\(fn &optional AMENDING)" t nil) + +(autoload 'magit-add-change-log-entry "magit-extras" "\ +Find change log file and add date entry and item for current change. +This differs from `add-change-log-entry' (which see) in that +it acts on the current hunk in a Magit buffer instead of on +a position in a file-visiting buffer. + +\(fn &optional WHOAMI FILE-NAME OTHER-WINDOW)" t nil) + +(autoload 'magit-add-change-log-entry-other-window "magit-extras" "\ +Find change log file in other window and add entry and item. +This differs from `add-change-log-entry-other-window' (which see) +in that it acts on the current hunk in a Magit buffer instead of +on a position in a file-visiting buffer. + +\(fn &optional WHOAMI FILE-NAME)" t nil) + +(autoload 'magit-edit-line-commit "magit-extras" "\ +Edit the commit that added the current line. + +With a prefix argument edit the commit that removes the line, +if any. The commit is determined using `git blame' and made +editable using `git rebase --interactive' if it is reachable +from `HEAD', or by checking out the commit (or a branch that +points at it) otherwise. + +\(fn &optional TYPE)" t nil) + +(autoload 'magit-diff-edit-hunk-commit "magit-extras" "\ +From a hunk, edit the respective commit and visit the file. + +First visit the file being modified by the hunk at the correct +location using `magit-diff-visit-file'. This actually visits a +blob. When point is on a diff header, not within an individual +hunk, then this visits the blob the first hunk is about. + +Then invoke `magit-edit-line-commit', which uses an interactive +rebase to make the commit editable, or if that is not possible +because the commit is not reachable from `HEAD' by checking out +that commit directly. This also causes the actual worktree file +to be visited. + +Neither the blob nor the file buffer are killed when finishing +the rebase. If that is undesirable, then it might be better to +use `magit-rebase-edit-command' instead of this command. + +\(fn FILE)" t nil) + +(autoload 'magit-reshelve-since "magit-extras" "\ +Change the author and committer dates of the commits since REV. + +Ask the user for the first reachable commit whose dates should +be changed. Then read the new date for that commit. The initial +minibuffer input and the previous history element offer good +values. The next commit will be created one minute later and so +on. + +This command is only intended for interactive use and should only +be used on highly rearranged and unpublished history. + +If KEYID is non-nil, then use that to sign all reshelved commits. +Interactively use the value of the \"--gpg-sign\" option in the +list returned by `magit-rebase-arguments'. + +\(fn REV KEYID)" t nil) + +(autoload 'magit-pop-revision-stack "magit-extras" "\ +Insert a representation of a revision into the current buffer. + +Pop a revision from the `magit-revision-stack' and insert it into +the current buffer according to `magit-pop-revision-stack-format'. +Revisions can be put on the stack using `magit-copy-section-value' +and `magit-copy-buffer-revision'. + +If the stack is empty or with a prefix argument, instead read a +revision in the minibuffer. By using the minibuffer history this +allows selecting an item which was popped earlier or to insert an +arbitrary reference or revision without first pushing it onto the +stack. + +When reading the revision from the minibuffer, then it might not +be possible to guess the correct repository. When this command +is called inside a repository (e.g. while composing a commit +message), then that repository is used. Otherwise (e.g. while +composing an email) then the repository recorded for the top +element of the stack is used (even though we insert another +revision). If not called inside a repository and with an empty +stack, or with two prefix arguments, then read the repository in +the minibuffer too. + +\(fn REV TOPLEVEL)" t nil) + +(autoload 'magit-copy-section-value "magit-extras" "\ +Save the value of the current section for later use. + +Save the section value to the `kill-ring', and, provided that +the current section is a commit, branch, or tag section, push +the (referenced) revision to the `magit-revision-stack' for use +with `magit-pop-revision-stack'. + +When `magit-copy-revision-abbreviated' is non-nil, save the +abbreviated revision to the `kill-ring' and the +`magit-revision-stack'. + +When the current section is a branch or a tag, and a prefix +argument is used, then save the revision at its tip to the +`kill-ring' instead of the reference name. + +When the region is active, then save that to the `kill-ring', +like `kill-ring-save' would, instead of behaving as described +above. If a prefix argument is used and the region is within +a hunk, then strip the diff marker column and keep only either +the added or removed lines, depending on the sign of the prefix +argument. + +\(fn ARG)" t nil) + +(autoload 'magit-copy-buffer-revision "magit-extras" "\ +Save the revision of the current buffer for later use. + +Save the revision shown in the current buffer to the `kill-ring' +and push it to the `magit-revision-stack'. + +This command is mainly intended for use in `magit-revision-mode' +buffers, the only buffers where it is always unambiguous exactly +which revision should be saved. + +Most other Magit buffers usually show more than one revision, in +some way or another, so this command has to select one of them, +and that choice might not always be the one you think would have +been the best pick. + +In such buffers it is often more useful to save the value of +the current section instead, using `magit-copy-section-value'. + +When the region is active, then save that to the `kill-ring', +like `kill-ring-save' would, instead of behaving as described +above. + +When `magit-copy-revision-abbreviated' is non-nil, save the +abbreviated revision to the `kill-ring' and the +`magit-revision-stack'." t nil) + +(autoload 'magit-display-repository-buffer "magit-extras" "\ +Display a Magit buffer belonging to the current Git repository. +The buffer is displayed using `magit-display-buffer', which see. + +\(fn BUFFER)" t nil) + +(autoload 'magit-switch-to-repository-buffer "magit-extras" "\ +Switch to a Magit buffer belonging to the current Git repository. + +\(fn BUFFER)" t nil) + +(autoload 'magit-switch-to-repository-buffer-other-window "magit-extras" "\ +Switch to a Magit buffer belonging to the current Git repository. + +\(fn BUFFER)" t nil) + +(autoload 'magit-switch-to-repository-buffer-other-frame "magit-extras" "\ +Switch to a Magit buffer belonging to the current Git repository. + +\(fn BUFFER)" t nil) + +(autoload 'magit-abort-dwim "magit-extras" "\ +Abort current operation. +Depending on the context, this will abort a merge, a rebase, a +patch application, a cherry-pick, a revert, or a bisect." t nil) + +(register-definition-prefixes "magit-extras" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-fetch" "magit-fetch.el" (0 0 0 0)) +;;; Generated autoloads from magit-fetch.el + (autoload 'magit-fetch "magit-fetch" nil t) + (autoload 'magit-fetch-from-pushremote "magit-fetch" nil t) + (autoload 'magit-fetch-from-upstream "magit-fetch" nil t) + +(autoload 'magit-fetch-other "magit-fetch" "\ +Fetch from another repository. + +\(fn REMOTE ARGS)" t nil) + +(autoload 'magit-fetch-branch "magit-fetch" "\ +Fetch a BRANCH from a REMOTE. + +\(fn REMOTE BRANCH ARGS)" t nil) + +(autoload 'magit-fetch-refspec "magit-fetch" "\ +Fetch a REFSPEC from a REMOTE. + +\(fn REMOTE REFSPEC ARGS)" t nil) + +(autoload 'magit-fetch-all "magit-fetch" "\ +Fetch from all remotes. + +\(fn ARGS)" t nil) + +(autoload 'magit-fetch-all-prune "magit-fetch" "\ +Fetch from all remotes, and prune. +Prune remote tracking branches for branches that have been +removed on the respective remote." t nil) + +(autoload 'magit-fetch-all-no-prune "magit-fetch" "\ +Fetch from all remotes." t nil) + (autoload 'magit-fetch-modules "magit-fetch" nil t) + +(register-definition-prefixes "magit-fetch" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-files" "magit-files.el" (0 0 0 0)) +;;; Generated autoloads from magit-files.el + +(autoload 'magit-find-file "magit-files" "\ +View FILE from REV. +Switch to a buffer visiting blob REV:FILE, creating one if none +already exists. If prior to calling this command the current +buffer and/or cursor position is about the same file, then go +to the line and column corresponding to that location. + +\(fn REV FILE)" t nil) + +(autoload 'magit-find-file-other-window "magit-files" "\ +View FILE from REV, in another window. +Switch to a buffer visiting blob REV:FILE, creating one if none +already exists. If prior to calling this command the current +buffer and/or cursor position is about the same file, then go to +the line and column corresponding to that location. + +\(fn REV FILE)" t nil) + +(autoload 'magit-find-file-other-frame "magit-files" "\ +View FILE from REV, in another frame. +Switch to a buffer visiting blob REV:FILE, creating one if none +already exists. If prior to calling this command the current +buffer and/or cursor position is about the same file, then go to +the line and column corresponding to that location. + +\(fn REV FILE)" t nil) + (autoload 'magit-file-dispatch "magit" nil t) + +(autoload 'magit-blob-visit-file "magit-files" "\ +View the file from the worktree corresponding to the current blob. +When visiting a blob or the version from the index, then go to +the same location in the respective file in the working tree." t nil) + +(autoload 'magit-file-checkout "magit-files" "\ +Checkout FILE from REV. + +\(fn REV FILE)" t nil) + +(register-definition-prefixes "magit-files" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-git" "magit-git.el" (0 0 0 0)) +;;; Generated autoloads from magit-git.el + +(register-definition-prefixes "magit-git" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-gitignore" "magit-gitignore.el" (0 0 +;;;;;; 0 0)) +;;; Generated autoloads from magit-gitignore.el + (autoload 'magit-gitignore "magit-gitignore" nil t) + +(autoload 'magit-gitignore-in-topdir "magit-gitignore" "\ +Add the Git ignore RULE to the top-level \".gitignore\" file. +Since this file is tracked, it is shared with other clones of the +repository. Also stage the file. + +\(fn RULE)" t nil) + +(autoload 'magit-gitignore-in-subdir "magit-gitignore" "\ +Add the Git ignore RULE to a \".gitignore\" file in DIRECTORY. +Prompt the user for a directory and add the rule to the +\".gitignore\" file in that directory. Since such files are +tracked, they are shared with other clones of the repository. +Also stage the file. + +\(fn RULE DIRECTORY)" t nil) + +(autoload 'magit-gitignore-in-gitdir "magit-gitignore" "\ +Add the Git ignore RULE to \"$GIT_DIR/info/exclude\". +Rules in that file only affects this clone of the repository. + +\(fn RULE)" t nil) + +(autoload 'magit-gitignore-on-system "magit-gitignore" "\ +Add the Git ignore RULE to the file specified by `core.excludesFile'. +Rules that are defined in that file affect all local repositories. + +\(fn RULE)" t nil) + +(autoload 'magit-skip-worktree "magit-gitignore" "\ +Call \"git update-index --skip-worktree -- FILE\". + +\(fn FILE)" t nil) + +(autoload 'magit-no-skip-worktree "magit-gitignore" "\ +Call \"git update-index --no-skip-worktree -- FILE\". + +\(fn FILE)" t nil) + +(autoload 'magit-assume-unchanged "magit-gitignore" "\ +Call \"git update-index --assume-unchanged -- FILE\". + +\(fn FILE)" t nil) + +(autoload 'magit-no-assume-unchanged "magit-gitignore" "\ +Call \"git update-index --no-assume-unchanged -- FILE\". + +\(fn FILE)" t nil) + +(register-definition-prefixes "magit-gitignore" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-log" "magit-log.el" (0 0 0 0)) +;;; Generated autoloads from magit-log.el + (autoload 'magit-log "magit-log" nil t) + (autoload 'magit-log-refresh "magit-log" nil t) + +(autoload 'magit-log-current "magit-log" "\ +Show log for the current branch. +When `HEAD' is detached or with a prefix argument show log for +one or more revs read from the minibuffer. + +\(fn REVS &optional ARGS FILES)" t nil) + +(autoload 'magit-log-head "magit-log" "\ +Show log for `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-related "magit-log" "\ +Show log for the current branch, its upstream and its push target. +When the upstream is a local branch, then also show its own +upstream. When `HEAD' is detached, then show log for that, the +previously checked out branch and its upstream and push-target. + +\(fn REVS &optional ARGS FILES)" t nil) + +(autoload 'magit-log-other "magit-log" "\ +Show log for one or more revs read from the minibuffer. +The user can input any revision or revisions separated by a +space, or even ranges, but only branches and tags, and a +representation of the commit at point, are available as +completion candidates. + +\(fn REVS &optional ARGS FILES)" t nil) + +(autoload 'magit-log-branches "magit-log" "\ +Show log for all local branches and `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-matching-branches "magit-log" "\ +Show log for all branches matching PATTERN and `HEAD'. + +\(fn PATTERN &optional ARGS FILES)" t nil) + +(autoload 'magit-log-matching-tags "magit-log" "\ +Show log for all tags matching PATTERN and `HEAD'. + +\(fn PATTERN &optional ARGS FILES)" t nil) + +(autoload 'magit-log-all-branches "magit-log" "\ +Show log for all local and remote branches and `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-all "magit-log" "\ +Show log for all references and `HEAD'. + +\(fn &optional ARGS FILES)" t nil) + +(autoload 'magit-log-buffer-file "magit-log" "\ +Show log for the blob or file visited in the current buffer. +With a prefix argument or when `--follow' is an active log +argument, then follow renames. When the region is active, +restrict the log to the lines that the region touches. + +\(fn &optional FOLLOW BEG END)" t nil) + +(autoload 'magit-log-trace-definition "magit-log" "\ +Show log for the definition at point. + +\(fn FILE FN REV)" t nil) + +(autoload 'magit-log-merged "magit-log" "\ +Show log for the merge of COMMIT into BRANCH. + +More precisely, find merge commit M that brought COMMIT into +BRANCH, and show the log of the range \"M^1..M\". If COMMIT is +directly on BRANCH, then show approximately +`magit-log-merged-commit-count' surrounding commits instead. + +This command requires git-when-merged, which is available from +https://github.com/mhagger/git-when-merged. + +\(fn COMMIT BRANCH &optional ARGS FILES)" t nil) + +(autoload 'magit-log-move-to-parent "magit-log" "\ +Move to the Nth parent of the current commit. + +\(fn &optional N)" t nil) + (autoload 'magit-shortlog "magit-log" nil t) + +(autoload 'magit-shortlog-since "magit-log" "\ +Show a history summary for commits since REV. + +\(fn REV ARGS)" t nil) + +(autoload 'magit-shortlog-range "magit-log" "\ +Show a history summary for commit or range REV-OR-RANGE. + +\(fn REV-OR-RANGE ARGS)" t nil) + +(autoload 'magit-cherry "magit-log" "\ +Show commits in a branch that are not merged in the upstream branch. + +\(fn HEAD UPSTREAM)" t nil) + +(register-definition-prefixes "magit-log" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-margin" "magit-margin.el" (0 0 0 0)) +;;; Generated autoloads from magit-margin.el + +(register-definition-prefixes "magit-margin" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-merge" "magit-merge.el" (0 0 0 0)) +;;; Generated autoloads from magit-merge.el + (autoload 'magit-merge "magit" nil t) + +(autoload 'magit-merge-plain "magit-merge" "\ +Merge commit REV into the current branch; using default message. + +Unless there are conflicts or a prefix argument is used create a +merge commit using a generic commit message and without letting +the user inspect the result. With a prefix argument pretend the +merge failed to give the user the opportunity to inspect the +merge. + +\(git merge --no-edit|--no-commit [ARGS] REV) + +\(fn REV &optional ARGS NOCOMMIT)" t nil) + +(autoload 'magit-merge-editmsg "magit-merge" "\ +Merge commit REV into the current branch; and edit message. +Perform the merge and prepare a commit message but let the user +edit it. + +\(git merge --edit --no-ff [ARGS] REV) + +\(fn REV &optional ARGS)" t nil) + +(autoload 'magit-merge-nocommit "magit-merge" "\ +Merge commit REV into the current branch; pretending it failed. +Pretend the merge failed to give the user the opportunity to +inspect the merge and change the commit message. + +\(git merge --no-commit --no-ff [ARGS] REV) + +\(fn REV &optional ARGS)" t nil) + +(autoload 'magit-merge-into "magit-merge" "\ +Merge the current branch into BRANCH and remove the former. + +Before merging, force push the source branch to its push-remote, +provided the respective remote branch already exists, ensuring +that the respective pull-request (if any) won't get stuck on some +obsolete version of the commits that are being merged. Finally +if `forge-branch-pullreq' was used to create the merged branch, +then also remove the respective remote branch. + +\(fn BRANCH &optional ARGS)" t nil) + +(autoload 'magit-merge-absorb "magit-merge" "\ +Merge BRANCH into the current branch and remove the former. + +Before merging, force push the source branch to its push-remote, +provided the respective remote branch already exists, ensuring +that the respective pull-request (if any) won't get stuck on some +obsolete version of the commits that are being merged. Finally +if `forge-branch-pullreq' was used to create the merged branch, +then also remove the respective remote branch. + +\(fn BRANCH &optional ARGS)" t nil) + +(autoload 'magit-merge-squash "magit-merge" "\ +Squash commit REV into the current branch; don't create a commit. + +\(git merge --squash REV) + +\(fn REV)" t nil) + +(autoload 'magit-merge-preview "magit-merge" "\ +Preview result of merging REV into the current branch. + +\(fn REV)" t nil) + +(autoload 'magit-merge-abort "magit-merge" "\ +Abort the current merge operation. + +\(git merge --abort)" t nil) + +(register-definition-prefixes "magit-merge" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-mode" "magit-mode.el" (0 0 0 0)) +;;; Generated autoloads from magit-mode.el + +(autoload 'magit-info "magit-mode" "\ +Visit the Magit manual." t nil) + +(register-definition-prefixes "magit-mode" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-notes" "magit-notes.el" (0 0 0 0)) +;;; Generated autoloads from magit-notes.el + (autoload 'magit-notes "magit" nil t) + +(register-definition-prefixes "magit-notes" '("magit-notes-")) + +;;;*** + +;;;### (autoloads nil "magit-obsolete" "magit-obsolete.el" (0 0 0 +;;;;;; 0)) +;;; Generated autoloads from magit-obsolete.el + +(register-definition-prefixes "magit-obsolete" '("magit--magit-popup-warning")) + +;;;*** + +;;;### (autoloads nil "magit-patch" "magit-patch.el" (0 0 0 0)) +;;; Generated autoloads from magit-patch.el + (autoload 'magit-patch "magit-patch" nil t) + (autoload 'magit-patch-create "magit-patch" nil t) + (autoload 'magit-patch-apply "magit-patch" nil t) + +(autoload 'magit-patch-save "magit-patch" "\ +Write current diff into patch FILE. + +What arguments are used to create the patch depends on the value +of `magit-patch-save-arguments' and whether a prefix argument is +used. + +If the value is the symbol `buffer', then use the same arguments +as the buffer. With a prefix argument use no arguments. + +If the value is a list beginning with the symbol `exclude', then +use the same arguments as the buffer except for those matched by +entries in the cdr of the list. The comparison is done using +`string-prefix-p'. With a prefix argument use the same arguments +as the buffer. + +If the value is a list of strings (including the empty list), +then use those arguments. With a prefix argument use the same +arguments as the buffer. + +Of course the arguments that are required to actually show the +same differences as those shown in the buffer are always used. + +\(fn FILE &optional ARG)" t nil) + +(autoload 'magit-request-pull "magit-patch" "\ +Request upstream to pull from your public repository. + +URL is the url of your publicly accessible repository. +START is a commit that already is in the upstream repository. +END is the last commit, usually a branch name, which upstream +is asked to pull. START has to be reachable from that commit. + +\(fn URL START END)" t nil) + +(register-definition-prefixes "magit-patch" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-process" "magit-process.el" (0 0 0 0)) +;;; Generated autoloads from magit-process.el + +(register-definition-prefixes "magit-process" '("magit-" "tramp-sh-handle-")) + +;;;*** + +;;;### (autoloads nil "magit-pull" "magit-pull.el" (0 0 0 0)) +;;; Generated autoloads from magit-pull.el + (autoload 'magit-pull "magit-pull" nil t) + (autoload 'magit-pull-from-pushremote "magit-pull" nil t) + (autoload 'magit-pull-from-upstream "magit-pull" nil t) + +(autoload 'magit-pull-branch "magit-pull" "\ +Pull from a branch read in the minibuffer. + +\(fn SOURCE ARGS)" t nil) + +(register-definition-prefixes "magit-pull" '("magit-pull-")) + +;;;*** + +;;;### (autoloads nil "magit-push" "magit-push.el" (0 0 0 0)) +;;; Generated autoloads from magit-push.el + (autoload 'magit-push "magit-push" nil t) + (autoload 'magit-push-current-to-pushremote "magit-push" nil t) + (autoload 'magit-push-current-to-upstream "magit-push" nil t) + +(autoload 'magit-push-current "magit-push" "\ +Push the current branch to a branch read in the minibuffer. + +\(fn TARGET ARGS)" t nil) + +(autoload 'magit-push-other "magit-push" "\ +Push an arbitrary branch or commit somewhere. +Both the source and the target are read in the minibuffer. + +\(fn SOURCE TARGET ARGS)" t nil) + +(autoload 'magit-push-refspecs "magit-push" "\ +Push one or multiple REFSPECS to a REMOTE. +Both the REMOTE and the REFSPECS are read in the minibuffer. To +use multiple REFSPECS, separate them with commas. Completion is +only available for the part before the colon, or when no colon +is used. + +\(fn REMOTE REFSPECS ARGS)" t nil) + +(autoload 'magit-push-matching "magit-push" "\ +Push all matching branches to another repository. +If multiple remotes exist, then read one from the user. +If just one exists, use that without requiring confirmation. + +\(fn REMOTE &optional ARGS)" t nil) + +(autoload 'magit-push-tags "magit-push" "\ +Push all tags to another repository. +If only one remote exists, then push to that. Otherwise prompt +for a remote, offering the remote configured for the current +branch as default. + +\(fn REMOTE &optional ARGS)" t nil) + +(autoload 'magit-push-tag "magit-push" "\ +Push a tag to another repository. + +\(fn TAG REMOTE &optional ARGS)" t nil) + +(autoload 'magit-push-notes-ref "magit-push" "\ +Push a notes ref to another repository. + +\(fn REF REMOTE &optional ARGS)" t nil) + (autoload 'magit-push-implicitly "magit-push" nil t) + (autoload 'magit-push-to-remote "magit-push" nil t) + +(register-definition-prefixes "magit-push" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-reflog" "magit-reflog.el" (0 0 0 0)) +;;; Generated autoloads from magit-reflog.el + +(autoload 'magit-reflog-current "magit-reflog" "\ +Display the reflog of the current branch. +If `HEAD' is detached, then show the reflog for that instead." t nil) + +(autoload 'magit-reflog-other "magit-reflog" "\ +Display the reflog of a branch or another ref. + +\(fn REF)" t nil) + +(autoload 'magit-reflog-head "magit-reflog" "\ +Display the `HEAD' reflog." t nil) + +(register-definition-prefixes "magit-reflog" '("magit-reflog-")) + +;;;*** + +;;;### (autoloads nil "magit-refs" "magit-refs.el" (0 0 0 0)) +;;; Generated autoloads from magit-refs.el + (autoload 'magit-show-refs "magit-refs" nil t) + +(autoload 'magit-show-refs-head "magit-refs" "\ +List and compare references in a dedicated buffer. +Compared with `HEAD'. + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-show-refs-current "magit-refs" "\ +List and compare references in a dedicated buffer. +Compare with the current branch or `HEAD' if it is detached. + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-show-refs-other "magit-refs" "\ +List and compare references in a dedicated buffer. +Compared with a branch read from the user. + +\(fn &optional REF ARGS)" t nil) + +(register-definition-prefixes "magit-refs" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-remote" "magit-remote.el" (0 0 0 0)) +;;; Generated autoloads from magit-remote.el + (autoload 'magit-remote "magit-remote" nil t) + +(autoload 'magit-remote-add "magit-remote" "\ +Add a remote named REMOTE and fetch it. + +\(fn REMOTE URL &optional ARGS)" t nil) + +(autoload 'magit-remote-rename "magit-remote" "\ +Rename the remote named OLD to NEW. + +\(fn OLD NEW)" t nil) + +(autoload 'magit-remote-remove "magit-remote" "\ +Delete the remote named REMOTE. + +\(fn REMOTE)" t nil) + +(autoload 'magit-remote-prune "magit-remote" "\ +Remove stale remote-tracking branches for REMOTE. + +\(fn REMOTE)" t nil) + +(autoload 'magit-remote-prune-refspecs "magit-remote" "\ +Remove stale refspecs for REMOTE. + +A refspec is stale if there no longer exists at least one branch +on the remote that would be fetched due to that refspec. A stale +refspec is problematic because its existence causes Git to refuse +to fetch according to the remaining non-stale refspecs. + +If only stale refspecs remain, then offer to either delete the +remote or to replace the stale refspecs with the default refspec. + +Also remove the remote-tracking branches that were created due to +the now stale refspecs. Other stale branches are not removed. + +\(fn REMOTE)" t nil) + +(autoload 'magit-remote-set-head "magit-remote" "\ +Set the local representation of REMOTE's default branch. +Query REMOTE and set the symbolic-ref refs/remotes//HEAD +accordingly. With a prefix argument query for the branch to be +used, which allows you to select an incorrect value if you fancy +doing that. + +\(fn REMOTE &optional BRANCH)" t nil) + +(autoload 'magit-remote-unset-head "magit-remote" "\ +Unset the local representation of REMOTE's default branch. +Delete the symbolic-ref \"refs/remotes//HEAD\". + +\(fn REMOTE)" t nil) + +(autoload 'magit-remote-unshallow "magit-remote" "\ +Convert a shallow remote into a full one. +If only a single refspec is set and it does not contain a +wildcard, then also offer to replace it with the standard +refspec. + +\(fn REMOTE)" t nil) + (autoload 'magit-remote-configure "magit-remote" nil t) + +(register-definition-prefixes "magit-remote" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-repos" "magit-repos.el" (0 0 0 0)) +;;; Generated autoloads from magit-repos.el + +(autoload 'magit-list-repositories "magit-repos" "\ +Display a list of repositories. + +Use the options `magit-repository-directories' to control which +repositories are displayed." t nil) + +(register-definition-prefixes "magit-repos" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-reset" "magit-reset.el" (0 0 0 0)) +;;; Generated autoloads from magit-reset.el + (autoload 'magit-reset "magit" nil t) + +(autoload 'magit-reset-mixed "magit-reset" "\ +Reset the `HEAD' and index to COMMIT, but not the working tree. + +\(git reset --mixed COMMIT) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-soft "magit-reset" "\ +Reset the `HEAD' to COMMIT, but not the index and working tree. + +\(git reset --soft REVISION) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-hard "magit-reset" "\ +Reset the `HEAD', index, and working tree to COMMIT. + +\(git reset --hard REVISION) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-keep "magit-reset" "\ +Reset the `HEAD' and index to COMMIT, while keeping uncommitted changes. + +\(git reset --keep REVISION) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-index "magit-reset" "\ +Reset the index to COMMIT. +Keep the `HEAD' and working tree as-is, so if COMMIT refers to the +head this effectively unstages all changes. + +\(git reset COMMIT .) + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-worktree "magit-reset" "\ +Reset the worktree to COMMIT. +Keep the `HEAD' and index as-is. + +\(fn COMMIT)" t nil) + +(autoload 'magit-reset-quickly "magit-reset" "\ +Reset the `HEAD' and index to COMMIT, and possibly the working tree. +With a prefix argument reset the working tree otherwise don't. + +\(git reset --mixed|--hard COMMIT) + +\(fn COMMIT &optional HARD)" t nil) + +(register-definition-prefixes "magit-reset" '("magit-reset-")) + +;;;*** + +;;;### (autoloads nil "magit-sequence" "magit-sequence.el" (0 0 0 +;;;;;; 0)) +;;; Generated autoloads from magit-sequence.el + +(autoload 'magit-sequencer-continue "magit-sequence" "\ +Resume the current cherry-pick or revert sequence." t nil) + +(autoload 'magit-sequencer-skip "magit-sequence" "\ +Skip the stopped at commit during a cherry-pick or revert sequence." t nil) + +(autoload 'magit-sequencer-abort "magit-sequence" "\ +Abort the current cherry-pick or revert sequence. +This discards all changes made since the sequence started." t nil) + (autoload 'magit-cherry-pick "magit-sequence" nil t) + +(autoload 'magit-cherry-copy "magit-sequence" "\ +Copy COMMITS from another branch onto the current branch. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then pick all of them, +without prompting. + +\(fn COMMITS &optional ARGS)" t nil) + +(autoload 'magit-cherry-apply "magit-sequence" "\ +Apply the changes in COMMITS but do not commit them. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then apply all of them, +without prompting. + +\(fn COMMITS &optional ARGS)" t nil) + +(autoload 'magit-cherry-harvest "magit-sequence" "\ +Move COMMITS from another BRANCH onto the current branch. +Remove the COMMITS from BRANCH and stay on the current branch. +If a conflict occurs, then you have to fix that and finish the +process manually. + +\(fn COMMITS BRANCH &optional ARGS)" t nil) + +(autoload 'magit-cherry-donate "magit-sequence" "\ +Move COMMITS from the current branch onto another existing BRANCH. +Remove COMMITS from the current branch and stay on that branch. +If a conflict occurs, then you have to fix that and finish the +process manually. `HEAD' is allowed to be detached initially. + +\(fn COMMITS BRANCH &optional ARGS)" t nil) + +(autoload 'magit-cherry-spinout "magit-sequence" "\ +Move COMMITS from the current branch onto a new BRANCH. +Remove COMMITS from the current branch and stay on that branch. +If a conflict occurs, then you have to fix that and finish the +process manually. + +\(fn COMMITS BRANCH START-POINT &optional ARGS)" t nil) + +(autoload 'magit-cherry-spinoff "magit-sequence" "\ +Move COMMITS from the current branch onto a new BRANCH. +Remove COMMITS from the current branch and checkout BRANCH. +If a conflict occurs, then you have to fix that and finish +the process manually. + +\(fn COMMITS BRANCH START-POINT &optional ARGS)" t nil) + (autoload 'magit-revert "magit-sequence" nil t) + +(autoload 'magit-revert-and-commit "magit-sequence" "\ +Revert COMMIT by creating a new commit. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting. + +\(fn COMMIT &optional ARGS)" t nil) + +(autoload 'magit-revert-no-commit "magit-sequence" "\ +Revert COMMIT by applying it in reverse to the worktree. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting. + +\(fn COMMIT &optional ARGS)" t nil) + (autoload 'magit-am "magit-sequence" nil t) + +(autoload 'magit-am-apply-patches "magit-sequence" "\ +Apply the patches FILES. + +\(fn &optional FILES ARGS)" t nil) + +(autoload 'magit-am-apply-maildir "magit-sequence" "\ +Apply the patches from MAILDIR. + +\(fn &optional MAILDIR ARGS)" t nil) + +(autoload 'magit-am-continue "magit-sequence" "\ +Resume the current patch applying sequence." t nil) + +(autoload 'magit-am-skip "magit-sequence" "\ +Skip the stopped at patch during a patch applying sequence." t nil) + +(autoload 'magit-am-abort "magit-sequence" "\ +Abort the current patch applying sequence. +This discards all changes made since the sequence started." t nil) + (autoload 'magit-rebase "magit-sequence" nil t) + (autoload 'magit-rebase-onto-pushremote "magit-sequence" nil t) + (autoload 'magit-rebase-onto-upstream "magit-sequence" nil t) + +(autoload 'magit-rebase-branch "magit-sequence" "\ +Rebase the current branch onto a branch read in the minibuffer. +All commits that are reachable from `HEAD' but not from the +selected branch TARGET are being rebased. + +\(fn TARGET ARGS)" t nil) + +(autoload 'magit-rebase-subset "magit-sequence" "\ +Rebase a subset of the current branch's history onto a new base. +Rebase commits from START to `HEAD' onto NEWBASE. +START has to be selected from a list of recent commits. + +\(fn NEWBASE START ARGS)" t nil) + +(autoload 'magit-rebase-interactive "magit-sequence" "\ +Start an interactive rebase sequence. + +\(fn COMMIT ARGS)" t nil) + +(autoload 'magit-rebase-autosquash "magit-sequence" "\ +Combine squash and fixup commits with their intended targets. + +\(fn ARGS)" t nil) + +(autoload 'magit-rebase-edit-commit "magit-sequence" "\ +Edit a single older commit using rebase. + +\(fn COMMIT ARGS)" t nil) + +(autoload 'magit-rebase-reword-commit "magit-sequence" "\ +Reword a single older commit using rebase. + +\(fn COMMIT ARGS)" t nil) + +(autoload 'magit-rebase-remove-commit "magit-sequence" "\ +Remove a single older commit using rebase. + +\(fn COMMIT ARGS)" t nil) + +(autoload 'magit-rebase-continue "magit-sequence" "\ +Restart the current rebasing operation. +In some cases this pops up a commit message buffer for you do +edit. With a prefix argument the old message is reused as-is. + +\(fn &optional NOEDIT)" t nil) + +(autoload 'magit-rebase-skip "magit-sequence" "\ +Skip the current commit and restart the current rebase operation." t nil) + +(autoload 'magit-rebase-edit "magit-sequence" "\ +Edit the todo list of the current rebase operation." t nil) + +(autoload 'magit-rebase-abort "magit-sequence" "\ +Abort the current rebase operation, restoring the original branch." t nil) + +(register-definition-prefixes "magit-sequence" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-sparse-checkout" "magit-sparse-checkout.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from magit-sparse-checkout.el + (autoload 'magit-sparse-checkout "magit-sparse-checkout" nil t) + +(autoload 'magit-sparse-checkout-enable "magit-sparse-checkout" "\ +Convert the working tree to a sparse checkout. + +\(fn &optional ARGS)" t nil) + +(autoload 'magit-sparse-checkout-set "magit-sparse-checkout" "\ +Restrict working tree to DIRECTORIES. +To extend rather than override the currently configured +directories, call `magit-sparse-checkout-add' instead. + +\(fn DIRECTORIES)" t nil) + +(autoload 'magit-sparse-checkout-add "magit-sparse-checkout" "\ +Add DIRECTORIES to the working tree. +To override rather than extend the currently configured +directories, call `magit-sparse-checkout-set' instead. + +\(fn DIRECTORIES)" t nil) + +(autoload 'magit-sparse-checkout-reapply "magit-sparse-checkout" "\ +Reapply the sparse checkout rules to the working tree. +Some operations such as merging or rebasing may need to check out +files that aren't included in the sparse checkout. Call this +command to reset to the sparse checkout state." t nil) + +(autoload 'magit-sparse-checkout-disable "magit-sparse-checkout" "\ +Convert sparse checkout to full checkout. +Note that disabling the sparse checkout does not clear the +configured directories. Call `magit-sparse-checkout-enable' to +restore the previous sparse checkout." t nil) + +(register-definition-prefixes "magit-sparse-checkout" '("magit-sparse-checkout-")) + +;;;*** + +;;;### (autoloads nil "magit-stash" "magit-stash.el" (0 0 0 0)) +;;; Generated autoloads from magit-stash.el + (autoload 'magit-stash "magit-stash" nil t) + +(autoload 'magit-stash-both "magit-stash" "\ +Create a stash of the index and working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn MESSAGE &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-stash-index "magit-stash" "\ +Create a stash of the index only. +Unstaged and untracked changes are not stashed. The stashed +changes are applied in reverse to both the index and the +worktree. This command can fail when the worktree is not clean. +Applying the resulting stash has the inverse effect. + +\(fn MESSAGE)" t nil) + +(autoload 'magit-stash-worktree "magit-stash" "\ +Create a stash of unstaged changes in the working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn MESSAGE &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-stash-keep-index "magit-stash" "\ +Create a stash of the index and working tree, keeping index intact. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn MESSAGE &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-snapshot-both "magit-stash" "\ +Create a snapshot of the index and working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn &optional INCLUDE-UNTRACKED)" t nil) + +(autoload 'magit-snapshot-index "magit-stash" "\ +Create a snapshot of the index only. +Unstaged and untracked changes are not stashed." t nil) + +(autoload 'magit-snapshot-worktree "magit-stash" "\ +Create a snapshot of unstaged changes in the working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'. + +\(fn &optional INCLUDE-UNTRACKED)" t nil) + (autoload 'magit-stash-push "magit-stash" nil t) + +(autoload 'magit-stash-apply "magit-stash" "\ +Apply a stash to the working tree. +Try to preserve the stash index. If that fails because there +are staged changes, apply without preserving the stash index. + +\(fn STASH)" t nil) + +(autoload 'magit-stash-pop "magit-stash" "\ +Apply a stash to the working tree and remove it from stash list. +Try to preserve the stash index. If that fails because there +are staged changes, apply without preserving the stash index +and forgo removing the stash. + +\(fn STASH)" t nil) + +(autoload 'magit-stash-drop "magit-stash" "\ +Remove a stash from the stash list. +When the region is active offer to drop all contained stashes. + +\(fn STASH)" t nil) + +(autoload 'magit-stash-clear "magit-stash" "\ +Remove all stashes saved in REF's reflog by deleting REF. + +\(fn REF)" t nil) + +(autoload 'magit-stash-branch "magit-stash" "\ +Create and checkout a new BRANCH from STASH. + +\(fn STASH BRANCH)" t nil) + +(autoload 'magit-stash-branch-here "magit-stash" "\ +Create and checkout a new BRANCH and apply STASH. +The branch is created using `magit-branch-and-checkout', using the +current branch or `HEAD' as the start-point. + +\(fn STASH BRANCH)" t nil) + +(autoload 'magit-stash-format-patch "magit-stash" "\ +Create a patch from STASH + +\(fn STASH)" t nil) + +(autoload 'magit-stash-list "magit-stash" "\ +List all stashes in a buffer." t nil) + +(autoload 'magit-stash-show "magit-stash" "\ +Show all diffs of a stash in a buffer. + +\(fn STASH &optional ARGS FILES)" t nil) + +(register-definition-prefixes "magit-stash" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-status" "magit-status.el" (0 0 0 0)) +;;; Generated autoloads from magit-status.el + +(autoload 'magit-init "magit-status" "\ +Initialize a Git repository, then show its status. + +If the directory is below an existing repository, then the user +has to confirm that a new one should be created inside. If the +directory is the root of the existing repository, then the user +has to confirm that it should be reinitialized. + +Non-interactively DIRECTORY is (re-)initialized unconditionally. + +\(fn DIRECTORY)" t nil) + +(autoload 'magit-status "magit-status" "\ +Show the status of the current Git repository in a buffer. + +If the current directory isn't located within a Git repository, +then prompt for an existing repository or an arbitrary directory, +depending on option `magit-repository-directories', and show the +status of the selected repository instead. + +* If that option specifies any existing repositories, then offer + those for completion and show the status buffer for the + selected one. + +* Otherwise read an arbitrary directory using regular file-name + completion. If the selected directory is the top-level of an + existing working tree, then show the status buffer for that. + +* Otherwise offer to initialize the selected directory as a new + repository. After creating the repository show its status + buffer. + +These fallback behaviors can also be forced using one or more +prefix arguments: + +* With two prefix arguments (or more precisely a numeric prefix + value of 16 or greater) read an arbitrary directory and act on + it as described above. The same could be accomplished using + the command `magit-init'. + +* With a single prefix argument read an existing repository, or + if none can be found based on `magit-repository-directories', + then fall back to the same behavior as with two prefix + arguments. + +\(fn &optional DIRECTORY CACHE)" t nil) + +(defalias 'magit #'magit-status "\ +An alias for `magit-status' for better discoverability. + +Instead of invoking this alias for `magit-status' using +\"M-x magit RET\", you should bind a key to `magit-status' +and read the info node `(magit)Getting Started', which +also contains other useful hints.") + +(autoload 'magit-status-here "magit-status" "\ +Like `magit-status' but with non-nil `magit-status-goto-file-position'." t nil) + +(autoload 'magit-status-quick "magit-status" "\ +Show the status of the current Git repository, maybe without refreshing. + +If the status buffer of the current Git repository exists but +isn't being displayed in the selected frame, then display it +without refreshing it. + +If the status buffer is being displayed in the selected frame, +then also refresh it. + +Prefix arguments have the same meaning as for `magit-status', +and additionally cause the buffer to be refresh. + +To use this function instead of `magit-status', add this to your +init file: (global-set-key (kbd \"C-x g\") \\='magit-status-quick)." t nil) + +(autoload 'magit-status-setup-buffer "magit-status" "\ + + +\(fn &optional DIRECTORY)" nil nil) + +(register-definition-prefixes "magit-status" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-submodule" "magit-submodule.el" (0 0 +;;;;;; 0 0)) +;;; Generated autoloads from magit-submodule.el + (autoload 'magit-submodule "magit-submodule" nil t) + (autoload 'magit-submodule-add "magit-submodule" nil t) + +(autoload 'magit-submodule-read-name-for-path "magit-submodule" "\ + + +\(fn PATH &optional PREFER-SHORT)" nil nil) + (autoload 'magit-submodule-register "magit-submodule" nil t) + (autoload 'magit-submodule-populate "magit-submodule" nil t) + (autoload 'magit-submodule-update "magit-submodule" nil t) + (autoload 'magit-submodule-synchronize "magit-submodule" nil t) + (autoload 'magit-submodule-unpopulate "magit-submodule" nil t) + +(autoload 'magit-submodule-remove "magit-submodule" "\ +Unregister MODULES and remove their working directories. + +For safety reasons, do not remove the gitdirs and if a module has +uncommitted changes, then do not remove it at all. If a module's +gitdir is located inside the working directory, then move it into +the gitdir of the superproject first. + +With the \"--force\" argument offer to remove dirty working +directories and with a prefix argument offer to delete gitdirs. +Both actions are very dangerous and have to be confirmed. There +are additional safety precautions in place, so you might be able +to recover from making a mistake here, but don't count on it. + +\(fn MODULES ARGS TRASH-GITDIRS)" t nil) + +(autoload 'magit-insert-modules "magit-submodule" "\ +Insert submodule sections. +Hook `magit-module-sections-hook' controls which module sections +are inserted, and option `magit-module-sections-nested' controls +whether they are wrapped in an additional section." nil nil) + +(autoload 'magit-insert-modules-overview "magit-submodule" "\ +Insert sections for all modules. +For each section insert the path and the output of `git describe --tags', +or, failing that, the abbreviated HEAD commit hash." nil nil) + +(autoload 'magit-insert-modules-unpulled-from-upstream "magit-submodule" "\ +Insert sections for modules that haven't been pulled from the upstream. +These sections can be expanded to show the respective commits." nil nil) + +(autoload 'magit-insert-modules-unpulled-from-pushremote "magit-submodule" "\ +Insert sections for modules that haven't been pulled from the push-remote. +These sections can be expanded to show the respective commits." nil nil) + +(autoload 'magit-insert-modules-unpushed-to-upstream "magit-submodule" "\ +Insert sections for modules that haven't been pushed to the upstream. +These sections can be expanded to show the respective commits." nil nil) + +(autoload 'magit-insert-modules-unpushed-to-pushremote "magit-submodule" "\ +Insert sections for modules that haven't been pushed to the push-remote. +These sections can be expanded to show the respective commits." nil nil) + +(autoload 'magit-list-submodules "magit-submodule" "\ +Display a list of the current repository's submodules." t nil) + +(register-definition-prefixes "magit-submodule" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-subtree" "magit-subtree.el" (0 0 0 0)) +;;; Generated autoloads from magit-subtree.el + (autoload 'magit-subtree "magit-subtree" nil t) + (autoload 'magit-subtree-import "magit-subtree" nil t) + (autoload 'magit-subtree-export "magit-subtree" nil t) + +(autoload 'magit-subtree-add "magit-subtree" "\ +Add REF from REPOSITORY as a new subtree at PREFIX. + +\(fn PREFIX REPOSITORY REF ARGS)" t nil) + +(autoload 'magit-subtree-add-commit "magit-subtree" "\ +Add COMMIT as a new subtree at PREFIX. + +\(fn PREFIX COMMIT ARGS)" t nil) + +(autoload 'magit-subtree-merge "magit-subtree" "\ +Merge COMMIT into the PREFIX subtree. + +\(fn PREFIX COMMIT ARGS)" t nil) + +(autoload 'magit-subtree-pull "magit-subtree" "\ +Pull REF from REPOSITORY into the PREFIX subtree. + +\(fn PREFIX REPOSITORY REF ARGS)" t nil) + +(autoload 'magit-subtree-push "magit-subtree" "\ +Extract the history of the subtree PREFIX and push it to REF on REPOSITORY. + +\(fn PREFIX REPOSITORY REF ARGS)" t nil) + +(autoload 'magit-subtree-split "magit-subtree" "\ +Extract the history of the subtree PREFIX. + +\(fn PREFIX COMMIT ARGS)" t nil) + +(register-definition-prefixes "magit-subtree" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-tag" "magit-tag.el" (0 0 0 0)) +;;; Generated autoloads from magit-tag.el + (autoload 'magit-tag "magit" nil t) + +(autoload 'magit-tag-create "magit-tag" "\ +Create a new tag with the given NAME at REV. +With a prefix argument annotate the tag. + +\(git tag [--annotate] NAME REV) + +\(fn NAME REV &optional ARGS)" t nil) + +(autoload 'magit-tag-delete "magit-tag" "\ +Delete one or more tags. +If the region marks multiple tags (and nothing else), then offer +to delete those, otherwise prompt for a single tag to be deleted, +defaulting to the tag at point. + +\(git tag -d TAGS) + +\(fn TAGS)" t nil) + +(autoload 'magit-tag-prune "magit-tag" "\ +Offer to delete tags missing locally from REMOTE, and vice versa. + +\(fn TAGS REMOTE-TAGS REMOTE)" t nil) + +(autoload 'magit-tag-release "magit-tag" "\ +Create a release tag for `HEAD'. + +Assume that release tags match `magit-release-tag-regexp'. + +If `HEAD's message matches `magit-release-commit-regexp', then +base the tag on the version string specified by that. Otherwise +prompt for the name of the new tag using the highest existing +tag as initial input and leaving it to the user to increment the +desired part of the version string. + +If `--annotate' is enabled, then prompt for the message of the +new tag. Base the proposed tag message on the message of the +highest tag, provided that that contains the corresponding +version string and substituting the new version string for that. +Otherwise propose something like \"Foo-Bar 1.2.3\", given, for +example, a TAG \"v1.2.3\" and a repository located at something +like \"/path/to/foo-bar\". + +\(fn TAG MSG &optional ARGS)" t nil) + +(register-definition-prefixes "magit-tag" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-transient" "magit-transient.el" (0 0 +;;;;;; 0 0)) +;;; Generated autoloads from magit-transient.el + +(register-definition-prefixes "magit-transient" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-wip" "magit-wip.el" (0 0 0 0)) +;;; Generated autoloads from magit-wip.el + +(defvar magit-wip-mode nil "\ +Non-nil if Magit-Wip mode is enabled. +See the `magit-wip-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `magit-wip-mode'.") + +(custom-autoload 'magit-wip-mode "magit-wip" nil) + +(autoload 'magit-wip-mode "magit-wip" "\ +Save uncommitted changes to work-in-progress refs. + +This is a minor mode. If called interactively, toggle the +`Magit-Wip mode' mode. If the prefix argument is positive, +enable the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='magit-wip-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +Whenever appropriate (i.e. when dataloss would be a possibility +otherwise) this mode causes uncommitted changes to be committed +to dedicated work-in-progress refs. + +For historic reasons this mode is implemented on top of four +other `magit-wip-*' modes, which can also be used individually, +if you want finer control over when the wip refs are updated; +but that is discouraged. + +\(fn &optional ARG)" t nil) + +(put 'magit-wip-after-save-mode 'globalized-minor-mode t) + +(defvar magit-wip-after-save-mode nil "\ +Non-nil if Magit-Wip-After-Save mode is enabled. +See the `magit-wip-after-save-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `magit-wip-after-save-mode'.") + +(custom-autoload 'magit-wip-after-save-mode "magit-wip" nil) + +(autoload 'magit-wip-after-save-mode "magit-wip" "\ +Toggle Magit-Wip-After-Save-Local mode in all buffers. +With prefix ARG, enable Magit-Wip-After-Save mode if ARG is positive; +otherwise, disable it. + +If called from Lisp, toggle the mode if ARG is `toggle'. +Enable the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +Magit-Wip-After-Save-Local mode is enabled in all buffers where +`magit-wip-after-save-local-mode-turn-on' would do it. + +See `magit-wip-after-save-local-mode' for more information on +Magit-Wip-After-Save-Local mode. + +\(fn &optional ARG)" t nil) + +(defvar magit-wip-after-apply-mode nil "\ +Non-nil if Magit-Wip-After-Apply mode is enabled. +See the `magit-wip-after-apply-mode' command +for a description of this minor mode.") + +(custom-autoload 'magit-wip-after-apply-mode "magit-wip" nil) + +(autoload 'magit-wip-after-apply-mode "magit-wip" "\ +Commit to work-in-progress refs. + +This is a minor mode. If called interactively, toggle the +`Magit-Wip-After-Apply mode' mode. If the prefix argument is +positive, enable the mode, and if it is zero or negative, disable +the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='magit-wip-after-apply-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +After applying a change using any \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected files to the current wip refs. For each branch there +may be two wip refs; one contains snapshots of the files as found +in the worktree and the other contains snapshots of the entries +in the index. + +\(fn &optional ARG)" t nil) + +(defvar magit-wip-before-change-mode nil "\ +Non-nil if Magit-Wip-Before-Change mode is enabled. +See the `magit-wip-before-change-mode' command +for a description of this minor mode.") + +(custom-autoload 'magit-wip-before-change-mode "magit-wip" nil) + +(autoload 'magit-wip-before-change-mode "magit-wip" "\ +Commit to work-in-progress refs before certain destructive changes. + +This is a minor mode. If called interactively, toggle the +`Magit-Wip-Before-Change mode' mode. If the prefix argument is +positive, enable the mode, and if it is zero or negative, disable +the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='magit-wip-before-change-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +Before invoking a revert command or an \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected tracked files to the current wip refs. For each branch +there may be two wip refs; one contains snapshots of the files +as found in the worktree and the other contains snapshots of the +entries in the index. + +Only changes to files which could potentially be affected by the +command which is about to be called are committed. + +\(fn &optional ARG)" t nil) + +(autoload 'magit-wip-commit-initial-backup "magit-wip" "\ +Before saving, commit current file to a worktree wip ref. + +The user has to add this function to `before-save-hook'. + +Commit the current state of the visited file before saving the +current buffer to that file. This backs up the same version of +the file as `backup-buffer' would, but stores the backup in the +worktree wip ref, which is also used by the various Magit Wip +modes, instead of in a backup file as `backup-buffer' would. + +This function ignores the variables that affect `backup-buffer' +and can be used along-side that function, which is recommended +because this function only backs up files that are tracked in +a Git repository." nil nil) + +(register-definition-prefixes "magit-wip" '("magit-")) + +;;;*** + +;;;### (autoloads nil "magit-worktree" "magit-worktree.el" (0 0 0 +;;;;;; 0)) +;;; Generated autoloads from magit-worktree.el + (autoload 'magit-worktree "magit-worktree" nil t) + +(autoload 'magit-worktree-checkout "magit-worktree" "\ +Checkout BRANCH in a new worktree at PATH. + +\(fn PATH BRANCH)" t nil) + +(autoload 'magit-worktree-branch "magit-worktree" "\ +Create a new BRANCH and check it out in a new worktree at PATH. + +\(fn PATH BRANCH START-POINT &optional FORCE)" t nil) + +(autoload 'magit-worktree-move "magit-worktree" "\ +Move WORKTREE to PATH. + +\(fn WORKTREE PATH)" t nil) + +(register-definition-prefixes "magit-worktree" '("magit-")) + +;;;*** + +;;;### (autoloads nil nil ("magit-bookmark.el" "magit-core.el" "magit-pkg.el") +;;;;;; (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; magit-autoloads.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-autorevert.el b/code/elpa/magit-20220821.1819/magit-autorevert.el new file mode 100644 index 0000000..68fc07b --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-autorevert.el @@ -0,0 +1,261 @@ +;;; magit-autorevert.el --- Revert buffers when files in repository change -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Code: + +(require 'magit-git) + +(require 'autorevert) + +;;; Options + +(defgroup magit-auto-revert nil + "Revert buffers when files in repository change." + :link '(custom-group-link auto-revert) + :link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers") + :group 'auto-revert + :group 'magit-essentials + :group 'magit-modes) + +(defcustom auto-revert-buffer-list-filter nil + "Filter that determines which buffers `auto-revert-buffers' reverts. + +This option is provided by Magit, which also advises +`auto-revert-buffers' to respect it. Magit users who do not turn +on the local mode `auto-revert-mode' themselves, are best served +by setting the value to `magit-auto-revert-repository-buffer-p'. + +However the default is nil, so as not to disturb users who do use +the local mode directly. If you experience delays when running +Magit commands, then you should consider using one of the +predicates provided by Magit - especially if you also use Tramp. + +Users who do turn on `auto-revert-mode' in buffers in which Magit +doesn't do that for them, should likely not use any filter. +Users who turn on `global-auto-revert-mode', do not have to worry +about this option, because it is disregarded if the global mode +is enabled." + :package-version '(magit . "2.4.2") + :group 'auto-revert + :group 'magit-auto-revert + :group 'magit-related + :type '(radio (const :tag "No filter" nil) + (function-item magit-auto-revert-buffer-p) + (function-item magit-auto-revert-repository-buffer-p) + function)) + +(defcustom magit-auto-revert-tracked-only t + "Whether `magit-auto-revert-mode' only reverts tracked files." + :package-version '(magit . "2.4.0") + :group 'magit-auto-revert + :type 'boolean + :set (lambda (var val) + (set var val) + (when (and (bound-and-true-p magit-auto-revert-mode) + (featurep 'magit-autorevert)) + (magit-auto-revert-mode -1) + (magit-auto-revert-mode)))) + +(defcustom magit-auto-revert-immediately t + "Whether Magit reverts buffers immediately. + +If this is non-nil and either `global-auto-revert-mode' or +`magit-auto-revert-mode' is enabled, then Magit immediately +reverts buffers by explicitly calling `auto-revert-buffers' +after running Git for side-effects. + +If `auto-revert-use-notify' is non-nil (and file notifications +are actually supported), then `magit-auto-revert-immediately' +does not have to be non-nil, because the reverts happen +immediately anyway. + +If `magit-auto-revert-immediately' and `auto-revert-use-notify' +are both nil, then reverts happen after `auto-revert-interval' +seconds of user inactivity. That is not desirable." + :package-version '(magit . "2.4.0") + :group 'magit-auto-revert + :type 'boolean) + +;;; Mode + +(defun magit-turn-on-auto-revert-mode-if-desired (&optional file) + (if file + (--when-let (find-buffer-visiting file) + (with-current-buffer it + (magit-turn-on-auto-revert-mode-if-desired))) + (when (and (not auto-revert-mode) ; see #3014 + (not global-auto-revert-mode) ; see #3460 + buffer-file-name + (file-readable-p buffer-file-name) + (compat-executable-find (magit-git-executable) t) + (magit-toplevel) + (or (not magit-auto-revert-tracked-only) + (magit-file-tracked-p buffer-file-name))) + (auto-revert-mode 1)))) + +;;;###autoload +(define-globalized-minor-mode magit-auto-revert-mode auto-revert-mode + magit-turn-on-auto-revert-mode-if-desired + :package-version '(magit . "2.4.0") + :link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers") + :group 'magit-auto-revert + :group 'magit-essentials + ;; - When `global-auto-revert-mode' is enabled, then this mode is + ;; redundant. + ;; - In all other cases enable the mode because if buffers are not + ;; automatically reverted that would make many very common tasks + ;; much more cumbersome. + :init-value (not (or global-auto-revert-mode + noninteractive))) +;; - Unfortunately `:init-value t' only sets the value of the mode +;; variable but does not cause the mode function to be called. +;; - I don't think it works like this on purpose, but since one usually +;; should not enable global modes by default, it is understandable. +;; - If the user has set the variable `magit-auto-revert-mode' to nil +;; after loading magit (instead of doing so before loading magit or +;; by using the function), then we should still respect that setting. +;; - If the user sets one of these variables after loading magit and +;; after `after-init-hook' has run, then that won't have an effect +;; and there is nothing we can do about it. +(defun magit-auto-revert-mode--init-kludge () + "This is an internal kludge to be used on `after-init-hook'. +Do not use this function elsewhere, and don't remove it from +the `after-init-hook'. For more information see the comments +and code surrounding the definition of this function." + (if magit-auto-revert-mode + (let ((start (current-time))) + (magit-message "Turning on magit-auto-revert-mode...") + (magit-auto-revert-mode 1) + (magit-message + "Turning on magit-auto-revert-mode...done%s" + (let ((elapsed (float-time (time-subtract nil start)))) + (if (> elapsed 0.2) + (format " (%.3fs, %s buffers checked)" elapsed + (length (buffer-list))) + "")))) + (magit-auto-revert-mode -1))) +(if after-init-time + ;; Since `after-init-hook' has already been + ;; run, turn the mode on or off right now. + (magit-auto-revert-mode--init-kludge) + ;; By the time the init file has been fully loaded the + ;; values of the relevant variables might have changed. + (add-hook 'after-init-hook #'magit-auto-revert-mode--init-kludge t)) + +(put 'magit-auto-revert-mode 'function-documentation + "Toggle Magit Auto Revert mode. +If called interactively, enable Magit Auto Revert mode if ARG is +positive, and disable it if ARG is zero or negative. If called +from Lisp, also enable the mode if ARG is omitted or nil, and +toggle it if ARG is `toggle'; disable the mode otherwise. + +Magit Auto Revert mode is a global minor mode that reverts +buffers associated with a file that is located inside a Git +repository when the file changes on disk. Use `auto-revert-mode' +to revert a particular buffer. Or use `global-auto-revert-mode' +to revert all file-visiting buffers, not just those that visit +a file located inside a Git repository. + +This global mode works by turning on the buffer-local mode +`auto-revert-mode' at the time a buffer is first created. The +local mode is turned on if the visited file is being tracked in +a Git repository at the time when the buffer is created. + +If `magit-auto-revert-tracked-only' is non-nil (the default), +then only tracked files are reverted. But if you stage a +previously untracked file using `magit-stage', then this mode +notices that. + +Unlike `global-auto-revert-mode', this mode never reverts any +buffers that are not visiting files. + +The behavior of this mode can be customized using the options +in the `autorevert' and `magit-autorevert' groups. + +This function calls the hook `magit-auto-revert-mode-hook'. + +Like nearly every mode, this mode should be enabled or disabled +by calling the respective mode function, the reason being that +changing the state of a mode involves more than merely toggling +a single switch, so setting the mode variable is not enough. +Also, you should not use `after-init-hook' to disable this mode.") + +(defun magit-auto-revert-buffers () + (when (and magit-auto-revert-immediately + (or global-auto-revert-mode + (and magit-auto-revert-mode auto-revert-buffer-list))) + (let ((auto-revert-buffer-list-filter + (or auto-revert-buffer-list-filter + #'magit-auto-revert-repository-buffer-p))) + (auto-revert-buffers)))) + +(defvar magit-auto-revert-toplevel nil) + +(defvar magit-auto-revert-counter 1 + "Incremented each time `auto-revert-buffers' is called.") + +(defun magit-auto-revert-buffer-p (buffer) + "Return non-nil if BUFFER visits a file inside the current repository. +The current repository is the one containing `default-directory'. +If there is no current repository, then return t for any BUFFER." + (magit-auto-revert-repository-buffer-p buffer t)) + +(defun magit-auto-revert-repository-buffer-p (buffer &optional fallback) + "Return non-nil if BUFFER visits a file inside the current repository. +The current repository is the one containing `default-directory'. +If there is no current repository, then return FALLBACK (which +defaults to nil) for any BUFFER." + ;; Call `magit-toplevel' just once per cycle. + (unless (and magit-auto-revert-toplevel + (= (cdr magit-auto-revert-toplevel) + magit-auto-revert-counter)) + (setq magit-auto-revert-toplevel + (cons (or (magit-toplevel) 'no-repo) + magit-auto-revert-counter))) + (let ((top (car magit-auto-revert-toplevel))) + (if (eq top 'no-repo) + fallback + (let ((dir (buffer-local-value 'default-directory buffer))) + (and (equal (file-remote-p dir) + (file-remote-p top)) + ;; ^ `tramp-handle-file-in-directory-p' lacks this optimization. + (file-in-directory-p dir top)))))) + +(defun auto-revert-buffers--buffer-list-filter (fn) + (cl-incf magit-auto-revert-counter) + (if (or global-auto-revert-mode + (not auto-revert-buffer-list) + (not auto-revert-buffer-list-filter)) + (funcall fn) + (let ((auto-revert-buffer-list + (-filter auto-revert-buffer-list-filter + auto-revert-buffer-list))) + (funcall fn)) + (unless auto-revert-timer + (auto-revert-set-timer)))) + +(advice-add 'auto-revert-buffers :around + #'auto-revert-buffers--buffer-list-filter) + +;;; _ +(provide 'magit-autorevert) +;;; magit-autorevert.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-base.el b/code/elpa/magit-20220821.1819/magit-base.el new file mode 100644 index 0000000..0a9ab29 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-base.el @@ -0,0 +1,1260 @@ +;;; magit-base.el --- Early birds -*- lexical-binding:t; coding:utf-8 -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;; This file contains code taken from GNU Emacs, which is +;; Copyright (C) 1976-2022 Free Software Foundation, Inc. + +;;; Commentary: + +;; This library defines utility functions, options and other things that +;; have to be available early on because they are used by several other +;; libraries, which cannot depend on one another, because that would lead +;; to circular dependencies. + +;;; Code: + +(defconst magit--minimal-git "2.2.0") +(defconst magit--minimal-emacs "25.1") + +(require 'cl-lib) +(require 'compat) +(require 'compat-26) +(require 'compat-27) +(require 'dash) +(require 'eieio) +(require 'seq) +(require 'subr-x) + +(require 'crm) + +(require 'magit-section) + +(eval-when-compile (require 'ido)) +(declare-function Info-get-token "info" (pos start all &optional errorstring)) + +(eval-when-compile (require 'vc-git)) +(declare-function vc-git--run-command-string "vc-git" (file &rest args)) + +(eval-when-compile (require 'which-func)) +(declare-function which-function "which-func" ()) + +;;; Options + +(defcustom magit-completing-read-function #'magit-builtin-completing-read + "Function to be called when requesting input from the user. + +If you have enabled `ivy-mode' or `helm-mode', then you don't +have to customize this option; `magit-builtin-completing-read' +will work just fine. However, if you use Ido completion, then +you do have to use `magit-ido-completing-read', because Ido is +less well behaved than the former, more modern alternatives. + +If you would like to use Ivy or Helm completion with Magit but +not enable the respective modes globally, then customize this +option to use `ivy-completing-read' or +`helm--completing-read-default'. If you choose to use +`ivy-completing-read', note that the items may always be shown in +alphabetical order, depending on your version of Ivy." + :group 'magit-essentials + :type '(radio (function-item magit-builtin-completing-read) + (function-item magit-ido-completing-read) + (function-item ivy-completing-read) + (function-item helm--completing-read-default) + (function :tag "Other function"))) + +(defcustom magit-dwim-selection + '((magit-stash-apply nil t) + (magit-ediff-resolve-all nil t) + (magit-ediff-resolve-rest nil t) + (magit-stash-branch nil t) + (magit-stash-branch-here nil t) + (magit-stash-format-patch nil t) + (magit-stash-drop nil ask) + (magit-stash-pop nil ask) + (forge-browse-dwim nil t) + (forge-browse-commit nil t) + (forge-browse-branch nil t) + (forge-browse-remote nil t) + (forge-browse-issue nil t) + (forge-browse-pullreq nil t) + (forge-edit-topic-title nil t) + (forge-edit-topic-state nil t) + (forge-edit-topic-draft nil t) + (forge-edit-topic-milestone nil t) + (forge-edit-topic-labels nil t) + (forge-edit-topic-marks nil t) + (forge-edit-topic-assignees nil t) + (forge-edit-topic-review-requests nil t) + (forge-edit-topic-note nil t) + (forge-pull-pullreq nil t) + (forge-visit-issue nil t) + (forge-visit-pullreq nil t) + (forge-visit-topic nil t)) + "When not to offer alternatives and ask for confirmation. + +Many commands by default ask the user to select from a list of +possible candidates. They do so even when there is a thing at +point that they can act on, which is then offered as the default. + +This option can be used to tell certain commands to use the thing +at point instead of asking the user to select a candidate to act +on, with or without confirmation. + +The value has the form ((COMMAND nil|PROMPT DEFAULT)...). + +- COMMAND is the command that should not prompt for a choice. + To have an effect, the command has to use the function + `magit-completing-read' or a utility function which in turn uses + that function. + +- If the command uses `magit-completing-read' multiple times, then + PROMPT can be used to only affect one of these uses. PROMPT, if + non-nil, is a regular expression that is used to match against + the PROMPT argument passed to `magit-completing-read'. + +- DEFAULT specifies how to use the default. If it is t, then + the DEFAULT argument passed to `magit-completing-read' is used + without confirmation. If it is `ask', then the user is given + a chance to abort. DEFAULT can also be nil, in which case the + entry has no effect." + :package-version '(magit . "2.12.0") + :group 'magit-commands + :type '(repeat + (list (symbol :tag "Command") ; It might not be fboundp yet. + (choice (const :tag "for all prompts" nil) + (regexp :tag "for prompts matching regexp")) + (choice (const :tag "offer other choices" nil) + (const :tag "require confirmation" ask) + (const :tag "use default without confirmation" t))))) + +(defconst magit--confirm-actions + '((const discard) + (const reverse) + (const stage-all-changes) + (const unstage-all-changes) + (const delete) + (const trash) + (const resurrect) + (const untrack) + (const rename) + (const reset-bisect) + (const abort-rebase) + (const abort-merge) + (const merge-dirty) + (const delete-unmerged-branch) + (const delete-branch-on-remote) + (const delete-pr-remote) + (const drop-stashes) + (const set-and-push) + (const amend-published) + (const rebase-published) + (const edit-published) + (const remove-modules) + (const remove-dirty-modules) + (const trash-module-gitdirs) + (const kill-process) + (const safe-with-wip))) + +(defcustom magit-no-confirm '(set-and-push) + "A list of symbols for actions Magit should not confirm, or t. + +Many potentially dangerous commands by default ask the user for +confirmation. Each of the below symbols stands for an action +which, when invoked unintentionally or without being fully aware +of the consequences, could lead to tears. In many cases there +are several commands that perform variations of a certain action, +so we don't use the command names but more generic symbols. + +Applying changes: + + `discard' Discarding one or more changes (i.e. hunks or the + complete diff for a file) loses that change, obviously. + + `reverse' Reverting one or more changes can usually be undone + by reverting the reversion. + + `stage-all-changes', `unstage-all-changes' When there are both + staged and unstaged changes, then un-/staging everything would + destroy that distinction. Of course that also applies when + un-/staging a single change, but then less is lost and one does + that so often that having to confirm every time would be + unacceptable. + +Files: + + `delete' When a file that isn't yet tracked by Git is deleted + then it is completely lost, not just the last changes. Very + dangerous. + + `trash' Instead of deleting a file it can also be move to the + system trash. Obviously much less dangerous than deleting it. + + Also see option `magit-delete-by-moving-to-trash'. + + `resurrect' A deleted file can easily be resurrected by + \"deleting\" the deletion, which is done using the same command + that was used to delete the same file in the first place. + + `untrack' Untracking a file can be undone by tracking it again. + + `rename' Renaming a file can easily be undone. + +Sequences: + + `reset-bisect' Aborting (known to Git as \"resetting\") a + bisect operation loses all information collected so far. + + `abort-rebase' Aborting a rebase throws away all already + modified commits, but it's possible to restore those from the + reflog. + + `abort-merge' Aborting a merge throws away all conflict + resolutions which has already been carried out by the user. + + `merge-dirty' Merging with a dirty worktree can make it hard to + go back to the state before the merge was initiated. + +References: + + `delete-unmerged-branch' Once a branch has been deleted it can + only be restored using low-level recovery tools provided by + Git. And even then the reflog is gone. The user always has + to confirm the deletion of a branch by accepting the default + choice (or selecting another branch), but when a branch has + not been merged yet, also make sure the user is aware of that. + + `delete-branch-on-remote' Deleting a \"remote branch\" may mean + deleting the (local) \"remote-tracking\" branch only, or also + removing it from the remote itself. The latter often makes more + sense because otherwise simply fetching from the remote would + restore the remote-tracking branch, but doing that can be + surprising and hard to recover from, so we ask. + + `delete-pr-remote' When deleting a branch that was created from + a pull-request and if no other branches still exist on that + remote, then `magit-branch-delete' offers to delete the remote + as well. This should be safe because it only happens if no + other refs exist in the remotes namespace, and you can recreate + the remote if necessary. + + `drop-stashes' Dropping a stash is dangerous because Git stores + stashes in the reflog. Once a stash is removed, there is no + going back without using low-level recovery tools provided by + Git. When a single stash is dropped, then the user always has + to confirm by accepting the default (or selecting another). + This action only concerns the deletion of multiple stashes at + once. + +Publishing: + + `set-and-push' When pushing to the upstream or the push-remote + and that isn't actually configured yet, then the user can first + set the target. If s/he confirms the default too quickly, then + s/he might end up pushing to the wrong branch and if the remote + repository is configured to disallow fixing such mistakes, then + that can be quite embarrassing and annoying. + +Edit published history: + + Without adding these symbols here, you will be warned before + editing commits that have already been pushed to one of the + branches listed in `magit-published-branches'. + + `amend-published' Affects most commands that amend to `HEAD'. + + `rebase-published' Affects commands that perform interactive + rebases. This includes commands from the commit popup that + modify a commit other than `HEAD', namely the various fixup + and squash variants. + + `edit-published' Affects the commands `magit-edit-line-commit' + and `magit-diff-edit-hunk-commit'. These two commands make + it quite easy to accidentally edit a published commit, so you + should think twice before configuring them not to ask for + confirmation. + + To disable confirmation completely, add all three symbols here + or set `magit-published-branches' to nil. + +Removing modules: + + `remove-modules' When you remove the working directory of a + module that does not contain uncommitted changes, then that is + safer than doing so when there are uncommitted changes and/or + when you also remove the gitdir. Still, you don't want to do + that by accident. + + `remove-dirty-modules' When you remove the working directory of + a module that contains uncommitted changes, then those changes + are gone for good. It is better to go to the module, inspect + these changes and only if appropriate discard them manually. + + `trash-module-gitdirs' When you remove the gitdir of a module, + then all unpushed changes are gone for good. It is very easy + to forget that you have some unfinished work on an unpublished + feature branch or even in a stash. + + Actually there are some safety precautions in place, that might + help you out if you make an unwise choice here, but don't count + on it. In case of emergency, stay calm and check the stash and + the `trash-directory' for traces of lost work. + +Various: + + `kill-process' There seldom is a reason to kill a process. + +Global settings: + + Instead of adding all of the above symbols to the value of this + option you can also set it to the atom `t', which has the same + effect as adding all of the above symbols. Doing that most + certainly is a bad idea, especially because other symbols might + be added in the future. So even if you don't want to be asked + for confirmation for any of these actions, you are still better + of adding all of the respective symbols individually. + + When `magit-wip-before-change-mode' is enabled then these actions + can fairly easily be undone: `discard', `reverse', + `stage-all-changes', and `unstage-all-changes'. If and only if + this mode is enabled, then `safe-with-wip' has the same effect + as adding all of these symbols individually." + :package-version '(magit . "2.1.0") + :group 'magit-essentials + :group 'magit-commands + :type `(choice (const :tag "Always require confirmation" nil) + (const :tag "Never require confirmation" t) + (set :tag "Require confirmation except for" + ;; `remove-dirty-modules' and + ;; `trash-module-gitdirs' intentionally + ;; omitted. + ,@magit--confirm-actions))) + +(defcustom magit-slow-confirm '(drop-stashes) + "Whether to ask user \"y or n\" or \"yes or no\" questions. + +When this is nil, then `y-or-n-p' is used when the user has to +confirm a potentially destructive action. When this is t, then +`yes-or-no-p' is used instead. If this is a list of symbols +identifying actions, then `yes-or-no-p' is used for those, +`y-or-no-p' for all others. The list of actions is the same as +for `magit-no-confirm' (which see)." + :package-version '(magit . "2.9.0") + :group 'magit-miscellaneous + :type `(choice (const :tag "Always ask \"yes or no\" questions" t) + (const :tag "Always ask \"y or n\" questions" nil) + (set :tag "Ask \"yes or no\" questions only for" + ,@magit--confirm-actions))) + +(defcustom magit-no-message nil + "A list of messages Magit should not display. + +Magit displays most echo area messages using `message', but a few +are displayed using `magit-message' instead, which takes the same +arguments as the former, FORMAT-STRING and ARGS. `magit-message' +forgoes printing a message if any member of this list is a prefix +of the respective FORMAT-STRING. + +If Magit prints a message which causes you grief, then please +first investigate whether there is another option which can be +used to suppress it. If that is not the case, then ask the Magit +maintainers to start using `magit-message' instead of `message' +in that case. We are not proactively replacing all uses of +`message' with `magit-message', just in case someone *might* find +some of these messages useless. + +Messages which can currently be suppressed using this option are: +* \"Turning on magit-auto-revert-mode...\"" + :package-version '(magit . "2.8.0") + :group 'magit-miscellaneous + :type '(repeat string)) + +(defcustom magit-ellipsis (if (char-displayable-p ?…) "…" "...") + "String used to abbreviate text in process buffers. + +Currently this is only used to elide `magit-git-global-arguments' +in process buffers. In the future it may be used in other places +as well, but not the following: + +- Author names in the log margin are always abbreviated using + \"…\" or if that is not displayable, then \">\". + +- Whether collapsed sections are indicated using ellipsis is + controlled by `magit-section-visibility-indicator'." + :package-version '(magit . "3.0.0") + :group 'magit-miscellaneous + :type 'string) + +(defcustom magit-update-other-window-delay 0.2 + "Delay before automatically updating the other window. + +When moving around in certain buffers, then certain other +buffers, which are being displayed in another window, may +optionally be updated to display information about the +section at point. + +When holding down a key to move by more than just one section, +then that would update that buffer for each section on the way. +To prevent that, updating the revision buffer is delayed, and +this option controls for how long. For optimal experience you +might have to adjust this delay and/or the keyboard repeat rate +and delay of your graphical environment or operating system." + :package-version '(magit . "2.3.0") + :group 'magit-miscellaneous + :type 'number) + +(defcustom magit-view-git-manual-method 'info + "How links to Git documentation are followed from Magit's Info manuals. + +`info' Follow the link to the node in the `gitman' Info manual + as usual. Unfortunately that manual is not installed by + default on some platforms, and when it is then the nodes + look worse than the actual manpages. + +`man' View the respective man-page using the `man' package. + +`woman' View the respective man-page using the `woman' package." + :package-version '(magit . "2.9.0") + :group 'magit-miscellaneous + :type '(choice (const :tag "view info manual" info) + (const :tag "view manpage using `man'" man) + (const :tag "view manpage using `woman'" woman))) + +;;; Section Classes + +(defclass magit-commit-section (magit-section) ()) + +(setf (alist-get 'commit magit--section-type-alist) 'magit-commit-section) + +(defclass magit-diff-section (magit-section) () :abstract t) + +(defclass magit-file-section (magit-diff-section) + ((keymap :initform 'magit-file-section-map) + (source :initform nil) + (header :initform nil))) + +(defclass magit-module-section (magit-file-section) + ((keymap :initform 'magit-module-section-map) + (range :initform nil))) + +(defclass magit-hunk-section (magit-diff-section) + ((keymap :initform 'magit-hunk-section-map) + (refined :initform nil) + (combined :initform nil) + (from-range :initform nil) + (from-ranges :initform nil) + (to-range :initform nil) + (about :initform nil))) + +(setf (alist-get 'file magit--section-type-alist) 'magit-file-section) +(setf (alist-get 'module magit--section-type-alist) 'magit-module-section) +(setf (alist-get 'hunk magit--section-type-alist) 'magit-hunk-section) + +(defclass magit-log-section (magit-section) () :abstract t) +(defclass magit-unpulled-section (magit-log-section) ()) +(defclass magit-unpushed-section (magit-log-section) ()) +(defclass magit-unmerged-section (magit-log-section) ()) + +(setf (alist-get 'unpulled magit--section-type-alist) 'magit-unpulled-section) +(setf (alist-get 'unpushed magit--section-type-alist) 'magit-unpushed-section) +(setf (alist-get 'unmerged magit--section-type-alist) 'magit-unmerged-section) + +;;; User Input + +(defvar helm-completion-in-region-default-sort-fn) +(defvar helm-crm-default-separator) +(defvar ivy-sort-functions-alist) +(defvar ivy-sort-matches-functions-alist) + +(defvar magit-completing-read--silent-default nil) + +(defun magit-completing-read (prompt collection &optional + predicate require-match initial-input + hist def fallback) + "Read a choice in the minibuffer, or use the default choice. + +This is the function that Magit commands use when they need the +user to select a single thing to act on. The arguments have the +same meaning as for `completing-read', except for FALLBACK, which +is unique to this function and is described below. + +Instead of asking the user to choose from a list of possible +candidates, this function may instead just return the default +specified by DEF, with or without requiring user confirmation. +Whether that is the case depends on PROMPT, `this-command' and +`magit-dwim-selection'. See the documentation of the latter for +more information. + +If it does use the default without the user even having to +confirm that, then `magit-completing-read--silent-default' is set +to t, otherwise nil. + +If it does read a value in the minibuffer, then this function +acts similarly to `completing-read', except for the following: + +- COLLECTION must be a list of choices. A function is not + supported. + +- If REQUIRE-MATCH is nil and the user exits without a choice, + then nil is returned instead of an empty string. + +- If REQUIRE-MATCH is non-nil and the user exits without a + choice, `user-error' is raised. + +- FALLBACK specifies a secondary default that is only used if + the primary default DEF is nil. The secondary default is not + subject to `magit-dwim-selection' — if DEF is nil but FALLBACK + is not, then this function always asks the user to choose a + candidate, just as if both defaults were nil. + +- \": \" is appended to PROMPT. + +- PROMPT is modified to end with \" (default DEF|FALLBACK): \" + provided that DEF or FALLBACK is non-nil, that neither + `ivy-mode' nor `helm-mode' is enabled, and that + `magit-completing-read-function' is set to its default value of + `magit-builtin-completing-read'." + (setq magit-completing-read--silent-default nil) + (if-let ((dwim (and def + (nth 2 (-first (pcase-lambda (`(,cmd ,re ,_)) + (and (eq this-command cmd) + (or (not re) + (string-match-p re prompt)))) + magit-dwim-selection))))) + (if (eq dwim 'ask) + (if (y-or-n-p (format "%s %s? " prompt def)) + def + (user-error "Abort")) + (setq magit-completing-read--silent-default t) + def) + (unless def + (setq def fallback)) + (let ((command this-command) + (reply (funcall magit-completing-read-function + (concat prompt ": ") + (if (and def (not (member def collection))) + (cons def collection) + collection) + predicate + require-match initial-input hist def))) + (setq this-command command) + ;; Note: Avoid `string=' to support `helm-comp-read-use-marked'. + (if (equal reply "") + (if require-match + (user-error "Nothing selected") + nil) + reply)))) + +(defun magit--completion-table (collection) + (lambda (string pred action) + (if (eq action 'metadata) + '(metadata (display-sort-function . identity)) + (complete-with-action action collection string pred)))) + +(defun magit-builtin-completing-read + (prompt choices &optional predicate require-match initial-input hist def) + "Magit wrapper for standard `completing-read' function." + (unless (or (bound-and-true-p helm-mode) + (bound-and-true-p ivy-mode) + (bound-and-true-p vertico-mode) + (bound-and-true-p selectrum-mode)) + (setq prompt (magit-prompt-with-default prompt def))) + (unless (or (bound-and-true-p helm-mode) + (bound-and-true-p ivy-mode)) + (setq choices (magit--completion-table choices))) + (cl-letf (((symbol-function #'completion-pcm--all-completions))) + (when (< emacs-major-version 26) + (fset 'completion-pcm--all-completions + 'magit-completion-pcm--all-completions)) + (let ((ivy-sort-functions-alist nil)) + (completing-read prompt choices + predicate require-match + initial-input hist def)))) + +(defun magit-completing-read-multiple + (prompt choices &optional sep default hist keymap) + "Read multiple items from CHOICES, separated by SEP. + +Set up the `crm' variables needed to read multiple values with +`read-from-minibuffer'. + +SEP is a regexp matching characters that can separate choices. +When SEP is nil, it defaults to `crm-separator'. DEFAULT, HIST, +and KEYMAP are passed to `read-from-minibuffer'. When KEYMAP is +nil, it defaults to `crm-local-completion-map'. + +Unlike `completing-read-multiple', the return value is not split +into a list." + (declare (obsolete magit-completing-read-multiple* "Magit 3.1.0")) + (let* ((crm-separator (or sep crm-separator)) + (crm-completion-table (magit--completion-table choices)) + (choose-completion-string-functions + '(crm--choose-completion-string)) + (minibuffer-completion-table #'crm--collection-fn) + (minibuffer-completion-confirm t) + (helm-completion-in-region-default-sort-fn nil) + (helm-crm-default-separator nil) + (ivy-sort-matches-functions-alist nil) + (input + (cl-letf (((symbol-function #'completion-pcm--all-completions))) + (when (< emacs-major-version 26) + (fset 'completion-pcm--all-completions + 'magit-completion-pcm--all-completions)) + (read-from-minibuffer + (concat prompt (and default (format " (%s)" default)) ": ") + nil (or keymap crm-local-completion-map) + nil hist default)))) + (when (string-equal input "") + (or (setq input default) + (user-error "Nothing selected"))) + input)) + +(defun magit-completing-read-multiple* + (prompt table &optional predicate require-match initial-input + hist def inherit-input-method + no-split) + "Read multiple strings in the minibuffer, with completion. +Like `completing-read-multiple' but don't mess with order of +TABLE and take an additional argument NO-SPLIT, which causes +the user input to be returned as a single unmodified string. +Also work around various incompatible features of various +third-party completion frameworks." + (cl-letf* + (;; To implement NO-SPLIT we have to manipulate the respective + ;; `split-string' invocation. We cannot simply advice it to + ;; return the input string because `SELECTRUM' would choke on + ;; that string. Use a variable to pass along the raw user + ;; input string. aa5f098ab + (input nil) + (split-string (symbol-function #'split-string)) + ((symbol-function #'split-string) + (lambda (string &optional separators omit-nulls trim) + (when (and no-split + (equal separators crm-separator) + (equal omit-nulls t)) + (setq input string)) + (funcall split-string string separators omit-nulls trim))) + ;; In Emacs 25 this function has a bug, so we use a copy of the + ;; version from Emacs 26. bef9c7aa3 + ((symbol-function #'completion-pcm--all-completions) + (if (< emacs-major-version 26) + 'magit-completion-pcm--all-completions + (symbol-function #'completion-pcm--all-completions))) + ;; Prevent `BUILT-IN' completion from messing up our existing + ;; order of the completion candidates. aa5f098ab + (table (magit--completion-table table)) + ;; Prevent `IVY' from messing up our existing order. c7af78726 + (ivy-sort-matches-functions-alist nil) + ;; Prevent `HELM' from messing up our existing order. 6fcf994bd + (helm-completion-in-region-default-sort-fn nil) + ;; Prevent `HELM' from automatically appending the separator, + ;; which is counterproductive when NO-SPLIT is non-nil and/or + ;; when reading commit ranges. 798aff564 + (helm-crm-default-separator + (if no-split nil (bound-and-true-p helm-crm-default-separator))) + ;; And now, the moment we have all been waiting for... + (values (completing-read-multiple + prompt table predicate require-match initial-input + hist def inherit-input-method))) + (if no-split input values))) + +(defun magit-ido-completing-read + (prompt choices &optional predicate require-match initial-input hist def) + "Ido-based `completing-read' almost-replacement. + +Unfortunately `ido-completing-read' is not suitable as a +drop-in replacement for `completing-read', instead we use +`ido-completing-read+' from the third-party package by the +same name." + (if (and (require 'ido-completing-read+ nil t) + (fboundp 'ido-completing-read+)) + (ido-completing-read+ prompt choices predicate require-match + initial-input hist + (or def (and require-match (car choices)))) + (display-warning 'magit "ido-completing-read+ is not installed + +To use Ido completion with Magit you need to install the +third-party `ido-completing-read+' packages. Falling +back to built-in `completing-read' for now." :error) + (magit-builtin-completing-read prompt choices predicate require-match + initial-input hist def))) + +(defun magit-prompt-with-default (prompt def) + (if (and def (length> prompt 2) + (string-equal ": " (substring prompt -2))) + (format "%s (default %s): " (substring prompt 0 -2) def) + prompt)) + +(defvar magit-minibuffer-local-ns-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map minibuffer-local-map) + (define-key map "\s" #'magit-whitespace-disallowed) + (define-key map "\t" #'magit-whitespace-disallowed) + map)) + +(defun magit-whitespace-disallowed () + "Beep to tell the user that whitespace is not allowed." + (interactive) + (ding) + (message "Whitespace isn't allowed here") + (setq defining-kbd-macro nil) + (force-mode-line-update)) + +(defun magit-read-string (prompt &optional initial-input history default-value + inherit-input-method no-whitespace) + "Read a string from the minibuffer, prompting with string PROMPT. + +This is similar to `read-string', but +* empty input is only allowed if DEFAULT-VALUE is non-nil in + which case that is returned, +* whitespace is not allowed and leading and trailing whitespace is + removed automatically if NO-WHITESPACE is non-nil, +* \": \" is appended to PROMPT, and +* an invalid DEFAULT-VALUE is silently ignored." + (when default-value + (when (consp default-value) + (setq default-value (car default-value))) + (unless (stringp default-value) + (setq default-value nil))) + (let* ((minibuffer-completion-table nil) + (val (read-from-minibuffer + (magit-prompt-with-default (concat prompt ": ") default-value) + initial-input (and no-whitespace magit-minibuffer-local-ns-map) + nil history default-value inherit-input-method)) + (trim (lambda (regexp string) + (save-match-data + (if (string-match regexp string) + (replace-match "" t t string) + string))))) + (when (and (string= val "") default-value) + (setq val default-value)) + (when no-whitespace + (setq val (funcall trim "\\`\\(?:[ \t\n\r]+\\)" + (funcall trim "\\(?:[ \t\n\r]+\\)\\'" val)))) + (cond ((string= val "") + (user-error "Need non-empty input")) + ((and no-whitespace (string-match-p "[\s\t\n]" val)) + (user-error "Input contains whitespace")) + (t val)))) + +(defun magit-read-string-ns (prompt &optional initial-input history + default-value inherit-input-method) + "Call `magit-read-string' with non-nil NO-WHITESPACE." + (magit-read-string prompt initial-input history default-value + inherit-input-method t)) + +(defmacro magit-read-char-case (prompt verbose &rest clauses) + (declare (indent 2) + (debug (form form &rest (characterp form body)))) + `(prog1 (pcase (read-char-choice + (concat ,prompt + (mapconcat #'identity + (list ,@(mapcar #'cadr clauses)) + ", ") + ,(if verbose ", or [C-g] to abort " " ")) + ',(mapcar #'car clauses)) + ,@(--map `(,(car it) ,@(cddr it)) clauses)) + (message ""))) + +(defun magit-y-or-n-p (prompt &optional action) + "Ask user a \"y or n\" or a \"yes or no\" question using PROMPT. +Which kind of question is used depends on whether +ACTION is a member of option `magit-slow-confirm'." + (if (or (eq magit-slow-confirm t) + (and action (member action magit-slow-confirm))) + (yes-or-no-p prompt) + (y-or-n-p prompt))) + +(defvar magit--no-confirm-alist + '((safe-with-wip magit-wip-before-change-mode + discard reverse stage-all-changes unstage-all-changes))) + +(cl-defun magit-confirm (action &optional prompt prompt-n noabort + (items nil sitems)) + (declare (indent defun)) + (setq prompt-n (format (concat (or prompt-n prompt) "? ") (length items))) + (setq prompt (format (concat (or prompt (magit-confirm-make-prompt action)) + "? ") + (car items))) + (or (cond ((and (not (eq action t)) + (or (eq magit-no-confirm t) + (memq action magit-no-confirm) + (cl-member-if (pcase-lambda (`(,key ,var . ,sub)) + (and (memq key magit-no-confirm) + (memq action sub) + (or (not var) + (and (boundp var) + (symbol-value var))))) + magit--no-confirm-alist))) + (or (not sitems) items)) + ((not sitems) + (magit-y-or-n-p prompt action)) + ((length= items 1) + (and (magit-y-or-n-p prompt action) items)) + ((length> items 1) + (and (magit-y-or-n-p (concat (mapconcat #'identity items "\n") + "\n\n" prompt-n) + action) + items))) + (if noabort nil (user-error "Abort")))) + +(defun magit-confirm-files (action files &optional prompt) + (when files + (unless prompt + (setq prompt (magit-confirm-make-prompt action))) + (magit-confirm action + (concat prompt " %s") + (concat prompt " %i files") + nil files))) + +(defun magit-confirm-make-prompt (action) + (let ((prompt (symbol-name action))) + (string-replace "-" " " + (concat (upcase (substring prompt 0 1)) + (substring prompt 1))))) + +(defun magit-read-number-string (prompt &optional default _history) + "Like `read-number' but return value is a string. +DEFAULT may be a number or a numeric string." + (number-to-string + (read-number prompt (if (stringp default) + (string-to-number default) + default)))) + +;;; Debug Utilities + +;;;###autoload +(defun magit-emacs-Q-command () + "Show a shell command that runs an uncustomized Emacs with only Magit loaded. +See info node `(magit)Debugging Tools' for more information." + (interactive) + (let ((cmd (mapconcat + #'shell-quote-argument + `(,(concat invocation-directory invocation-name) + "-Q" "--eval" "(setq debug-on-error t)" + ,@(cl-mapcan + (lambda (dir) (list "-L" dir)) + (delete-dups + (cl-mapcan + (lambda (lib) + (let ((path (locate-library lib))) + (cond + (path + (list (file-name-directory path))) + ((not (equal lib "libgit")) + (error "Cannot find mandatory dependency %s" lib))))) + '(;; Like `LOAD_PATH' in `default.mk'. + "compat" + "dash" + "libgit" + "transient" + "with-editor" + ;; Obviously `magit' itself is needed too. + "magit" + ;; While these are part of the Magit repository, + ;; they are distributed as separate packages. + "magit-section" + "git-commit" + )))) + ;; Avoid Emacs bug#16406 by using full path. + "-l" ,(file-name-sans-extension (locate-library "magit"))) + " "))) + (message "Uncustomized Magit command saved to kill-ring, %s" + "please run it in a terminal.") + (kill-new cmd))) + +;;; Text Utilities + +(defmacro magit-bind-match-strings (varlist string &rest body) + "Bind variables to submatches according to VARLIST then evaluate BODY. +Bind the symbols in VARLIST to submatches of the current match +data, starting with 1 and incrementing by 1 for each symbol. If +the last match was against a string, then that has to be provided +as STRING." + (declare (indent 2) (debug (listp form body))) + (let ((s (cl-gensym "string")) + (i 0)) + `(let ((,s ,string)) + (let ,(save-match-data + (cl-mapcan (lambda (sym) + (cl-incf i) + (and (not (eq (aref (symbol-name sym) 0) ?_)) + (list (list sym (list 'match-string i s))))) + varlist)) + ,@body)))) + +(defun magit-delete-line () + "Delete the rest of the current line." + (delete-region (point) (1+ (line-end-position)))) + +(defun magit-delete-match (&optional num) + "Delete text matched by last search. +If optional NUM is specified, only delete that subexpression." + (delete-region (match-beginning (or num 0)) + (match-end (or num 0)))) + +(defun magit-file-line (file) + "Return the first line of FILE as a string." + (when (file-regular-p file) + (with-temp-buffer + (insert-file-contents file) + (buffer-substring-no-properties (point-min) + (line-end-position))))) + +(defun magit-file-lines (file &optional keep-empty-lines) + "Return a list of strings containing one element per line in FILE. +Unless optional argument KEEP-EMPTY-LINES is t, trim all empty lines." + (when (file-regular-p file) + (with-temp-buffer + (insert-file-contents file) + (split-string (buffer-string) "\n" (not keep-empty-lines))))) + +(defun magit-set-header-line-format (string) + "Set the header-line using STRING. +Propertize STRING with the `magit-header-line'. If the `face' +property of any part of STRING is already set, then that takes +precedence. Also pad the left side of STRING so that it aligns +with the text area." + (setq header-line-format + (concat (propertize " " 'display '(space :align-to 0)) + string))) + +(defun magit--format-spec (format specification) + "Like `format-spec' but preserve text properties in SPECIFICATION." + (with-temp-buffer + (insert format) + (goto-char (point-min)) + (while (search-forward "%" nil t) + (cond + ;; Quoted percent sign. + ((eq (char-after) ?%) + (delete-char 1)) + ;; Valid format spec. + ((looking-at "\\([-0-9.]*\\)\\([a-zA-Z]\\)") + (let* ((num (match-string 1)) + (spec (string-to-char (match-string 2))) + (val (assq spec specification))) + (unless val + (error "Invalid format character: `%%%c'" spec)) + (setq val (cdr val)) + ;; Pad result to desired length. + (let ((text (format (concat "%" num "s") val))) + ;; Insert first, to preserve text properties. + (if (next-property-change 0 (concat " " text)) + ;; If the inserted text has properties, then preserve those. + (insert text) + ;; Otherwise preserve FORMAT's properties, like `format-spec'. + (insert-and-inherit text)) + ;; Delete the specifier body. + (delete-region (+ (match-beginning 0) (length text)) + (+ (match-end 0) (length text))) + ;; Delete the percent sign. + (delete-region (1- (match-beginning 0)) (match-beginning 0))))) + ;; Signal an error on bogus format strings. + (t + (error "Invalid format string")))) + (buffer-string))) + +;;; Missing from Emacs + +(defun magit-kill-this-buffer () + "Kill the current buffer." + (interactive) + (kill-buffer (current-buffer))) + +(defun magit--buffer-string (&optional min max trim) + "Like `buffer-substring-no-properties' but the arguments are optional. + +This combines the benefits of `buffer-string', `buffer-substring' +and `buffer-substring-no-properties' into one function that is +not as painful to use as the latter. I.e. you can write + (magit--buffer-string) +instead of + (buffer-substring-no-properties (point-min) + (point-max)) + +Optional MIN defaults to the value of `point-min'. +Optional MAX defaults to the value of `point-max'. + +If optional TRIM is non-nil, then all leading and trailing +whitespace is remove. If it is the newline character, then +one trailing newline is added." + ;; Lets write that one last time and be done with it: + (let ((str (buffer-substring-no-properties (or min (point-min)) + (or max (point-max))))) + (if trim + (concat (string-trim str) + (and (eq trim ?\n) "\n")) + str))) + +(defun magit--version> (v1 v2) + "Return t if version V1 is higher (younger) than V2. +This function should be named `version>' and be part of Emacs." + (version-list-< (version-to-list v2) (version-to-list v1))) + +(defun magit--version>= (v1 v2) + "Return t if version V1 is higher (younger) than or equal to V2. +This function should be named `version>=' and be part of Emacs." + (version-list-<= (version-to-list v2) (version-to-list v1))) + +;;; Kludges for Emacs Bugs + +(defun magit-file-accessible-directory-p (filename) + "Like `file-accessible-directory-p' but work around an Apple bug. +See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=21573#17 +and https://github.com/magit/magit/issues/2295." + (and (file-directory-p filename) + (file-accessible-directory-p filename))) + +(when (< emacs-major-version 27) + ;; Work around https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559. + ;; Fixed by cb55ccae8be946f1562d74718086a4c8c8308ee5 in Emacs 27.1. + (with-eval-after-load 'vc-git + (defun vc-git-conflicted-files (directory) + "Return the list of files with conflicts in DIRECTORY." + (let* ((status + (vc-git--run-command-string directory "diff-files" + "--name-status")) + (lines (when status (split-string status "\n" 'omit-nulls))) + files) + (dolist (line lines files) + (when (string-match "\\([ MADRCU?!]\\)[ \t]+\\(.+\\)" line) + (let ((state (match-string 1 line)) + (file (match-string 2 line))) + (when (equal state "U") + (push (expand-file-name file directory) files))))))))) + +(when (< emacs-major-version 27) + (defun vc-git--call@bug21559 (fn buffer command &rest args) + "Backport https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559." + (let ((process-environment process-environment)) + (when revert-buffer-in-progress-p + (push "GIT_OPTIONAL_LOCKS=0" process-environment)) + (apply fn buffer command args))) + (advice-add 'vc-git--call :around 'vc-git--call@bug21559) + + (defun vc-git-command@bug21559 + (fn buffer okstatus file-or-list &rest flags) + "Backport https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559." + (let ((process-environment process-environment)) + (when revert-buffer-in-progress-p + (push "GIT_OPTIONAL_LOCKS=0" process-environment)) + (apply fn buffer okstatus file-or-list flags))) + (advice-add 'vc-git-command :around 'vc-git-command@bug21559) + + (defun auto-revert-handler@bug21559 (fn) + "Backport https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21559." + (let ((revert-buffer-in-progress-p t)) + (funcall fn))) + (advice-add 'auto-revert-handler :around 'auto-revert-handler@bug21559) + ) + +(when (< emacs-major-version 26) + ;; In Emacs 25 `completion-pcm--all-completions' reverses the + ;; completion list. This is the version from Emacs 26, which + ;; fixes that issue. bug#24676 + (defun magit-completion-pcm--all-completions (prefix pattern table pred) + (if (completion-pcm--pattern-trivial-p pattern) + (all-completions (concat prefix (car pattern)) table pred) + (let* ((regex (completion-pcm--pattern->regex pattern)) + (case-fold-search completion-ignore-case) + (completion-regexp-list (cons regex completion-regexp-list)) + (compl (all-completions + (concat prefix + (if (stringp (car pattern)) (car pattern) "")) + table pred))) + (if (not (functionp table)) + compl + (let ((poss ())) + (dolist (c compl) + (when (string-match-p regex c) (push c poss))) + (nreverse poss))))))) + +(defun magit-which-function () + "Return current function name based on point. + +This is a simple wrapper around `which-function', that resets +Imenu's potentially outdated and therefore unreliable cache by +setting `imenu--index-alist' to nil before calling that function." + (setq imenu--index-alist nil) + (which-function)) + +;;; Kludges for Custom + +(defun magit-custom-initialize-reset (symbol exp) + "Initialize SYMBOL based on EXP. +Set the value of the variable SYMBOL, using `set-default' +\(unlike `custom-initialize-reset', which uses the `:set' +function if any). The value is either the symbol's current +value (as obtained using the `:get' function), if any, or +the value in the symbol's `saved-value' property if any, or +\(last of all) the value of EXP." + (set-default-toplevel-value + symbol + (condition-case nil + (let ((def (default-toplevel-value symbol)) + (getter (get symbol 'custom-get))) + (if getter (funcall getter symbol) def)) + (error + (eval (let ((sv (get symbol 'saved-value))) + (if sv (car sv) exp))))))) + +(defun magit-hook-custom-get (symbol) + (if (symbol-file symbol 'defvar) + (default-toplevel-value symbol) + ;; + ;; Called by `custom-initialize-reset' on behalf of `symbol's + ;; `defcustom', which is being evaluated for the first time to + ;; set the initial value, but there's already a default value, + ;; which most likely was established by one or more `add-hook' + ;; calls. + ;; + ;; We combine the `standard-value' and the current value, while + ;; preserving the order established by `:options', and return + ;; the result of that to be used as the "initial" default value. + ;; + (let ((standard (eval (car (get symbol 'standard-value)))) + (current (default-toplevel-value symbol)) + (value nil)) + (dolist (fn (get symbol 'custom-options)) + (when (or (memq fn standard) + (memq fn current)) + (push fn value))) + (dolist (fn current) + (unless (memq fn value) + (push fn value))) + (nreverse value)))) + +;;; Kludges for Info Manuals + +;;;###autoload +(defun Info-follow-nearest-node--magit-gitman (fn &optional fork) + (let ((node (Info-get-token + (point) "\\*note[ \n\t]+" + "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))) + (if (and node (string-match "^(gitman)\\(.+\\)" node)) + (pcase magit-view-git-manual-method + ('info (funcall fn fork)) + ('man (require 'man) + (man (match-string 1 node))) + ('woman (require 'woman) + (woman (match-string 1 node))) + (_ + (user-error "Invalid value for `magit-view-git-manual-method'"))) + (funcall fn fork)))) + +;;;###autoload +(advice-add 'Info-follow-nearest-node :around + #'Info-follow-nearest-node--magit-gitman) + +;; When making changes here, then also adjust the copy in docs/Makefile. +;;;###autoload +(advice-add 'org-man-export :around #'org-man-export--magit-gitman) +;;;###autoload +(defun org-man-export--magit-gitman (fn link description format) + (if (and (eq format 'texinfo) + (string-prefix-p "git" link)) + (string-replace "%s" link " +@ifinfo +@ref{%s,,,gitman,}. +@end ifinfo +@ifhtml +@html +the %s(1) manpage. +@end html +@end ifhtml +@iftex +the %s(1) manpage. +@end iftex +") + (funcall fn link description format))) + +;;; Kludges for Package Managers + +(defun magit--straight-chase-links (filename) + "Chase links in FILENAME until a name that is not a link. + +This is the same as `file-chase-links', except that it also +handles fake symlinks that are created by the package manager +straight.el on Windows. + +See ." + (when (and (bound-and-true-p straight-symlink-emulation-mode) + (fboundp 'straight-chase-emulated-symlink)) + (when-let ((target (straight-chase-emulated-symlink filename))) + (unless (eq target 'broken) + (setq filename target)))) + (file-chase-links filename)) + +;;; Kludges for older Emacs versions + +(if (fboundp 'with-connection-local-variables) + (defalias 'magit--with-connection-local-variables + #'with-connection-local-variables) + (defmacro magit--with-connection-local-variables (&rest body) + "Abridged `with-connection-local-variables' for pre Emacs 27 compatibility. +Bind shell file name and switch for remote execution. +`with-connection-local-variables' isn't available until Emacs 27. +This kludge provides the minimal functionality required by +Magit." + `(if (file-remote-p default-directory) + (pcase-let ((`(,shell-file-name ,shell-command-switch) + (with-no-warnings ; about unknown tramp functions + (require 'tramp) + (let ((vec (tramp-dissect-file-name + default-directory))) + (list (tramp-get-method-parameter + vec 'tramp-remote-shell) + (mapconcat #'identity + (tramp-get-method-parameter + vec 'tramp-remote-shell-args) + " ")))))) + ,@body) + ,@body))) + +;;; Miscellaneous + +(defun magit-message (format-string &rest args) + "Display a message at the bottom of the screen, or not. +Like `message', except that if the users configured option +`magit-no-message' to prevent the message corresponding to +FORMAT-STRING to be displayed, then don't." + (unless (--first (string-prefix-p it format-string) magit-no-message) + (apply #'message format-string args))) + +(defun magit-msg (format-string &rest args) + "Display a message at the bottom of the screen, but don't log it. +Like `message', except that `message-log-max' is bound to nil." + (let ((message-log-max nil)) + (apply #'message format-string args))) + +(defmacro magit--with-temp-position (buf pos &rest body) + (declare (indent 2)) + `(with-current-buffer ,buf + (save-excursion + (save-restriction + (widen) + (goto-char (or ,pos 1)) + ,@body)))) + +;;; _ +(provide 'magit-base) +;;; magit-base.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-bisect.el b/code/elpa/magit-20220821.1819/magit-bisect.el new file mode 100644 index 0000000..92dc6ab --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-bisect.el @@ -0,0 +1,307 @@ +;;; magit-bisect.el --- Bisect support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; Use a binary search to find the commit that introduced a bug. + +;;; Code: + +(require 'magit) + +;;; Options + +(defcustom magit-bisect-show-graph t + "Whether to use `--graph' in the log showing commits yet to be bisected." + :package-version '(magit . "2.8.0") + :group 'magit-status + :type 'boolean) + +(defface magit-bisect-good + '((t :foreground "DarkOliveGreen")) + "Face for good bisect revisions." + :group 'magit-faces) + +(defface magit-bisect-skip + '((t :foreground "DarkGoldenrod")) + "Face for skipped bisect revisions." + :group 'magit-faces) + +(defface magit-bisect-bad + '((t :foreground "IndianRed4")) + "Face for bad bisect revisions." + :group 'magit-faces) + +;;; Commands + +;;;###autoload (autoload 'magit-bisect "magit-bisect" nil t) +(transient-define-prefix magit-bisect () + "Narrow in on the commit that introduced a bug." + :man-page "git-bisect" + [:class transient-subgroups + :if-not magit-bisect-in-progress-p + ["Arguments" + ("-n" "Don't checkout commits" "--no-checkout") + ("-p" "Follow only first parent of a merge" "--first-parent" + :if (lambda () (magit-git-version>= "2.29"))) + (6 magit-bisect:--term-old + :if (lambda () (magit-git-version>= "2.7"))) + (6 magit-bisect:--term-new + :if (lambda () (magit-git-version>= "2.7")))] + ["Actions" + ("B" "Start" magit-bisect-start) + ("s" "Start script" magit-bisect-run)]] + ["Actions" + :if magit-bisect-in-progress-p + ("B" "Bad" magit-bisect-bad) + ("g" "Good" magit-bisect-good) + (6 "m" "Mark" magit-bisect-mark + :if (lambda () (magit-git-version>= "2.7"))) + ("k" "Skip" magit-bisect-skip) + ("r" "Reset" magit-bisect-reset) + ("s" "Run script" magit-bisect-run)]) + +(transient-define-argument magit-bisect:--term-old () + :description "Old/good term" + :class 'transient-option + :key "=o" + :argument "--term-old=") + +(transient-define-argument magit-bisect:--term-new () + :description "New/bad term" + :class 'transient-option + :key "=n" + :argument "--term-new=") + +;;;###autoload +(defun magit-bisect-start (bad good args) + "Start a bisect session. + +Bisecting a bug means to find the commit that introduced it. +This command starts such a bisect session by asking for a known +good and a known bad commit. To move the session forward use the +other actions from the bisect transient command (\ +\\\\[magit-bisect])." + (interactive (if (magit-bisect-in-progress-p) + (user-error "Already bisecting") + (magit-bisect-start-read-args))) + (unless (magit-rev-ancestor-p good bad) + (user-error + "The %s revision (%s) has to be an ancestor of the %s one (%s)" + (or (transient-arg-value "--term-old=" args) "good") + good + (or (transient-arg-value "--term-new=" args) "bad") + bad)) + (when (magit-anything-modified-p) + (user-error "Cannot bisect with uncommitted changes")) + (magit-git-bisect "start" (list args bad good) t)) + +(defun magit-bisect-start-read-args () + (let* ((args (transient-args 'magit-bisect)) + (bad (magit-read-branch-or-commit + (format "Start bisect with %s revision" + (or (transient-arg-value "--term-new=" args) + "bad"))))) + (list bad + (magit-read-other-branch-or-commit + (format "%s revision" (or (transient-arg-value "--term-old=" args) + "Good")) + bad) + args))) + +;;;###autoload +(defun magit-bisect-reset () + "After bisecting, cleanup bisection state and return to original `HEAD'." + (interactive) + (magit-confirm 'reset-bisect) + (magit-run-git "bisect" "reset") + (ignore-errors (delete-file (magit-git-dir "BISECT_CMD_OUTPUT")))) + +;;;###autoload +(defun magit-bisect-good () + "While bisecting, mark the current commit as good. +Use this after you have asserted that the commit does not contain +the bug in question." + (interactive) + (magit-git-bisect (or (cadr (magit-bisect-terms)) + (user-error "Not bisecting")))) + +;;;###autoload +(defun magit-bisect-bad () + "While bisecting, mark the current commit as bad. +Use this after you have asserted that the commit does contain the +bug in question." + (interactive) + (magit-git-bisect (or (car (magit-bisect-terms)) + (user-error "Not bisecting")))) + +;;;###autoload +(defun magit-bisect-mark () + "While bisecting, mark the current commit with a bisect term. +During a bisect using alternate terms, commits can still be +marked with `magit-bisect-good' and `magit-bisect-bad', as those +commands map to the correct term (\"good\" to --term-old's value +and \"bad\" to --term-new's). However, in some cases, it can be +difficult to keep that mapping straight in your head; this +command provides an interface that exposes the underlying terms." + (interactive) + (magit-git-bisect + (pcase-let ((`(,term-new ,term-old) (or (magit-bisect-terms) + (user-error "Not bisecting")))) + (pcase (read-char-choice + (format "Mark HEAD as %s ([n]ew) or %s ([o]ld)" + term-new term-old) + (list ?n ?o)) + (?n term-new) + (?o term-old))))) + +;;;###autoload +(defun magit-bisect-skip () + "While bisecting, skip the current commit. +Use this if for some reason the current commit is not a good one +to test. This command lets Git choose a different one." + (interactive) + (magit-git-bisect "skip")) + +;;;###autoload +(defun magit-bisect-run (cmdline &optional bad good args) + "Bisect automatically by running commands after each step. + +Unlike `git bisect run' this can be used before bisecting has +begun. In that case it behaves like `git bisect start; git +bisect run'." + (interactive (let ((args (and (not (magit-bisect-in-progress-p)) + (magit-bisect-start-read-args)))) + (cons (read-shell-command "Bisect shell command: ") args))) + (when (and bad good) + ;; Avoid `magit-git-bisect' because it's asynchronous, but the + ;; next `git bisect run' call requires the bisect to be started. + (magit-with-toplevel + (magit-process-git + (list :file (magit-git-dir "BISECT_CMD_OUTPUT")) + (magit-process-git-arguments + (list "bisect" "start" bad good args))) + (magit-refresh))) + (magit--with-connection-local-variables + (magit-git-bisect "run" (list shell-file-name + shell-command-switch cmdline)))) + +(defun magit-git-bisect (subcommand &optional args no-assert) + (unless (or no-assert (magit-bisect-in-progress-p)) + (user-error "Not bisecting")) + (message "Bisecting...") + (magit-with-toplevel + (magit-run-git-async "bisect" subcommand args)) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (> (process-exit-status process) 0) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (when (buffer-live-p (process-buffer process)) + (with-current-buffer (process-buffer process) + (when-let* ((section (magit-section-at)) + (output (buffer-substring-no-properties + (oref section content) + (oref section end)))) + (with-temp-file (magit-git-dir "BISECT_CMD_OUTPUT") + (insert output))))) + (magit-refresh)) + (message "Bisecting...done"))))) + +;;; Sections + +(defun magit-bisect-in-progress-p () + (file-exists-p (magit-git-dir "BISECT_LOG"))) + +(defun magit-bisect-terms () + (magit-file-lines (magit-git-dir "BISECT_TERMS"))) + +(defun magit-insert-bisect-output () + "While bisecting, insert section with output from `git bisect'." + (when (magit-bisect-in-progress-p) + (let* ((lines + (or (magit-file-lines (magit-git-dir "BISECT_CMD_OUTPUT")) + (list "Bisecting: (no saved bisect output)" + "It appears you have invoked `git bisect' from a shell." + "There is nothing wrong with that, we just cannot display" + "anything useful here. Consult the shell output instead."))) + (done-re "^\\([a-z0-9]\\{40,\\}\\) is the first bad commit$") + (bad-line (or (and (string-match done-re (car lines)) + (pop lines)) + (--first (string-match done-re it) lines)))) + (magit-insert-section ((eval (if bad-line 'commit 'bisect-output)) + (and bad-line (match-string 1 bad-line))) + (magit-insert-heading + (propertize (or bad-line (pop lines)) + 'font-lock-face 'magit-section-heading)) + (dolist (line lines) + (insert line "\n")))) + (insert "\n"))) + +(defun magit-insert-bisect-rest () + "While bisecting, insert section visualizing the bisect state." + (when (magit-bisect-in-progress-p) + (magit-insert-section (bisect-view) + (magit-insert-heading "Bisect Rest:") + (magit-git-wash (apply-partially #'magit-log-wash-log 'bisect-vis) + "bisect" "visualize" "git" "log" + "--format=%h%x00%D%x00%s" "--decorate=full" + (and magit-bisect-show-graph "--graph"))))) + +(defun magit-insert-bisect-log () + "While bisecting, insert section logging bisect progress." + (when (magit-bisect-in-progress-p) + (magit-insert-section (bisect-log) + (magit-insert-heading "Bisect Log:") + (magit-git-wash #'magit-wash-bisect-log "bisect" "log") + (insert ?\n)))) + +(defun magit-wash-bisect-log (_args) + (let (beg) + (while (progn (setq beg (point-marker)) + (re-search-forward "^\\(git bisect [^\n]+\n\\)" nil t)) + (magit-bind-match-strings (heading) nil + (magit-delete-match) + (save-restriction + (narrow-to-region beg (point)) + (goto-char (point-min)) + (magit-insert-section (bisect-item heading t) + (insert (propertize heading 'font-lock-face + 'magit-section-secondary-heading)) + (magit-insert-heading) + (magit-wash-sequence + (apply-partially #'magit-log-wash-rev 'bisect-log + (magit-abbrev-length))) + (insert ?\n))))) + (when (re-search-forward + "# first bad commit: \\[\\([a-z0-9]\\{40,\\}\\)\\] [^\n]+\n" nil t) + (magit-bind-match-strings (hash) nil + (magit-delete-match) + (magit-insert-section (bisect-item) + (insert hash " is the first bad commit\n")))))) + +;;; _ +(provide 'magit-bisect) +;;; magit-bisect.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-blame.el b/code/elpa/magit-20220821.1819/magit-blame.el new file mode 100644 index 0000000..d27f84f --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-blame.el @@ -0,0 +1,984 @@ +;;; magit-blame.el --- Blame support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; Annotates each line in file-visiting buffer with information from +;; the revision which last modified the line. + +;;; Code: + +(require 'magit) + +;;; Options + +(defgroup magit-blame nil + "Blame support for Magit." + :link '(info-link "(magit)Blaming") + :group 'magit-modes) + +(defcustom magit-blame-styles + '((headings + (heading-format . "%-20a %C %s\n")) + (highlight + (highlight-face . magit-blame-highlight)) + (lines + (show-lines . t) + (show-message . t))) + "List of styles used to visualize blame information. + +The style used in the current buffer can be cycled from the blame +popup. Blame commands (except `magit-blame-echo') use the first +style as the initial style when beginning to blame in a buffer. + +Each entry has the form (IDENT (KEY . VALUE)...). IDENT has +to be a symbol uniquely identifying the style. The following +KEYs are recognized: + + `show-lines' + Whether to prefix each chunk of lines with a thin line. + This has no effect if `heading-format' is non-nil. + `show-message' + Whether to display a commit's summary line in the echo area + when crossing chunks. + `highlight-face' + Face used to highlight the first line of each chunk. + If this is nil, then those lines are not highlighted. + `heading-format' + String specifying the information to be shown above each + chunk of lines. It must end with a newline character. + `margin-format' + String specifying the information to be shown in the left + buffer margin. It must NOT end with a newline character. + This can also be a list of formats used for the lines at + the same positions within the chunk. If the chunk has + more lines than formats are specified, then the last is + repeated. WARNING: Adding this key affects performance; + see the note at the end of this docstring. + `margin-width' + Width of the margin, provided `margin-format' is non-nil. + `margin-face' + Face used in the margin, provided `margin-format' is + non-nil. This face is used in combination with the faces + that are specific to the used %-specs. If this is nil, + then `magit-blame-margin' is used. + `margin-body-face' + Face used in the margin for all but first line of a chunk. + This face is used in combination with the faces that are + specific to the used %-specs. This can also be a list of + faces (usually one face), in which case only these faces + are used and the %-spec faces are ignored. A good value + might be `(magit-blame-dimmed)'. If this is nil, then + the same face as for the first line is used. + +The following %-specs can be used in `heading-format' and +`margin-format': + + %H hash using face `magit-blame-hash' + %s summary using face `magit-blame-summary' + %a author using face `magit-blame-name' + %A author time using face `magit-blame-date' + %c committer using face `magit-blame-name' + %C committer time using face `magit-blame-date' + +Additionally if `margin-format' ends with %f, then the string +that is displayed in the margin is made at least `margin-width' +characters wide, which may be desirable if the used face sets +the background color. + +Blame information is displayed using overlays. Such extensive +use of overlays is known to slow down even basic operations, such +as moving the cursor. To reduce the number of overlays the margin +style had to be removed from the default value of this option. + +Note that the margin overlays are created even if another style +is currently active. This can only be prevented by not even +defining a style that uses the margin. If you want to use this +style anyway, you can restore this definition, which used to be +part of the default value: + + (margin + (margin-format . (\" %s%f\" \" %C %a\" \" %H\")) + (margin-width . 42) + (margin-face . magit-blame-margin) + (margin-body-face . (magit-blame-dimmed)))" + :package-version '(magit . "2.13.0") + :group 'magit-blame + :type 'string) + +(defcustom magit-blame-echo-style 'lines + "The blame visualization style used by `magit-blame-echo'. +A symbol that has to be used as the identifier for one of the +styles defined in `magit-blame-styles'." + :package-version '(magit . "2.13.0") + :group 'magit-blame + :type 'symbol) + +(defcustom magit-blame-time-format "%F %H:%M" + "Format for time strings in blame headings." + :group 'magit-blame + :type 'string) + +(defcustom magit-blame-read-only t + "Whether to initially make the blamed buffer read-only." + :package-version '(magit . "2.13.0") + :group 'magit-blame + :type 'boolean) + +(defcustom magit-blame-disable-modes '(fci-mode yascroll-bar-mode) + "List of modes not compatible with Magit-Blame mode. +This modes are turned off when Magit-Blame mode is turned on, +and then turned on again when turning off the latter." + :group 'magit-blame + :type '(repeat (symbol :tag "Mode"))) + +(defcustom magit-blame-mode-lighter " Blame" + "The mode-line lighter of the Magit-Blame mode." + :group 'magit-blame + :type '(choice (const :tag "No lighter" "") string)) + +(defcustom magit-blame-goto-chunk-hook + '(magit-blame-maybe-update-revision-buffer + magit-blame-maybe-show-message) + "Hook run after point entered another chunk." + :package-version '(magit . "2.13.0") + :group 'magit-blame + :type 'hook + :get #'magit-hook-custom-get + :options '(magit-blame-maybe-update-revision-buffer + magit-blame-maybe-show-message)) + +;;; Faces + +(defface magit-blame-highlight + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey80" + :foreground "black") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey25" + :foreground "white")) + "Face used for highlighting when blaming. +Also see option `magit-blame-styles'." + :group 'magit-faces) + +(defface magit-blame-margin + '((t :inherit magit-blame-highlight + :weight normal + :slant normal)) + "Face used for the blame margin by default when blaming. +Also see option `magit-blame-styles'." + :group 'magit-faces) + +(defface magit-blame-dimmed + '((t :inherit magit-dimmed + :weight normal + :slant normal)) + "Face used for the blame margin in some cases when blaming. +Also see option `magit-blame-styles'." + :group 'magit-faces) + +(defface magit-blame-heading + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-blame-highlight + :weight normal + :slant normal)) + "Face used for blame headings by default when blaming. +Also see option `magit-blame-styles'." + :group 'magit-faces) + +(defface magit-blame-summary '((t nil)) + "Face used for commit summaries when blaming." + :group 'magit-faces) + +(defface magit-blame-hash '((t nil)) + "Face used for commit hashes when blaming." + :group 'magit-faces) + +(defface magit-blame-name '((t nil)) + "Face used for author and committer names when blaming." + :group 'magit-faces) + +(defface magit-blame-date '((t nil)) + "Face used for dates when blaming." + :group 'magit-faces) + +;;; Variables + +(defvar-local magit-blame-buffer-read-only nil) +(defvar-local magit-blame-cache nil) +(defvar-local magit-blame-disabled-modes nil) +(defvar-local magit-blame-process nil) +(defvar-local magit-blame-recursive-p nil) +(defvar-local magit-blame-type nil) +(defvar-local magit-blame-separator nil) +(defvar-local magit-blame-previous-chunk nil) + +(defvar-local magit-blame--make-margin-overlays nil) +(defvar-local magit-blame--style nil) + +;;; Chunks + +(defclass magit-blame-chunk () + (;; + (orig-rev :initarg :orig-rev) + (orig-line :initarg :orig-line) + (final-line :initarg :final-line) + (num-lines :initarg :num-lines) + ;; previous + (prev-rev :initform nil) + (prev-file :initform nil) + ;; filename + (orig-file))) + +(defun magit-current-blame-chunk (&optional type noerror) + (or (and (not (and type (not (eq type magit-blame-type)))) + (magit-blame-chunk-at (point))) + (and type + (let ((rev (or magit-buffer-refname magit-buffer-revision)) + (file (and (not (derived-mode-p 'dired-mode)) + (magit-file-relative-name + nil (not magit-buffer-file-name)))) + (line (format "%i,+1" (line-number-at-pos)))) + (cond (file (with-temp-buffer + (magit-with-toplevel + (magit-git-insert + "blame" "--porcelain" + (if (memq magit-blame-type '(final removal)) + (cons "--reverse" (magit-blame-arguments)) + (magit-blame-arguments)) + "-L" line rev "--" file) + (goto-char (point-min)) + (if (eobp) + (unless noerror + (error "Cannot get blame chunk at eob")) + (car (magit-blame--parse-chunk type)))))) + (noerror nil) + (t (error "Buffer does not visit a tracked file"))))))) + +(defun magit-blame-chunk-at (pos) + (--some (overlay-get it 'magit-blame-chunk) + (overlays-at pos))) + +(defun magit-blame--overlay-at (&optional pos key) + (unless pos + (setq pos (point))) + (--first (overlay-get it (or key 'magit-blame-chunk)) + (nconc (overlays-at pos) + (overlays-in pos pos)))) + +;;; Keymaps + +(defvar magit-blame-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-q") 'magit-blame-quit) + map) + "Keymap for `magit-blame-mode'. +Note that most blaming key bindings are defined +in `magit-blame-read-only-mode-map' instead.") + +(defvar magit-blame-read-only-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-m") #'magit-show-commit) + (define-key map (kbd "p") #'magit-blame-previous-chunk) + (define-key map (kbd "P") #'magit-blame-previous-chunk-same-commit) + (define-key map (kbd "n") #'magit-blame-next-chunk) + (define-key map (kbd "N") #'magit-blame-next-chunk-same-commit) + (define-key map (kbd "b") #'magit-blame-addition) + (define-key map (kbd "r") #'magit-blame-removal) + (define-key map (kbd "f") #'magit-blame-reverse) + (define-key map (kbd "B") #'magit-blame) + (define-key map (kbd "c") #'magit-blame-cycle-style) + (define-key map (kbd "q") #'magit-blame-quit) + (define-key map (kbd "M-w") #'magit-blame-copy-hash) + (define-key map (kbd "SPC") #'magit-diff-show-or-scroll-up) + (define-key map (kbd "S-SPC") #'magit-diff-show-or-scroll-down) + (define-key map (kbd "DEL") #'magit-diff-show-or-scroll-down) + map) + "Keymap for `magit-blame-read-only-mode'.") + +;;; Modes +;;;; Base Mode + +(define-minor-mode magit-blame-mode + "Display blame information inline." + :lighter magit-blame-mode-lighter + (cond (magit-blame-mode + (when (called-interactively-p 'any) + (setq magit-blame-mode nil) + (user-error + (concat "Don't call `magit-blame-mode' directly; " + "instead use `magit-blame'"))) + (add-hook 'after-save-hook #'magit-blame--refresh t t) + (add-hook 'post-command-hook #'magit-blame-goto-chunk-hook t t) + (add-hook 'before-revert-hook #'magit-blame--remove-overlays t t) + (add-hook 'after-revert-hook #'magit-blame--refresh t t) + (add-hook 'read-only-mode-hook #'magit-blame-toggle-read-only t t) + (setq magit-blame-buffer-read-only buffer-read-only) + (when (or magit-blame-read-only magit-buffer-file-name) + (read-only-mode 1)) + (dolist (mode magit-blame-disable-modes) + (when (and (boundp mode) (symbol-value mode)) + (funcall mode -1) + (push mode magit-blame-disabled-modes))) + (setq magit-blame-separator (magit-blame--format-separator)) + (unless magit-blame--style + (setq magit-blame--style (car magit-blame-styles))) + (setq magit-blame--make-margin-overlays + (and (cl-find-if (lambda (style) + (assq 'margin-format (cdr style))) + magit-blame-styles))) + (magit-blame--update-margin)) + (t + (when (process-live-p magit-blame-process) + (kill-process magit-blame-process) + (while magit-blame-process + (sit-for 0.01))) ; avoid racing the sentinel + (remove-hook 'after-save-hook #'magit-blame--refresh t) + (remove-hook 'post-command-hook #'magit-blame-goto-chunk-hook t) + (remove-hook 'before-revert-hook #'magit-blame--remove-overlays t) + (remove-hook 'after-revert-hook #'magit-blame--refresh t) + (remove-hook 'read-only-mode-hook #'magit-blame-toggle-read-only t) + (unless magit-blame-buffer-read-only + (read-only-mode -1)) + (magit-blame-read-only-mode -1) + (dolist (mode magit-blame-disabled-modes) + (funcall mode 1)) + (kill-local-variable 'magit-blame-disabled-modes) + (kill-local-variable 'magit-blame-type) + (kill-local-variable 'magit-blame--style) + (magit-blame--update-margin) + (magit-blame--remove-overlays)))) + +(defun magit-blame--refresh () + (magit-blame--run (magit-blame-arguments))) + +(defun magit-blame-goto-chunk-hook () + (let ((chunk (magit-blame-chunk-at (point)))) + (when (cl-typep chunk 'magit-blame-chunk) + (unless (eq chunk magit-blame-previous-chunk) + (run-hooks 'magit-blame-goto-chunk-hook)) + (setq magit-blame-previous-chunk chunk)))) + +(defun magit-blame-toggle-read-only () + (magit-blame-read-only-mode (if buffer-read-only 1 -1))) + +;;;; Read-Only Mode + +(define-minor-mode magit-blame-read-only-mode + "Provide keybindings for Magit-Blame mode. + +This minor-mode provides the key bindings for Magit-Blame mode, +but only when Read-Only mode is also enabled because these key +bindings would otherwise conflict badly with regular bindings. + +When both Magit-Blame mode and Read-Only mode are enabled, then +this mode gets automatically enabled too and when one of these +modes is toggled, then this mode also gets toggled automatically. + +\\{magit-blame-read-only-mode-map}") + +;;;; Kludges + +(defun magit-blame-put-keymap-before-view-mode () + "Put `magit-blame-read-only-mode' ahead of `view-mode' in `minor-mode-map-alist'." + (--when-let (assq 'magit-blame-read-only-mode + (cl-member 'view-mode minor-mode-map-alist :key #'car)) + (setq minor-mode-map-alist + (cons it (delq it minor-mode-map-alist)))) + (remove-hook 'view-mode-hook #'magit-blame-put-keymap-before-view-mode)) + +(add-hook 'view-mode-hook #'magit-blame-put-keymap-before-view-mode) + +;;; Process + +(defun magit-blame--run (args) + (magit-with-toplevel + (unless magit-blame-mode + (magit-blame-mode 1)) + (message "Blaming...") + (magit-blame-run-process + (or magit-buffer-refname magit-buffer-revision) + (magit-file-relative-name nil (not magit-buffer-file-name)) + (if (memq magit-blame-type '(final removal)) + (cons "--reverse" args) + args) + (list (line-number-at-pos (window-start)) + (line-number-at-pos (1- (window-end nil t))))) + (set-process-sentinel magit-this-process + #'magit-blame-process-quickstart-sentinel))) + +(defun magit-blame-run-process (revision file args &optional lines) + (let ((process (magit-parse-git-async + "blame" "--incremental" args + (and lines (list "-L" (apply #'format "%s,%s" lines))) + revision "--" file))) + (set-process-filter process #'magit-blame-process-filter) + (set-process-sentinel process #'magit-blame-process-sentinel) + (process-put process 'arguments (list revision file args)) + (setq magit-blame-cache (make-hash-table :test #'equal)) + (setq magit-blame-process process))) + +(defun magit-blame-process-quickstart-sentinel (process event) + (when (memq (process-status process) '(exit signal)) + (magit-blame-process-sentinel process event t) + (magit-blame-assert-buffer process) + (with-current-buffer (process-get process 'command-buf) + (when magit-blame-mode + (let ((default-directory (magit-toplevel))) + (apply #'magit-blame-run-process + (process-get process 'arguments))))))) + +(defun magit-blame-process-sentinel (process _event &optional quiet) + (let ((status (process-status process))) + (when (memq status '(exit signal)) + (kill-buffer (process-buffer process)) + (if (and (eq status 'exit) + (zerop (process-exit-status process))) + (unless quiet + (message "Blaming...done")) + (magit-blame-assert-buffer process) + (with-current-buffer (process-get process 'command-buf) + (if magit-blame-mode + (progn (magit-blame-mode -1) + (message "Blaming...failed")) + (message "Blaming...aborted")))) + (kill-local-variable 'magit-blame-process)))) + +(defun magit-blame-process-filter (process string) + (internal-default-process-filter process string) + (let ((buf (process-get process 'command-buf)) + (pos (process-get process 'parsed)) + (mark (process-mark process)) + type cache) + (with-current-buffer buf + (setq type magit-blame-type) + (setq cache magit-blame-cache)) + (with-current-buffer (process-buffer process) + (goto-char pos) + (while (and (< (point) mark) + (save-excursion (re-search-forward "^filename .+\n" nil t))) + (pcase-let* ((`(,chunk ,revinfo) + (magit-blame--parse-chunk type)) + (rev (oref chunk orig-rev))) + (if revinfo + (puthash rev revinfo cache) + (setq revinfo + (or (gethash rev cache) + (puthash rev (magit-blame--commit-alist rev) cache)))) + (magit-blame--make-overlays buf chunk revinfo)) + (process-put process 'parsed (point)))))) + +(defun magit-blame--parse-chunk (type) + (let (chunk revinfo) + (unless (looking-at "^\\(.\\{40,\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)") + (error "Blaming failed due to unexpected output: %s" + (buffer-substring-no-properties (point) (line-end-position)))) + (with-slots (orig-rev orig-file prev-rev prev-file) + (setq chunk (magit-blame-chunk + :orig-rev (match-string 1) + :orig-line (string-to-number (match-string 2)) + :final-line (string-to-number (match-string 3)) + :num-lines (string-to-number (match-string 4)))) + (forward-line) + (let (done) + (while (not done) + (cond ((looking-at "^filename \\(.+\\)") + (setq done t) + (setf orig-file (magit-decode-git-path (match-string 1)))) + ((looking-at "^previous \\(.\\{40,\\}\\) \\(.+\\)") + (setf prev-rev (match-string 1)) + (setf prev-file (magit-decode-git-path (match-string 2)))) + ((looking-at "^\\([^ ]+\\) \\(.+\\)") + (push (cons (match-string 1) + (match-string 2)) revinfo))) + (forward-line))) + (when (and (eq type 'removal) prev-rev) + (cl-rotatef orig-rev prev-rev) + (cl-rotatef orig-file prev-file) + (setq revinfo nil))) + (list chunk revinfo))) + +(defun magit-blame--commit-alist (rev) + (cl-mapcar 'cons + '("summary" + "author" "author-time" "author-tz" + "committer" "committer-time" "committer-tz") + (split-string (magit-rev-format "%s\v%an\v%ad\v%cn\v%cd" rev + "--date=format:%s\v%z") + "\v"))) + +(defun magit-blame-assert-buffer (process) + (unless (buffer-live-p (process-get process 'command-buf)) + (kill-process process) + (user-error "Buffer being blamed has been killed"))) + +;;; Display + +(defsubst magit-blame--style-get (key) + (cdr (assoc key (cdr magit-blame--style)))) + +(defun magit-blame--make-overlays (buf chunk revinfo) + (with-current-buffer buf + (save-excursion + (save-restriction + (widen) + (let* ((line (oref chunk final-line)) + (beg (magit-blame--line-beginning-position line)) + (end (magit-blame--line-beginning-position + (+ line (oref chunk num-lines)))) + (before (magit-blame-chunk-at (1- beg)))) + (when (and before + (equal (oref before orig-rev) + (oref chunk orig-rev))) + (setq beg (magit-blame--line-beginning-position + (oset chunk final-line (oref before final-line)))) + (cl-incf (oref chunk num-lines) + (oref before num-lines))) + (magit-blame--remove-overlays beg end) + (when magit-blame--make-margin-overlays + (magit-blame--make-margin-overlays chunk revinfo beg end)) + (magit-blame--make-heading-overlay chunk revinfo beg end) + (magit-blame--make-highlight-overlay chunk beg)))))) + +(defun magit-blame--line-beginning-position (line) + (save-excursion + (goto-char (point-min)) + (forward-line (1- line)) + (point))) + +(defun magit-blame--make-margin-overlays (chunk revinfo _beg end) + (save-excursion + (let ((line 0)) + (while (< (point) end) + (magit-blame--make-margin-overlay chunk revinfo line) + (forward-line) + (cl-incf line))))) + +(defun magit-blame--make-margin-overlay (chunk revinfo line) + (let* ((end (line-end-position)) + ;; If possible avoid putting this on the first character + ;; of the line to avoid a conflict with the line overlay. + (beg (min (1+ (line-beginning-position)) end)) + (ov (make-overlay beg end))) + (overlay-put ov 'magit-blame-chunk chunk) + (overlay-put ov 'magit-blame-revinfo revinfo) + (overlay-put ov 'magit-blame-margin line) + (magit-blame--update-margin-overlay ov))) + +(defun magit-blame--make-heading-overlay (chunk revinfo beg end) + (let ((ov (make-overlay beg end))) + (overlay-put ov 'magit-blame-chunk chunk) + (overlay-put ov 'magit-blame-revinfo revinfo) + (overlay-put ov 'magit-blame-heading t) + (magit-blame--update-heading-overlay ov))) + +(defun magit-blame--make-highlight-overlay (chunk beg) + (let ((ov (make-overlay beg (save-excursion + (goto-char beg) + (1+ (line-end-position)))))) + (overlay-put ov 'magit-blame-chunk chunk) + (overlay-put ov 'magit-blame-highlight t) + (magit-blame--update-highlight-overlay ov))) + +(defun magit-blame--update-margin () + (setq left-margin-width (or (magit-blame--style-get 'margin-width) 0)) + (set-window-buffer (selected-window) (current-buffer))) + +(defun magit-blame--update-overlays () + (save-restriction + (widen) + (dolist (ov (overlays-in (point-min) (point-max))) + (cond ((overlay-get ov 'magit-blame-heading) + (magit-blame--update-heading-overlay ov)) + ((overlay-get ov 'magit-blame-margin) + (magit-blame--update-margin-overlay ov)) + ((overlay-get ov 'magit-blame-highlight) + (magit-blame--update-highlight-overlay ov)))))) + +(defun magit-blame--update-margin-overlay (ov) + (overlay-put + ov 'before-string + (and (magit-blame--style-get 'margin-width) + (propertize + "o" 'display + (list (list 'margin 'left-margin) + (let ((line (overlay-get ov 'magit-blame-margin)) + (format (magit-blame--style-get 'margin-format)) + (face (magit-blame--style-get 'margin-face))) + (magit-blame--format-string + ov + (or (and (atom format) + format) + (nth line format) + (car (last format))) + (or (and (not (zerop line)) + (magit-blame--style-get 'margin-body-face)) + face + 'magit-blame-margin)))))))) + +(defun magit-blame--update-heading-overlay (ov) + (overlay-put + ov 'before-string + (--if-let (magit-blame--style-get 'heading-format) + (magit-blame--format-string ov it 'magit-blame-heading) + (and (magit-blame--style-get 'show-lines) + (or (not (magit-blame--style-get 'margin-format)) + (save-excursion + (goto-char (overlay-start ov)) + ;; Special case of the special case described in + ;; `magit-blame--make-margin-overlay'. For empty + ;; lines it is not possible to show both overlays + ;; without the line being to high. + (not (= (point) (line-end-position))))) + magit-blame-separator)))) + +(defun magit-blame--update-highlight-overlay (ov) + (overlay-put ov 'font-lock-face (magit-blame--style-get 'highlight-face))) + +(defun magit-blame--format-string (ov format face) + (let* ((chunk (overlay-get ov 'magit-blame-chunk)) + (revinfo (overlay-get ov 'magit-blame-revinfo)) + (key (list format face)) + (string (cdr (assoc key revinfo)))) + (unless string + (setq string + (and format + (magit-blame--format-string-1 (oref chunk orig-rev) + revinfo format face))) + (nconc revinfo (list (cons key string)))) + string)) + +(defun magit-blame--format-string-1 (rev revinfo format face) + (let ((str + (if (string-match-p "\\`0\\{40,\\}\\'" rev) + (propertize (concat (if (string-prefix-p "\s" format) "\s" "") + "Not Yet Committed" + (if (string-suffix-p "\n" format) "\n" "")) + 'font-lock-face face) + (magit--format-spec + (propertize format 'font-lock-face face) + (cl-flet* ((p0 (s f) + (propertize s 'font-lock-face + (if face + (if (listp face) + face + (list f face)) + f))) + (p1 (k f) + (p0 (cdr (assoc k revinfo)) f)) + (p2 (k1 k2 f) + (p0 (magit-blame--format-time-string + (cdr (assoc k1 revinfo)) + (cdr (assoc k2 revinfo))) + f))) + `((?H . ,(p0 rev 'magit-blame-hash)) + (?s . ,(p1 "summary" 'magit-blame-summary)) + (?a . ,(p1 "author" 'magit-blame-name)) + (?c . ,(p1 "committer" 'magit-blame-name)) + (?A . ,(p2 "author-time" "author-tz" 'magit-blame-date)) + (?C . ,(p2 "committer-time" "committer-tz" 'magit-blame-date)) + (?f . ""))))))) + (if-let ((width (and (string-suffix-p "%f" format) + (magit-blame--style-get 'margin-width)))) + (concat str + (propertize (make-string (max 0 (- width (length str))) ?\s) + 'font-lock-face face)) + str))) + +(defun magit-blame--format-separator () + (propertize + (concat (propertize "\s" 'display '(space :height (2))) + (propertize "\n" 'line-height t)) + 'font-lock-face `(:background + ,(face-attribute 'magit-blame-heading + :background nil t) + ,@(and (>= emacs-major-version 27) '(:extend t))))) + +(defun magit-blame--format-time-string (time tz) + (let* ((time-format (or (magit-blame--style-get 'time-format) + magit-blame-time-format)) + (tz-in-second (and (string-search "%z" time-format) + (car (last (parse-time-string tz)))))) + (format-time-string time-format + (seconds-to-time (string-to-number time)) + tz-in-second))) + +(defun magit-blame--remove-overlays (&optional beg end) + (save-restriction + (widen) + (dolist (ov (overlays-in (or beg (point-min)) + (or end (point-max)))) + (when (overlay-get ov 'magit-blame-chunk) + (delete-overlay ov))))) + +(defun magit-blame-maybe-show-message () + (when (magit-blame--style-get 'show-message) + (let ((message-log-max 0)) + (if-let ((msg (cdr (assoc "summary" + (gethash (oref (magit-current-blame-chunk) + orig-rev) + magit-blame-cache))))) + (progn (set-text-properties 0 (length msg) nil msg) + (message msg)) + (message "Commit data not available yet. Still blaming."))))) + +;;; Commands + +;;;###autoload (autoload 'magit-blame-echo "magit-blame" nil t) +(transient-define-suffix magit-blame-echo (args) + "For each line show the revision in which it was added. +Show the information about the chunk at point in the echo area +when moving between chunks. Unlike other blaming commands, do +not turn on `read-only-mode'." + :if (lambda () + (and buffer-file-name + (or (not magit-blame-mode) + buffer-read-only))) + (interactive (list (magit-blame-arguments))) + (when magit-buffer-file-name + (user-error "Blob buffers aren't supported")) + (setq-local magit-blame--style + (assq magit-blame-echo-style magit-blame-styles)) + (setq-local magit-blame-disable-modes + (cons 'eldoc-mode magit-blame-disable-modes)) + (if (not magit-blame-mode) + (let ((magit-blame-read-only nil)) + (magit-blame--pre-blame-assert 'addition) + (magit-blame--pre-blame-setup 'addition) + (magit-blame--run args)) + (read-only-mode -1) + (magit-blame--update-overlays))) + +;;;###autoload (autoload 'magit-blame-addition "magit-blame" nil t) +(transient-define-suffix magit-blame-addition (args) + "For each line show the revision in which it was added." + (interactive (list (magit-blame-arguments))) + (magit-blame--pre-blame-assert 'addition) + (magit-blame--pre-blame-setup 'addition) + (magit-blame--run args)) + +;;;###autoload (autoload 'magit-blame-removal "magit-blame" nil t) +(transient-define-suffix magit-blame-removal (args) + "For each line show the revision in which it was removed." + :if-nil 'buffer-file-name + (interactive (list (magit-blame-arguments))) + (unless magit-buffer-file-name + (user-error "Only blob buffers can be blamed in reverse")) + (magit-blame--pre-blame-assert 'removal) + (magit-blame--pre-blame-setup 'removal) + (magit-blame--run args)) + +;;;###autoload (autoload 'magit-blame-reverse "magit-blame" nil t) +(transient-define-suffix magit-blame-reverse (args) + "For each line show the last revision in which it still exists." + :if-nil 'buffer-file-name + (interactive (list (magit-blame-arguments))) + (unless magit-buffer-file-name + (user-error "Only blob buffers can be blamed in reverse")) + (magit-blame--pre-blame-assert 'final) + (magit-blame--pre-blame-setup 'final) + (magit-blame--run args)) + +(defun magit-blame--pre-blame-assert (type) + (unless (magit-toplevel) + (magit--not-inside-repository-error)) + (if (and magit-blame-mode + (eq type magit-blame-type)) + (if-let ((chunk (magit-current-blame-chunk))) + (unless (oref chunk prev-rev) + (user-error "Chunk has no further history")) + (user-error "Commit data not available yet. Still blaming.")) + (unless (magit-file-relative-name nil (not magit-buffer-file-name)) + (if buffer-file-name + (user-error "Buffer isn't visiting a tracked file") + (user-error "Buffer isn't visiting a file"))))) + +(defun magit-blame--pre-blame-setup (type) + (when magit-blame-mode + (if (eq type magit-blame-type) + (let ((style magit-blame--style)) + (magit-blame-visit-other-file) + (setq-local magit-blame--style style) + (setq-local magit-blame-recursive-p t) + ;; Set window-start for the benefit of quickstart. + (redisplay)) + (magit-blame--remove-overlays))) + (setq magit-blame-type type)) + +(defun magit-blame-visit-other-file () + "Visit another blob related to the current chunk." + (interactive) + (with-slots (prev-rev prev-file orig-line) + (magit-current-blame-chunk) + (unless prev-rev + (user-error "Chunk has no further history")) + (magit-with-toplevel + (magit-find-file prev-rev prev-file)) + ;; TODO Adjust line like magit-diff-visit-file. + (goto-char (point-min)) + (forward-line (1- orig-line)))) + +(defun magit-blame-visit-file () + "Visit the blob related to the current chunk." + (interactive) + (with-slots (orig-rev orig-file orig-line) + (magit-current-blame-chunk) + (magit-with-toplevel + (magit-find-file orig-rev orig-file)) + (goto-char (point-min)) + (forward-line (1- orig-line)))) + +(transient-define-suffix magit-blame-quit () + "Turn off Magit-Blame mode. +If the buffer was created during a recursive blame, +then also kill the buffer." + :if-non-nil 'magit-blame-mode + (interactive) + (magit-blame-mode -1) + (when magit-blame-recursive-p + (kill-buffer))) + +(defun magit-blame-next-chunk () + "Move to the next chunk." + (interactive) + (--if-let (next-single-char-property-change (point) 'magit-blame-chunk) + (goto-char it) + (user-error "No more chunks"))) + +(defun magit-blame-previous-chunk () + "Move to the previous chunk." + (interactive) + (--if-let (previous-single-char-property-change (point) 'magit-blame-chunk) + (goto-char it) + (user-error "No more chunks"))) + +(defun magit-blame-next-chunk-same-commit (&optional previous) + "Move to the next chunk from the same commit.\n\n(fn)" + (interactive) + (if-let ((rev (oref (magit-current-blame-chunk) orig-rev))) + (let ((pos (point)) ov) + (save-excursion + (while (and (not ov) + (not (= pos (if previous (point-min) (point-max)))) + (setq pos (funcall + (if previous + #'previous-single-char-property-change + #'next-single-char-property-change) + pos 'magit-blame-chunk))) + (--when-let (magit-blame--overlay-at pos) + (when (equal (oref (magit-blame-chunk-at pos) orig-rev) rev) + (setq ov it))))) + (if ov + (goto-char (overlay-start ov)) + (user-error "No more chunks from same commit"))) + (user-error "This chunk hasn't been blamed yet"))) + +(defun magit-blame-previous-chunk-same-commit () + "Move to the previous chunk from the same commit." + (interactive) + (magit-blame-next-chunk-same-commit #'previous-single-char-property-change)) + +(defun magit-blame-cycle-style () + "Change how blame information is visualized. +Cycle through the elements of option `magit-blame-styles'." + (interactive) + (setq magit-blame--style + (or (cadr (cl-member (car magit-blame--style) + magit-blame-styles :key #'car)) + (car magit-blame-styles))) + (magit-blame--update-margin) + (magit-blame--update-overlays)) + +(defun magit-blame-copy-hash () + "Save hash of the current chunk's commit to the kill ring. + +When the region is active, then save the region's content +instead of the hash, like `kill-ring-save' would." + (interactive) + (if (use-region-p) + (call-interactively #'copy-region-as-kill) + (kill-new (message "%s" (oref (magit-current-blame-chunk) orig-rev))))) + +;;; Popup + +;;;###autoload (autoload 'magit-blame "magit-blame" nil t) +(transient-define-prefix magit-blame () + "Show the commits that added or removed lines in the visited file." + :man-page "git-blame" + :value '("-w") + ["Arguments" + ("-w" "Ignore whitespace" "-w") + ("-r" "Do not treat root commits as boundaries" "--root") + ("-P" "Follow only first parent" "--first-parent") + (magit-blame:-M) + (magit-blame:-C)] + ["Actions" + ("b" "Show commits adding lines" magit-blame-addition) + ("r" "Show commits removing lines" magit-blame-removal) + ("f" "Show last commits that still have lines" magit-blame-reverse) + ("m" "Blame echo" magit-blame-echo) + ("q" "Quit blaming" magit-blame-quit)] + ["Refresh" + :if-non-nil magit-blame-mode + ("c" "Cycle style" magit-blame-cycle-style :transient t)]) + +(defun magit-blame-arguments () + (transient-args 'magit-blame)) + +(transient-define-argument magit-blame:-M () + :description "Detect lines moved or copied within a file" + :class 'transient-option + :argument "-M" + :allow-empty t + :reader #'transient-read-number-N+) + +(transient-define-argument magit-blame:-C () + :description "Detect lines moved or copied between files" + :class 'transient-option + :argument "-C" + :allow-empty t + :reader #'transient-read-number-N+) + +;;; Utilities + +(defun magit-blame-maybe-update-revision-buffer () + (when-let* ((chunk (magit-current-blame-chunk)) + (commit (oref chunk orig-rev)) + (buffer (magit-get-mode-buffer 'magit-revision-mode nil t))) + (if magit--update-revision-buffer + (setq magit--update-revision-buffer (list commit buffer)) + (setq magit--update-revision-buffer (list commit buffer)) + (run-with-idle-timer + magit-update-other-window-delay nil + (lambda () + (pcase-let ((`(,rev ,buf) magit--update-revision-buffer)) + (setq magit--update-revision-buffer nil) + (when (buffer-live-p buf) + (let ((magit-display-buffer-noselect t)) + (apply #'magit-show-commit rev + (magit-diff-arguments 'magit-revision-mode)))))))))) + +;;; _ +(provide 'magit-blame) +;;; magit-blame.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-bookmark.el b/code/elpa/magit-20220821.1819/magit-bookmark.el new file mode 100644 index 0000000..c8780e9 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-bookmark.el @@ -0,0 +1,131 @@ +;;; magit-bookmark.el --- Bookmark support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Inspired by an earlier implementation by Yuri Khan. + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; Support for bookmarks for most Magit buffers. + +;;; Code: + +(require 'magit) + +;;; Diff +;;;; Diff + +(put 'magit-diff-mode 'magit-bookmark-variables + '(magit-buffer-range-hashed + magit-buffer-typearg + magit-buffer-diff-args + magit-buffer-diff-files)) + +(cl-defmethod magit-bookmark-name (&context (major-mode magit-diff-mode)) + (format "magit-diff(%s%s)" + (pcase (magit-diff-type) + ('staged "staged") + ('unstaged "unstaged") + ('committed magit-buffer-range) + ('undefined + (delq nil (list magit-buffer-typearg magit-buffer-range-hashed)))) + (if magit-buffer-diff-files + (concat " -- " (mapconcat #'identity magit-buffer-diff-files " ")) + ""))) + +;;;; Revision + +(put 'magit-revision-mode 'magit-bookmark-variables + '(magit-buffer-revision-hash + magit-buffer-diff-args + magit-buffer-diff-files)) + +(cl-defmethod magit-bookmark-name (&context (major-mode magit-revision-mode)) + (format "magit-revision(%s %s)" + (magit-rev-abbrev magit-buffer-revision) + (if magit-buffer-diff-files + (mapconcat #'identity magit-buffer-diff-files " ") + (magit-rev-format "%s" magit-buffer-revision)))) + +;;;; Stash + +(put 'magit-stash-mode 'magit-bookmark-variables + '(magit-buffer-revision-hash + magit-buffer-diff-args + magit-buffer-diff-files)) + +(cl-defmethod magit-bookmark-name (&context (major-mode magit-stash-mode)) + (format "magit-stash(%s %s)" + (magit-rev-abbrev magit-buffer-revision) + (if magit-buffer-diff-files + (mapconcat #'identity magit-buffer-diff-files " ") + (magit-rev-format "%s" magit-buffer-revision)))) + +;;; Log +;;;; Log + +(put 'magit-log-mode 'magit-bookmark-variables + '(magit-buffer-revisions + magit-buffer-log-args + magit-buffer-log-files)) + +(cl-defmethod magit-bookmark-name (&context (major-mode magit-log-mode)) + (format "magit-log(%s%s)" + (mapconcat #'identity magit-buffer-revisions " ") + (if magit-buffer-log-files + (concat " -- " (mapconcat #'identity magit-buffer-log-files " ")) + ""))) + +;;;; Cherry + +(put 'magit-cherry-mode 'magit-bookmark-variables + '(magit-buffer-refname + magit-buffer-upstream)) + +(cl-defmethod magit-bookmark-name (&context (major-mode magit-cherry-mode)) + (format "magit-cherry(%s > %s)" + magit-buffer-refname + magit-buffer-upstream)) + +;;;; Reflog + +(put 'magit-reflog-mode 'magit-bookmark-variables + '(magit-buffer-refname)) + +(cl-defmethod magit-bookmark-name (&context (major-mode magit-reflog-mode)) + (format "magit-reflog(%s)" magit-buffer-refname)) + +;;; Misc + +(put 'magit-status-mode 'magit-bookmark-variables nil) + +(put 'magit-refs-mode 'magit-bookmark-variables + '(magit-buffer-upstream + magit-buffer-arguments)) + +(put 'magit-stashes-mode 'magit-bookmark-variables nil) + +(cl-defmethod magit-bookmark-name (&context (major-mode magit-stashes-mode)) + (format "magit-states(%s)" magit-buffer-refname)) + +;;; _ +(provide 'magit-bookmark) +;;; magit-bookmark.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-branch.el b/code/elpa/magit-20220821.1819/magit-branch.el new file mode 100644 index 0000000..724d4c5 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-branch.el @@ -0,0 +1,934 @@ +;;; magit-branch.el --- Branch support -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for branches. It defines commands +;; for creating, checking out, manipulating, and configuring branches. +;; Commands defined here are mainly concerned with branches as +;; pointers, commands that deal with what a branch points at, are +;; defined elsewhere. + +;;; Code: + +(require 'magit) +(require 'magit-reset) + +;;; Options + +(defcustom magit-branch-read-upstream-first t + "Whether to read upstream before name of new branch when creating a branch. + +`nil' Read the branch name first. +`t' Read the upstream first. +`fallback' Read the upstream first, but if it turns out that the chosen + value is not a valid upstream (because it cannot be resolved + as an existing revision), then treat it as the name of the + new branch and continue by reading the upstream next." + :package-version '(magit . "2.2.0") + :group 'magit-commands + :type '(choice (const :tag "read branch name first" nil) + (const :tag "read upstream first" t) + (const :tag "read upstream first, with fallback" fallback))) + +(defcustom magit-branch-prefer-remote-upstream nil + "Whether to favor remote upstreams when creating new branches. + +When a new branch is created, then the branch, commit, or stash +at point is suggested as the default starting point of the new +branch, or if there is no such revision at point the current +branch. In either case the user may choose another starting +point. + +If the chosen starting point is a branch, then it may also be set +as the upstream of the new branch, depending on the value of the +Git variable `branch.autoSetupMerge'. By default this is done +for remote branches, but not for local branches. + +You might prefer to always use some remote branch as upstream. +If the chosen starting point is (1) a local branch, (2) whose +name matches a member of the value of this option, (3) the +upstream of that local branch is a remote branch with the same +name, and (4) that remote branch can be fast-forwarded to the +local branch, then the chosen branch is used as starting point, +but its own upstream is used as the upstream of the new branch. + +Members of this option's value are treated as branch names that +have to match exactly unless they contain a character that makes +them invalid as a branch name. Recommended characters to use +to trigger interpretation as a regexp are \"*\" and \"^\". Some +other characters which you might expect to be invalid, actually +are not, e.g. \".+$\" are all perfectly valid. More precisely, +if `git check-ref-format --branch STRING' exits with a non-zero +status, then treat STRING as a regexp. + +Assuming the chosen branch matches these conditions you would end +up with with e.g.: + + feature --upstream--> origin/master + +instead of + + feature --upstream--> master --upstream--> origin/master + +Which you prefer is a matter of personal preference. If you do +prefer the former, then you should add branches such as \"master\", +\"next\", and \"maint\" to the value of this options." + :package-version '(magit . "2.4.0") + :group 'magit-commands + :type '(repeat string)) + +(defcustom magit-branch-adjust-remote-upstream-alist nil + "Alist of upstreams to be used when branching from remote branches. + +When creating a local branch from an ephemeral branch located +on a remote, e.g. a feature or hotfix branch, then that remote +branch should usually not be used as the upstream branch, since +the push-remote already allows accessing it and having both the +upstream and the push-remote reference the same related branch +would be wasteful. Instead a branch like \"maint\" or \"master\" +should be used as the upstream. + +This option allows specifying the branch that should be used as +the upstream when branching certain remote branches. The value +is an alist of the form ((UPSTREAM . RULE)...). The first +element is used whose UPSTREAM exists and whose RULE matches +the name of the new branch. Subsequent elements are ignored. + +UPSTREAM is the branch to be used as the upstream for branches +specified by RULE. It can be a local or a remote branch. + +RULE can either be a regular expression, matching branches whose +upstream should be the one specified by UPSTREAM. Or it can be +a list of the only branches that should *not* use UPSTREAM; all +other branches will. Matching is done after stripping the remote +part of the name of the branch that is being branched from. + +If you use a finite set of non-ephemeral branches across all your +repositories, then you might use something like: + + ((\"origin/master\" . (\"master\" \"next\" \"maint\"))) + +Or if the names of all your ephemeral branches contain a slash, +at least in some repositories, then a good value could be: + + ((\"origin/master\" . \"/\")) + +Of course you can also fine-tune: + + ((\"origin/maint\" . \"\\\\\\=`hotfix/\") + (\"origin/master\" . \"\\\\\\=`feature/\")) + +UPSTREAM can be a local branch: + + ((\"master\" . (\"master\" \"next\" \"maint\"))) + +Because the main branch is no longer almost always named \"master\" +you should also account for other common names: + + ((\"main\" . (\"main\" \"master\" \"next\" \"maint\")) + (\"master\" . (\"main\" \"master\" \"next\" \"maint\"))) + +If you use remote branches as UPSTREAM, then you might also want +to set `magit-branch-prefer-remote-upstream' to a non-nil value. +However, I recommend that you use local branches as UPSTREAM." + :package-version '(magit . "2.9.0") + :group 'magit-commands + :type '(repeat (cons (string :tag "Use upstream") + (choice :tag "for branches" + (regexp :tag "matching") + (repeat :tag "except" + (string :tag "branch")))))) + +(defcustom magit-branch-rename-push-target t + "Whether the push-remote setup is preserved when renaming a branch. + +The command `magit-branch-rename' renames a branch named OLD to +NEW. This option controls how much of the push-remote setup is +preserved when doing so. + +When nil, then preserve nothing and unset `branch.OLD.pushRemote'. + +When `local-only', then first set `branch.NEW.pushRemote' to the + same value as `branch.OLD.pushRemote', provided the latter is + actually set and unless the former already has another value. + +When t, then rename the branch named OLD on the remote specified + by `branch.OLD.pushRemote' to NEW, provided OLD exists on that + remote and unless NEW already exists on the remote. + +When `forge-only' and the `forge' package is available, then + behave like `t' if the remote points to a repository on a forge + (currently Github or Gitlab), otherwise like `local-only'. + +Another supported but obsolete value is `github-only'. It is a + misnomer because it now treated as an alias for `forge-only'." + :package-version '(magit . "2.90.0") + :group 'magit-commands + :type '(choice + (const :tag "Don't preserve push-remote setup" nil) + (const :tag "Preserve push-remote setup" local-only) + (const :tag "... and rename corresponding branch on remote" t) + (const :tag "... but only if remote is on a forge" forge-only))) + +(defcustom magit-branch-direct-configure t + "Whether the command `magit-branch' shows Git variables. +When set to nil, no variables are displayed by this transient +command, instead the sub-transient `magit-branch-configure' +has to be used to view and change branch related variables." + :package-version '(magit . "2.7.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-published-branches '("origin/master") + "List of branches that are considered to be published." + :package-version '(magit . "2.13.0") + :group 'magit-commands + :type '(repeat string)) + +;;; Commands + +;;;###autoload (autoload 'magit-branch "magit" nil t) +(transient-define-prefix magit-branch (branch) + "Add, configure or remove a branch." + :man-page "git-branch" + ["Arguments" + (7 "-r" "Recurse submodules when checking out an existing branch" + "--recurse-submodules" + :if (lambda () (magit-git-version>= "2.13")))] + ["Variables" + :if (lambda () + (and magit-branch-direct-configure + (oref transient--prefix scope))) + ("d" magit-branch..description) + ("u" magit-branch..merge/remote) + ("r" magit-branch..rebase) + ("p" magit-branch..pushRemote)] + [["Checkout" + ("b" "branch/revision" magit-checkout) + ("l" "local branch" magit-branch-checkout) + (6 "o" "new orphan" magit-branch-orphan)] + ["" + ("c" "new branch" magit-branch-and-checkout) + ("s" "new spin-off" magit-branch-spinoff) + (5 "w" "new worktree" magit-worktree-checkout)] + ["Create" + ("n" "new branch" magit-branch-create) + ("S" "new spin-out" magit-branch-spinout) + (5 "W" "new worktree" magit-worktree-branch)] + ["Do" + ("C" "configure..." magit-branch-configure) + ("m" "rename" magit-branch-rename) + ("x" "reset" magit-branch-reset) + ("k" "delete" magit-branch-delete)] + ["" + (7 "h" "shelve" magit-branch-shelve) + (7 "H" "unshelve" magit-branch-unshelve)]] + (interactive (list (magit-get-current-branch))) + (transient-setup 'magit-branch nil nil :scope branch)) + +(defun magit-branch-arguments () + (transient-args 'magit-branch)) + +;;;###autoload +(defun magit-checkout (revision &optional args) + "Checkout REVISION, updating the index and the working tree. +If REVISION is a local branch, then that becomes the current +branch. If it is something else, then `HEAD' becomes detached. +Checkout fails if the working tree or the staging area contain +changes. +\n(git checkout REVISION)." + (interactive (list (magit-read-other-branch-or-commit "Checkout") + (magit-branch-arguments))) + (when (string-match "\\`heads/\\(.+\\)" revision) + (setq revision (match-string 1 revision))) + (magit-run-git "checkout" args revision)) + +;;;###autoload +(defun magit-branch-create (branch start-point) + "Create BRANCH at branch or revision START-POINT." + (interactive (magit-branch-read-args "Create branch")) + (magit-call-git "branch" branch start-point) + (magit-branch-maybe-adjust-upstream branch start-point) + (magit-refresh)) + +;;;###autoload +(defun magit-branch-and-checkout (branch start-point &optional args) + "Create and checkout BRANCH at branch or revision START-POINT." + (interactive (append (magit-branch-read-args "Create and checkout branch") + (list (magit-branch-arguments)))) + (if (string-match-p "^stash@{[0-9]+}$" start-point) + (magit-run-git "stash" "branch" branch start-point) + (magit-call-git "checkout" args "-b" branch start-point) + (magit-branch-maybe-adjust-upstream branch start-point) + (magit-refresh))) + +;;;###autoload +(defun magit-branch-or-checkout (arg &optional start-point) + "Hybrid between `magit-checkout' and `magit-branch-and-checkout'. + +Ask the user for an existing branch or revision. If the user +input actually can be resolved as a branch or revision, then +check that out, just like `magit-checkout' would. + +Otherwise create and checkout a new branch using the input as +its name. Before doing so read the starting-point for the new +branch. This is similar to what `magit-branch-and-checkout' +does." + (interactive + (let ((arg (magit-read-other-branch-or-commit "Checkout"))) + (list arg + (and (not (magit-commit-p arg)) + (magit-read-starting-point "Create and checkout branch" arg))))) + (when (string-match "\\`heads/\\(.+\\)" arg) + (setq arg (match-string 1 arg))) + (if start-point + (magit-branch-and-checkout arg start-point) + (magit-checkout arg))) + +;;;###autoload +(defun magit-branch-checkout (branch &optional start-point) + "Checkout an existing or new local branch. + +Read a branch name from the user offering all local branches and +a subset of remote branches as candidates. Omit remote branches +for which a local branch by the same name exists from the list +of candidates. The user can also enter a completely new branch +name. + +- If the user selects an existing local branch, then check that + out. + +- If the user selects a remote branch, then create and checkout + a new local branch with the same name. Configure the selected + remote branch as push target. + +- If the user enters a new branch name, then create and check + that out, after also reading the starting-point from the user. + +In the latter two cases the upstream is also set. Whether it is +set to the chosen START-POINT or something else depends on the +value of `magit-branch-adjust-remote-upstream-alist', just like +when using `magit-branch-and-checkout'." + (interactive + (let* ((current (magit-get-current-branch)) + (local (magit-list-local-branch-names)) + (remote (--filter (and (string-match "[^/]+/" it) + (not (member (substring it (match-end 0)) + (cons "HEAD" local)))) + (magit-list-remote-branch-names))) + (choices (nconc (delete current local) remote)) + (atpoint (magit-branch-at-point)) + (choice (magit-completing-read + "Checkout branch" choices + nil nil nil 'magit-revision-history + (or (car (member atpoint choices)) + (and atpoint + (car (member (and (string-match "[^/]+/" atpoint) + (substring atpoint (match-end 0))) + choices))))))) + (cond ((member choice remote) + (list (and (string-match "[^/]+/" choice) + (substring choice (match-end 0))) + choice)) + ((member choice local) + (list choice)) + (t + (list choice (magit-read-starting-point "Create" choice)))))) + (if (not start-point) + (magit-checkout branch (magit-branch-arguments)) + (when (magit-anything-modified-p t) + (user-error "Cannot checkout when there are uncommitted changes")) + (let ((magit-inhibit-refresh t)) + (magit-branch-and-checkout branch start-point)) + (when (magit-remote-branch-p start-point) + (pcase-let ((`(,remote . ,remote-branch) + (magit-split-branch-name start-point))) + (when (and (equal branch remote-branch) + (not (equal remote (magit-get "remote.pushDefault")))) + (magit-set remote "branch" branch "pushRemote")))) + (magit-refresh))) + +(defun magit-branch-maybe-adjust-upstream (branch start-point) + (--when-let + (or (and (magit-get-upstream-branch branch) + (magit-get-indirect-upstream-branch start-point)) + (and (magit-remote-branch-p start-point) + (let ((name (cdr (magit-split-branch-name start-point)))) + (-some (pcase-lambda (`(,upstream . ,rule)) + (and (magit-branch-p upstream) + (if (listp rule) + (not (member name rule)) + (string-match-p rule name)) + upstream)) + magit-branch-adjust-remote-upstream-alist)))) + (magit-call-git "branch" (concat "--set-upstream-to=" it) branch))) + +;;;###autoload +(defun magit-branch-orphan (branch start-point) + "Create and checkout an orphan BRANCH with contents from revision START-POINT." + (interactive (magit-branch-read-args "Create and checkout orphan branch")) + (magit-run-git "checkout" "--orphan" branch start-point)) + +(defun magit-branch-read-args (prompt &optional default-start) + (if magit-branch-read-upstream-first + (let ((choice (magit-read-starting-point prompt nil default-start))) + (if (magit-rev-verify choice) + (list (magit-read-string-ns + (if magit-completing-read--silent-default + (format "%s (starting at `%s')" prompt choice) + "Name for new branch") + (let ((def (mapconcat #'identity + (cdr (split-string choice "/")) + "/"))) + (and (member choice (magit-list-remote-branch-names)) + (not (member def (magit-list-local-branch-names))) + def))) + choice) + (if (eq magit-branch-read-upstream-first 'fallback) + (list choice + (magit-read-starting-point prompt choice default-start)) + (user-error "Not a valid starting-point: %s" choice)))) + (let ((branch (magit-read-string-ns (concat prompt " named")))) + (list branch (magit-read-starting-point prompt branch default-start))))) + +;;;###autoload +(defun magit-branch-spinout (branch &optional from) + "Create new branch from the unpushed commits. +Like `magit-branch-spinoff' but remain on the current branch. +If there are any uncommitted changes, then behave exactly like +`magit-branch-spinoff'." + (interactive (list (magit-read-string-ns "Spin out branch") + (car (last (magit-region-values 'commit))))) + (magit--branch-spinoff branch from nil)) + +;;;###autoload +(defun magit-branch-spinoff (branch &optional from) + "Create new branch from the unpushed commits. + +Create and checkout a new branch starting at and tracking the +current branch. That branch in turn is reset to the last commit +it shares with its upstream. If the current branch has no +upstream or no unpushed commits, then the new branch is created +anyway and the previously current branch is not touched. + +This is useful to create a feature branch after work has already +began on the old branch (likely but not necessarily \"master\"). + +If the current branch is a member of the value of option +`magit-branch-prefer-remote-upstream' (which see), then the +current branch will be used as the starting point as usual, but +the upstream of the starting-point may be used as the upstream +of the new branch, instead of the starting-point itself. + +If optional FROM is non-nil, then the source branch is reset +to `FROM~', instead of to the last commit it shares with its +upstream. Interactively, FROM is only ever non-nil, if the +region selects some commits, and among those commits, FROM is +the commit that is the fewest commits ahead of the source +branch. + +The commit at the other end of the selection actually does not +matter, all commits between FROM and `HEAD' are moved to the new +branch. If FROM is not reachable from `HEAD' or is reachable +from the source branch's upstream, then an error is raised." + (interactive (list (magit-read-string-ns "Spin off branch") + (car (last (magit-region-values 'commit))))) + (magit--branch-spinoff branch from t)) + +(defun magit--branch-spinoff (branch from checkout) + (when (magit-branch-p branch) + (user-error "Cannot spin off %s. It already exists" branch)) + (when (and (not checkout) + (magit-anything-modified-p)) + (message "Staying on HEAD due to uncommitted changes") + (setq checkout t)) + (if-let ((current (magit-get-current-branch))) + (let ((tracked (magit-get-upstream-branch current)) + base) + (when from + (unless (magit-rev-ancestor-p from current) + (user-error "Cannot spin off %s. %s is not reachable from %s" + branch from current)) + (when (and tracked + (magit-rev-ancestor-p from tracked)) + (user-error "Cannot spin off %s. %s is ancestor of upstream %s" + branch from tracked))) + (let ((magit-process-raise-error t)) + (if checkout + (magit-call-git "checkout" "-b" branch current) + (magit-call-git "branch" branch current))) + (--when-let (magit-get-indirect-upstream-branch current) + (magit-call-git "branch" "--set-upstream-to" it branch)) + (when (and tracked + (setq base + (if from + (concat from "^") + (magit-git-string "merge-base" current tracked))) + (not (magit-rev-eq base current))) + (if checkout + (magit-call-git "update-ref" "-m" + (format "reset: moving to %s" base) + (concat "refs/heads/" current) base) + (magit-call-git "reset" "--hard" base)))) + (if checkout + (magit-call-git "checkout" "-b" branch) + (magit-call-git "branch" branch))) + (magit-refresh)) + +;;;###autoload +(defun magit-branch-reset (branch to &optional set-upstream) + "Reset a branch to the tip of another branch or any other commit. + +When the branch being reset is the current branch, then do a +hard reset. If there are any uncommitted changes, then the user +has to confirm the reset because those changes would be lost. + +This is useful when you have started work on a feature branch but +realize it's all crap and want to start over. + +When resetting to another branch and a prefix argument is used, +then also set the target branch as the upstream of the branch +that is being reset." + (interactive + (let* ((atpoint (magit-local-branch-at-point)) + (branch (magit-read-local-branch "Reset branch" atpoint))) + (list branch + (magit-completing-read (format "Reset %s to" branch) + (delete branch (magit-list-branch-names)) + nil nil nil 'magit-revision-history + (or (and (not (equal branch atpoint)) atpoint) + (magit-get-upstream-branch branch))) + current-prefix-arg))) + (let ((magit-inhibit-refresh t)) + (if (equal branch (magit-get-current-branch)) + (if (and (magit-anything-modified-p) + (not (yes-or-no-p + "Uncommitted changes will be lost. Proceed? "))) + (user-error "Abort") + (magit-reset-hard to)) + (magit-call-git "update-ref" + "-m" (format "reset: moving to %s" to) + (magit-git-string "rev-parse" "--symbolic-full-name" + branch) + to)) + (when (and set-upstream (magit-branch-p to)) + (magit-set-upstream-branch branch to) + (magit-branch-maybe-adjust-upstream branch to))) + (magit-refresh)) + +(defvar magit-branch-delete-never-verify nil + "Whether `magit-branch-delete' always pushes with \"--no-verify\".") + +;;;###autoload +(defun magit-branch-delete (branches &optional force) + "Delete one or multiple branches. +If the region marks multiple branches, then offer to delete +those, otherwise prompt for a single branch to be deleted, +defaulting to the branch at point." + ;; One would expect this to be a command as simple as, for example, + ;; `magit-branch-rename'; but it turns out everyone wants to squeeze + ;; a bit of extra functionality into this one, including myself. + (interactive + (let ((branches (magit-region-values 'branch t)) + (force current-prefix-arg)) + (if (length> branches 1) + (magit-confirm t nil "Delete %i branches" nil branches) + (setq branches + (list (magit-read-branch-prefer-other + (if force "Force delete branch" "Delete branch"))))) + (unless force + (when-let ((unmerged (-remove #'magit-branch-merged-p branches))) + (if (magit-confirm 'delete-unmerged-branch + "Delete unmerged branch %s" + "Delete %i unmerged branches" + 'noabort unmerged) + (setq force branches) + (or (setq branches (-difference branches unmerged)) + (user-error "Abort"))))) + (list branches force))) + (let* ((refs (mapcar #'magit-ref-fullname branches)) + (ambiguous (--remove it refs))) + (when ambiguous + (user-error + "%s ambiguous. Please cleanup using git directly." + (let ((len (length ambiguous))) + (cond + ((= len 1) + (format "%s is" (-first #'magit-ref-ambiguous-p branches))) + ((= len (length refs)) + (format "These %s names are" len)) + (t + (format "%s of these names are" len)))))) + (cond + ((string-match "^refs/remotes/\\([^/]+\\)" (car refs)) + (let* ((remote (match-string 1 (car refs))) + (offset (1+ (length remote)))) + (cond + ((magit-confirm 'delete-branch-on-remote + "Delete %s on the remote (not just locally)" + "Delete %i branches on the remote (not just locally)" + 'noabort branches) + ;; The ref may actually point at another rev on the remote, + ;; but this is better than nothing. + (dolist (ref refs) + (message "Delete %s (was %s)" ref + (magit-rev-parse "--short" ref))) + ;; Assume the branches actually still exist on the remote. + (magit-run-git-async + "push" + (and (or force magit-branch-delete-never-verify) "--no-verify") + remote + (--map (concat ":" (substring it offset)) branches)) + ;; If that is not the case, then this deletes the tracking branches. + (set-process-sentinel + magit-this-process + (apply-partially #'magit-delete-remote-branch-sentinel remote refs))) + (t + (dolist (ref refs) + (message "Delete %s (was %s)" ref + (magit-rev-parse "--short" ref)) + (magit-call-git "update-ref" "-d" ref)) + (magit-refresh))))) + ((length> branches 1) + (setq branches (delete (magit-get-current-branch) branches)) + (mapc #'magit-branch-maybe-delete-pr-remote branches) + (mapc #'magit-branch-unset-pushRemote branches) + (magit-run-git "branch" (if force "-D" "-d") branches)) + (t ; And now for something completely different. + (let* ((branch (car branches)) + (prompt (format "Branch %s is checked out. " branch)) + (target (magit-get-upstream-branch))) + (when (equal branch (magit-get-current-branch)) + (when (or (equal branch target) + (not target)) + (setq target (magit-main-branch))) + (pcase (if (or (equal branch target) + (not target)) + (magit-read-char-case prompt nil + (?d "[d]etach HEAD & delete" 'detach) + (?a "[a]bort" 'abort)) + (magit-read-char-case prompt nil + (?d "[d]etach HEAD & delete" 'detach) + (?c (format "[c]heckout %s & delete" target) 'target) + (?a "[a]bort" 'abort))) + (`detach (unless (or (equal force '(4)) + (member branch force) + (magit-branch-merged-p branch t)) + (magit-confirm 'delete-unmerged-branch + "Delete unmerged branch %s" "" + nil (list branch))) + (magit-call-git "checkout" "--detach")) + (`target (unless (or (equal force '(4)) + (member branch force) + (magit-branch-merged-p branch target)) + (magit-confirm 'delete-unmerged-branch + "Delete unmerged branch %s" "" + nil (list branch))) + (magit-call-git "checkout" target)) + (`abort (user-error "Abort"))) + (setq force t)) + (magit-branch-maybe-delete-pr-remote branch) + (magit-branch-unset-pushRemote branch) + (magit-run-git "branch" (if force "-D" "-d") branch)))))) + +(put 'magit-branch-delete 'interactive-only t) + +(defun magit-branch-maybe-delete-pr-remote (branch) + (when-let ((remote (magit-get "branch" branch "pullRequestRemote"))) + (let* ((variable (format "remote.%s.fetch" remote)) + (refspecs (magit-get-all variable))) + (unless (member (format "+refs/heads/*:refs/remotes/%s/*" remote) + refspecs) + (let ((refspec + (if (equal (magit-get "branch" branch "pushRemote") remote) + (format "+refs/heads/%s:refs/remotes/%s/%s" + branch remote branch) + (let ((merge (magit-get "branch" branch "merge"))) + (and merge + (string-prefix-p "refs/heads/" merge) + (setq merge (substring merge 11)) + (format "+refs/heads/%s:refs/remotes/%s/%s" + merge remote merge)))))) + (when (member refspec refspecs) + (if (and (length= refspecs 1) + (magit-confirm 'delete-pr-remote + (format "Also delete remote %s (%s)" remote + "no pull-request branch remains") + nil t)) + (magit-call-git "remote" "rm" remote) + (magit-call-git "config" "--unset-all" variable + (format "^%s$" (regexp-quote refspec)))))))))) + +(defun magit-branch-unset-pushRemote (branch) + (magit-set nil "branch" branch "pushRemote")) + +(defun magit-delete-remote-branch-sentinel (remote refs process event) + (when (memq (process-status process) '(exit signal)) + (if (= (process-exit-status process) 1) + (if-let ((on-remote (--map (concat "refs/remotes/" remote "/" it) + (magit-remote-list-branches remote))) + (rest (--filter (and (not (member it on-remote)) + (magit-ref-exists-p it)) + refs))) + (progn + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (setq magit-this-error nil) + (message "Some remote branches no longer exist. %s" + "Deleting just the local tracking refs instead...") + (dolist (ref rest) + (magit-call-git "update-ref" "-d" ref)) + (magit-refresh) + (message "Deleting local remote-tracking refs...done")) + (magit-process-sentinel process event)) + (magit-process-sentinel process event)))) + +;;;###autoload +(defun magit-branch-rename (old new &optional force) + "Rename the branch named OLD to NEW. + +With a prefix argument FORCE, rename even if a branch named NEW +already exists. + +If `branch.OLD.pushRemote' is set, then unset it. Depending on +the value of `magit-branch-rename-push-target' (which see) maybe +set `branch.NEW.pushRemote' and maybe rename the push-target on +the remote." + (interactive + (let ((branch (magit-read-local-branch "Rename branch"))) + (list branch + (magit-read-string-ns (format "Rename branch '%s' to" branch) + nil 'magit-revision-history) + current-prefix-arg))) + (when (string-match "\\`heads/\\(.+\\)" old) + (setq old (match-string 1 old))) + (when (equal old new) + (user-error "Old and new branch names are the same")) + (magit-call-git "branch" (if force "-M" "-m") old new) + (when magit-branch-rename-push-target + (let ((remote (magit-get-push-remote old)) + (old-specified (magit-get "branch" old "pushRemote")) + (new-specified (magit-get "branch" new "pushRemote"))) + (when (and old-specified (or force (not new-specified))) + ;; Keep the target setting branch specified, even if that is + ;; redundant. But if a branch by the same name existed before + ;; and the rename isn't forced, then do not change a leftover + ;; setting. Such a leftover setting may or may not conform to + ;; what we expect here... + (magit-set old-specified "branch" new "pushRemote")) + (when (and (equal (magit-get-push-remote new) remote) + ;; ...and if it does not, then we must abort. + (not (eq magit-branch-rename-push-target 'local-only)) + (or (not (memq magit-branch-rename-push-target + '(forge-only github-only))) + (and (require (quote forge) nil t) + (fboundp 'forge--forge-remote-p) + (forge--forge-remote-p remote)))) + (let ((old-target (magit-get-push-branch old t)) + (new-target (magit-get-push-branch new t)) + (remote (magit-get-push-remote new))) + (when (and old-target + (not new-target) + (magit-y-or-n-p (format "Also rename %S to %S on \"%s\"" + old new remote))) + ;; Rename on (i.e. within) the remote, but only if the + ;; destination ref doesn't exist yet. If that ref already + ;; exists, then it probably is of some value and we better + ;; not touch it. Ignore what the local ref points at, + ;; i.e. if the local and the remote ref didn't point at + ;; the same commit before the rename then keep it that way. + (magit-call-git "push" "-v" remote + (format "%s:refs/heads/%s" old-target new) + (format ":refs/heads/%s" old))))))) + (magit-branch-unset-pushRemote old) + (magit-refresh)) + +;;;###autoload +(defun magit-branch-shelve (branch) + "Shelve a BRANCH. +Rename \"refs/heads/BRANCH\" to \"refs/shelved/BRANCH\", +and also rename the respective reflog file." + (interactive (list (magit-read-other-local-branch "Shelve branch"))) + (let ((old (concat "refs/heads/" branch)) + (new (concat "refs/shelved/" branch))) + (magit-git "update-ref" new old "") + (magit--rename-reflog-file old new) + (magit-branch-unset-pushRemote branch) + (magit-run-git "branch" "-D" branch))) + +;;;###autoload +(defun magit-branch-unshelve (branch) + "Unshelve a BRANCH +Rename \"refs/shelved/BRANCH\" to \"refs/heads/BRANCH\", +and also rename the respective reflog file." + (interactive + (list (magit-completing-read + "Unshelve branch" + (--map (substring it 8) + (magit-list-refnames "refs/shelved")) + nil t))) + (let ((old (concat "refs/shelved/" branch)) + (new (concat "refs/heads/" branch))) + (magit-git "update-ref" new old "") + (magit--rename-reflog-file old new) + (magit-run-git "update-ref" "-d" old))) + +(defun magit--rename-reflog-file (old new) + (let ((old (magit-git-dir (concat "logs/" old))) + (new (magit-git-dir (concat "logs/" new)))) + (when (file-exists-p old) + (make-directory (file-name-directory new) t) + (rename-file old new t)))) + +;;; Configure + +;;;###autoload (autoload 'magit-branch-configure "magit-branch" nil t) +(transient-define-prefix magit-branch-configure (branch) + "Configure a branch." + :man-page "git-branch" + [:description + (lambda () + (concat + (propertize "Configure " 'face 'transient-heading) + (propertize (oref transient--prefix scope) 'face 'magit-branch-local))) + ("d" magit-branch..description) + ("u" magit-branch..merge/remote) + ("r" magit-branch..rebase) + ("p" magit-branch..pushRemote)] + ["Configure repository defaults" + ("R" magit-pull.rebase) + ("P" magit-remote.pushDefault)] + ["Configure branch creation" + ("a m" magit-branch.autoSetupMerge) + ("a r" magit-branch.autoSetupRebase)] + (interactive + (list (or (and (not current-prefix-arg) + (not (and magit-branch-direct-configure + (eq transient-current-command 'magit-branch))) + (magit-get-current-branch)) + (magit--read-branch-scope)))) + (transient-setup 'magit-branch-configure nil nil :scope branch)) + +(defun magit--read-branch-scope (&optional obj) + (magit-read-local-branch + (if obj + (format "Set %s for branch" + (format (oref obj variable) "")) + "Configure branch"))) + +(transient-define-suffix magit-branch..description (branch) + "Edit the description of BRANCH." + :class 'magit--git-variable + :transient nil + :variable "branch.%s.description" + (interactive (list (oref transient-current-prefix scope))) + (magit-run-git-with-editor "branch" "--edit-description" branch)) + +(add-hook 'find-file-hook #'magit-branch-description-check-buffers) + +(defun magit-branch-description-check-buffers () + (and buffer-file-name + (string-match-p "/\\(BRANCH\\|EDIT\\)_DESCRIPTION\\'" buffer-file-name))) + +(defclass magit--git-branch:upstream (magit--git-variable) + ((format :initform " %k %m %M\n %r %R"))) + +(transient-define-infix magit-branch..merge/remote () + :class 'magit--git-branch:upstream) + +(cl-defmethod transient-init-value ((obj magit--git-branch:upstream)) + (when-let* ((branch (oref transient--prefix scope)) + (remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge"))) + (oset obj value (list remote merge)))) + +(cl-defmethod transient-infix-read ((obj magit--git-branch:upstream)) + (if (oref obj value) + (oset obj value nil) + (magit-read-upstream-branch (oref transient--prefix scope) "Upstream"))) + +(cl-defmethod transient-infix-set ((obj magit--git-branch:upstream) refname) + (magit-set-upstream-branch (oref transient--prefix scope) refname) + (oset obj value + (and-let* ((branch (oref transient--prefix scope)) + (r (magit-get "branch" branch "remote")) + (m (magit-get "branch" branch "merge"))) + (list r m))) + (magit-refresh)) + +(cl-defmethod transient-format ((obj magit--git-branch:upstream)) + (let ((branch (oref transient--prefix scope))) + (format-spec + (oref obj format) + `((?k . ,(transient-format-key obj)) + (?r . ,(format "branch.%s.remote" branch)) + (?m . ,(format "branch.%s.merge" branch)) + (?R . ,(transient-format-value obj #'car)) + (?M . ,(transient-format-value obj #'cadr)))))) + +(cl-defmethod transient-format-value ((obj magit--git-branch:upstream) key) + (if-let ((value (funcall key (oref obj value)))) + (propertize value 'face 'transient-argument) + (propertize "unset" 'face 'transient-inactive-argument))) + +(transient-define-infix magit-branch..rebase () + :class 'magit--git-variable:choices + :scope #'magit--read-branch-scope + :variable "branch.%s.rebase" + :fallback "pull.rebase" + :choices '("true" "false") + :default "false") + +(transient-define-infix magit-branch..pushRemote () + :class 'magit--git-variable:choices + :scope #'magit--read-branch-scope + :variable "branch.%s.pushRemote" + :fallback "remote.pushDefault" + :choices #'magit-list-remotes) + +(transient-define-infix magit-pull.rebase () + :class 'magit--git-variable:choices + :variable "pull.rebase" + :choices '("true" "false") + :default "false") + +(transient-define-infix magit-remote.pushDefault () + :class 'magit--git-variable:choices + :variable "remote.pushDefault" + :choices #'magit-list-remotes) + +(transient-define-infix magit-branch.autoSetupMerge () + :class 'magit--git-variable:choices + :variable "branch.autoSetupMerge" + :choices '("always" "true" "false") + :default "true") + +(transient-define-infix magit-branch.autoSetupRebase () + :class 'magit--git-variable:choices + :variable "branch.autoSetupRebase" + :choices '("always" "local" "remote" "never") + :default "never") + +;;; _ +(provide 'magit-branch) +;;; magit-branch.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-bundle.el b/code/elpa/magit-20220821.1819/magit-bundle.el new file mode 100644 index 0000000..0310ed8 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-bundle.el @@ -0,0 +1,132 @@ +;;; magit-bundle.el --- Bundle support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-bundle "magit-bundle" nil t) +(transient-define-prefix magit-bundle () + "Create or verify Git bundles." + :man-page "git-bundle" + ["Actions" + ("c" "create" magit-bundle-create) + ("v" "verify" magit-bundle-verify) + ("l" "list-heads" magit-bundle-list-heads)]) + +;;;###autoload (autoload 'magit-bundle-import "magit-bundle" nil t) +(transient-define-prefix magit-bundle-create (&optional file refs args) + "Create a bundle." + :man-page "git-bundle" + ["Arguments" + ("-a" "Include all refs" "--all") + ("-b" "Include branches" "--branches=" :allow-empty t) + ("-t" "Include tags" "--tags=" :allow-empty t) + ("-r" "Include remotes" "--remotes=" :allow-empty t) + ("-g" "Include refs" "--glob=") + ("-e" "Exclude refs" "--exclude=") + (magit-log:-n) + (magit-log:--since) + (magit-log:--until)] + ["Actions" + ("c" "create regular bundle" magit-bundle-create) + ("t" "create tracked bundle" magit-bundle-create-tracked) + ("u" "update tracked bundle" magit-bundle-update-tracked)] + (interactive + (and (eq transient-current-command 'magit-bundle-create) + (list (read-file-name "Create bundle: " nil nil nil + (concat (file-name-nondirectory + (directory-file-name (magit-toplevel))) + ".bundle")) + (magit-completing-read-multiple* "Refnames (zero or more): " + (magit-list-refnames)) + (transient-args 'magit-bundle-create)))) + (if file + (magit-git-bundle "create" file refs args) + (transient-setup 'magit-bundle-create))) + +;;;###autoload +(defun magit-bundle-create-tracked (file tag branch refs args) + "Create and track a new bundle." + (interactive + (let ((tag (magit-read-tag "Track bundle using tag")) + (branch (magit-read-branch "Bundle branch")) + (refs (magit-completing-read-multiple* + "Additional refnames (zero or more): " + (magit-list-refnames)))) + (list (read-file-name "File: " nil nil nil (concat tag ".bundle")) + tag branch + (if (equal branch (magit-get-current-branch)) + (cons "HEAD" refs) + refs) + (transient-args 'magit-bundle-create)))) + (magit-git-bundle "create" file (cons branch refs) args) + (magit-git "tag" "--force" tag branch + "-m" (concat ";; git-bundle tracking\n" + (pp-to-string `((file . ,file) + (branch . ,branch) + (refs . ,refs) + (args . ,args)))))) + +;;;###autoload +(defun magit-bundle-update-tracked (tag) + "Update a bundle that is being tracked using TAG." + (interactive (list (magit-read-tag "Update bundle tracked by tag" t))) + (let (msg) + (let-alist (magit--with-temp-process-buffer + (save-excursion + (magit-git-insert "for-each-ref" "--format=%(contents)" + (concat "refs/tags/" tag))) + (setq msg (buffer-string)) + (ignore-errors (read (current-buffer)))) + (unless (and .file .branch) + (error "Tag %s does not appear to track a bundle" tag)) + (magit-git-bundle "create" .file + (cons (concat tag ".." .branch) .refs) + .args) + (magit-git "tag" "--force" tag .branch "-m" msg)))) + +;;;###autoload +(defun magit-bundle-verify (file) + "Check whether FILE is valid and applies to the current repository." + (interactive (list (magit-bundle--read-file-name "Verify bundle: "))) + (magit-process-buffer) + (magit-git-bundle "verify" file)) + +;;;###autoload +(defun magit-bundle-list-heads (file) + "List the refs in FILE." + (interactive (list (magit-bundle--read-file-name "List heads of bundle: "))) + (magit-process-buffer) + (magit-git-bundle "list-heads" file)) + +(defun magit-bundle--read-file-name (prompt) + (read-file-name prompt nil nil t (magit-file-at-point) #'file-regular-p)) + +(defun magit-git-bundle (command file &optional refs args) + (magit-git "bundle" command (magit-convert-filename-for-git file) refs args)) + +;;; _ +(provide 'magit-bundle) +;;; magit-bundle.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-clone.el b/code/elpa/magit-20220821.1819/magit-clone.el new file mode 100644 index 0000000..e93344b --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-clone.el @@ -0,0 +1,340 @@ +;;; magit-clone.el --- Clone a repository -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements clone commands. + +;;; Code: + +(require 'magit) + +;;; Options + +(defcustom magit-clone-set-remote-head nil + "Whether cloning creates the symbolic-ref `/HEAD'." + :package-version '(magit . "2.4.2") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-clone-set-remote.pushDefault 'ask + "Whether to set the value of `remote.pushDefault' after cloning. + +If t, then set without asking. If nil, then don't set. If +`ask', then ask." + :package-version '(magit . "2.4.0") + :group 'magit-commands + :type '(choice (const :tag "set" t) + (const :tag "ask" ask) + (const :tag "don't set" nil))) + +(defcustom magit-clone-default-directory nil + "Default directory to use when `magit-clone' reads destination. +If nil (the default), then use the value of `default-directory'. +If a directory, then use that. If a function, then call that +with the remote url as only argument and use the returned value." + :package-version '(magit . "2.90.0") + :group 'magit-commands + :type '(choice (const :tag "value of default-directory") + (directory :tag "constant directory") + (function :tag "function's value"))) + +(defcustom magit-clone-always-transient nil + "Whether `magit-clone' always acts as a transient prefix command. +If nil, then a prefix argument has to be used to show the transient +popup instead of invoking the default suffix `magit-clone-regular' +directly." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-clone-name-alist + '(("\\`\\(?:github:\\|gh:\\)?\\([^:]+\\)\\'" "github.com" "github.user") + ("\\`\\(?:gitlab:\\|gl:\\)\\([^:]+\\)\\'" "gitlab.com" "gitlab.user")) + "Alist mapping repository names to repository urls. + +Each element has the form (REGEXP HOSTNAME USER). When the user +enters a name when a cloning command asks for a name or url, then +that is looked up in this list. The first element whose REGEXP +matches is used. + +The format specified by option `magit-clone-url-format' is used +to turn the name into an url, using HOSTNAME and the repository +name. If the provided name contains a slash, then that is used. +Otherwise if the name omits the owner of the repository, then the +default user specified in the matched entry is used. + +If USER contains a dot, then it is treated as a Git variable and +the value of that is used as the username. Otherwise it is used +as the username itself." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type '(repeat (list regexp + (string :tag "hostname") + (string :tag "user name or git variable")))) + +(defcustom magit-clone-url-format "git@%h:%n.git" + "Format(s) used when turning repository names into urls. +%h is the hostname and %n is the repository name, including the +name of the owner. The value can be a string (representing a +single static format) or an alist with elements (HOSTNAME +. FORMAT) mapping hostnames to formats. When an alist is used, +the nil key represents the default. Also see +`magit-clone-name-alist'." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type '(choice (string) + (alist :key-type string :value-type string))) + +;;; Commands + +;;;###autoload (autoload 'magit-clone "magit-clone" nil t) +(transient-define-prefix magit-clone (&optional transient) + "Clone a repository." + :man-page "git-clone" + ["Fetch arguments" + ("-B" "Clone a single branch" "--single-branch") + ("-n" "Do not clone tags" "--no-tags") + ("-S" "Clones submodules" "--recurse-submodules" :level 6) + ("-l" "Do not optimize" "--no-local" :level 7)] + ["Setup arguments" + ("-o" "Set name of remote" ("-o" "--origin=")) + ("-b" "Set HEAD branch" ("-b" "--branch=")) + (magit-clone:--filter + :if (lambda () (magit-git-version>= "2.17.0")) + :level 7) + ("-g" "Separate git directory" "--separate-git-dir=" + transient-read-directory :level 7) + ("-t" "Use template directory" "--template=" + transient-read-existing-directory :level 6)] + ["Local sharing arguments" + ("-s" "Share objects" ("-s" "--shared" :level 7)) + ("-h" "Do not use hardlinks" "--no-hardlinks")] + ["Clone" + ("C" "regular" magit-clone-regular) + ("s" "shallow" magit-clone-shallow) + ("d" "shallow since date" magit-clone-shallow-since :level 7) + ("e" "shallow excluding" magit-clone-shallow-exclude :level 7) + (">" "sparse checkout" magit-clone-sparse + :if (lambda () (magit-git-version>= "2.25.0")) + :level 6) + ("b" "bare" magit-clone-bare) + ("m" "mirror" magit-clone-mirror)] + (interactive (list (or magit-clone-always-transient current-prefix-arg))) + (if transient + (transient-setup 'magit-clone) + (call-interactively #'magit-clone-regular))) + +(transient-define-argument magit-clone:--filter () + :description "Filter some objects" + :class 'transient-option + :key "-f" + :argument "--filter=" + :reader #'magit-clone-read-filter) + +(defun magit-clone-read-filter (prompt initial-input history) + (magit-completing-read prompt + (list "blob:none" "tree:0") + nil nil initial-input history)) + +;;;###autoload +(defun magit-clone-regular (repository directory args) + "Create a clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository." + (interactive (magit-clone-read-args)) + (magit-clone-internal repository directory args)) + +;;;###autoload +(defun magit-clone-shallow (repository directory args depth) + "Create a shallow clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. +With a prefix argument read the DEPTH of the clone; +otherwise use 1." + (interactive (append (magit-clone-read-args) + (list (if current-prefix-arg + (read-number "Depth: " 1) + 1)))) + (magit-clone-internal repository directory + (cons (format "--depth=%s" depth) args))) + +;;;###autoload +(defun magit-clone-shallow-since (repository directory args date) + "Create a shallow clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. +Exclude commits before DATE, which is read from the +user." + (interactive (append (magit-clone-read-args) + (list (transient-read-date "Exclude commits before: " + nil nil)))) + (magit-clone-internal repository directory + (cons (format "--shallow-since=%s" date) args))) + +;;;###autoload +(defun magit-clone-shallow-exclude (repository directory args exclude) + "Create a shallow clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository. +Exclude commits reachable from EXCLUDE, which is a +branch or tag read from the user." + (interactive (append (magit-clone-read-args) + (list (read-string "Exclude commits reachable from: ")))) + (magit-clone-internal repository directory + (cons (format "--shallow-exclude=%s" exclude) args))) + +;;;###autoload +(defun magit-clone-bare (repository directory args) + "Create a bare clone of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository." + (interactive (magit-clone-read-args)) + (magit-clone-internal repository directory (cons "--bare" args))) + +;;;###autoload +(defun magit-clone-mirror (repository directory args) + "Create a mirror of REPOSITORY in DIRECTORY. +Then show the status buffer for the new repository." + (interactive (magit-clone-read-args)) + (magit-clone-internal repository directory (cons "--mirror" args))) + +;;;###autoload +(defun magit-clone-sparse (repository directory args) + "Clone REPOSITORY into DIRECTORY and create a sparse checkout." + (interactive (magit-clone-read-args)) + (magit-clone-internal repository directory (cons "--no-checkout" args) + 'sparse)) + +(defun magit-clone-internal (repository directory args &optional sparse) + (let* ((checkout (not (memq (car args) '("--bare" "--mirror")))) + (remote (or (transient-arg-value "--origin" args) + (magit-get "clone.defaultRemote") + "origin")) + (set-push-default + (and checkout + (or (eq magit-clone-set-remote.pushDefault t) + (and magit-clone-set-remote.pushDefault + (y-or-n-p (format "Set `remote.pushDefault' to %S? " + remote))))))) + (run-hooks 'magit-credential-hook) + (setq directory (file-name-as-directory (expand-file-name directory))) + (when (file-exists-p directory) + (if (file-directory-p directory) + (when (length> (directory-files directory) 2) + (let ((name (magit-clone--url-to-name repository))) + (unless (and name + (setq directory (file-name-as-directory + (expand-file-name name directory))) + (not (file-exists-p directory))) + (user-error "%s already exists" directory)))) + (user-error "%s already exists and is not a directory" directory))) + (magit-run-git-async "clone" args "--" repository + (magit-convert-filename-for-git directory)) + ;; Don't refresh the buffer we're calling from. + (process-put magit-this-process 'inhibit-refresh t) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (let ((magit-process-raise-error t)) + (magit-process-sentinel process event))) + (when (and (eq (process-status process) 'exit) + (= (process-exit-status process) 0)) + (when checkout + (let ((default-directory directory)) + (when set-push-default + (setf (magit-get "remote.pushDefault") remote)) + (unless magit-clone-set-remote-head + (magit-remote-unset-head remote)))) + (when (and sparse checkout) + (when (magit-git-version< "2.25.0") + (user-error + "`git sparse-checkout' not available until Git v2.25")) + (let ((default-directory directory)) + (magit-call-git "sparse-checkout" "init" "--cone") + (magit-call-git "checkout" (magit-get-current-branch)))) + (with-current-buffer (process-get process 'command-buf) + (magit-status-setup-buffer directory))))))) + +(defun magit-clone-read-args () + (let ((repo (magit-clone-read-repository))) + (list repo + (read-directory-name + "Clone to: " + (if (functionp magit-clone-default-directory) + (funcall magit-clone-default-directory repo) + magit-clone-default-directory) + nil nil + (magit-clone--url-to-name repo)) + (transient-args 'magit-clone)))) + +(defun magit-clone-read-repository () + (magit-read-char-case "Clone from " nil + (?u "[u]rl or name" + (let ((str (magit-read-string-ns "Clone from url or name"))) + (if (string-match-p "\\(://\\|@\\)" str) + str + (magit-clone--name-to-url str)))) + (?p "[p]ath" + (magit-convert-filename-for-git + (read-directory-name "Clone repository: "))) + (?l "[l]ocal url" + (concat "file://" + (magit-convert-filename-for-git + (read-directory-name "Clone repository: file://")))) + (?b "or [b]undle" + (magit-convert-filename-for-git + (read-file-name "Clone from bundle: "))))) + +(defun magit-clone--url-to-name (url) + (and (string-match "\\([^/:]+?\\)\\(/?\\.git\\)?$" url) + (match-string 1 url))) + +(defun magit-clone--name-to-url (name) + (or (seq-some + (pcase-lambda (`(,re ,host ,user)) + (and (string-match re name) + (let ((repo (match-string 1 name))) + (magit-clone--format-url host user repo)))) + magit-clone-name-alist) + (user-error "Not an url and no matching entry in `%s'" + 'magit-clone-name-alist))) + +(defun magit-clone--format-url (host user repo) + (if-let ((url-format + (cond ((listp magit-clone-url-format) + (cdr (or (assoc host magit-clone-url-format) + (assoc nil magit-clone-url-format)))) + ((stringp magit-clone-url-format) + magit-clone-url-format)))) + (format-spec + url-format + `((?h . ,host) + (?n . ,(if (string-search "/" repo) + repo + (if (string-search "." user) + (if-let ((user (magit-get user))) + (concat user "/" repo) + (user-error "Set %S or specify owner explicitly" user)) + (concat user "/" repo)))))) + (user-error + "Bogus `magit-clone-url-format' (bad type or missing default)"))) + +;;; _ +(provide 'magit-clone) +;;; magit-clone.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-commit.el b/code/elpa/magit-20220821.1819/magit-commit.el new file mode 100644 index 0000000..98f9764 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-commit.el @@ -0,0 +1,700 @@ +;;; magit-commit.el --- Create Git commits -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements commands for creating Git commits. These +;; commands just initiate the commit, support for writing the commit +;; messages is implemented in `git-commit.el'. + +;;; Code: + +(require 'magit) +(require 'magit-sequence) + +;;; Options + +(defcustom magit-commit-ask-to-stage 'verbose + "Whether to ask to stage everything when committing and nothing is staged." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type '(choice (const :tag "Ask" t) + (const :tag "Ask showing diff" verbose) + (const :tag "Stage without confirmation" stage) + (const :tag "Don't ask" nil))) + +(defcustom magit-commit-show-diff t + "Whether the relevant diff is automatically shown when committing." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-extend-override-date t + "Whether using `magit-commit-extend' changes the committer date." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-reword-override-date t + "Whether using `magit-commit-reword' changes the committer date." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-squash-confirm t + "Whether the commit targeted by squash and fixup has to be confirmed. +When non-nil then the commit at point (if any) is used as default +choice, otherwise it has to be confirmed. This option only +affects `magit-commit-squash' and `magit-commit-fixup'. The +\"instant\" variants always require confirmation because making +an error while using those is harder to recover from." + :package-version '(magit . "2.1.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-post-commit-hook nil + "Hook run after creating a commit without the user editing a message. + +This hook is run by `magit-refresh' if `this-command' is a member +of `magit-post-stage-hook-commands'. This only includes commands +named `magit-commit-*' that do *not* require that the user edits +the commit message in a buffer and then finishes by pressing +\\\\[with-editor-finish]. + +Also see `git-commit-post-finish-hook'." + :package-version '(magit . "2.90.0") + :group 'magit-commands + :type 'hook) + +(defcustom magit-commit-diff-inhibit-same-window nil + "Whether to inhibit use of same window when showing diff while committing. + +When writing a commit, then a diff of the changes to be committed +is automatically shown. The idea is that the diff is shown in a +different window of the same frame and for most users that just +works. In other words most users can completely ignore this +option because its value doesn't make a difference for them. + +However for users who configured Emacs to never create a new +window even when the package explicitly tries to do so, then +displaying two new buffers necessarily means that the first is +immediately replaced by the second. In our case the message +buffer is immediately replaced by the diff buffer, which is of +course highly undesirable. + +A workaround is to suppress this user configuration in this +particular case. Users have to explicitly opt-in by toggling +this option. We cannot enable the workaround unconditionally +because that again causes issues for other users: if the frame +is too tiny or the relevant settings too aggressive, then the +diff buffer would end up being displayed in a new frame. + +Also see https://github.com/magit/magit/issues/4132." + :package-version '(magit . "3.3.0") + :group 'magit-commands + :type 'boolean) + +;;; Popup + +;;;###autoload (autoload 'magit-commit "magit-commit" nil t) +(transient-define-prefix magit-commit () + "Create a new commit or replace an existing commit." + :info-manual "(magit)Initiating a Commit" + :man-page "git-commit" + ["Arguments" + ("-a" "Stage all modified and deleted files" ("-a" "--all")) + ("-e" "Allow empty commit" "--allow-empty") + ("-v" "Show diff of changes to be committed" ("-v" "--verbose")) + ("-n" "Disable hooks" ("-n" "--no-verify")) + ("-R" "Claim authorship and reset author date" "--reset-author") + (magit:--author :description "Override the author") + (7 "-D" "Override the author date" "--date=" transient-read-date) + ("-s" "Add Signed-off-by line" ("-s" "--signoff")) + (5 magit:--gpg-sign) + (magit-commit:--reuse-message)] + [["Create" + ("c" "Commit" magit-commit-create)] + ["Edit HEAD" + ("e" "Extend" magit-commit-extend) + ("w" "Reword" magit-commit-reword) + ("a" "Amend" magit-commit-amend) + (6 "n" "Reshelve" magit-commit-reshelve)] + ["Edit" + ("f" "Fixup" magit-commit-fixup) + ("s" "Squash" magit-commit-squash) + ("A" "Augment" magit-commit-augment) + (6 "x" "Absorb changes" magit-commit-autofixup) + (6 "X" "Absorb modules" magit-commit-absorb-modules)] + ["" + ("F" "Instant fixup" magit-commit-instant-fixup) + ("S" "Instant squash" magit-commit-instant-squash)]] + (interactive) + (if-let ((buffer (magit-commit-message-buffer))) + (switch-to-buffer buffer) + (transient-setup 'magit-commit))) + +(defun magit-commit-arguments nil + (transient-args 'magit-commit)) + +(transient-define-argument magit-commit:--reuse-message () + :description "Reuse commit message" + :class 'transient-option + :shortarg "-C" + :argument "--reuse-message=" + :reader #'magit-read-reuse-message + :history-key 'magit-revision-history) + +(defun magit-read-reuse-message (prompt &optional default history) + (magit-completing-read prompt (magit-list-refnames) + nil nil nil history + (or default + (and (magit-rev-verify "ORIG_HEAD") + "ORIG_HEAD")))) + +;;; Commands + +;;;###autoload +(defun magit-commit-create (&optional args) + "Create a new commit on `HEAD'. +With a prefix argument, amend to the commit at `HEAD' instead. +\n(git commit [--amend] ARGS)" + (interactive (if current-prefix-arg + (list (cons "--amend" (magit-commit-arguments))) + (list (magit-commit-arguments)))) + (when (member "--all" args) + (setq this-command 'magit-commit--all)) + (when (setq args (magit-commit-assert args)) + (let ((default-directory (magit-toplevel))) + (magit-run-git-with-editor "commit" args)))) + +;;;###autoload +(defun magit-commit-amend (&optional args) + "Amend the last commit. +\n(git commit --amend ARGS)" + (interactive (list (magit-commit-arguments))) + (magit-commit-amend-assert) + (magit-run-git-with-editor "commit" "--amend" args)) + +;;;###autoload +(defun magit-commit-extend (&optional args override-date) + "Amend the last commit, without editing the message. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-extend-override-date' can be used +to inverse the meaning of the prefix argument. \n(git commit +--amend --no-edit)" + (interactive (list (magit-commit-arguments) + (if current-prefix-arg + (not magit-commit-extend-override-date) + magit-commit-extend-override-date))) + (when (setq args (magit-commit-assert args)) + (magit-commit-amend-assert) + (if override-date + (magit-run-git-with-editor "commit" "--amend" "--no-edit" args) + (with-environment-variables + (("GIT_COMMITTER_DATE" (magit-rev-format "%cD"))) + (magit-run-git-with-editor "commit" "--amend" "--no-edit" args))))) + +;;;###autoload +(defun magit-commit-reword (&optional args override-date) + "Reword the last commit, ignoring staged changes. + +With a prefix argument keep the committer date, otherwise change +it. The option `magit-commit-reword-override-date' can be used +to inverse the meaning of the prefix argument. + +Non-interactively respect the optional OVERRIDE-DATE argument +and ignore the option. +\n(git commit --amend --only)" + (interactive (list (magit-commit-arguments) + (if current-prefix-arg + (not magit-commit-reword-override-date) + magit-commit-reword-override-date))) + (magit-commit-amend-assert) + (cl-pushnew "--allow-empty" args :test #'equal) + (if override-date + (magit-run-git-with-editor "commit" "--amend" "--only" args) + (with-environment-variables + (("GIT_COMMITTER_DATE" (magit-rev-format "%cD"))) + (magit-run-git-with-editor "commit" "--amend" "--only" args)))) + +;;;###autoload +(defun magit-commit-fixup (&optional commit args) + "Create a fixup commit. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup" commit args)) + +;;;###autoload +(defun magit-commit-squash (&optional commit args) + "Create a squash commit, without editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'. + +If you want to immediately add a message to the squash commit, +then use `magit-commit-augment' instead of this command." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash" commit args)) + +;;;###autoload +(defun magit-commit-augment (&optional commit args) + "Create a squash commit, editing the squash message. + +With a prefix argument the target COMMIT has to be confirmed. +Otherwise the commit at point may be used without confirmation +depending on the value of option `magit-commit-squash-confirm'." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash" commit args nil t)) + +;;;###autoload +(defun magit-commit-instant-fixup (&optional commit args) + "Create a fixup commit targeting COMMIT and instantly rebase." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup" commit args t)) + +;;;###autoload +(defun magit-commit-instant-squash (&optional commit args) + "Create a squash commit targeting COMMIT and instantly rebase." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash" commit args t)) + +(defun magit-commit-squash-internal + (option commit &optional args rebase edit confirmed) + (when-let ((args (magit-commit-assert args (not edit)))) + (when commit + (when (and rebase (not (magit-rev-ancestor-p commit "HEAD"))) + (magit-read-char-case + (format "%s isn't an ancestor of HEAD. " commit) nil + (?c "[c]reate without rebasing" (setq rebase nil)) + (?s "[s]elect other" (setq commit nil)) + (?a "[a]bort" (user-error "Quit"))))) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit + (or confirmed + (not (or rebase + current-prefix-arg + magit-commit-squash-confirm)))) + (let ((magit-commit-show-diff nil)) + (push (concat option "=" commit) args) + (unless edit + (push "--no-edit" args)) + (if rebase + (magit-with-editor + (magit-call-git + "commit" "--no-gpg-sign" + (-remove-first + (apply-partially #'string-prefix-p "--gpg-sign=") + args))) + (magit-run-git-with-editor "commit" args)) + t) ; The commit was created; used by below lambda. + (magit-log-select + (lambda (commit) + (when (and (magit-commit-squash-internal option commit args + rebase edit t) + rebase) + (magit-commit-amend-assert commit) + (magit-rebase-interactive-1 commit + (list "--autosquash" "--autostash" "--keep-empty") + "" "true" nil t))) + (format "Type %%p on a commit to %s into it," + (substring option 2)) + nil nil nil commit) + (when magit-commit-show-diff + (let ((magit-display-buffer-noselect t)) + (apply #'magit-diff-staged nil (magit-diff-arguments))))))) + +(defun magit-commit-amend-assert (&optional commit) + (--when-let (magit-list-publishing-branches commit) + (let ((m1 "This commit has already been published to ") + (m2 ".\nDo you really want to modify it")) + (magit-confirm 'amend-published + (concat m1 "%s" m2) + (concat m1 "%i public branches" m2) + nil it)))) + +(defun magit-commit-assert (args &optional strict) + (cond + ((or (magit-anything-staged-p) + (and (magit-anything-unstaged-p) + ;; ^ Everything of nothing is still nothing. + (member "--all" args)) + (and (not strict) + ;; ^ For amend variants that don't make sense otherwise. + (or (member "--amend" args) + (member "--allow-empty" args) + (member "--reset-author" args) + (member "--signoff" args) + (transient-arg-value "--author=" args) + (transient-arg-value "--date=" args)))) + (or args (list "--"))) + ((and (magit-rebase-in-progress-p) + (not (magit-anything-unstaged-p)) + (y-or-n-p "Nothing staged. Continue in-progress rebase? ")) + (setq this-command #'magit-rebase-continue) + (magit-run-git-sequencer "rebase" "--continue") + nil) + ((and (file-exists-p (magit-git-dir "MERGE_MSG")) + (not (magit-anything-unstaged-p))) + (or args (list "--"))) + ((not (magit-anything-unstaged-p)) + (user-error "Nothing staged (or unstaged)")) + (magit-commit-ask-to-stage + (when (eq magit-commit-ask-to-stage 'verbose) + (magit-diff-unstaged)) + (prog1 (when (or (eq magit-commit-ask-to-stage 'stage) + (y-or-n-p + "Nothing staged. Commit all uncommitted changes? ")) + (setq this-command 'magit-commit--all) + (cons "--all" (or args (list "--")))) + (when (and (eq magit-commit-ask-to-stage 'verbose) + (derived-mode-p 'magit-diff-mode)) + (magit-mode-bury-buffer)))) + (t + (user-error "Nothing staged")))) + +(defvar magit--reshelve-history nil) + +;;;###autoload +(defun magit-commit-reshelve (date update-author &optional args) + "Change the committer date and possibly the author date of `HEAD'. + +The current time is used as the initial minibuffer input and the +original author or committer date is available as the previous +history element. + +Both the author and the committer dates are changes, unless one +of the following is true, in which case only the committer date +is updated: +- You are not the author of the commit that is being reshelved. +- The command was invoked with a prefix argument. +- Non-interactively if UPDATE-AUTHOR is nil." + (interactive + (let ((update-author (and (magit-rev-author-p "HEAD") + (not current-prefix-arg)))) + (push (magit-rev-format (if update-author "%ad" "%cd") "HEAD" + (concat "--date=format:%F %T %z")) + magit--reshelve-history) + (list (read-string (if update-author + "Change author and committer dates to: " + "Change committer date to: ") + (cons (format-time-string "%F %T %z") 17) + 'magit--reshelve-history) + update-author + (magit-commit-arguments)))) + (with-environment-variables (("GIT_COMMITTER_DATE" date)) + (magit-run-git "commit" "--amend" "--no-edit" + (and update-author (concat "--date=" date)) + args))) + +;;;###autoload +(defun magit-commit-absorb-modules (phase commit) + "Spread modified modules across recent commits." + (interactive (list 'select (magit-get-upstream-branch))) + (let ((modules (magit-list-modified-modules))) + (unless modules + (user-error "There are no modified modules that could be absorbed")) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit (eq phase 'run)) + (progn + (dolist (module modules) + (when-let ((msg (magit-git-string + "log" "-1" "--format=%s" + (concat commit "..") "--" module))) + (magit-git "commit" "-m" (concat "fixup! " msg) + "--only" "--" module))) + (magit-refresh) + t) + (magit-log-select + (lambda (commit) + (magit-commit-absorb-modules 'run commit)) + nil nil nil nil commit)))) + +;;;###autoload (autoload 'magit-commit-absorb "magit-commit" nil t) +(transient-define-prefix magit-commit-absorb (phase commit args) + "Spread staged changes across recent commits. +With a prefix argument use a transient command to select infix +arguments. This command requires git-absorb executable, which +is available from https://github.com/tummychow/git-absorb. +See `magit-commit-autofixup' for an alternative implementation." + ["Arguments" + ("-f" "Skip safety checks" ("-f" "--force")) + ("-v" "Display more output" ("-v" "--verbose"))] + ["Actions" + ("x" "Absorb" magit-commit-absorb)] + (interactive (if current-prefix-arg + (list 'transient nil nil) + (list 'select + (magit-get-upstream-branch) + (transient-args 'magit-commit-absorb)))) + (if (eq phase 'transient) + (transient-setup 'magit-commit-absorb) + (unless (compat-executable-find "git-absorb" t) + (user-error "This command requires the git-absorb executable, which %s" + "is available from https://github.com/tummychow/git-absorb")) + (unless (magit-anything-staged-p) + (if (magit-anything-unstaged-p) + (if (y-or-n-p "Nothing staged. Absorb all unstaged changes? ") + (magit-with-toplevel + (magit-run-git "add" "-u" ".")) + (user-error "Abort")) + (user-error "There are no changes that could be absorbed"))) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit (eq phase 'run)) + (progn (magit-run-git-async "absorb" "-v" args "-b" commit) t) + (magit-log-select + (lambda (commit) + (with-no-warnings ; about non-interactive use + (magit-commit-absorb 'run commit args))) + nil nil nil nil commit)))) + +;;;###autoload (autoload 'magit-commit-autofixup "magit-commit" nil t) +(transient-define-prefix magit-commit-autofixup (phase commit args) + "Spread staged or unstaged changes across recent commits. + +If there are any staged then spread only those, otherwise +spread all unstaged changes. With a prefix argument use a +transient command to select infix arguments. + +This command requires the git-autofixup script, which is +available from https://github.com/torbiak/git-autofixup. +See `magit-commit-absorb' for an alternative implementation." + ["Arguments" + (magit-autofixup:--context) + (magit-autofixup:--strict)] + ["Actions" + ("x" "Absorb" magit-commit-autofixup)] + (interactive (if current-prefix-arg + (list 'transient nil nil) + (list 'select + (magit-get-upstream-branch) + (transient-args 'magit-commit-autofixup)))) + (if (eq phase 'transient) + (transient-setup 'magit-commit-autofixup) + (unless (compat-executable-find "git-autofixup" t) + (user-error "This command requires the git-autofixup script, which %s" + "is available from https://github.com/torbiak/git-autofixup")) + (unless (magit-anything-modified-p) + (user-error "There are no changes that could be absorbed")) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit (eq phase 'run)) + (progn (magit-run-git-async "autofixup" "-vv" args commit) t) + (magit-log-select + (lambda (commit) + (with-no-warnings ; about non-interactive use + (magit-commit-autofixup 'run commit args))) + nil nil nil nil commit)))) + +(transient-define-argument magit-autofixup:--context () + :description "Diff context lines" + :class 'transient-option + :shortarg "-c" + :argument "--context=" + :reader #'transient-read-number-N0) + +(transient-define-argument magit-autofixup:--strict () + :description "Strictness" + :class 'transient-option + :shortarg "-s" + :argument "--strict=" + :reader #'transient-read-number-N0) + +(defvar magit-post-commit-hook-commands + '(magit-commit-extend + magit-commit-fixup + magit-commit-augment + magit-commit-instant-fixup + magit-commit-instant-squash)) + +(defun magit-run-post-commit-hook () + (when (and (not this-command) + (memq last-command magit-post-commit-hook-commands)) + (run-hooks 'magit-post-commit-hook))) + +;;; Pending Diff + +(defun magit-commit-diff () + (magit-repository-local-set 'this-commit-command + (if (eq this-command 'with-editor-finish) + 'magit-commit--rebase + last-command)) + (when (and git-commit-mode magit-commit-show-diff) + (when-let ((diff-buffer (magit-get-mode-buffer 'magit-diff-mode))) + ;; This window just started displaying the commit message + ;; buffer. Without this that buffer would immediately be + ;; replaced with the diff buffer. See #2632. + (unrecord-window-buffer nil diff-buffer)) + (message "Diffing changes to be committed (C-g to abort diffing)") + (let ((inhibit-quit nil)) + (condition-case nil + (magit-commit-diff-1) + (quit))))) + +(defun magit-commit-diff-1 () + (let ((rev nil) + (arg "--cached") + (command (magit-repository-local-get 'this-commit-command)) + (staged (magit-anything-staged-p)) + (unstaged + ;; Escape $GIT_DIR because `magit-anything-unstaged-p' + ;; requires a working tree. + (magit-with-toplevel + (magit-anything-unstaged-p))) + (squash (let ((f (magit-git-dir "rebase-merge/rewritten-pending"))) + (and (file-exists-p f) (length (magit-file-lines f))))) + (noalt nil)) + (pcase (list staged unstaged command) + ((and `(,_ ,_ magit-commit--rebase) + (guard (integerp squash))) + (setq rev (format "HEAD~%s" squash))) + (`(,_ ,_ magit-commit-amend) + (setq rev "HEAD^")) + ((or `(,_ ,_ magit-commit-reword) + `(nil nil ,_)) + (setq rev "HEAD^..HEAD") + (setq arg nil)) + (`(,_ t magit-commit--all) + (setq rev "HEAD") + (setq arg nil)) + (`(nil t handle-switch-frame) + ;; Either --all or --allow-empty. Assume it is the former. + (setq rev "HEAD") + (setq arg nil))) + (cond + ((not + (and (eq this-command 'magit-diff-while-committing) + (and-let* ((buf (magit-get-mode-buffer + 'magit-diff-mode nil 'selected))) + (and (equal rev (buffer-local-value 'magit-buffer-range buf)) + (equal arg (buffer-local-value 'magit-buffer-typearg buf))))))) + ((eq command 'magit-commit-amend) + (setq rev nil)) + ((or squash (file-exists-p (magit-git-dir "rebase-merge/amend"))) + (setq rev "HEAD^")) + (t + (message "No alternative diff while committing") + (setq noalt t))) + (unless noalt + (let ((magit-inhibit-save-previous-winconf 'unset) + (magit-display-buffer-noselect t) + (display-buffer-overriding-action + display-buffer-overriding-action)) + (when magit-commit-diff-inhibit-same-window + (setq display-buffer-overriding-action + '(nil (inhibit-same-window t)))) + (magit-diff-setup-buffer rev arg (car (magit-diff-arguments)) nil))))) + +(add-hook 'server-switch-hook #'magit-commit-diff) +(add-hook 'with-editor-filter-visit-hook #'magit-commit-diff) + +(add-to-list 'with-editor-server-window-alist + (cons git-commit-filename-regexp #'switch-to-buffer)) + +(defun magit-commit--reset-command () + (magit-repository-local-delete 'this-commit-command)) + +;;; Message Utilities + +(defun magit-commit-message-buffer () + (let* ((find-file-visit-truename t) ; git uses truename of COMMIT_EDITMSG + (topdir (magit-toplevel))) + (--first (equal topdir (with-current-buffer it + (and git-commit-mode (magit-toplevel)))) + (append (buffer-list (selected-frame)) + (buffer-list))))) + +(defvar magit-commit-add-log-insert-function #'magit-commit-add-log-insert + "Used by `magit-commit-add-log' to insert a single entry.") + +(defun magit-commit-add-log () + "Add a stub for the current change into the commit message buffer. +If no commit is in progress, then initiate it. Use the function +specified by variable `magit-commit-add-log-insert-function' to +actually insert the entry." + (interactive) + (pcase-let* ((hunk (and (magit-section-match 'hunk) + (magit-current-section))) + (log (magit-commit-message-buffer)) + (`(,buf ,pos) (magit-diff-visit-file--noselect))) + (unless log + (unless (magit-commit-assert nil) + (user-error "Abort")) + (magit-commit-create) + (while (not (setq log (magit-commit-message-buffer))) + (sit-for 0.01))) + (magit--with-temp-position buf pos + (funcall magit-commit-add-log-insert-function log + (magit-file-relative-name) + (and hunk (add-log-current-defun)))))) + +(defun magit-commit-add-log-insert (buffer file defun) + (with-current-buffer buffer + (undo-boundary) + (goto-char (point-max)) + (while (re-search-backward (concat "^" comment-start) nil t)) + (save-restriction + (narrow-to-region (point-min) (point)) + (cond ((re-search-backward (format "* %s\\(?: (\\([^)]+\\))\\)?: " file) + nil t) + (when (equal (match-string 1) defun) + (setq defun nil)) + (re-search-forward ": ")) + (t + (when (re-search-backward "^[\\*(].+\n" nil t) + (goto-char (match-end 0))) + (while (re-search-forward "^[^\\*\n].*\n" nil t)) + (if defun + (progn (insert (format "* %s (%s): \n" file defun)) + (setq defun nil)) + (insert (format "* %s: \n" file))) + (backward-char) + (unless (looking-at "\n[\n\\']") + (insert ?\n) + (backward-char)))) + (when defun + (forward-line) + (let ((limit (save-excursion + (and (re-search-forward "^\\*" nil t) + (point))))) + (unless (or (looking-back (format "(%s): " defun) + (line-beginning-position)) + (re-search-forward (format "^(%s): " defun) limit t)) + (while (re-search-forward "^[^\\*\n].*\n" limit t)) + (insert (format "(%s): \n" defun)) + (backward-char))))))) + +;;; _ +(provide 'magit-commit) +;;; magit-commit.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-core.el b/code/elpa/magit-20220821.1819/magit-core.el new file mode 100644 index 0000000..1105f82 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-core.el @@ -0,0 +1,131 @@ +;;; magit-core.el --- Core functionality -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library requires several other libraries, so that yet other +;; libraries can just require this one, instead of having to require +;; all the other ones. In other words this separates the low-level +;; stuff from the rest. It also defines some Custom groups. + +;;; Code: + +(require 'magit-base) +(require 'magit-git) +(require 'magit-mode) +(require 'magit-margin) +(require 'magit-process) +(require 'magit-transient) +(require 'magit-autorevert) + +(when (magit--libgit-available-p) + (condition-case err + (require 'magit-libgit) + (error + (setq magit-inhibit-libgit 'error) + (message "Error while loading `magit-libgit': %S" err) + (message "That is not fatal. The `libegit2' module just won't be used.")))) + +;;; Options + +(defgroup magit nil + "Controlling Git from Emacs." + :link '(url-link "https://magit.vc") + :link '(info-link "(magit)FAQ") + :link '(info-link "(magit)") + :group 'tools) + +(defgroup magit-essentials nil + "Options that every Magit user should briefly think about. + +Each of these options falls into one or more of these categories: + +* Options that affect Magit's behavior in fundamental ways. +* Options that affect safety. +* Options that affect performance. +* Options that are of a personal nature." + :link '(info-link "(magit)Essential Settings") + :group 'magit) + +(defgroup magit-miscellaneous nil + "Miscellaneous Magit options." + :group 'magit) + +(defgroup magit-commands nil + "Options controlling behavior of certain commands." + :group 'magit) + +(defgroup magit-modes nil + "Modes used or provided by Magit." + :group 'magit) + +(defgroup magit-buffers nil + "Options concerning Magit buffers." + :link '(info-link "(magit)Modes and Buffers") + :group 'magit) + +(defgroup magit-refresh nil + "Options controlling how Magit buffers are refreshed." + :link '(info-link "(magit)Automatic Refreshing of Magit Buffers") + :group 'magit + :group 'magit-buffers) + +(defgroup magit-faces nil + "Faces used by Magit." + :group 'magit + :group 'faces) + +(custom-add-to-group 'magit-faces 'diff-refine-added 'custom-face) +(custom-add-to-group 'magit-faces 'diff-refine-removed 'custom-face) + +(defgroup magit-extensions nil + "Extensions to Magit." + :group 'magit) + +(custom-add-to-group 'magit-modes 'git-commit 'custom-group) +(custom-add-to-group 'magit-faces 'git-commit-faces 'custom-group) +(custom-add-to-group 'magit-modes 'git-rebase 'custom-group) +(custom-add-to-group 'magit-faces 'git-rebase-faces 'custom-group) +(custom-add-to-group 'magit 'magit-section 'custom-group) +(custom-add-to-group 'magit-faces 'magit-section-faces 'custom-group) +(custom-add-to-group 'magit-process 'with-editor 'custom-group) + +(defgroup magit-related nil + "Options that are relevant to Magit but that are defined elsewhere." + :link '(custom-group-link vc) + :link '(custom-group-link smerge) + :link '(custom-group-link ediff) + :link '(custom-group-link auto-revert) + :group 'magit + :group 'magit-extensions + :group 'magit-essentials) + +(custom-add-to-group 'magit-related 'auto-revert-check-vc-info 'custom-variable) +(custom-add-to-group 'magit-auto-revert 'auto-revert-check-vc-info 'custom-variable) + +(custom-add-to-group 'magit-related 'ediff-window-setup-function 'custom-variable) +(custom-add-to-group 'magit-related 'smerge-refine-ignore-whitespace 'custom-variable) +(custom-add-to-group 'magit-related 'vc-follow-symlinks 'custom-variable) + +;;; _ +(provide 'magit-core) +;;; magit-core.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-diff.el b/code/elpa/magit-20220821.1819/magit-diff.el new file mode 100644 index 0000000..38872a5 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-diff.el @@ -0,0 +1,3466 @@ +;;; magit-diff.el --- Inspect Git diffs -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for looking at Git diffs and +;; commits. + +;;; Code: + +(require 'magit-core) +(require 'git-commit) + +(eval-when-compile (require 'ansi-color)) +(require 'diff-mode) +(require 'image) +(require 'smerge-mode) + +;; For `magit-diff-popup' +(declare-function magit-stash-show "magit-stash" (stash &optional args files)) +;; For `magit-diff-visit-file' +(declare-function magit-find-file-noselect "magit-files" (rev file)) +(declare-function magit-status-setup-buffer "magit-status" (&optional directory)) +;; For `magit-diff-while-committing' +(declare-function magit-commit-diff-1 "magit-commit" ()) +(declare-function magit-commit-message-buffer "magit-commit" ()) +;; For `magit-insert-revision-gravatar' +(defvar gravatar-size) +;; For `magit-show-commit' and `magit-diff-show-or-scroll' +(declare-function magit-current-blame-chunk "magit-blame" (&optional type noerror)) +(declare-function magit-blame-mode "magit-blame" (&optional arg)) +(defvar magit-blame-mode) +;; For `magit-diff-show-or-scroll' +(declare-function git-rebase-current-line "git-rebase" ()) +;; For `magit-diff-unmerged' +(declare-function magit-merge-in-progress-p "magit-merge" ()) +(declare-function magit--merge-range "magit-merge" (&optional head)) +;; For `magit-diff--dwim' +(declare-function forge--pullreq-range "forge-pullreq" + (pullreq &optional endpoints)) +(declare-function forge--pullreq-ref "forge-pullreq" (pullreq)) +;; For `magit-diff-wash-diff' +(declare-function ansi-color-apply-on-region "ansi-color") +;; For `magit-diff-wash-submodule' +(declare-function magit-log-wash-log "magit-log" (style args)) +;; For keymaps and menus +(declare-function magit-apply "magit-apply" (&rest args)) +(declare-function magit-stage "magit-apply" (&optional indent)) +(declare-function magit-unstage "magit-apply" ()) +(declare-function magit-discard "magit-apply" ()) +(declare-function magit-reverse "magit-apply" (&rest args)) +(declare-function magit-file-rename "magit-files" (file newname)) +(declare-function magit-file-untrack "magit-files" (files &optional force)) +(declare-function magit-commit-add-log "magit-commit" ()) +(declare-function magit-diff-trace-definition "magit-log" ()) +(declare-function magit-patch-save "magit-patch" (files &optional arg)) +(declare-function magit-do-async-shell-command "magit-extras" (file)) +(declare-function magit-add-change-log-entry "magit-extras" + (&optional whoami file-name other-window)) +(declare-function magit-add-change-log-entry-other-window "magit-extras" + (&optional whoami file-name)) +(declare-function magit-diff-edit-hunk-commit "magit-extras" (file)) +(declare-function magit-smerge-keep-current "magit-apply" ()) +(declare-function magit-smerge-keep-upper "magit-apply" ()) +(declare-function magit-smerge-keep-base "magit-apply" ()) +(declare-function magit-smerge-keep-lower "magit-apply" ()) + +(eval-when-compile + (cl-pushnew 'orig-rev eieio--known-slot-names) + (cl-pushnew 'action-type eieio--known-slot-names) + (cl-pushnew 'target eieio--known-slot-names)) + +;;; Options +;;;; Diff Mode + +(defgroup magit-diff nil + "Inspect and manipulate Git diffs." + :link '(info-link "(magit)Diffing") + :group 'magit-commands + :group 'magit-modes) + +(defcustom magit-diff-mode-hook nil + "Hook run after entering Magit-Diff mode." + :group 'magit-diff + :type 'hook) + +(defcustom magit-diff-sections-hook + '(magit-insert-diff + magit-insert-xref-buttons) + "Hook run to insert sections into a `magit-diff-mode' buffer." + :package-version '(magit . "2.3.0") + :group 'magit-diff + :type 'hook) + +(defcustom magit-diff-expansion-threshold 60 + "After how many seconds not to expand anymore diffs. + +Except in status buffers, diffs usually start out fully expanded. +Because that can take a long time, all diffs that haven't been +fontified during a refresh before the threshold defined here are +instead displayed with their bodies collapsed. + +Note that this can cause sections that were previously expanded +to be collapsed. So you should not pick a very low value here. + +The hook function `magit-diff-expansion-threshold' has to be a +member of `magit-section-set-visibility-hook' for this option +to have any effect." + :package-version '(magit . "2.9.0") + :group 'magit-diff + :type 'float) + +(defcustom magit-diff-highlight-hunk-body t + "Whether to highlight bodies of selected hunk sections. +This only has an effect if `magit-diff-highlight' is a +member of `magit-section-highlight-hook', which see." + :package-version '(magit . "2.1.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-highlight-hunk-region-functions + '(magit-diff-highlight-hunk-region-dim-outside + magit-diff-highlight-hunk-region-using-overlays) + "The functions used to highlight the hunk-internal region. + +`magit-diff-highlight-hunk-region-dim-outside' overlays the outside +of the hunk internal selection with a face that causes the added and +removed lines to have the same background color as context lines. +This function should not be removed from the value of this option. + +`magit-diff-highlight-hunk-region-using-overlays' and +`magit-diff-highlight-hunk-region-using-underline' emphasize the +region by placing delimiting horizontal lines before and after it. +The underline variant was implemented because Eli said that is +how we should do it. However the overlay variant actually works +better. Also see https://github.com/magit/magit/issues/2758. + +Instead of, or in addition to, using delimiting horizontal lines, +to emphasize the boundaries, you may wish to emphasize the text +itself, using `magit-diff-highlight-hunk-region-using-face'. + +In terminal frames it's not possible to draw lines as the overlay +and underline variants normally do, so there they fall back to +calling the face function instead." + :package-version '(magit . "2.9.0") + :set-after '(magit-diff-show-lines-boundaries) + :group 'magit-diff + :type 'hook + :options '(magit-diff-highlight-hunk-region-dim-outside + magit-diff-highlight-hunk-region-using-underline + magit-diff-highlight-hunk-region-using-overlays + magit-diff-highlight-hunk-region-using-face)) + +(defcustom magit-diff-unmarked-lines-keep-foreground t + "Whether `magit-diff-highlight-hunk-region-dim-outside' preserves foreground. +When this is set to nil, then that function only adjusts the +foreground color but added and removed lines outside the region +keep their distinct foreground colors." + :package-version '(magit . "2.9.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-refine-hunk nil + "Whether to show word-granularity differences within diff hunks. + +nil Never show fine differences. +t Show fine differences for the current diff hunk only. +`all' Show fine differences for all displayed diff hunks." + :group 'magit-diff + :safe (lambda (val) (memq val '(nil t all))) + :type '(choice (const :tag "Never" nil) + (const :tag "Current" t) + (const :tag "All" all))) + +(defcustom magit-diff-refine-ignore-whitespace smerge-refine-ignore-whitespace + "Whether to ignore whitespace changes in word-granularity differences." + :package-version '(magit . "3.0.0") + :set-after '(smerge-refine-ignore-whitespace) + :group 'magit-diff + :safe 'booleanp + :type 'boolean) + +(put 'magit-diff-refine-hunk 'permanent-local t) + +(defcustom magit-diff-adjust-tab-width nil + "Whether to adjust the width of tabs in diffs. + +Determining the correct width can be expensive if it requires +opening large and/or many files, so the widths are cached in +the variable `magit-diff--tab-width-cache'. Set that to nil +to invalidate the cache. + +nil Never adjust tab width. Use `tab-width's value from + the Magit buffer itself instead. + +t If the corresponding file-visiting buffer exits, then + use `tab-width's value from that buffer. Doing this is + cheap, so this value is used even if a corresponding + cache entry exists. + +`always' If there is no such buffer, then temporarily visit the + file to determine the value. + +NUMBER Like `always', but don't visit files larger than NUMBER + bytes." + :package-version '(magit . "2.12.0") + :group 'magit-diff + :type '(choice (const :tag "Never" nil) + (const :tag "If file-visiting buffer exists" t) + (integer :tag "If file isn't larger than N bytes") + (const :tag "Always" always))) + +(defcustom magit-diff-paint-whitespace t + "Specify where to highlight whitespace errors. + +nil Never highlight whitespace errors. +t Highlight whitespace errors everywhere. +`uncommitted' Only highlight whitespace errors in diffs + showing uncommitted changes. + +For backward compatibility `status' is treated as a synonym +for `uncommitted'. + +The option `magit-diff-paint-whitespace-lines' controls for +what lines (added/remove/context) errors are highlighted. + +The options `magit-diff-highlight-trailing' and +`magit-diff-highlight-indentation' control what kind of +whitespace errors are highlighted." + :group 'magit-diff + :safe (lambda (val) (memq val '(t nil uncommitted status))) + :type '(choice (const :tag "In all diffs" t) + (const :tag "Only in uncommitted changes" uncommitted) + (const :tag "Never" nil))) + +(defcustom magit-diff-paint-whitespace-lines t + "Specify in what kind of lines to highlight whitespace errors. + +t Highlight only in added lines. +`both' Highlight in added and removed lines. +`all' Highlight in added, removed and context lines." + :package-version '(magit . "3.0.0") + :group 'magit-diff + :safe (lambda (val) (memq val '(t both all))) + :type '(choice (const :tag "in added lines" t) + (const :tag "in added and removed lines" both) + (const :tag "in added, removed and context lines" all))) + +(defcustom magit-diff-highlight-trailing t + "Whether to highlight whitespace at the end of a line in diffs. +Used only when `magit-diff-paint-whitespace' is non-nil." + :group 'magit-diff + :safe 'booleanp + :type 'boolean) + +(defcustom magit-diff-highlight-indentation nil + "Highlight the \"wrong\" indentation style. +Used only when `magit-diff-paint-whitespace' is non-nil. + +The value is an alist of the form ((REGEXP . INDENT)...). The +path to the current repository is matched against each element +in reverse order. Therefore if a REGEXP matches, then earlier +elements are not tried. + +If the used INDENT is `tabs', highlight indentation with tabs. +If INDENT is an integer, highlight indentation with at least +that many spaces. Otherwise, highlight neither." + :group 'magit-diff + :type `(repeat (cons (string :tag "Directory regexp") + (choice (const :tag "Tabs" tabs) + (integer :tag "Spaces" :value ,tab-width) + (const :tag "Neither" nil))))) + +(defcustom magit-diff-hide-trailing-cr-characters + (and (memq system-type '(ms-dos windows-nt)) t) + "Whether to hide ^M characters at the end of a line in diffs." + :package-version '(magit . "2.6.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-highlight-keywords t + "Whether to highlight bracketed keywords in commit messages." + :package-version '(magit . "2.12.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-extra-stat-arguments nil + "Additional arguments to be used alongside `--stat'. + +A list of zero or more arguments or a function that takes no +argument and returns such a list. These arguments are allowed +here: `--stat-width', `--stat-name-width', `--stat-graph-width' +and `--compact-summary'. See the git-diff(1) manpage." + :package-version '(magit . "3.0.0") + :group 'magit-diff + :type '(radio (function-item magit-diff-use-window-width-as-stat-width) + function + (list string) + (const :tag "None" nil))) + +;;;; File Diff + +(defcustom magit-diff-buffer-file-locked t + "Whether `magit-diff-buffer-file' uses a dedicated buffer." + :package-version '(magit . "2.7.0") + :group 'magit-commands + :group 'magit-diff + :type 'boolean) + +;;;; Revision Mode + +(defgroup magit-revision nil + "Inspect and manipulate Git commits." + :link '(info-link "(magit)Revision Buffer") + :group 'magit-modes) + +(defcustom magit-revision-mode-hook + '(bug-reference-mode + goto-address-mode) + "Hook run after entering Magit-Revision mode." + :group 'magit-revision + :type 'hook + :options '(bug-reference-mode + goto-address-mode)) + +(defcustom magit-revision-sections-hook + '(magit-insert-revision-tag + magit-insert-revision-headers + magit-insert-revision-message + magit-insert-revision-notes + magit-insert-revision-diff + magit-insert-xref-buttons) + "Hook run to insert sections into a `magit-revision-mode' buffer." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type 'hook) + +(defcustom magit-revision-headers-format "\ +Author: %aN <%aE> +AuthorDate: %ad +Commit: %cN <%cE> +CommitDate: %cd +" + "Format string used to insert headers in revision buffers. + +All headers in revision buffers are inserted by the section +inserter `magit-insert-revision-headers'. Some of the headers +are created by calling `git show --format=FORMAT' where FORMAT +is the format specified here. Other headers are hard coded or +subject to option `magit-revision-insert-related-refs'." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type 'string) + +(defcustom magit-revision-insert-related-refs t + "Whether to show related branches in revision buffers + +`nil' Don't show any related branches. +`t' Show related local branches. +`all' Show related local and remote branches. +`mixed' Show all containing branches and local merged branches." + :package-version '(magit . "2.1.0") + :group 'magit-revision + :type '(choice (const :tag "don't" nil) + (const :tag "local only" t) + (const :tag "all related" all) + (const :tag "all containing, local merged" mixed))) + +(defcustom magit-revision-use-hash-sections 'quicker + "Whether to turn hashes inside the commit message into sections. + +If non-nil, then hashes inside the commit message are turned into +`commit' sections. There is a trade off to be made between +performance and reliability: + +- `slow' calls git for every word to be absolutely sure. +- `quick' skips words less than seven characters long. +- `quicker' additionally skips words that don't contain a number. +- `quickest' uses all words that are at least seven characters + long and which contain at least one number as well as at least + one letter. + +If nil, then no hashes are turned into sections, but you can +still visit the commit at point using \"RET\"." + :package-version '(magit . "2.12.0") + :group 'magit-revision + :type '(choice (const :tag "Use sections, quickest" quickest) + (const :tag "Use sections, quicker" quicker) + (const :tag "Use sections, quick" quick) + (const :tag "Use sections, slow" slow) + (const :tag "Don't use sections" nil))) + +(defcustom magit-revision-show-gravatars nil + "Whether to show gravatar images in revision buffers. + +If nil, then don't insert any gravatar images. If t, then insert +both images. If `author' or `committer', then insert only the +respective image. + +If you have customized the option `magit-revision-header-format' +and want to insert the images then you might also have to specify +where to do so. In that case the value has to be a cons-cell of +two regular expressions. The car specifies where to insert the +author's image. The top half of the image is inserted right +after the matched text, the bottom half on the next line in the +same column. The cdr specifies where to insert the committer's +image, accordingly. Either the car or the cdr may be nil." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type '(choice (const :tag "Don't show gravatars" nil) + (const :tag "Show gravatars" t) + (const :tag "Show author gravatar" author) + (const :tag "Show committer gravatar" committer) + (cons :tag "Show gravatars using custom pattern." + (regexp :tag "Author regexp" "^Author: ") + (regexp :tag "Committer regexp" "^Commit: ")))) + +(defcustom magit-revision-use-gravatar-kludge nil + "Whether to work around a bug which affects display of gravatars. + +Gravatar images are spliced into two halves which are then +displayed on separate lines. On macOS the splicing has a bug in +some Emacs builds, which causes the top and bottom halves to be +interchanged. Enabling this option works around this issue by +interchanging the halves once more, which cancels out the effect +of the bug. + +See https://github.com/magit/magit/issues/2265 +and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=7847. + +Starting with Emacs 26.1 this kludge should not be required for +any build." + :package-version '(magit . "2.3.0") + :group 'magit-revision + :type 'boolean) + +(defcustom magit-revision-fill-summary-line nil + "Whether to fill excessively long summary lines. + +If this is an integer, then the summary line is filled if it is +longer than either the limit specified here or `window-width'. + +You may want to only set this locally in \".dir-locals-2.el\" for +repositories known to contain bad commit messages. + +The body of the message is left alone because (a) most people who +write excessively long summary lines usually don't add a body and +(b) even people who have the decency to wrap their lines may have +a good reason to include a long line in the body sometimes." + :package-version '(magit . "2.90.0") + :group 'magit-revision + :type '(choice (const :tag "Don't fill" nil) + (integer :tag "Fill if longer than"))) + +(defcustom magit-revision-filter-files-on-follow nil + "Whether to honor file filter if log arguments include --follow. + +When a commit is displayed from a log buffer, the resulting +revision buffer usually shares the log's file arguments, +restricting the diff to those files. However, there's a +complication when the log arguments include --follow: if the log +follows a file across a rename event, keeping the file +restriction would mean showing an empty diff in revision buffers +for commits before the rename event. + +When this option is nil, the revision buffer ignores the log's +filter if the log arguments include --follow. If non-nil, the +log's file filter is always honored." + :package-version '(magit . "3.0.0") + :group 'magit-revision + :type 'boolean) + +;;;; Visit Commands + +(defcustom magit-diff-visit-previous-blob t + "Whether `magit-diff-visit-file' may visit the previous blob. + +When this is t and point is on a removed line in a diff for a +committed change, then `magit-diff-visit-file' visits the blob +from the last revision which still had that line. + +Currently this is only supported for committed changes, for +staged and unstaged changes `magit-diff-visit-file' always +visits the file in the working tree." + :package-version '(magit . "2.9.0") + :group 'magit-diff + :type 'boolean) + +(defcustom magit-diff-visit-avoid-head-blob nil + "Whether `magit-diff-visit-file' avoids visiting a blob from `HEAD'. + +By default `magit-diff-visit-file' always visits the blob that +added the current line, while `magit-diff-visit-worktree-file' +visits the respective file in the working tree. For the `HEAD' +commit, the former command used to visit the worktree file too, +but that made it impossible to visit a blob from `HEAD'. + +When point is on a removed line and that change has not been +committed yet, then `magit-diff-visit-file' now visits the last +blob that still had that line, which is a blob from `HEAD'. +Previously this function used to visit the worktree file not +only for added lines but also for such removed lines. + +If you prefer the old behaviors, then set this to t." + :package-version '(magit . "3.0.0") + :group 'magit-diff + :type 'boolean) + +;;; Faces + +(defface magit-diff-file-heading + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :weight bold)) + "Face for diff file headings." + :group 'magit-faces) + +(defface magit-diff-file-heading-highlight + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-section-highlight)) + "Face for current diff file headings." + :group 'magit-faces) + +(defface magit-diff-file-heading-selection + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-diff-file-heading-highlight + :foreground "salmon4") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-diff-file-heading-highlight + :foreground "LightSalmon3")) + "Face for selected diff file headings." + :group 'magit-faces) + +(defface magit-diff-hunk-heading + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey90" + :foreground "grey20") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey25" + :foreground "grey95")) + "Face for diff hunk headings." + :group 'magit-faces) + +(defface magit-diff-hunk-heading-highlight + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey80" + :foreground "grey20") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey35" + :foreground "grey95")) + "Face for current diff hunk headings." + :group 'magit-faces) + +(defface magit-diff-hunk-heading-selection + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-diff-hunk-heading-highlight + :foreground "salmon4") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-diff-hunk-heading-highlight + :foreground "LightSalmon3")) + "Face for selected diff hunk headings." + :group 'magit-faces) + +(defface magit-diff-hunk-region + `((t :inherit bold + ,@(and (>= emacs-major-version 27) + (list :extend (ignore-errors (face-attribute 'region :extend)))))) + "Face used by `magit-diff-highlight-hunk-region-using-face'. + +This face is overlaid over text that uses other hunk faces, +and those normally set the foreground and background colors. +The `:foreground' and especially the `:background' properties +should be avoided here. Setting the latter would cause the +loss of information. Good properties to set here are `:weight' +and `:slant'." + :group 'magit-faces) + +(defface magit-diff-revision-summary + '((t :inherit magit-diff-hunk-heading)) + "Face for commit message summaries." + :group 'magit-faces) + +(defface magit-diff-revision-summary-highlight + '((t :inherit magit-diff-hunk-heading-highlight)) + "Face for highlighted commit message summaries." + :group 'magit-faces) + +(defface magit-diff-lines-heading + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-diff-hunk-heading-highlight + :background "LightSalmon3") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :inherit magit-diff-hunk-heading-highlight + :foreground "grey80" + :background "salmon4")) + "Face for diff hunk heading when lines are marked." + :group 'magit-faces) + +(defface magit-diff-lines-boundary + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) ; !important + :inherit magit-diff-lines-heading)) + "Face for boundary of marked lines in diff hunk." + :group 'magit-faces) + +(defface magit-diff-conflict-heading + '((t :inherit magit-diff-hunk-heading)) + "Face for conflict markers." + :group 'magit-faces) + +(defface magit-diff-added + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#ddffdd" + :foreground "#22aa22") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#335533" + :foreground "#ddffdd")) + "Face for lines in a diff that have been added." + :group 'magit-faces) + +(defface magit-diff-removed + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#ffdddd" + :foreground "#aa2222") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#553333" + :foreground "#ffdddd")) + "Face for lines in a diff that have been removed." + :group 'magit-faces) + +(defface magit-diff-our + '((t :inherit magit-diff-removed)) + "Face for lines in a diff for our side in a conflict." + :group 'magit-faces) + +(defface magit-diff-base + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#ffffcc" + :foreground "#aaaa11") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#555522" + :foreground "#ffffcc")) + "Face for lines in a diff for the base side in a conflict." + :group 'magit-faces) + +(defface magit-diff-their + '((t :inherit magit-diff-added)) + "Face for lines in a diff for their side in a conflict." + :group 'magit-faces) + +(defface magit-diff-context + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "grey50") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "grey70")) + "Face for lines in a diff that are unchanged." + :group 'magit-faces) + +(defface magit-diff-added-highlight + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#cceecc" + :foreground "#22aa22") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#336633" + :foreground "#cceecc")) + "Face for lines in a diff that have been added." + :group 'magit-faces) + +(defface magit-diff-removed-highlight + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#eecccc" + :foreground "#aa2222") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#663333" + :foreground "#eecccc")) + "Face for lines in a diff that have been removed." + :group 'magit-faces) + +(defface magit-diff-our-highlight + '((t :inherit magit-diff-removed-highlight)) + "Face for lines in a diff for our side in a conflict." + :group 'magit-faces) + +(defface magit-diff-base-highlight + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#eeeebb" + :foreground "#aaaa11") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "#666622" + :foreground "#eeeebb")) + "Face for lines in a diff for the base side in a conflict." + :group 'magit-faces) + +(defface magit-diff-their-highlight + '((t :inherit magit-diff-added-highlight)) + "Face for lines in a diff for their side in a conflict." + :group 'magit-faces) + +(defface magit-diff-context-highlight + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey95" + :foreground "grey50") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey20" + :foreground "grey70")) + "Face for lines in the current context in a diff." + :group 'magit-faces) + +(defface magit-diff-whitespace-warning + '((t :inherit trailing-whitespace)) + "Face for highlighting whitespace errors added lines." + :group 'magit-faces) + +(defface magit-diffstat-added + '((((class color) (background light)) :foreground "#22aa22") + (((class color) (background dark)) :foreground "#448844")) + "Face for plus sign in diffstat." + :group 'magit-faces) + +(defface magit-diffstat-removed + '((((class color) (background light)) :foreground "#aa2222") + (((class color) (background dark)) :foreground "#aa4444")) + "Face for minus sign in diffstat." + :group 'magit-faces) + +;;; Arguments +;;;; Prefix Classes + +(defclass magit-diff-prefix (transient-prefix) + ((history-key :initform 'magit-diff) + (major-mode :initform 'magit-diff-mode))) + +(defclass magit-diff-refresh-prefix (magit-diff-prefix) + ((history-key :initform 'magit-diff) + (major-mode :initform nil))) + +;;;; Prefix Methods + +(cl-defmethod transient-init-value ((obj magit-diff-prefix)) + (pcase-let ((`(,args ,files) + (magit-diff--get-value 'magit-diff-mode + magit-prefix-use-buffer-arguments))) + (unless (eq transient-current-command 'magit-dispatch) + (when-let ((file (magit-file-relative-name))) + (setq files (list file)))) + (oset obj value (if files `(("--" ,@files) ,args) args)))) + +(cl-defmethod transient-init-value ((obj magit-diff-refresh-prefix)) + (oset obj value (if magit-buffer-diff-files + `(("--" ,@magit-buffer-diff-files) + ,magit-buffer-diff-args) + magit-buffer-diff-args))) + +(cl-defmethod transient-set-value ((obj magit-diff-prefix)) + (magit-diff--set-value obj)) + +(cl-defmethod transient-save-value ((obj magit-diff-prefix)) + (magit-diff--set-value obj 'save)) + +;;;; Argument Access + +(defun magit-diff-arguments (&optional mode) + "Return the current diff arguments." + (if (memq transient-current-command '(magit-diff magit-diff-refresh)) + (pcase-let ((`(,args ,alist) + (-separate #'atom (transient-get-value)))) + (list args (cdr (assoc "--" alist)))) + (magit-diff--get-value (or mode 'magit-diff-mode)))) + +(defun magit-diff--get-value (mode &optional use-buffer-args) + (unless use-buffer-args + (setq use-buffer-args magit-direct-use-buffer-arguments)) + (let (args files) + (cond + ((and (memq use-buffer-args '(always selected current)) + (eq major-mode mode)) + (setq args magit-buffer-diff-args) + (setq files magit-buffer-diff-files)) + ((and (memq use-buffer-args '(always selected)) + (when-let ((buffer (magit-get-mode-buffer + mode nil + (eq use-buffer-args 'selected)))) + (setq args (buffer-local-value 'magit-buffer-diff-args buffer)) + (setq files (buffer-local-value 'magit-buffer-diff-files buffer)) + t))) + ((plist-member (symbol-plist mode) 'magit-diff-current-arguments) + (setq args (get mode 'magit-diff-current-arguments))) + ((when-let ((elt (assq (intern (format "magit-diff:%s" mode)) + transient-values))) + (setq args (cdr elt)) + t)) + (t + (setq args (get mode 'magit-diff-default-arguments)))) + (list args files))) + +(defun magit-diff--set-value (obj &optional save) + (pcase-let* ((obj (oref obj prototype)) + (mode (or (oref obj major-mode) major-mode)) + (key (intern (format "magit-diff:%s" mode))) + (`(,args ,alist) + (-separate #'atom (transient-get-value))) + (files (cdr (assoc "--" alist)))) + (put mode 'magit-diff-current-arguments args) + (when save + (setf (alist-get key transient-values) args) + (transient-save-values)) + (transient--history-push obj) + (setq magit-buffer-diff-args args) + (setq magit-buffer-diff-files files) + (magit-refresh))) + +;;; Commands +;;;; Prefix Commands + +;;;###autoload (autoload 'magit-diff "magit-diff" nil t) +(transient-define-prefix magit-diff () + "Show changes between different versions." + :man-page "git-diff" + :class 'magit-diff-prefix + ["Limit arguments" + (magit:--) + (magit-diff:--ignore-submodules) + ("-b" "Ignore whitespace changes" ("-b" "--ignore-space-change")) + ("-w" "Ignore all whitespace" ("-w" "--ignore-all-space")) + (5 "-D" "Omit preimage for deletes" ("-D" "--irreversible-delete"))] + ["Context arguments" + (magit-diff:-U) + ("-W" "Show surrounding functions" ("-W" "--function-context"))] + ["Tune arguments" + (magit-diff:--diff-algorithm) + (magit-diff:-M) + (magit-diff:-C) + ("-x" "Disallow external diff drivers" "--no-ext-diff") + ("-s" "Show stats" "--stat") + ("=g" "Show signature" "--show-signature") + (5 "-R" "Reverse sides" "-R") + (5 magit-diff:--color-moved) + (5 magit-diff:--color-moved-ws)] + ["Actions" + [("d" "Dwim" magit-diff-dwim) + ("r" "Diff range" magit-diff-range) + ("p" "Diff paths" magit-diff-paths)] + [("u" "Diff unstaged" magit-diff-unstaged) + ("s" "Diff staged" magit-diff-staged) + ("w" "Diff worktree" magit-diff-working-tree)] + [("c" "Show commit" magit-show-commit) + ("t" "Show stash" magit-stash-show)]]) + +;;;###autoload (autoload 'magit-diff-refresh "magit-diff" nil t) +(transient-define-prefix magit-diff-refresh () + "Change the arguments used for the diff(s) in the current buffer." + :man-page "git-diff" + :class 'magit-diff-refresh-prefix + ["Limit arguments" + (magit:--) + (magit-diff:--ignore-submodules) + ("-b" "Ignore whitespace changes" ("-b" "--ignore-space-change")) + ("-w" "Ignore all whitespace" ("-w" "--ignore-all-space")) + (5 "-D" "Omit preimage for deletes" ("-D" "--irreversible-delete"))] + ["Context arguments" + (magit-diff:-U) + ("-W" "Show surrounding functions" ("-W" "--function-context"))] + ["Tune arguments" + (magit-diff:--diff-algorithm) + (magit-diff:-M) + (magit-diff:-C) + ("-x" "Disallow external diff drivers" "--no-ext-diff") + ("-s" "Show stats" "--stat" + :if-derived magit-diff-mode) + ("=g" "Show signature" "--show-signature" + :if-derived magit-diff-mode) + (5 "-R" "Reverse sides" "-R" + :if-derived magit-diff-mode) + (5 magit-diff:--color-moved) + (5 magit-diff:--color-moved-ws)] + [["Refresh" + ("g" "buffer" magit-diff-refresh) + ("s" "buffer and set defaults" transient-set :transient nil) + ("w" "buffer and save defaults" transient-save :transient nil)] + ["Toggle" + ("t" "hunk refinement" magit-diff-toggle-refine-hunk) + ("F" "file filter" magit-diff-toggle-file-filter) + ("b" "buffer lock" magit-toggle-buffer-lock + :if-mode (magit-diff-mode magit-revision-mode magit-stash-mode))] + [:if-mode magit-diff-mode + :description "Do" + ("r" "switch range type" magit-diff-switch-range-type) + ("f" "flip revisions" magit-diff-flip-revs)]] + (interactive) + (if (not (eq transient-current-command 'magit-diff-refresh)) + (transient-setup 'magit-diff-refresh) + (pcase-let ((`(,args ,files) (magit-diff-arguments))) + (setq magit-buffer-diff-args args) + (setq magit-buffer-diff-files files)) + (magit-refresh))) + +;;;; Infix Commands + +(transient-define-argument magit:-- () + :description "Limit to files" + :class 'transient-files + :key "--" + :argument "--" + :prompt "Limit to file,s: " + :reader #'magit-read-files + :multi-value t) + +(defun magit-read-files (prompt initial-input history &optional list-fn) + (magit-completing-read-multiple* prompt + (funcall (or list-fn #'magit-list-files)) + nil nil + (or initial-input (magit-file-at-point)) + history)) + +(transient-define-argument magit-diff:-U () + :description "Context lines" + :class 'transient-option + :argument "-U" + :reader #'transient-read-number-N0) + +(transient-define-argument magit-diff:-M () + :description "Detect renames" + :class 'transient-option + :argument "-M" + :allow-empty t + :reader #'transient-read-number-N+) + +(transient-define-argument magit-diff:-C () + :description "Detect copies" + :class 'transient-option + :argument "-C" + :allow-empty t + :reader #'transient-read-number-N+) + +(transient-define-argument magit-diff:--diff-algorithm () + :description "Diff algorithm" + :class 'transient-option + :key "-A" + :argument "--diff-algorithm=" + :reader #'magit-diff-select-algorithm) + +(defun magit-diff-select-algorithm (&rest _ignore) + (magit-read-char-case nil t + (?d "[d]efault" "default") + (?m "[m]inimal" "minimal") + (?p "[p]atience" "patience") + (?h "[h]istogram" "histogram"))) + +(transient-define-argument magit-diff:--ignore-submodules () + :description "Ignore submodules" + :class 'transient-option + :key "-i" + :argument "--ignore-submodules=" + :reader #'magit-diff-select-ignore-submodules) + +(defun magit-diff-select-ignore-submodules (&rest _ignored) + (magit-read-char-case "Ignore submodules " t + (?u "[u]ntracked" "untracked") + (?d "[d]irty" "dirty") + (?a "[a]ll" "all"))) + +(transient-define-argument magit-diff:--color-moved () + :description "Color moved lines" + :class 'transient-option + :key "-m" + :argument "--color-moved=" + :reader #'magit-diff-select-color-moved-mode) + +(defun magit-diff-select-color-moved-mode (&rest _ignore) + (magit-read-char-case "Color moved " t + (?d "[d]efault" "default") + (?p "[p]lain" "plain") + (?b "[b]locks" "blocks") + (?z "[z]ebra" "zebra") + (?Z "[Z] dimmed-zebra" "dimmed-zebra"))) + +(transient-define-argument magit-diff:--color-moved-ws () + :description "Whitespace treatment for --color-moved" + :class 'transient-option + :key "=w" + :argument "--color-moved-ws=" + :reader #'magit-diff-select-color-moved-ws-mode) + +(defun magit-diff-select-color-moved-ws-mode (&rest _ignore) + (magit-read-char-case "Ignore whitespace " t + (?i "[i]ndentation" "allow-indentation-change") + (?e "[e]nd of line" "ignore-space-at-eol") + (?s "[s]pace change" "ignore-space-change") + (?a "[a]ll space" "ignore-all-space") + (?n "[n]o" "no"))) + +;;;; Setup Commands + +;;;###autoload +(defun magit-diff-dwim (&optional args files) + "Show changes for the thing at point." + (interactive (magit-diff-arguments)) + (let ((default-directory default-directory) + (section (magit-current-section))) + (cond + ((magit-section-match 'module section) + (setq default-directory + (expand-file-name + (file-name-as-directory (oref section value)))) + (magit-diff-range (oref section range))) + (t + (when (magit-section-match 'module-commit section) + (setq args nil) + (setq files nil) + (setq default-directory + (expand-file-name + (file-name-as-directory (magit-section-parent-value section))))) + (pcase (magit-diff--dwim) + ('unmerged (magit-diff-unmerged args files)) + ('unstaged (magit-diff-unstaged args files)) + ('staged + (let ((file (magit-file-at-point))) + (if (and file (equal (cddr (car (magit-file-status file))) '(?D ?U))) + ;; File was deleted by us and modified by them. Show the latter. + (magit-diff-unmerged args (list file)) + (magit-diff-staged nil args files)))) + (`(stash . ,value) (magit-stash-show value args)) + (`(commit . ,value) + (magit-diff-range (format "%s^..%s" value value) args files)) + ((and range (pred stringp)) + (magit-diff-range range args files)) + (_ (call-interactively #'magit-diff-range))))))) + +(defun magit-diff--dwim () + "Return information for performing DWIM diff. + +The information can be in three forms: +1. TYPE + A symbol describing a type of diff where no additional information + is needed to generate the diff. Currently, this includes `staged', + `unstaged' and `unmerged'. +2. (TYPE . VALUE) + Like #1 but the diff requires additional information, which is + given by VALUE. Currently, this includes `commit' and `stash', + where VALUE is the given commit or stash, respectively. +3. RANGE + A string indicating a diff range. + +If no DWIM context is found, nil is returned." + (cond + ((when-let* ((commits (magit-region-values '(commit branch) t))) + ;; Cannot use and-let* because of debbugs#31840. + (deactivate-mark) + (concat (car (last commits)) ".." (car commits)))) + (magit-buffer-refname + (cons 'commit magit-buffer-refname)) + ((derived-mode-p 'magit-stash-mode) + (cons 'commit + (magit-section-case + (commit (oref it value)) + (file (thread-first it + (oref parent) + (oref value))) + (hunk (thread-first it + (oref parent) + (oref parent) + (oref value)))))) + ((derived-mode-p 'magit-revision-mode) + (cons 'commit magit-buffer-revision)) + ((derived-mode-p 'magit-diff-mode) + magit-buffer-range) + (t + (magit-section-case + ([* unstaged] 'unstaged) + ([* staged] 'staged) + (unmerged 'unmerged) + (unpushed (magit-diff--range-to-endpoints (oref it value))) + (unpulled (magit-diff--range-to-endpoints (oref it value))) + (branch (let ((current (magit-get-current-branch)) + (atpoint (oref it value))) + (if (equal atpoint current) + (--if-let (magit-get-upstream-branch) + (format "%s...%s" it current) + (if (magit-anything-modified-p) + current + (cons 'commit current))) + (format "%s...%s" + (or current "HEAD") + atpoint)))) + (commit (cons 'commit (oref it value))) + ([file commit] (cons 'commit (oref (oref it parent) value))) + ([hunk file commit] + (cons 'commit (oref (oref (oref it parent) parent) value))) + (stash (cons 'stash (oref it value))) + (pullreq (forge--pullreq-range (oref it value) t)))))) + +(defun magit-diff--range-to-endpoints (range) + (cond ((string-match "\\.\\.\\." range) (replace-match ".." nil nil range)) + ((string-match "\\.\\." range) (replace-match "..." nil nil range)) + (t range))) + +(defun magit-diff--region-range (&optional interactive mbase) + (when-let* ((commits (magit-region-values '(commit branch) t)) ;debbugs#31840 + (revA (car (last commits))) + (revB (car commits))) + (when interactive + (deactivate-mark)) + (if mbase + (let ((base (magit-git-string "merge-base" revA revB))) + (cond + ((string= (magit-rev-parse revA) base) + (format "%s..%s" revA revB)) + ((string= (magit-rev-parse revB) base) + (format "%s..%s" revB revA)) + (interactive + (let ((main (magit-completing-read "View changes along" + (list revA revB) + nil t nil nil revB))) + (format "%s...%s" + (if (string= main revB) revA revB) main))) + (t "%s...%s" revA revB))) + (format "%s..%s" revA revB)))) + +(defun magit-diff-read-range-or-commit (prompt &optional secondary-default mbase) + "Read range or revision with special diff range treatment. +If MBASE is non-nil, prompt for which rev to place at the end of +a \"revA...revB\" range. Otherwise, always construct +\"revA..revB\" range." + (or (magit-diff--region-range t mbase) + (magit-read-range prompt + (or (pcase (magit-diff--dwim) + (`(commit . ,value) + (format "%s^..%s" value value)) + ((and range (pred stringp)) + range)) + secondary-default + (magit-get-current-branch))))) + +;;;###autoload +(defun magit-diff-range (rev-or-range &optional args files) + "Show differences between two commits. + +REV-OR-RANGE should be a range or a single revision. If it is a +revision, then show changes in the working tree relative to that +revision. If it is a range, but one side is omitted, then show +changes relative to `HEAD'. + +If the region is active, use the revisions on the first and last +line of the region as the two sides of the range. With a prefix +argument, instead of diffing the revisions, choose a revision to +view changes along, starting at the common ancestor of both +revisions (i.e., use a \"...\" range)." + (interactive (cons (magit-diff-read-range-or-commit "Diff for range" + nil current-prefix-arg) + (magit-diff-arguments))) + (magit-diff-setup-buffer rev-or-range nil args files)) + +;;;###autoload +(defun magit-diff-working-tree (&optional rev args files) + "Show changes between the current working tree and the `HEAD' commit. +With a prefix argument show changes between the working tree and +a commit read from the minibuffer." + (interactive + (cons (and current-prefix-arg + (magit-read-branch-or-commit "Diff working tree and commit")) + (magit-diff-arguments))) + (magit-diff-setup-buffer (or rev "HEAD") nil args files)) + +;;;###autoload +(defun magit-diff-staged (&optional rev args files) + "Show changes between the index and the `HEAD' commit. +With a prefix argument show changes between the index and +a commit read from the minibuffer." + (interactive + (cons (and current-prefix-arg + (magit-read-branch-or-commit "Diff index and commit")) + (magit-diff-arguments))) + (magit-diff-setup-buffer rev "--cached" args files)) + +;;;###autoload +(defun magit-diff-unstaged (&optional args files) + "Show changes between the working tree and the index." + (interactive (magit-diff-arguments)) + (magit-diff-setup-buffer nil nil args files)) + +;;;###autoload +(defun magit-diff-unmerged (&optional args files) + "Show changes that are being merged." + (interactive (magit-diff-arguments)) + (unless (magit-merge-in-progress-p) + (user-error "No merge is in progress")) + (magit-diff-setup-buffer (magit--merge-range) nil args files)) + +;;;###autoload +(defun magit-diff-while-committing () + "While committing, show the changes that are about to be committed. +While amending, invoking the command again toggles between +showing just the new changes or all the changes that will +be committed." + (interactive) + (unless (magit-commit-message-buffer) + (user-error "No commit in progress")) + (magit-commit-diff-1)) + +(define-key git-commit-mode-map + (kbd "C-c C-d") #'magit-diff-while-committing) + +;;;###autoload +(defun magit-diff-buffer-file () + "Show diff for the blob or file visited in the current buffer. + +When the buffer visits a blob, then show the respective commit. +When the buffer visits a file, then show the differences between +`HEAD' and the working tree. In both cases limit the diff to +the file or blob." + (interactive) + (require 'magit) + (if-let ((file (magit-file-relative-name))) + (if magit-buffer-refname + (magit-show-commit magit-buffer-refname + (car (magit-show-commit--arguments)) + (list file)) + (save-buffer) + (let ((line (line-number-at-pos)) + (col (current-column))) + (with-current-buffer + (magit-diff-setup-buffer (or (magit-get-current-branch) "HEAD") + nil + (car (magit-diff-arguments)) + (list file) + magit-diff-buffer-file-locked) + (magit-diff--goto-position file line col)))) + (user-error "Buffer isn't visiting a file"))) + +;;;###autoload +(defun magit-diff-paths (a b) + "Show changes between any two files on disk." + (interactive (list (read-file-name "First file: " nil nil t) + (read-file-name "Second file: " nil nil t))) + (magit-diff-setup-buffer nil "--no-index" + nil (list (magit-convert-filename-for-git + (expand-file-name a)) + (magit-convert-filename-for-git + (expand-file-name b))))) + +(defun magit-show-commit--arguments () + (pcase-let ((`(,args ,diff-files) + (magit-diff-arguments 'magit-revision-mode))) + (list args (if (derived-mode-p 'magit-log-mode) + (and (or magit-revision-filter-files-on-follow + (not (member "--follow" magit-buffer-log-args))) + magit-buffer-log-files) + diff-files)))) + +;;;###autoload +(defun magit-show-commit (rev &optional args files module) + "Visit the revision at point in another buffer. +If there is no revision at point or with a prefix argument prompt +for a revision." + (interactive + (pcase-let* ((mcommit (magit-section-value-if 'module-commit)) + (atpoint (or mcommit + (magit-thing-at-point 'git-revision t) + (magit-branch-or-commit-at-point))) + (`(,args ,files) (magit-show-commit--arguments))) + (list (or (and (not current-prefix-arg) atpoint) + (magit-read-branch-or-commit "Show commit" atpoint)) + args + files + (and mcommit + (magit-section-parent-value (magit-current-section)))))) + (require 'magit) + (let* ((file (magit-file-relative-name)) + (ln (and file (line-number-at-pos)))) + (magit-with-toplevel + (when module + (setq default-directory + (expand-file-name (file-name-as-directory module)))) + (unless (magit-commit-p rev) + (user-error "%s is not a commit" rev)) + (when file + (save-buffer)) + (let ((buf (magit-revision-setup-buffer rev args files))) + (when file + (let ((line (magit-diff-visit--offset file (list "-R" rev) ln)) + (col (current-column))) + (with-current-buffer buf + (magit-diff--goto-position file line col)))))))) + +(defun magit-diff--locate-hunk (file line &optional parent) + (and-let* ((diff (cl-find-if (lambda (section) + (and (cl-typep section 'magit-file-section) + (equal (oref section value) file))) + (oref (or parent magit-root-section) children)))) + (let (hunk (hunks (oref diff children))) + (cl-block nil + (while (setq hunk (pop hunks)) + (when-let ((range (oref hunk to-range))) + (pcase-let* ((`(,beg ,len) range) + (end (+ beg len))) + (cond ((> beg line) (cl-return (list diff nil))) + ((<= beg line end) (cl-return (list hunk t))) + ((null hunks) (cl-return (list hunk nil))))))))))) + +(defun magit-diff--goto-position (file line column &optional parent) + (when-let ((pos (magit-diff--locate-hunk file line parent))) + (pcase-let ((`(,section ,exact) pos)) + (cond ((cl-typep section 'magit-file-section) + (goto-char (oref section start))) + (exact + (goto-char (oref section content)) + (let ((pos (car (oref section to-range)))) + (while (or (< pos line) + (= (char-after) ?-)) + (unless (= (char-after) ?-) + (cl-incf pos)) + (forward-line))) + (forward-char (1+ column))) + (t + (goto-char (oref section start)) + (setq section (oref section parent)))) + (while section + (when (oref section hidden) + (magit-section-show section)) + (setq section (oref section parent)))) + (magit-section-update-highlight) + t)) + +;;;; Setting Commands + +(defun magit-diff-switch-range-type () + "Convert diff range type. +Change \"revA..revB\" to \"revA...revB\", or vice versa." + (interactive) + (if (and magit-buffer-range + (derived-mode-p 'magit-diff-mode) + (string-match magit-range-re magit-buffer-range)) + (setq magit-buffer-range + (replace-match (if (string= (match-string 2 magit-buffer-range) "..") + "..." + "..") + t t magit-buffer-range 2)) + (user-error "No range to change")) + (magit-refresh)) + +(defun magit-diff-flip-revs () + "Swap revisions in diff range. +Change \"revA..revB\" to \"revB..revA\"." + (interactive) + (if (and magit-buffer-range + (derived-mode-p 'magit-diff-mode) + (string-match magit-range-re magit-buffer-range)) + (progn + (setq magit-buffer-range + (concat (match-string 3 magit-buffer-range) + (match-string 2 magit-buffer-range) + (match-string 1 magit-buffer-range))) + (magit-refresh)) + (user-error "No range to swap"))) + +(defun magit-diff-toggle-file-filter () + "Toggle the file restriction of the current buffer's diffs. +If the current buffer's mode is derived from `magit-log-mode', +toggle the file restriction in the repository's revision buffer +instead." + (interactive) + (cl-flet ((toggle () + (if (or magit-buffer-diff-files + magit-buffer-diff-files-suspended) + (cl-rotatef magit-buffer-diff-files + magit-buffer-diff-files-suspended) + (setq magit-buffer-diff-files + (transient-infix-read 'magit:--))) + (magit-refresh))) + (cond + ((derived-mode-p 'magit-log-mode + 'magit-cherry-mode + 'magit-reflog-mode) + (if-let ((buffer (magit-get-mode-buffer 'magit-revision-mode))) + (with-current-buffer buffer (toggle)) + (message "No revision buffer"))) + ((local-variable-p 'magit-buffer-diff-files) + (toggle)) + (t + (user-error "Cannot toggle file filter in this buffer"))))) + +(defun magit-diff-less-context (&optional count) + "Decrease the context for diff hunks by COUNT lines." + (interactive "p") + (magit-diff-set-context (lambda (cur) (max 0 (- (or cur 0) count))))) + +(defun magit-diff-more-context (&optional count) + "Increase the context for diff hunks by COUNT lines." + (interactive "p") + (magit-diff-set-context (lambda (cur) (+ (or cur 0) count)))) + +(defun magit-diff-default-context () + "Reset context for diff hunks to the default height." + (interactive) + (magit-diff-set-context #'ignore)) + +(defun magit-diff-set-context (fn) + (let* ((def (--if-let (magit-get "diff.context") (string-to-number it) 3)) + (val magit-buffer-diff-args) + (arg (--first (string-match "^-U\\([0-9]+\\)?$" it) val)) + (num (--if-let (and arg (match-string 1 arg)) (string-to-number it) def)) + (val (delete arg val)) + (num (funcall fn num)) + (arg (and num (not (= num def)) (format "-U%i" num))) + (val (if arg (cons arg val) val))) + (setq magit-buffer-diff-args val)) + (magit-refresh)) + +(defun magit-diff-context-p () + (if-let ((arg (--first (string-match "^-U\\([0-9]+\\)$" it) + magit-buffer-diff-args))) + (not (equal arg "-U0")) + t)) + +(defun magit-diff-ignore-any-space-p () + (--any-p (member it magit-buffer-diff-args) + '("--ignore-cr-at-eol" + "--ignore-space-at-eol" + "--ignore-space-change" "-b" + "--ignore-all-space" "-w" + "--ignore-blank-space"))) + +(defun magit-diff-toggle-refine-hunk (&optional style) + "Turn diff-hunk refining on or off. + +If hunk refining is currently on, then hunk refining is turned off. +If hunk refining is off, then hunk refining is turned on, in +`selected' mode (only the currently selected hunk is refined). + +With a prefix argument, the \"third choice\" is used instead: +If hunk refining is currently on, then refining is kept on, but +the refining mode (`selected' or `all') is switched. +If hunk refining is off, then hunk refining is turned on, in +`all' mode (all hunks refined). + +Customize variable `magit-diff-refine-hunk' to change the default mode." + (interactive "P") + (setq-local magit-diff-refine-hunk + (if style + (if (eq magit-diff-refine-hunk 'all) t 'all) + (not magit-diff-refine-hunk))) + (magit-diff-update-hunk-refinement)) + +;;;; Visit Commands +;;;;; Dwim Variants + +(defun magit-diff-visit-file (file &optional other-window) + "From a diff visit the appropriate version of FILE. + +Display the buffer in the selected window. With a prefix +argument OTHER-WINDOW display the buffer in another window +instead. + +Visit the worktree version of the appropriate file. The location +of point inside the diff determines which file is being visited. +The visited version depends on what changes the diff is about. + +1. If the diff shows uncommitted changes (i.e. stage or unstaged + changes), then visit the file in the working tree (i.e. the + same \"real\" file that `find-file' would visit. In all other + cases visit a \"blob\" (i.e. the version of a file as stored + in some commit). + +2. If point is on a removed line, then visit the blob for the + first parent of the commit that removed that line, i.e. the + last commit where that line still exists. + +3. If point is on an added or context line, then visit the blob + that adds that line, or if the diff shows from more than a + single commit, then visit the blob from the last of these + commits. + +In the file-visiting buffer also go to the line that corresponds +to the line that point is on in the diff. + +Note that this command only works if point is inside a diff. +In other cases `magit-find-file' (which see) has to be used." + (interactive (list (magit-file-at-point t t) current-prefix-arg)) + (magit-diff-visit-file--internal file nil + (if other-window + #'switch-to-buffer-other-window + #'pop-to-buffer-same-window))) + +(defun magit-diff-visit-file-other-window (file) + "From a diff visit the appropriate version of FILE in another window. +Like `magit-diff-visit-file' but use +`switch-to-buffer-other-window'." + (interactive (list (magit-file-at-point t t))) + (magit-diff-visit-file--internal file nil #'switch-to-buffer-other-window)) + +(defun magit-diff-visit-file-other-frame (file) + "From a diff visit the appropriate version of FILE in another frame. +Like `magit-diff-visit-file' but use +`switch-to-buffer-other-frame'." + (interactive (list (magit-file-at-point t t))) + (magit-diff-visit-file--internal file nil #'switch-to-buffer-other-frame)) + +;;;;; Worktree Variants + +(defun magit-diff-visit-worktree-file (file &optional other-window) + "From a diff visit the worktree version of FILE. + +Display the buffer in the selected window. With a prefix +argument OTHER-WINDOW display the buffer in another window +instead. + +Visit the worktree version of the appropriate file. The location +of point inside the diff determines which file is being visited. + +Unlike `magit-diff-visit-file' always visits the \"real\" file in +the working tree, i.e the \"current version\" of the file. + +In the file-visiting buffer also go to the line that corresponds +to the line that point is on in the diff. Lines that were added +or removed in the working tree, the index and other commits in +between are automatically accounted for." + (interactive (list (magit-file-at-point t t) current-prefix-arg)) + (magit-diff-visit-file--internal file t + (if other-window + #'switch-to-buffer-other-window + #'pop-to-buffer-same-window))) + +(defun magit-diff-visit-worktree-file-other-window (file) + "From a diff visit the worktree version of FILE in another window. +Like `magit-diff-visit-worktree-file' but use +`switch-to-buffer-other-window'." + (interactive (list (magit-file-at-point t t))) + (magit-diff-visit-file--internal file t #'switch-to-buffer-other-window)) + +(defun magit-diff-visit-worktree-file-other-frame (file) + "From a diff visit the worktree version of FILE in another frame. +Like `magit-diff-visit-worktree-file' but use +`switch-to-buffer-other-frame'." + (interactive (list (magit-file-at-point t t))) + (magit-diff-visit-file--internal file t #'switch-to-buffer-other-frame)) + +;;;;; Internal + +(defun magit-diff-visit-file--internal (file force-worktree fn) + "From a diff visit the appropriate version of FILE. +If FORCE-WORKTREE is non-nil, then visit the worktree version of +the file, even if the diff is about a committed change. Use FN +to display the buffer in some window." + (if (magit-file-accessible-directory-p file) + (magit-diff-visit-directory file force-worktree) + (pcase-let ((`(,buf ,pos) + (magit-diff-visit-file--noselect file force-worktree))) + (funcall fn buf) + (magit-diff-visit-file--setup buf pos) + buf))) + +(defun magit-diff-visit-directory (directory &optional other-window) + "Visit DIRECTORY in some window. +Display the buffer in the selected window unless OTHER-WINDOW is +non-nil. If DIRECTORY is the top-level directory of the current +repository, then visit the containing directory using Dired and +in the Dired buffer put point on DIRECTORY. Otherwise display +the Magit-Status buffer for DIRECTORY." + (if (equal (magit-toplevel directory) + (magit-toplevel)) + (dired-jump other-window (concat directory "/.")) + (let ((display-buffer-overriding-action + (if other-window + '(nil (inhibit-same-window t)) + '(display-buffer-same-window)))) + (magit-status-setup-buffer directory)))) + +(defun magit-diff-visit-file--setup (buf pos) + (if-let ((win (get-buffer-window buf 'visible))) + (with-selected-window win + (when pos + (unless (<= (point-min) pos (point-max)) + (widen)) + (goto-char pos)) + (when (and buffer-file-name + (magit-anything-unmerged-p buffer-file-name)) + (smerge-start-session)) + (run-hooks 'magit-diff-visit-file-hook)) + (error "File buffer is not visible"))) + +(defun magit-diff-visit-file--noselect (&optional file goto-worktree) + (unless file + (setq file (magit-file-at-point t t))) + (let* ((hunk (magit-diff-visit--hunk)) + (goto-from (and hunk + (magit-diff-visit--goto-from-p hunk goto-worktree))) + (line (and hunk (magit-diff-hunk-line hunk goto-from))) + (col (and hunk (magit-diff-hunk-column hunk goto-from))) + (spec (magit-diff--dwim)) + (rev (if goto-from + (magit-diff-visit--range-from spec) + (magit-diff-visit--range-to spec))) + (buf (if (or goto-worktree + (and (not (stringp rev)) + (or magit-diff-visit-avoid-head-blob + (not goto-from)))) + (or (get-file-buffer file) + (find-file-noselect file)) + (magit-find-file-noselect (if (stringp rev) rev "HEAD") + file)))) + (if line + (with-current-buffer buf + (cond ((eq rev 'staged) + (setq line (magit-diff-visit--offset file nil line))) + ((and goto-worktree + (stringp rev)) + (setq line (magit-diff-visit--offset file rev line)))) + (list buf (save-restriction + (widen) + (goto-char (point-min)) + (forward-line (1- line)) + (move-to-column col) + (point)))) + (list buf nil)))) + +(defun magit-diff-visit--hunk () + (when-let* ((scope (magit-diff-scope)) ;debbugs#31840 + (section (magit-current-section))) + (cl-case scope + ((file files) + (setq section (car (oref section children)))) + (list + (setq section (car (oref section children))) + (when section + (setq section (car (oref section children)))))) + (and + ;; Unmerged files appear in the list of staged changes + ;; but unlike in the list of unstaged changes no diffs + ;; are shown here. In that case `section' is nil. + section + ;; Currently the `hunk' type is also abused for file + ;; mode changes, which we are not interested in here. + (not (equal (oref section value) '(chmod))) + section))) + +(defun magit-diff-visit--goto-from-p (section in-worktree) + (and magit-diff-visit-previous-blob + (not in-worktree) + (not (oref section combined)) + (not (< (magit-point) (oref section content))) + (= (char-after (line-beginning-position)) ?-))) + +(defvar magit-diff-visit-jump-to-change t) + +(defun magit-diff-hunk-line (section goto-from) + (save-excursion + (goto-char (line-beginning-position)) + (with-slots (content combined from-ranges from-range to-range) section + (when (or from-range to-range) + (when (and magit-diff-visit-jump-to-change (< (point) content)) + (goto-char content) + (re-search-forward "^[-+]")) + (+ (car (if goto-from from-range to-range)) + (let ((prefix (if combined (length from-ranges) 1)) + (target (point)) + (offset 0)) + (goto-char content) + (while (< (point) target) + (unless (string-search + (if goto-from "+" "-") + (buffer-substring (point) (+ (point) prefix))) + (cl-incf offset)) + (forward-line)) + offset)))))) + +(defun magit-diff-hunk-column (section goto-from) + (if (or (< (magit-point) + (oref section content)) + (and (not goto-from) + (= (char-after (line-beginning-position)) ?-))) + 0 + (max 0 (- (+ (current-column) 2) + (length (oref section value)))))) + +(defun magit-diff-visit--range-from (spec) + (cond ((consp spec) + (concat (cdr spec) "^")) + ((stringp spec) + (car (magit-split-range spec))) + (t + spec))) + +(defun magit-diff-visit--range-to (spec) + (if (symbolp spec) + spec + (let ((rev (if (consp spec) + (cdr spec) + (cdr (magit-split-range spec))))) + (if (and magit-diff-visit-avoid-head-blob + (magit-rev-head-p rev)) + 'unstaged + rev)))) + +(defun magit-diff-visit--offset (file rev line) + (let ((offset 0)) + (with-temp-buffer + (save-excursion + (magit-with-toplevel + (magit-git-insert "diff" rev "--" file))) + (catch 'found + (while (re-search-forward + "^@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@.*\n" + nil t) + (let ((from-beg (string-to-number (match-string 1))) + (from-len (string-to-number (match-string 2))) + ( to-len (string-to-number (match-string 4)))) + (if (<= from-beg line) + (if (< (+ from-beg from-len) line) + (cl-incf offset (- to-len from-len)) + (let ((rest (- line from-beg))) + (while (> rest 0) + (pcase (char-after) + (?\s (cl-decf rest)) + (?- (cl-decf offset) (cl-decf rest)) + (?+ (cl-incf offset))) + (forward-line)))) + (throw 'found nil)))))) + (+ line offset))) + +;;;; Scroll Commands + +(defun magit-diff-show-or-scroll-up () + "Update the commit or diff buffer for the thing at point. + +Either show the commit or stash at point in the appropriate +buffer, or if that buffer is already being displayed in the +current frame and contains information about that commit or +stash, then instead scroll the buffer up. If there is no +commit or stash at point, then prompt for a commit." + (interactive) + (magit-diff-show-or-scroll #'scroll-up)) + +(defun magit-diff-show-or-scroll-down () + "Update the commit or diff buffer for the thing at point. + +Either show the commit or stash at point in the appropriate +buffer, or if that buffer is already being displayed in the +current frame and contains information about that commit or +stash, then instead scroll the buffer down. If there is no +commit or stash at point, then prompt for a commit." + (interactive) + (magit-diff-show-or-scroll #'scroll-down)) + +(defun magit-diff-show-or-scroll (fn) + (let (rev cmd buf win) + (cond + ((and (bound-and-true-p magit-blame-mode) + (fboundp 'magit-current-blame-chunk)) + (setq rev (oref (magit-current-blame-chunk) orig-rev)) + (setq cmd #'magit-show-commit) + (setq buf (magit-get-mode-buffer 'magit-revision-mode))) + ((derived-mode-p 'git-rebase-mode) + (with-slots (action-type target) + (git-rebase-current-line) + (if (not (eq action-type 'commit)) + (user-error "No commit on this line") + (setq rev target) + (setq cmd #'magit-show-commit) + (setq buf (magit-get-mode-buffer 'magit-revision-mode))))) + (t + (magit-section-case + (branch + (setq rev (magit-ref-maybe-qualify (oref it value))) + (setq cmd #'magit-show-commit) + (setq buf (magit-get-mode-buffer 'magit-revision-mode))) + (commit + (setq rev (oref it value)) + (setq cmd #'magit-show-commit) + (setq buf (magit-get-mode-buffer 'magit-revision-mode))) + (stash + (setq rev (oref it value)) + (setq cmd #'magit-stash-show) + (setq buf (magit-get-mode-buffer 'magit-stash-mode)))))) + (if rev + (if (and buf + (setq win (get-buffer-window buf)) + (with-current-buffer buf + (and (equal rev magit-buffer-revision) + (equal (magit-rev-parse rev) + magit-buffer-revision-hash)))) + (with-selected-window win + (condition-case nil + (funcall fn) + (error + (goto-char (pcase fn + ('scroll-up (point-min)) + ('scroll-down (point-max))))))) + (let ((magit-display-buffer-noselect t)) + (if (eq cmd #'magit-show-commit) + (apply #'magit-show-commit rev (magit-show-commit--arguments)) + (funcall cmd rev)))) + (call-interactively #'magit-show-commit)))) + +;;;; Section Commands + +(defun magit-section-cycle-diffs () + "Cycle visibility of diff-related sections in the current buffer." + (interactive) + (when-let ((sections + (cond ((derived-mode-p 'magit-status-mode) + (--mapcat + (when it + (when (oref it hidden) + (magit-section-show it)) + (oref it children)) + (list (magit-get-section '((staged) (status))) + (magit-get-section '((unstaged) (status)))))) + ((derived-mode-p 'magit-diff-mode) + (-filter #'magit-file-section-p + (oref magit-root-section children)))))) + (if (--any-p (oref it hidden) sections) + (dolist (s sections) + (magit-section-show s) + (magit-section-hide-children s)) + (let ((children (--mapcat (oref it children) sections))) + (cond ((and (--any-p (oref it hidden) children) + (--any-p (oref it children) children)) + (mapc #'magit-section-show-headings sections)) + ((seq-some #'magit-section-hidden-body children) + (mapc #'magit-section-show-children sections)) + (t + (mapc #'magit-section-hide sections))))))) + +;;; Diff Mode + +(defvar magit-diff-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map (kbd "C-c C-d") #'magit-diff-while-committing) + (define-key map (kbd "C-c C-b") #'magit-go-backward) + (define-key map (kbd "C-c C-f") #'magit-go-forward) + (define-key map (kbd "SPC") #'scroll-up) + (define-key map (kbd "DEL") #'scroll-down) + (define-key map (kbd "j") #'magit-jump-to-diffstat-or-diff) + (define-key map [remap write-file] #'magit-patch-save) + map) + "Keymap for `magit-diff-mode'.") + +(define-derived-mode magit-diff-mode magit-mode "Magit Diff" + "Mode for looking at a Git diff. + +This mode is documented in info node `(magit)Diff Buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] to visit the hunk or file at point. + +Staging and applying changes is documented in info node +`(magit)Staging and Unstaging' and info node `(magit)Applying'. + +\\Type \ +\\[magit-apply] to apply the change at point, \ +\\[magit-stage] to stage, +\\[magit-unstage] to unstage, \ +\\[magit-discard] to discard, or \ +\\[magit-reverse] to reverse it. + +\\{magit-diff-mode-map}" + :group 'magit-diff + (hack-dir-local-variables-non-file-buffer) + (setq magit--imenu-item-types 'file)) + +(put 'magit-diff-mode 'magit-diff-default-arguments + '("--stat" "--no-ext-diff")) + +(defun magit-diff-setup-buffer (range typearg args files &optional locked) + (require 'magit) + (magit-setup-buffer #'magit-diff-mode locked + (magit-buffer-range range) + (magit-buffer-typearg typearg) + (magit-buffer-diff-args args) + (magit-buffer-diff-files files) + (magit-buffer-diff-files-suspended nil))) + +(defun magit-diff-refresh-buffer () + "Refresh the current `magit-diff-mode' buffer." + (magit-set-header-line-format + (if (equal magit-buffer-typearg "--no-index") + (apply #'format "Differences between %s and %s" magit-buffer-diff-files) + (concat (if magit-buffer-range + (if (string-match-p "\\(\\.\\.\\|\\^-\\)" + magit-buffer-range) + (format "Changes in %s" magit-buffer-range) + (let ((msg "Changes from %s to %s") + (end (if (equal magit-buffer-typearg "--cached") + "index" + "working tree"))) + (if (member "-R" magit-buffer-diff-args) + (format msg end magit-buffer-range) + (format msg magit-buffer-range end)))) + (cond ((equal magit-buffer-typearg "--cached") + "Staged changes") + ((and (magit-repository-local-get 'this-commit-command) + (not (magit-anything-staged-p))) + "Uncommitting changes") + (t "Unstaged changes"))) + (pcase (length magit-buffer-diff-files) + (0) + (1 (concat " in file " (car magit-buffer-diff-files))) + (_ (concat " in files " + (mapconcat #'identity magit-buffer-diff-files ", "))))))) + (setq magit-buffer-range-hashed + (and magit-buffer-range (magit-hash-range magit-buffer-range))) + (magit-insert-section (diffbuf) + (magit-run-section-hook 'magit-diff-sections-hook))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-diff-mode)) + (nconc (cond (magit-buffer-range + (delq nil (list magit-buffer-range magit-buffer-typearg))) + ((equal magit-buffer-typearg "--cached") + (list 'staged)) + (t + (list 'unstaged magit-buffer-typearg))) + (and magit-buffer-diff-files (cons "--" magit-buffer-diff-files)))) + +(cl-defmethod magit-menu-common-value ((_section magit-diff-section)) + (magit-diff-scope)) + +(define-obsolete-variable-alias 'magit-diff-section-base-map + 'magit-diff-section-map "Magit-Section 3.4.0") +(defvar magit-diff-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-cherry-apply] + #'magit-apply "Apply %x" + '(:enable (not (memq (magit-diff-type) '(unstaged staged))))) + (magit-menu-set map [magit-stage-file] + #'magit-stage "Stage %x" + '(:enable (eq (magit-diff-type) 'unstaged))) + (magit-menu-set map [magit-unstage-file] + #'magit-unstage "Unstage %x" + '(:enable (eq (magit-diff-type) 'staged))) + (magit-menu-set map [magit-delete-thing] + #'magit-discard "Discard %x" + '(:enable (not (memq (magit-diff-type) '(committed undefined))))) + (magit-menu-set map [magit-revert-no-commit] + #'magit-reverse "Reverse %x" + '(:enable (not (memq (magit-diff-type) '(untracked unstaged))))) + (magit-menu-set map [magit-visit-thing] + #'magit-diff-visit-file "Visit file") + (magit-menu-set map [magit-file-untrack] + #'magit-file-untrack "Untrack %x" + '(:enable (memq (magit-diff-scope) '(file files)))) + (magit-menu-set map [magit-file-rename] + #'magit-file-rename "Rename file" + '(:enable (eq (magit-diff-scope) 'file))) + (define-key map (kbd "C-j") #'magit-diff-visit-worktree-file) + (define-key map (kbd "C-") #'magit-diff-visit-worktree-file) + (define-key map (kbd "C-x 4 ") #'magit-diff-visit-file-other-window) + (define-key map (kbd "C-x 5 ") #'magit-diff-visit-file-other-frame) + (define-key map "&" #'magit-do-async-shell-command) + (define-key map "C" #'magit-commit-add-log) + (define-key map (kbd "C-x a") #'magit-add-change-log-entry) + (define-key map (kbd "C-x 4 a") #'magit-add-change-log-entry-other-window) + (define-key map (kbd "C-c C-t") #'magit-diff-trace-definition) + (define-key map (kbd "C-c C-e") #'magit-diff-edit-hunk-commit) + map) + "Keymap for diff sections. +The classes `magit-file-section' and `magit-hunk-section' derive +from the abstract `magit-diff-section' class. Accordingly this +keymap is the parent of their keymaps.") + +(defvar magit-file-section-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-diff-section-base-map) + map) + "Keymap for `file' sections.") + +(defvar magit-hunk-section-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-diff-section-base-map) + (let ((m (make-sparse-keymap))) + (define-key m (kbd "RET") #'magit-smerge-keep-current) + (define-key m (kbd "u") #'magit-smerge-keep-upper) + (define-key m (kbd "b") #'magit-smerge-keep-base) + (define-key m (kbd "l") #'magit-smerge-keep-lower) + (define-key map smerge-command-prefix m)) + map) + "Keymap for `hunk' sections.") + +(defconst magit-diff-conflict-headline-re + (concat "^" (regexp-opt + ;; Defined in merge-tree.c in this order. + '("merged" + "added in remote" + "added in both" + "added in local" + "removed in both" + "changed in both" + "removed in local" + "removed in remote")))) + +(defconst magit-diff-headline-re + (concat "^\\(@@@?\\|diff\\|Submodule\\|" + "\\* Unmerged path\\|" + (substring magit-diff-conflict-headline-re 1) + "\\)")) + +(defconst magit-diff-statline-re + (concat "^ ?" + "\\(.*\\)" ; file + "\\( +| +\\)" ; separator + "\\([0-9]+\\|Bin\\(?: +[0-9]+ -> [0-9]+ bytes\\)?$\\) ?" + "\\(\\+*\\)" ; add + "\\(-*\\)$")) ; del + +(defvar magit-diff--reset-non-color-moved + (list + "-c" "color.diff.context=normal" + "-c" "color.diff.plain=normal" ; historical synonym for context + "-c" "color.diff.meta=normal" + "-c" "color.diff.frag=normal" + "-c" "color.diff.func=normal" + "-c" "color.diff.old=normal" + "-c" "color.diff.new=normal" + "-c" "color.diff.commit=normal" + "-c" "color.diff.whitespace=normal" + ;; "git-range-diff" does not support "--color-moved", so we don't + ;; need to reset contextDimmed, oldDimmed, newDimmed, contextBold, + ;; oldBold, and newBold. + )) + +(defun magit-insert-diff () + "Insert the diff into this `magit-diff-mode' buffer." + (magit--insert-diff + "diff" magit-buffer-range "-p" "--no-prefix" + (and (member "--stat" magit-buffer-diff-args) "--numstat") + magit-buffer-typearg + magit-buffer-diff-args "--" + magit-buffer-diff-files)) + +(defun magit--insert-diff (&rest args) + (declare (indent 0)) + (pcase-let ((`(,cmd . ,args) + (flatten-tree args)) + (magit-git-global-arguments + (remove "--literal-pathspecs" magit-git-global-arguments))) + ;; As of Git 2.19.0, we need to generate diffs with + ;; --ita-visible-in-index so that `magit-stage' can work with + ;; intent-to-add files (see #4026). + (when (and (not (equal cmd "merge-tree")) + (magit-git-version>= "2.19.0")) + (push "--ita-visible-in-index" args)) + (setq args (magit-diff--maybe-add-stat-arguments args)) + (when (cl-member-if (lambda (arg) (string-prefix-p "--color-moved" arg)) args) + (push "--color=always" args) + (setq magit-git-global-arguments + (append magit-diff--reset-non-color-moved + magit-git-global-arguments))) + (magit-git-wash #'magit-diff-wash-diffs cmd args))) + +(defun magit-diff--maybe-add-stat-arguments (args) + (if (member "--stat" args) + (append (if (functionp magit-diff-extra-stat-arguments) + (funcall magit-diff-extra-stat-arguments) + magit-diff-extra-stat-arguments) + args) + args)) + +(defun magit-diff-use-window-width-as-stat-width () + "Use the `window-width' as the value of `--stat-width'." + (and-let* ((window (get-buffer-window (current-buffer) 'visible))) + (list (format "--stat-width=%d" (window-width window))))) + +(defun magit-diff-wash-diffs (args &optional limit) + (run-hooks 'magit-diff-wash-diffs-hook) + (when (member "--show-signature" args) + (magit-diff-wash-signature magit-buffer-revision-hash)) + (when (member "--stat" args) + (magit-diff-wash-diffstat)) + (when (re-search-forward magit-diff-headline-re limit t) + (goto-char (line-beginning-position)) + (magit-wash-sequence (apply-partially #'magit-diff-wash-diff args)) + (insert ?\n))) + +(defun magit-jump-to-diffstat-or-diff () + "Jump to the diffstat or diff. +When point is on a file inside the diffstat section, then jump +to the respective diff section, otherwise jump to the diffstat +section or a child thereof." + (interactive) + (--if-let (magit-get-section + (append (magit-section-case + ([file diffstat] `((file . ,(oref it value)))) + (file `((file . ,(oref it value)) (diffstat))) + (t '((diffstat)))) + (magit-section-ident magit-root-section))) + (magit-section-goto it) + (user-error "No diffstat in this buffer"))) + +(defun magit-diff-wash-signature (object) + (when (looking-at "^gpg: ") + (let (title end) + (save-excursion + (while (looking-at "^gpg: ") + (cond + ((looking-at "^gpg: Good signature from") + (setq title (propertize + (buffer-substring (point) (line-end-position)) + 'face 'magit-signature-good))) + ((looking-at "^gpg: Can't check signature") + (setq title (propertize + (buffer-substring (point) (line-end-position)) + 'face '(italic bold))))) + (forward-line)) + (setq end (point-marker))) + (magit-insert-section (signature object title) + (when title + (magit-insert-heading title)) + (goto-char end) + (set-marker end nil) + (insert "\n"))))) + +(defun magit-diff-wash-diffstat () + (let (heading (beg (point))) + (when (re-search-forward "^ ?\\([0-9]+ +files? change[^\n]*\n\\)" nil t) + (setq heading (match-string 1)) + (magit-delete-match) + (goto-char beg) + (magit-insert-section (diffstat) + (insert (propertize heading 'font-lock-face 'magit-diff-file-heading)) + (magit-insert-heading) + (let (files) + (while (looking-at "^[-0-9]+\t[-0-9]+\t\\(.+\\)$") + (push (magit-decode-git-path + (let ((f (match-string 1))) + (cond + ((string-match "\\`\\([^{]+\\){\\(.+\\) => \\(.+\\)}\\'" f) + (concat (match-string 1 f) + (match-string 3 f))) + ((string-match " => " f) + (substring f (match-end 0))) + (t f)))) + files) + (magit-delete-line)) + (setq files (nreverse files)) + (while (looking-at magit-diff-statline-re) + (magit-bind-match-strings (file sep cnt add del) nil + (magit-delete-line) + (when (string-match " +$" file) + (setq sep (concat (match-string 0 file) sep)) + (setq file (substring file 0 (match-beginning 0)))) + (let ((le (length file)) ld) + (setq file (magit-decode-git-path file)) + (setq ld (length file)) + (when (> le ld) + (setq sep (concat (make-string (- le ld) ?\s) sep)))) + (magit-insert-section (file (pop files)) + (insert (propertize file 'font-lock-face 'magit-filename) + sep cnt " ") + (when add + (insert (propertize add 'font-lock-face + 'magit-diffstat-added))) + (when del + (insert (propertize del 'font-lock-face + 'magit-diffstat-removed))) + (insert "\n"))))) + (if (looking-at "^$") (forward-line) (insert "\n")))))) + +(defun magit-diff-wash-diff (args) + (when (cl-member-if (lambda (arg) (string-prefix-p "--color-moved" arg)) args) + (require 'ansi-color) + (ansi-color-apply-on-region (point-min) (point-max))) + (cond + ((looking-at "^Submodule") + (magit-diff-wash-submodule)) + ((looking-at "^\\* Unmerged path \\(.*\\)") + (let ((file (magit-decode-git-path (match-string 1)))) + (magit-delete-line) + (unless (and (derived-mode-p 'magit-status-mode) + (not (member "--cached" args))) + (magit-insert-section (file file) + (insert (propertize + (format "unmerged %s%s" file + (pcase (cddr (car (magit-file-status file))) + ('(?D ?D) " (both deleted)") + ('(?D ?U) " (deleted by us)") + ('(?U ?D) " (deleted by them)") + ('(?A ?A) " (both added)") + ('(?A ?U) " (added by us)") + ('(?U ?A) " (added by them)") + ('(?U ?U) ""))) + 'font-lock-face 'magit-diff-file-heading)) + (insert ?\n)))) + t) + ((looking-at magit-diff-conflict-headline-re) + (let ((long-status (match-string 0)) + (status "BUG") + file orig base) + (if (equal long-status "merged") + (progn (setq status long-status) + (setq long-status nil)) + (setq status (pcase-exhaustive long-status + ("added in remote" "new file") + ("added in both" "new file") + ("added in local" "new file") + ("removed in both" "removed") + ("changed in both" "changed") + ("removed in local" "removed") + ("removed in remote" "removed")))) + (magit-delete-line) + (while (looking-at + "^ \\([^ ]+\\) +[0-9]\\{6\\} \\([a-z0-9]\\{40,\\}\\) \\(.+\\)$") + (magit-bind-match-strings (side _blob name) nil + (pcase side + ("result" (setq file name)) + ("our" (setq orig name)) + ("their" (setq file name)) + ("base" (setq base name)))) + (magit-delete-line)) + (when orig (setq orig (magit-decode-git-path orig))) + (when file (setq file (magit-decode-git-path file))) + (magit-diff-insert-file-section + (or file base) orig status nil nil nil long-status))) + ;; The files on this line may be ambiguous due to whitespace. + ;; That's okay. We can get their names from subsequent headers. + ((looking-at "^diff --\ +\\(?:\\(?1:git\\) \\(?:\\(?2:.+?\\) \\2\\)?\ +\\|\\(?:cc\\|combined\\) \\(?3:.+\\)\\)") + (let ((status (cond ((equal (match-string 1) "git") "modified") + ((derived-mode-p 'magit-revision-mode) "resolved") + (t "unmerged"))) + (orig nil) + (file (or (match-string 2) (match-string 3))) + (header (list (buffer-substring-no-properties + (line-beginning-position) (1+ (line-end-position))))) + (modes nil) + (rename nil)) + (magit-delete-line) + (while (not (or (eobp) (looking-at magit-diff-headline-re))) + (cond + ((looking-at "old mode \\(?:[^\n]+\\)\nnew mode \\(?:[^\n]+\\)\n") + (setq modes (match-string 0))) + ((looking-at "deleted file .+\n") + (setq status "deleted")) + ((looking-at "new file .+\n") + (setq status "new file")) + ((looking-at "rename from \\(.+\\)\nrename to \\(.+\\)\n") + (setq rename (match-string 0)) + (setq orig (match-string 1)) + (setq file (match-string 2)) + (setq status "renamed")) + ((looking-at "copy from \\(.+\\)\ncopy to \\(.+\\)\n") + (setq orig (match-string 1)) + (setq file (match-string 2)) + (setq status "new file")) + ((looking-at "similarity index .+\n")) + ((looking-at "dissimilarity index .+\n")) + ((looking-at "index .+\n")) + ((looking-at "--- \\(.+?\\)\t?\n") + (unless (equal (match-string 1) "/dev/null") + (setq orig (match-string 1)))) + ((looking-at "\\+\\+\\+ \\(.+?\\)\t?\n") + (unless (equal (match-string 1) "/dev/null") + (setq file (match-string 1)))) + ((looking-at "Binary files .+ and .+ differ\n")) + ((looking-at "Binary files differ\n")) + ;; TODO Use all combined diff extended headers. + ((looking-at "mode .+\n")) + (t + (error "BUG: Unknown extended header: %S" + (buffer-substring (point) (line-end-position))))) + ;; These headers are treated as some sort of special hunk. + (unless (or (string-prefix-p "old mode" (match-string 0)) + (string-prefix-p "rename" (match-string 0))) + (push (match-string 0) header)) + (magit-delete-match)) + (setq header (mapconcat #'identity (nreverse header) "")) + (when orig + (setq orig (magit-decode-git-path orig))) + (setq file (magit-decode-git-path file)) + ;; KLUDGE `git-diff' ignores `--no-prefix' for new files and renames at + ;; least. And `git-log' ignores `--no-prefix' when `-L' is used. + (when (or (and file orig + (string-prefix-p "a/" orig) + (string-prefix-p "b/" file)) + (and (derived-mode-p 'magit-log-mode) + (--first (string-prefix-p "-L" it) + magit-buffer-log-args))) + (setq file (substring file 2)) + (when orig + (setq orig (substring orig 2)))) + (magit-diff-insert-file-section file orig status modes rename header))))) + +(defun magit-diff-insert-file-section + (file orig status modes rename header &optional long-status) + (magit-insert-section section + (file file (or (equal status "deleted") + (derived-mode-p 'magit-status-mode))) + (insert (propertize (format "%-10s %s" status + (if (or (not orig) (equal orig file)) + file + (format "%s -> %s" orig file))) + 'font-lock-face 'magit-diff-file-heading)) + (when long-status + (insert (format " (%s)" long-status))) + (magit-insert-heading) + (unless (equal orig file) + (oset section source orig)) + (oset section header header) + (when modes + (magit-insert-section (hunk '(chmod)) + (insert modes) + (magit-insert-heading))) + (when rename + (magit-insert-section (hunk '(rename)) + (insert rename) + (magit-insert-heading))) + (magit-wash-sequence #'magit-diff-wash-hunk))) + +(defun magit-diff-wash-submodule () + ;; See `show_submodule_summary' in submodule.c and "this" commit. + (when (looking-at "^Submodule \\([^ ]+\\)") + (let ((module (match-string 1)) + untracked modified) + (when (looking-at "^Submodule [^ ]+ contains untracked content$") + (magit-delete-line) + (setq untracked t)) + (when (looking-at "^Submodule [^ ]+ contains modified content$") + (magit-delete-line) + (setq modified t)) + (cond + ((and (looking-at "^Submodule \\([^ ]+\\) \\([^ :]+\\)\\( (rewind)\\)?:$") + (equal (match-string 1) module)) + (magit-bind-match-strings (_module range rewind) nil + (magit-delete-line) + (while (looking-at "^ \\([<>]\\) \\(.*\\)$") + (magit-delete-line)) + (when rewind + (setq range (replace-regexp-in-string "[^.]\\(\\.\\.\\)[^.]" + "..." range t t 1))) + (magit-insert-section (magit-module-section module t) + (magit-insert-heading + (propertize (concat "modified " module) + 'font-lock-face 'magit-diff-file-heading) + " (" + (cond (rewind "rewind") + ((string-search "..." range) "non-ff") + (t "new commits")) + (and (or modified untracked) + (concat ", " + (and modified "modified") + (and modified untracked " and ") + (and untracked "untracked") + " content")) + ")") + (let ((default-directory + (file-name-as-directory + (expand-file-name module (magit-toplevel))))) + (magit-git-wash (apply-partially #'magit-log-wash-log 'module) + "log" "--oneline" "--left-right" range) + (delete-char -1))))) + ((and (looking-at "^Submodule \\([^ ]+\\) \\([^ ]+\\) (\\([^)]+\\))$") + (equal (match-string 1) module)) + (magit-bind-match-strings (_module _range msg) nil + (magit-delete-line) + (magit-insert-section (magit-module-section module) + (magit-insert-heading + (propertize (concat "submodule " module) + 'font-lock-face 'magit-diff-file-heading) + " (" msg ")")))) + (t + (magit-insert-section (magit-module-section module) + (magit-insert-heading + (propertize (concat "modified " module) + 'font-lock-face 'magit-diff-file-heading) + " (" + (and modified "modified") + (and modified untracked " and ") + (and untracked "untracked") + " content)"))))))) + +(defun magit-diff-wash-hunk () + (when (looking-at "^@\\{2,\\} \\(.+?\\) @\\{2,\\}\\(?: \\(.*\\)\\)?") + (let* ((heading (match-string 0)) + (ranges (mapcar + (lambda (str) + (let ((range + (mapcar #'string-to-number + (split-string (substring str 1) ",")))) + ;; A single line is +1 rather than +1,1. + (if (length= range 1) + (nconc range (list 1)) + range))) + (split-string (match-string 1)))) + (about (match-string 2)) + (combined (length= ranges 3)) + (value (cons about ranges))) + (magit-delete-line) + (magit-insert-section section (hunk value) + (insert (propertize (concat heading "\n") + 'font-lock-face 'magit-diff-hunk-heading)) + (magit-insert-heading) + (while (not (or (eobp) (looking-at "^[^-+\s\\]"))) + (forward-line)) + (oset section end (point)) + (oset section washer #'magit-diff-paint-hunk) + (oset section combined combined) + (if combined + (oset section from-ranges (butlast ranges)) + (oset section from-range (car ranges))) + (oset section to-range (car (last ranges))) + (oset section about about))) + t)) + +(defun magit-diff-expansion-threshold (section) + "Keep new diff sections collapsed if washing takes too long." + (and (magit-file-section-p section) + (> (float-time (time-subtract (current-time) magit-refresh-start-time)) + magit-diff-expansion-threshold) + 'hide)) + +(add-hook 'magit-section-set-visibility-hook #'magit-diff-expansion-threshold) + +;;; Revision Mode + +(define-derived-mode magit-revision-mode magit-diff-mode "Magit Rev" + "Mode for looking at a Git commit. + +This mode is documented in info node `(magit)Revision Buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] to visit the hunk or file at point. + +Staging and applying changes is documented in info node +`(magit)Staging and Unstaging' and info node `(magit)Applying'. + +\\Type \ +\\[magit-apply] to apply the change at point, \ +\\[magit-stage] to stage, +\\[magit-unstage] to unstage, \ +\\[magit-discard] to discard, or \ +\\[magit-reverse] to reverse it. + +\\{magit-revision-mode-map}" + :group 'magit-revision + (hack-dir-local-variables-non-file-buffer)) + +(put 'magit-revision-mode 'magit-diff-default-arguments + '("--stat" "--no-ext-diff")) + +(defun magit-revision-setup-buffer (rev args files) + (magit-setup-buffer #'magit-revision-mode nil + (magit-buffer-revision rev) + (magit-buffer-range (format "%s^..%s" rev rev)) + (magit-buffer-diff-args args) + (magit-buffer-diff-files files) + (magit-buffer-diff-files-suspended nil))) + +(defun magit-revision-refresh-buffer () + (setq magit-buffer-revision-hash (magit-rev-hash magit-buffer-revision)) + (magit-set-header-line-format + (concat (magit-object-type magit-buffer-revision-hash) + " " magit-buffer-revision + (pcase (length magit-buffer-diff-files) + (0) + (1 (concat " limited to file " (car magit-buffer-diff-files))) + (_ (concat " limited to files " + (mapconcat #'identity magit-buffer-diff-files ", ")))))) + (magit-insert-section (commitbuf) + (magit-run-section-hook 'magit-revision-sections-hook))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-revision-mode)) + (cons magit-buffer-revision magit-buffer-diff-files)) + +(defun magit-insert-revision-diff () + "Insert the diff into this `magit-revision-mode' buffer." + (magit--insert-diff + "show" "-p" "--cc" "--format=" "--no-prefix" + (and (member "--stat" magit-buffer-diff-args) "--numstat") + magit-buffer-diff-args + (magit--rev-dereference magit-buffer-revision) + "--" magit-buffer-diff-files)) + +(defun magit-insert-revision-tag () + "Insert tag message and headers into a revision buffer. +This function only inserts anything when `magit-show-commit' is +called with a tag as argument, when that is called with a commit +or a ref which is not a branch, then it inserts nothing." + (when (equal (magit-object-type magit-buffer-revision) "tag") + (magit-insert-section (taginfo) + (let ((beg (point))) + ;; "git verify-tag -v" would output what we need, but the gpg + ;; output is send to stderr and we have no control over the + ;; order in which stdout and stderr are inserted, which would + ;; make parsing hard. We are forced to use "git cat-file tag" + ;; instead, which inserts the signature instead of verifying + ;; it. We remove that later and then insert the verification + ;; output using "git verify-tag" (without the "-v"). + (magit-git-insert "cat-file" "tag" magit-buffer-revision) + (goto-char beg) + (forward-line 3) + (delete-region beg (point))) + (looking-at "^tagger \\([^<]+\\) <\\([^>]+\\)") + (let ((heading (format "Tagger: %s <%s>" + (match-string 1) + (match-string 2)))) + (magit-delete-line) + (insert (propertize heading 'font-lock-face + 'magit-section-secondary-heading))) + (magit-insert-heading) + (forward-line) + (magit-insert-section section (message) + (oset section heading-highlight-face + 'magit-diff-revision-summary-highlight) + (let ((beg (point))) + (forward-line) + (magit--add-face-text-property + beg (point) 'magit-diff-revision-summary)) + (magit-insert-heading) + (if (re-search-forward "-----BEGIN PGP SIGNATURE-----" nil t) + (goto-char (match-beginning 0)) + (goto-char (point-max))) + (insert ?\n)) + (if (re-search-forward "-----BEGIN PGP SIGNATURE-----" nil t) + (progn + (let ((beg (match-beginning 0))) + (re-search-forward "-----END PGP SIGNATURE-----\n") + (delete-region beg (point))) + (save-excursion + (magit-process-git t "verify-tag" magit-buffer-revision)) + (magit-diff-wash-signature magit-buffer-revision)) + (goto-char (point-max))) + (insert ?\n)))) + +(defvar magit-commit-message-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-show-commit "Visit %t" + '(:enable (magit-thing-at-point 'git-revision t))) + map) + "Keymap for `commit-message' sections.") + +(defun magit-insert-revision-message () + "Insert the commit message into a revision buffer." + (magit-insert-section section (commit-message) + (oset section heading-highlight-face 'magit-diff-revision-summary-highlight) + (let ((beg (point)) + (rev magit-buffer-revision)) + (insert (with-temp-buffer + (magit-rev-insert-format "%B" rev) + (magit-revision--wash-message))) + (if (= (point) (+ beg 2)) + (progn (backward-delete-char 2) + (insert "(no message)\n")) + (goto-char beg) + (save-excursion + (while (search-forward "\r\n" nil t) ; Remove trailing CRs. + (delete-region (match-beginning 0) (1+ (match-beginning 0))))) + (when magit-revision-fill-summary-line + (let ((fill-column (min magit-revision-fill-summary-line + (window-width)))) + (fill-region (point) (line-end-position)))) + (when magit-revision-use-hash-sections + (save-excursion + ;; Start after beg to prevent a (commit text) section from + ;; starting at the same point as the (commit-message) + ;; section. + (goto-char (1+ beg)) + (while (not (eobp)) + (re-search-forward "\\_<" nil 'move) + (let ((beg (point))) + (re-search-forward "\\_>" nil t) + (when (> (point) beg) + (let ((text (buffer-substring-no-properties beg (point)))) + (when (pcase magit-revision-use-hash-sections + ('quickest ; false negatives and positives + (and (>= (length text) 7) + (string-match-p "[0-9]" text) + (string-match-p "[a-z]" text))) + ('quicker ; false negatives (number-less hashes) + (and (>= (length text) 7) + (string-match-p "[0-9]" text) + (magit-commit-p text))) + ('quick ; false negatives (short hashes) + (and (>= (length text) 7) + (magit-commit-p text))) + ('slow + (magit-commit-p text))) + (put-text-property beg (point) + 'font-lock-face 'magit-hash) + (let ((end (point))) + (goto-char beg) + (magit-insert-section (commit text) + (goto-char end)))))))))) + (save-excursion + (forward-line) + (magit--add-face-text-property + beg (point) 'magit-diff-revision-summary) + (magit-insert-heading)) + (when magit-diff-highlight-keywords + (save-excursion + (while (re-search-forward "\\[[^[]*\\]" nil t) + (let ((beg (match-beginning 0)) + (end (match-end 0))) + (put-text-property + beg end 'font-lock-face + (if-let ((face (get-text-property beg 'font-lock-face))) + (list face 'magit-keyword) + 'magit-keyword)))))) + (goto-char (point-max)))))) + +(defun magit-insert-revision-notes () + "Insert commit notes into a revision buffer." + (let* ((var "core.notesRef") + (def (or (magit-get var) "refs/notes/commits"))) + (dolist (ref (or (magit-list-active-notes-refs))) + (magit-insert-section section (notes ref (not (equal ref def))) + (oset section heading-highlight-face 'magit-diff-hunk-heading-highlight) + (let ((beg (point)) + (rev magit-buffer-revision)) + (insert (with-temp-buffer + (magit-git-insert "-c" (concat "core.notesRef=" ref) + "notes" "show" rev) + (magit-revision--wash-message))) + (if (= (point) beg) + (magit-cancel-section) + (goto-char beg) + (end-of-line) + (insert (format " (%s)" + (propertize (if (string-prefix-p "refs/notes/" ref) + (substring ref 11) + ref) + 'font-lock-face 'magit-refname))) + (forward-char) + (magit--add-face-text-property beg (point) 'magit-diff-hunk-heading) + (magit-insert-heading) + (goto-char (point-max)) + (insert ?\n))))))) + +(defun magit-revision--wash-message () + (let ((major-mode 'git-commit-mode)) + (hack-dir-local-variables) + (hack-local-variables-apply)) + (unless (memq git-commit-major-mode '(nil text-mode)) + (funcall git-commit-major-mode) + (font-lock-ensure)) + (buffer-string)) + +(defun magit-insert-revision-headers () + "Insert headers about the commit into a revision buffer." + (magit-insert-section (headers) + (--when-let (magit-rev-format "%D" magit-buffer-revision "--decorate=full") + (insert (magit-format-ref-labels it) ?\s)) + (insert (propertize + (magit-rev-parse (magit--rev-dereference magit-buffer-revision)) + 'font-lock-face 'magit-hash)) + (magit-insert-heading) + (let ((beg (point))) + (magit-rev-insert-format magit-revision-headers-format + magit-buffer-revision) + (magit-insert-revision-gravatars magit-buffer-revision beg)) + (when magit-revision-insert-related-refs + (dolist (parent (magit-commit-parents magit-buffer-revision)) + (magit-insert-section (commit parent) + (let ((line (magit-rev-format "%h %s" parent))) + (string-match "^\\([^ ]+\\) \\(.*\\)" line) + (magit-bind-match-strings (hash msg) line + (insert "Parent: ") + (insert (propertize hash 'font-lock-face 'magit-hash)) + (insert " " msg "\n"))))) + (magit--insert-related-refs + magit-buffer-revision "--merged" "Merged" + (eq magit-revision-insert-related-refs 'all)) + (magit--insert-related-refs + magit-buffer-revision "--contains" "Contained" + (memq magit-revision-insert-related-refs '(all mixed))) + (when-let ((follows (magit-get-current-tag magit-buffer-revision t))) + (let ((tag (car follows)) + (cnt (cadr follows))) + (magit-insert-section (tag tag) + (insert + (format "Follows: %s (%s)\n" + (propertize tag 'font-lock-face 'magit-tag) + (propertize (number-to-string cnt) + 'font-lock-face 'magit-branch-local)))))) + (when-let ((precedes (magit-get-next-tag magit-buffer-revision t))) + (let ((tag (car precedes)) + (cnt (cadr precedes))) + (magit-insert-section (tag tag) + (insert (format "Precedes: %s (%s)\n" + (propertize tag 'font-lock-face 'magit-tag) + (propertize (number-to-string cnt) + 'font-lock-face 'magit-tag)))))) + (insert ?\n)))) + +(defun magit--insert-related-refs (rev arg title remote) + (when-let ((refs (magit-list-related-branches arg rev (and remote "-a")))) + (insert title ":" (make-string (- 10 (length title)) ?\s)) + (dolist (branch refs) + (if (<= (+ (current-column) 1 (length branch)) + (window-width)) + (insert ?\s) + (insert ?\n (make-string 12 ?\s))) + (magit-insert-section (branch branch) + (insert (propertize branch 'font-lock-face + (if (string-prefix-p "remotes/" branch) + 'magit-branch-remote + 'magit-branch-local))))) + (insert ?\n))) + +(defun magit-insert-revision-gravatars (rev beg) + (when (and magit-revision-show-gravatars + (window-system)) + (require 'gravatar) + (pcase-let ((`(,author . ,committer) + (pcase magit-revision-show-gravatars + ('t '("^Author: " . "^Commit: ")) + ('author '("^Author: " . nil)) + ('committer '(nil . "^Commit: ")) + (_ magit-revision-show-gravatars)))) + (--when-let (and author (magit-rev-format "%aE" rev)) + (magit-insert-revision-gravatar beg rev it author)) + (--when-let (and committer (magit-rev-format "%cE" rev)) + (magit-insert-revision-gravatar beg rev it committer))))) + +(defun magit-insert-revision-gravatar (beg rev email regexp) + (save-excursion + (goto-char beg) + (when (re-search-forward regexp nil t) + (when-let ((window (get-buffer-window))) + (let* ((column (length (match-string 0))) + (font-obj (query-font (font-at (point) window))) + (size (* 2 (+ (aref font-obj 4) + (aref font-obj 5)))) + (align-to (+ column + (ceiling (/ size (aref font-obj 7) 1.0)) + 1)) + (gravatar-size (- size 2))) + (ignore-errors ; service may be unreachable + (gravatar-retrieve email #'magit-insert-revision-gravatar-cb + (list gravatar-size rev + (point-marker) + align-to column)))))))) + +(defun magit-insert-revision-gravatar-cb (image size rev marker align-to column) + (unless (eq image 'error) + (when-let ((buffer (marker-buffer marker))) + (with-current-buffer buffer + (save-excursion + (goto-char marker) + ;; The buffer might display another revision by now or + ;; it might have been refreshed, in which case another + ;; process might already have inserted the image. + (when (and (equal rev magit-buffer-revision) + (not (eq (car-safe + (car-safe + (get-text-property (point) 'display))) + 'image))) + (setf (image-property image :ascent) 'center) + (setf (image-property image :relief) 1) + (setf (image-property image :scale) 1) + (setf (image-property image :height) size) + (let ((top (list image '(slice 0.0 0.0 1.0 0.5))) + (bot (list image '(slice 0.0 0.5 1.0 1.0))) + (align `((space :align-to ,align-to)))) + (when magit-revision-use-gravatar-kludge + (cl-rotatef top bot)) + (let ((inhibit-read-only t)) + (insert (propertize " " 'display top)) + (insert (propertize " " 'display align)) + (forward-line) + (forward-char column) + (insert (propertize " " 'display bot)) + (insert (propertize " " 'display align)))))))))) + +;;; Merge-Preview Mode + +(define-derived-mode magit-merge-preview-mode magit-diff-mode "Magit Merge" + "Mode for previewing a merge." + :group 'magit-diff + (hack-dir-local-variables-non-file-buffer)) + +(put 'magit-merge-preview-mode 'magit-diff-default-arguments + '("--no-ext-diff")) + +(defun magit-merge-preview-setup-buffer (rev) + (magit-setup-buffer #'magit-merge-preview-mode nil + (magit-buffer-revision rev) + (magit-buffer-range (format "%s^..%s" rev rev)))) + +(defun magit-merge-preview-refresh-buffer () + (let* ((branch (magit-get-current-branch)) + (head (or branch (magit-rev-verify "HEAD")))) + (magit-set-header-line-format (format "Preview merge of %s into %s" + magit-buffer-revision + (or branch "HEAD"))) + (magit-insert-section (diffbuf) + (magit--insert-diff + "merge-tree" (magit-git-string "merge-base" head magit-buffer-revision) + head magit-buffer-revision)))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-merge-preview-mode)) + magit-buffer-revision) + +;;; Hunk Section + +(defun magit-hunk-set-window-start (section) + "When SECTION is a `hunk', ensure that its beginning is visible. +It the SECTION has a different type, then do nothing." + (when (magit-hunk-section-p section) + (magit-section-set-window-start section))) + +(add-hook 'magit-section-movement-hook #'magit-hunk-set-window-start) + +(cl-defmethod magit-section-get-relative-position ((_section magit-hunk-section)) + (nconc (cl-call-next-method) + (and (region-active-p) + (progn + (goto-char (line-beginning-position)) + (when (looking-at "^[-+]") (forward-line)) + (while (looking-at "^[ @]") (forward-line)) + (let ((beg (magit-point))) + (list (cond + ((looking-at "^[-+]") + (forward-line) + (while (looking-at "^[-+]") (forward-line)) + (while (looking-at "^ ") (forward-line)) + (forward-line -1) + (regexp-quote (buffer-substring-no-properties + beg (line-end-position)))) + (t t)))))))) + +(cl-defmethod magit-section-goto-successor ((section magit-hunk-section) + line char &optional arg) + (or (magit-section-goto-successor--same section line char) + (and-let* ((parent (magit-get-section + (magit-section-ident + (oref section parent))))) + (let* ((children (oref parent children)) + (siblings (magit-section-siblings section 'prev)) + (previous (nth (length siblings) children))) + (if (not arg) + (when-let ((sibling (or previous (car (last children))))) + (magit-section-goto sibling) + t) + (when previous + (magit-section-goto previous)) + (if (and (stringp arg) + (re-search-forward arg (oref parent end) t)) + (goto-char (match-beginning 0)) + (goto-char (oref (car (last children)) end)) + (forward-line -1) + (while (looking-at "^ ") (forward-line -1)) + (while (looking-at "^[-+]") (forward-line -1)) + (forward-line))))) + (magit-section-goto-successor--related section))) + +;;; Diff Sections + +(defvar magit-unstaged-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-diff-unstaged "Visit diff") + (magit-menu-set map [magit-stage-file] #'magit-stage "Stage all") + (magit-menu-set map [magit-delete-thing] #'magit-discard "Discard all") + map) + "Keymap for the `unstaged' section.") + +(magit-define-section-jumper magit-jump-to-unstaged "Unstaged changes" unstaged) + +(defun magit-insert-unstaged-changes () + "Insert section showing unstaged changes." + (magit-insert-section (unstaged) + (magit-insert-heading "Unstaged changes:") + (magit--insert-diff + "diff" magit-buffer-diff-args "--no-prefix" + "--" magit-buffer-diff-files))) + +(defvar magit-staged-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-diff-staged "Visit diff") + (magit-menu-set map [magit-unstage-file] #'magit-unstage "Unstage all") + (magit-menu-set map [magit-delete-thing] #'magit-discard "Discard all") + (magit-menu-set map [magit-revert-no-commit] #'magit-reverse "Reverse all") + map) + "Keymap for the `staged' section.") + +(magit-define-section-jumper magit-jump-to-staged "Staged changes" staged) + +(defun magit-insert-staged-changes () + "Insert section showing staged changes." + ;; Avoid listing all files as deleted when visiting a bare repo. + (unless (magit-bare-repo-p) + (magit-insert-section (staged) + (magit-insert-heading "Staged changes:") + (magit--insert-diff + "diff" "--cached" magit-buffer-diff-args "--no-prefix" + "--" magit-buffer-diff-files)))) + +;;; Diff Type + +(defun magit-diff-type (&optional section) + "Return the diff type of SECTION. + +The returned type is one of the symbols `staged', `unstaged', +`committed', or `undefined'. This type serves a similar purpose +as the general type common to all sections (which is stored in +the `type' slot of the corresponding `magit-section' struct) but +takes additional information into account. When the SECTION +isn't related to diffs and the buffer containing it also isn't +a diff-only buffer, then return nil. + +Currently the type can also be one of `tracked' and `untracked' +but these values are not handled explicitly everywhere they +should be and a possible fix could be to just return nil here. + +The section has to be a `diff' or `hunk' section, or a section +whose children are of type `diff'. If optional SECTION is nil, +return the diff type for the current section. In buffers whose +major mode is `magit-diff-mode' SECTION is ignored and the type +is determined using other means. In `magit-revision-mode' +buffers the type is always `committed'. + +Do not confuse this with `magit-diff-scope' (which see)." + (--when-let (or section (magit-current-section)) + (cond ((derived-mode-p 'magit-revision-mode 'magit-stash-mode) 'committed) + ((derived-mode-p 'magit-diff-mode) + (let ((range magit-buffer-range) + (const magit-buffer-typearg)) + (cond ((equal const "--no-index") 'undefined) + ((or (not range) + (magit-rev-eq range "HEAD")) + (if (equal const "--cached") + 'staged + 'unstaged)) + ((equal const "--cached") + (if (magit-rev-head-p range) + 'staged + 'undefined)) ; i.e. committed and staged + (t 'committed)))) + ((derived-mode-p 'magit-status-mode) + (let ((stype (oref it type))) + (if (memq stype '(staged unstaged tracked untracked)) + stype + (pcase stype + ((or 'file 'module) + (let* ((parent (oref it parent)) + (type (oref parent type))) + (if (memq type '(file module)) + (magit-diff-type parent) + type))) + ('hunk (thread-first it + (oref parent) + (oref parent) + (oref type))))))) + ((derived-mode-p 'magit-log-mode) + (if (or (and (magit-section-match 'commit section) + (oref section children)) + (magit-section-match [* file commit] section)) + 'committed + 'undefined)) + (t 'undefined)))) + +(cl-defun magit-diff-scope (&optional (section nil ssection) strict) + "Return the diff scope of SECTION or the selected section(s). + +A diff's \"scope\" describes what part of a diff is selected, it is +a symbol, one of `region', `hunk', `hunks', `file', `files', or +`list'. Do not confuse this with the diff \"type\", as returned by +`magit-diff-type'. + +If optional SECTION is non-nil, then return the scope of that, +ignoring the sections selected by the region. Otherwise return +the scope of the current section, or if the region is active and +selects a valid group of diff related sections, the type of these +sections, i.e. `hunks' or `files'. If SECTION, or if that is nil +the current section, is a `hunk' section; and the region region +starts and ends inside the body of a that section, then the type +is `region'. If the region is empty after a mouse click, then +`hunk' is returned instead of `region'. + +If optional STRICT is non-nil, then return nil if the diff type of +the section at point is `untracked' or the section at point is not +actually a `diff' but a `diffstat' section." + (let ((siblings (and (not ssection) (magit-region-sections nil t)))) + (setq section (or section (car siblings) (magit-current-section))) + (when (and section + (or (not strict) + (and (not (eq (magit-diff-type section) 'untracked)) + (not (eq (--when-let (oref section parent) + (oref it type)) + 'diffstat))))) + (pcase (list (oref section type) + (and siblings t) + (magit-diff-use-hunk-region-p) + ssection) + (`(hunk nil t ,_) + (if (magit-section-internal-region-p section) 'region 'hunk)) + ('(hunk t t nil) 'hunks) + (`(hunk ,_ ,_ ,_) 'hunk) + ('(file t t nil) 'files) + (`(file ,_ ,_ ,_) 'file) + ('(module t t nil) 'files) + (`(module ,_ ,_ ,_) 'file) + (`(,(or 'staged 'unstaged 'untracked) nil ,_ ,_) 'list))))) + +(defun magit-diff-use-hunk-region-p () + (and (region-active-p) + ;; TODO implement this from first principals + ;; currently it's trial-and-error + (not (and (or (eq this-command #'mouse-drag-region) + (eq last-command #'mouse-drag-region) + ;; When another window was previously + ;; selected then the last-command is + ;; some byte-code function. + (byte-code-function-p last-command)) + (eq (region-end) (region-beginning)))))) + +;;; Diff Highlight + +(add-hook 'magit-section-unhighlight-hook #'magit-diff-unhighlight) +(add-hook 'magit-section-highlight-hook #'magit-diff-highlight) + +(defun magit-diff-unhighlight (section selection) + "Remove the highlighting of the diff-related SECTION." + (when (magit-hunk-section-p section) + (magit-diff-paint-hunk section selection nil) + t)) + +(defun magit-diff-highlight (section selection) + "Highlight the diff-related SECTION. +If SECTION is not a diff-related section, then do nothing and +return nil. If SELECTION is non-nil, then it is a list of sections +selected by the region, including SECTION. All of these sections +are highlighted." + (if (and (magit-section-match 'commit section) + (oref section children)) + (progn (if selection + (dolist (section selection) + (magit-diff-highlight-list section selection)) + (magit-diff-highlight-list section)) + t) + (when-let ((scope (magit-diff-scope section t))) + (cond ((eq scope 'region) + (magit-diff-paint-hunk section selection t)) + (selection + (dolist (section selection) + (magit-diff-highlight-recursive section selection))) + (t + (magit-diff-highlight-recursive section))) + t))) + +(defun magit-diff-highlight-recursive (section &optional selection) + (pcase (magit-diff-scope section) + ('list (magit-diff-highlight-list section selection)) + ('file (magit-diff-highlight-file section selection)) + ('hunk (magit-diff-highlight-heading section selection) + (magit-diff-paint-hunk section selection t)) + (_ (magit-section-highlight section nil)))) + +(defun magit-diff-highlight-list (section &optional selection) + (let ((beg (oref section start)) + (cnt (oref section content)) + (end (oref section end))) + (when (or (eq this-command #'mouse-drag-region) + (not selection)) + (unless (and (region-active-p) + (<= (region-beginning) beg)) + (magit-section-make-overlay beg cnt 'magit-section-highlight)) + (if (oref section hidden) + (oset section washer #'ignore) + (dolist (child (oref section children)) + (when (or (eq this-command #'mouse-drag-region) + (not (and (region-active-p) + (<= (region-beginning) + (oref child start))))) + (magit-diff-highlight-recursive child selection))))) + (when magit-diff-highlight-hunk-body + (magit-section-make-overlay (1- end) end 'magit-section-highlight)))) + +(defun magit-diff-highlight-file (section &optional selection) + (magit-diff-highlight-heading section selection) + (when (or (not (oref section hidden)) + (cl-typep section 'magit-module-section)) + (dolist (child (oref section children)) + (magit-diff-highlight-recursive child selection)))) + +(defun magit-diff-highlight-heading (section &optional selection) + (magit-section-make-overlay + (oref section start) + (or (oref section content) + (oref section end)) + (pcase (list (oref section type) + (and (member section selection) + (not (eq this-command #'mouse-drag-region)))) + ('(file t) 'magit-diff-file-heading-selection) + ('(file nil) 'magit-diff-file-heading-highlight) + ('(module t) 'magit-diff-file-heading-selection) + ('(module nil) 'magit-diff-file-heading-highlight) + ('(hunk t) 'magit-diff-hunk-heading-selection) + ('(hunk nil) 'magit-diff-hunk-heading-highlight)))) + +;;; Hunk Paint + +(cl-defun magit-diff-paint-hunk + (section &optional selection + (highlight (magit-section-selected-p section selection))) + (let (paint) + (unless magit-diff-highlight-hunk-body + (setq highlight nil)) + (cond (highlight + (unless (oref section hidden) + (add-to-list 'magit-section-highlighted-sections section) + (cond ((memq section magit-section-unhighlight-sections) + (setq magit-section-unhighlight-sections + (delq section magit-section-unhighlight-sections))) + (magit-diff-highlight-hunk-body + (setq paint t))))) + (t + (cond ((and (oref section hidden) + (memq section magit-section-unhighlight-sections)) + (add-to-list 'magit-section-highlighted-sections section) + (setq magit-section-unhighlight-sections + (delq section magit-section-unhighlight-sections))) + (t + (setq paint t))))) + (when paint + (save-excursion + (goto-char (oref section start)) + (let ((end (oref section end)) + (merging (looking-at "@@@")) + (diff-type (magit-diff-type)) + (stage nil) + (tab-width (magit-diff-tab-width + (magit-section-parent-value section)))) + (forward-line) + (while (< (point) end) + (when (and magit-diff-hide-trailing-cr-characters + (char-equal ?\r (char-before (line-end-position)))) + (put-text-property (1- (line-end-position)) (line-end-position) + 'invisible t)) + (put-text-property + (point) (1+ (line-end-position)) 'font-lock-face + (cond + ((looking-at "^\\+\\+?\\([<=|>]\\)\\{7\\}") + (setq stage (pcase (list (match-string 1) highlight) + ('("<" nil) 'magit-diff-our) + ('("<" t) 'magit-diff-our-highlight) + ('("|" nil) 'magit-diff-base) + ('("|" t) 'magit-diff-base-highlight) + ('("=" nil) 'magit-diff-their) + ('("=" t) 'magit-diff-their-highlight) + ('(">" nil) nil))) + 'magit-diff-conflict-heading) + ((looking-at (if merging "^\\(\\+\\| \\+\\)" "^\\+")) + (magit-diff-paint-tab merging tab-width) + (magit-diff-paint-whitespace merging 'added diff-type) + (or stage + (if highlight 'magit-diff-added-highlight 'magit-diff-added))) + ((looking-at (if merging "^\\(-\\| -\\)" "^-")) + (magit-diff-paint-tab merging tab-width) + (magit-diff-paint-whitespace merging 'removed diff-type) + (if highlight 'magit-diff-removed-highlight 'magit-diff-removed)) + (t + (magit-diff-paint-tab merging tab-width) + (magit-diff-paint-whitespace merging 'context diff-type) + (if highlight 'magit-diff-context-highlight 'magit-diff-context)))) + (forward-line)))))) + (magit-diff-update-hunk-refinement section)) + +(defvar magit-diff--tab-width-cache nil) + +(defun magit-diff-tab-width (file) + (setq file (expand-file-name file)) + (cl-flet ((cache (value) + (let ((elt (assoc file magit-diff--tab-width-cache))) + (if elt + (setcdr elt value) + (setq magit-diff--tab-width-cache + (cons (cons file value) + magit-diff--tab-width-cache)))) + value)) + (cond + ((not magit-diff-adjust-tab-width) + tab-width) + ((and-let* ((buffer (find-buffer-visiting file))) + (cache (buffer-local-value 'tab-width buffer)))) + ((and-let* ((elt (assoc file magit-diff--tab-width-cache))) + (or (cdr elt) + tab-width))) + ((or (eq magit-diff-adjust-tab-width 'always) + (and (numberp magit-diff-adjust-tab-width) + (>= magit-diff-adjust-tab-width + (nth 7 (file-attributes file))))) + (cache (buffer-local-value 'tab-width (find-file-noselect file)))) + (t + (cache nil) + tab-width)))) + +(defun magit-diff-paint-tab (merging width) + (save-excursion + (forward-char (if merging 2 1)) + (while (= (char-after) ?\t) + (put-text-property (point) (1+ (point)) + 'display (list (list 'space :width width))) + (forward-char)))) + +(defun magit-diff-paint-whitespace (merging line-type diff-type) + (when (and magit-diff-paint-whitespace + (or (not (memq magit-diff-paint-whitespace '(uncommitted status))) + (memq diff-type '(staged unstaged))) + (cl-case line-type + (added t) + (removed (memq magit-diff-paint-whitespace-lines '(all both))) + (context (memq magit-diff-paint-whitespace-lines '(all))))) + (let ((prefix (if merging "^[-\\+\s]\\{2\\}" "^[-\\+\s]")) + (indent + (if (local-variable-p 'magit-diff-highlight-indentation) + magit-diff-highlight-indentation + (setq-local + magit-diff-highlight-indentation + (cdr (--first (string-match-p (car it) default-directory) + (nreverse + (default-value + 'magit-diff-highlight-indentation)))))))) + (when (and magit-diff-highlight-trailing + (looking-at (concat prefix ".*?\\([ \t]+\\) ?$"))) + (let ((ov (make-overlay (match-beginning 1) (match-end 1) nil t))) + (overlay-put ov 'font-lock-face 'magit-diff-whitespace-warning) + (overlay-put ov 'priority 2) + (overlay-put ov 'evaporate t))) + (when (or (and (eq indent 'tabs) + (looking-at (concat prefix "\\( *\t[ \t]*\\)"))) + (and (integerp indent) + (looking-at (format "%s\\([ \t]* \\{%s,\\}[ \t]*\\)" + prefix indent)))) + (let ((ov (make-overlay (match-beginning 1) (match-end 1) nil t))) + (overlay-put ov 'font-lock-face 'magit-diff-whitespace-warning) + (overlay-put ov 'priority 2) + (overlay-put ov 'evaporate t)))))) + +(defun magit-diff-update-hunk-refinement (&optional section) + (if section + (unless (oref section hidden) + (pcase (list magit-diff-refine-hunk + (oref section refined) + (eq section (magit-current-section))) + ((or `(all nil ,_) '(t nil t)) + (oset section refined t) + (save-excursion + (goto-char (oref section start)) + ;; `diff-refine-hunk' does not handle combined diffs. + (unless (looking-at "@@@") + (let ((smerge-refine-ignore-whitespace + magit-diff-refine-ignore-whitespace) + ;; Avoid fsyncing many small temp files + (write-region-inhibit-fsync t)) + (diff-refine-hunk))))) + ((or `(nil t ,_) '(t t nil)) + (oset section refined nil) + (remove-overlays (oref section start) + (oref section end) + 'diff-mode 'fine)))) + (cl-labels ((recurse (section) + (if (magit-section-match 'hunk section) + (magit-diff-update-hunk-refinement section) + (dolist (child (oref section children)) + (recurse child))))) + (recurse magit-root-section)))) + + +;;; Hunk Region + +(defun magit-diff-hunk-region-beginning () + (save-excursion (goto-char (region-beginning)) + (line-beginning-position))) + +(defun magit-diff-hunk-region-end () + (save-excursion (goto-char (region-end)) + (line-end-position))) + +(defun magit-diff-update-hunk-region (section) + "Highlight the hunk-internal region if any." + (when (and (eq (oref section type) 'hunk) + (eq (magit-diff-scope section t) 'region)) + (magit-diff--make-hunk-overlay + (oref section start) + (1- (oref section content)) + 'font-lock-face 'magit-diff-lines-heading + 'display (magit-diff-hunk-region-header section) + 'after-string (magit-diff--hunk-after-string 'magit-diff-lines-heading)) + (run-hook-with-args 'magit-diff-highlight-hunk-region-functions section) + t)) + +(defun magit-diff-highlight-hunk-region-dim-outside (section) + "Dim the parts of the hunk that are outside the hunk-internal region. +This is done by using the same foreground and background color +for added and removed lines as for context lines." + (let ((face (if magit-diff-highlight-hunk-body + 'magit-diff-context-highlight + 'magit-diff-context))) + (when magit-diff-unmarked-lines-keep-foreground + (setq face `(,@(and (>= emacs-major-version 27) '(:extend t)) + :background ,(face-attribute face :background)))) + (magit-diff--make-hunk-overlay (oref section content) + (magit-diff-hunk-region-beginning) + 'font-lock-face face + 'priority 2) + (magit-diff--make-hunk-overlay (1+ (magit-diff-hunk-region-end)) + (oref section end) + 'font-lock-face face + 'priority 2))) + +(defun magit-diff-highlight-hunk-region-using-face (_section) + "Highlight the hunk-internal region by making it bold. +Or rather highlight using the face `magit-diff-hunk-region', though +changing only the `:weight' and/or `:slant' is recommended for that +face." + (magit-diff--make-hunk-overlay (magit-diff-hunk-region-beginning) + (1+ (magit-diff-hunk-region-end)) + 'font-lock-face 'magit-diff-hunk-region)) + +(defun magit-diff-highlight-hunk-region-using-overlays (section) + "Emphasize the hunk-internal region using delimiting horizontal lines. +This is implemented as single-pixel newlines places inside overlays." + (if (window-system) + (let ((beg (magit-diff-hunk-region-beginning)) + (end (magit-diff-hunk-region-end)) + (str (propertize + (concat (propertize "\s" 'display '(space :height (1))) + (propertize "\n" 'line-height t)) + 'font-lock-face 'magit-diff-lines-boundary))) + (magit-diff--make-hunk-overlay beg (1+ beg) 'before-string str) + (magit-diff--make-hunk-overlay end (1+ end) 'after-string str)) + (magit-diff-highlight-hunk-region-using-face section))) + +(defun magit-diff-highlight-hunk-region-using-underline (section) + "Emphasize the hunk-internal region using delimiting horizontal lines. +This is implemented by overlining and underlining the first and +last (visual) lines of the region." + (if (window-system) + (let* ((beg (magit-diff-hunk-region-beginning)) + (end (magit-diff-hunk-region-end)) + (beg-eol (save-excursion (goto-char beg) + (end-of-visual-line) + (point))) + (end-bol (save-excursion (goto-char end) + (beginning-of-visual-line) + (point))) + (color (face-background 'magit-diff-lines-boundary nil t))) + (cl-flet ((ln (b e &rest face) + (magit-diff--make-hunk-overlay + b e 'font-lock-face face 'after-string + (magit-diff--hunk-after-string face)))) + (if (= beg end-bol) + (ln beg beg-eol :overline color :underline color) + (ln beg beg-eol :overline color) + (ln end-bol end :underline color)))) + (magit-diff-highlight-hunk-region-using-face section))) + +(defun magit-diff--make-hunk-overlay (start end &rest args) + (let ((ov (make-overlay start end nil t))) + (overlay-put ov 'evaporate t) + (while args (overlay-put ov (pop args) (pop args))) + (push ov magit-section--region-overlays) + ov)) + +(defun magit-diff--hunk-after-string (face) + (propertize "\s" + 'font-lock-face face + 'display (list 'space :align-to + `(+ (0 . right) + ,(min (window-hscroll) + (- (line-end-position) + (line-beginning-position))))) + ;; This prevents the cursor from being rendered at the + ;; edge of the window. + 'cursor t)) + +;;; Hunk Utilities + +(defun magit-diff-inside-hunk-body-p () + "Return non-nil if point is inside the body of a hunk." + (and (magit-section-match 'hunk) + (and-let* ((content (oref (magit-current-section) content))) + (> (magit-point) content)))) + +;;; Diff Extract + +(defun magit-diff-file-header (section &optional no-rename) + (when (magit-hunk-section-p section) + (setq section (oref section parent))) + (and (magit-file-section-p section) + (let ((header (oref section header))) + (if no-rename + (replace-regexp-in-string + "^--- \\(.+\\)" (oref section value) header t t 1) + header)))) + +(defun magit-diff-hunk-region-header (section) + (let ((patch (magit-diff-hunk-region-patch section))) + (string-match "\n" patch) + (substring patch 0 (1- (match-end 0))))) + +(defun magit-diff-hunk-region-patch (section &optional args) + (let ((op (if (member "--reverse" args) "+" "-")) + (sbeg (oref section start)) + (rbeg (magit-diff-hunk-region-beginning)) + (rend (region-end)) + (send (oref section end)) + (patch nil)) + (save-excursion + (goto-char sbeg) + (while (< (point) send) + (looking-at "\\(.\\)\\([^\n]*\n\\)") + (cond ((or (string-match-p "[@ ]" (match-string-no-properties 1)) + (and (>= (point) rbeg) + (<= (point) rend))) + (push (match-string-no-properties 0) patch)) + ((equal op (match-string-no-properties 1)) + (push (concat " " (match-string-no-properties 2)) patch))) + (forward-line))) + (let ((buffer-list-update-hook nil)) ; #3759 + (with-temp-buffer + (insert (mapconcat #'identity (reverse patch) "")) + (diff-fixup-modifs (point-min) (point-max)) + (setq patch (buffer-string)))) + patch)) + +;;; _ +(provide 'magit-diff) +;;; magit-diff.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-ediff.el b/code/elpa/magit-20220821.1819/magit-ediff.el new file mode 100644 index 0000000..7b6dd0c --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-ediff.el @@ -0,0 +1,604 @@ +;;; magit-ediff.el --- Ediff extension for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library provides basic support for Ediff. + +;;; Code: + +(require 'magit) + +(require 'ediff) +(require 'smerge-mode) + +(defvar smerge-ediff-buf) +(defvar smerge-ediff-windows) + +;;; Options + +(defgroup magit-ediff nil + "Ediff support for Magit." + :link '(info-link "(magit)Ediffing") + :group 'magit-extensions) + +(defcustom magit-ediff-quit-hook + '(magit-ediff-cleanup-auxiliary-buffers + magit-ediff-restore-previous-winconf) + "Hooks to run after finishing Ediff, when that was invoked using Magit. +The hooks are run in the Ediff control buffer. This is similar +to `ediff-quit-hook' but takes the needs of Magit into account. +The `ediff-quit-hook' is ignored by Ediff sessions which were +invoked using Magit." + :package-version '(magit . "2.2.0") + :group 'magit-ediff + :type 'hook + :get #'magit-hook-custom-get + :options '(magit-ediff-cleanup-auxiliary-buffers + magit-ediff-restore-previous-winconf)) + +(defcustom magit-ediff-dwim-resolve-function #'magit-ediff-resolve-rest + "The function `magit-ediff-dwim' uses to resolve conflicts." + :package-version '(magit . "3.4.0") + :group 'magit-ediff + :type '(choice (const magit-ediff-resolve-rest) + (const magit-ediff-resolve-all) + (const magit-git-mergetool))) + +(defcustom magit-ediff-dwim-show-on-hunks nil + "Whether `magit-ediff-dwim' runs show variants on hunks. +If non-nil, `magit-ediff-show-staged' or +`magit-ediff-show-unstaged' are called based on what section the +hunk is in. Otherwise, `magit-ediff-dwim' runs +`magit-ediff-stage' when point is on an uncommitted hunk." + :package-version '(magit . "2.2.0") + :group 'magit-ediff + :type 'boolean) + +(defcustom magit-ediff-show-stash-with-index t + "Whether `magit-ediff-show-stash' shows the state of the index. + +If non-nil, use a third Ediff buffer to distinguish which changes +in the stash were staged. In cases where the stash contains no +staged changes, fall back to a two-buffer Ediff. + +More specifically, a stash is a merge commit, stash@{N}, with +potentially three parents. + +* stash@{N}^1 represents the `HEAD' commit at the time the stash + was created. + +* stash@{N}^2 records any changes that were staged when the stash + was made. + +* stash@{N}^3, if it exists, contains files that were untracked + when stashing. + +If this option is non-nil, `magit-ediff-show-stash' will run +Ediff on a file using three buffers: one for stash@{N}, another +for stash@{N}^1, and a third for stash@{N}^2. + +Otherwise, Ediff uses two buffers, comparing +stash@{N}^1..stash@{N}. Along with any unstaged changes, changes +in the index commit, stash@{N}^2, will be shown in this +comparison unless they conflicted with changes in the working +tree at the time of stashing." + :package-version '(magit . "2.6.0") + :group 'magit-ediff + :type 'boolean) + +(defvar magit-ediff-use-indirect-buffers nil + "Whether to use indirect buffers. +Ediff already does a lot of buffer and file shuffling and I +recommend you do not further complicate that by enabling this.") + +;;; Commands + +(defvar magit-ediff-previous-winconf nil) + +;;;###autoload (autoload 'magit-ediff "magit-ediff" nil) +(transient-define-prefix magit-ediff () + "Show differences using the Ediff package." + :info-manual "(ediff)" + ["Ediff" + [("E" "Dwim" magit-ediff-dwim) + ("s" "Stage" magit-ediff-stage)] + [("m" "Resolve rest" magit-ediff-resolve-rest) + ("M" "Resolve all conflicts" magit-ediff-resolve-all) + ("t" "Resolve using mergetool" magit-git-mergetool)] + [("u" "Show unstaged" magit-ediff-show-unstaged) + ("i" "Show staged" magit-ediff-show-staged) + ("w" "Show worktree" magit-ediff-show-working-tree)] + [("c" "Show commit" magit-ediff-show-commit) + ("r" "Show range" magit-ediff-compare) + ("z" "Show stash" magit-ediff-show-stash)]]) + +(defmacro magit-ediff-buffers (a b &optional c setup quit file) + "Run Ediff on two or three buffers. +This is a wrapper around `ediff-buffers-internal'. + +A, B and C have the form (GET-BUFFER CREATE-BUFFER). If +GET-BUFFER returns a non-nil value, then that buffer is used and +it is not killed when exiting Ediff. Otherwise CREATE-BUFFER +must return a buffer and that is killed when exiting Ediff. + +If non-nil, SETUP must be a function. It is called without +arguments after Ediff is done setting up buffers. + +If non-nil, QUIT must be a function. It is added to +`ediff-quit-hook' and is called without arguments. + +If FILE is non-nil, then perform a merge. The merge result +is put in FILE." + (let (get make kill (char ?A)) + (dolist (spec (list a b c)) + (if (not spec) + (push nil make) + (pcase-let ((`(,g ,m) spec)) + (let ((b (intern (format "buf%c" char)))) + (push `(,b ,g) get) + ;; This is an unfortunate complication that I have added for + ;; the benefit of one user. Pretend we used this instead: + ;; (push `(or ,b ,m) make) + (push `(if ,b + (if magit-ediff-use-indirect-buffers + (prog1 (make-indirect-buffer + ,b + (generate-new-buffer-name (buffer-name ,b)) + t) + (setq ,b nil)) + ,b) + ,m) + make) + (push `(unless ,b + ;; For merge jobs Ediff switches buffer names around. + ;; See (if ediff-merge-job ...) in `ediff-setup'. + (let ((var ,(if (and file (= char ?C)) + 'ediff-ancestor-buffer + (intern (format "ediff-buffer-%c" char))))) + (ediff-kill-buffer-carefully var))) + kill)) + (cl-incf char)))) + (setq get (nreverse get)) + (setq make (nreverse make)) + (setq kill (nreverse kill)) + (let ((mconf (cl-gensym "conf")) + (mfile (cl-gensym "file"))) + `(magit-with-toplevel + (let ((,mconf (current-window-configuration)) + (,mfile ,file) + ,@get) + (ediff-buffers-internal + ,@make + (list ,@(and setup (list setup)) + (lambda () + ;; We do not want to kill buffers that existed before + ;; Ediff was invoked, so we cannot use Ediff's default + ;; quit functions. Ediff splits quitting across two + ;; hooks for merge jobs but we only ever use one. + (setq-local ediff-quit-merge-hook nil) + (setq-local ediff-quit-hook + (list + ,@(and quit (list quit)) + (lambda () + ,@kill + (let ((magit-ediff-previous-winconf ,mconf)) + (run-hooks 'magit-ediff-quit-hook))))))) + (pcase (list ,(and c t) (and ,mfile t)) + ('(nil nil) 'ediff-buffers) + ('(nil t) 'ediff-merge-buffers) + ('(t nil) 'ediff-buffers3) + ('(t t) 'ediff-merge-buffers-with-ancestor)) + ,mfile)))))) + +;;;###autoload +(defun magit-ediff-resolve-all (file) + "Resolve all conflicts in the FILE at point using Ediff. + +If there is no file at point or if it doesn't have any unmerged +changes, then prompt for a file. + +See info node `(magit) Ediffing' for more information about this +and alternative commands." + (interactive (list (magit-read-unmerged-file))) + (magit-with-toplevel + (let* ((revA (or (magit-name-branch "HEAD") + (magit-commit-p "HEAD"))) + (revB (cl-find-if (lambda (head) (file-exists-p (magit-git-dir head))) + '("MERGE_HEAD" "CHERRY_PICK_HEAD" "REVERT_HEAD"))) + (revB (or (magit-name-branch revB) + (magit-commit-p revB))) + (revC (magit-commit-p (magit-git-string "merge-base" revA revB))) + (fileA (magit--rev-file-name file revA revB)) + (fileB (magit--rev-file-name file revB revA)) + (fileC (or (magit--rev-file-name file revC revA) + (magit--rev-file-name file revC revB)))) + ;; Ediff assumes that the FILE where it is going to store the merge + ;; result does not exist yet, so move the existing file out of the + ;; way. If a buffer visits FILE, then we have to kill that upfront. + (when-let ((buffer (find-buffer-visiting file))) + (when (and (buffer-modified-p buffer) + (not (y-or-n-p (format "Save buffer %s %s? " + (buffer-name buffer) + "(cannot continue otherwise)")))) + (user-error "Abort")) + (kill-buffer buffer)) + (let ((orig (concat file ".ORIG"))) + (when (file-exists-p orig) + (rename-file orig (make-temp-name (concat orig "_")))) + (rename-file file orig)) + (let ((setup (lambda () + ;; Use the same conflict marker style as Git uses. + (setq-local ediff-combination-pattern + '("<<<<<<< HEAD" A + ,(format "||||||| %s" revC) Ancestor + "=======" B + ,(format ">>>>>>> %s" revB))))) + (quit (lambda () + ;; For merge jobs Ediff switches buffer names around. + ;; At this point `ediff-buffer-C' no longer refer to + ;; the ancestor buffer but to the merge result buffer. + ;; See (if ediff-merge-job ...) in `ediff-setup'. + (when (buffer-live-p ediff-buffer-C) + (with-current-buffer ediff-buffer-C + (save-buffer) + (save-excursion + (goto-char (point-min)) + (unless (re-search-forward "^<<<<<<< " nil t) + (magit-stage-file file)))))))) + (if fileC + (magit-ediff-buffers + ((magit-get-revision-buffer revA fileA) + (magit-find-file-noselect revA fileA)) + ((magit-get-revision-buffer revB fileB) + (magit-find-file-noselect revB fileB)) + ((magit-get-revision-buffer revC fileC) + (magit-find-file-noselect revC fileC)) + setup quit file) + (magit-ediff-buffers + ((magit-get-revision-buffer revA fileA) + (magit-find-file-noselect revA fileA)) + ((magit-get-revision-buffer revB fileB) + (magit-find-file-noselect revB fileB)) + nil setup quit file)))))) + +;;;###autoload +(defun magit-ediff-resolve-rest (file) + "Resolve outstanding conflicts in the FILE at point using Ediff. + +If there is no file at point or if it doesn't have any unmerged +changes, then prompt for a file. + +See info node `(magit) Ediffing' for more information about this +and alternative commands." + (interactive (list (magit-read-unmerged-file))) + (magit-with-toplevel + (with-current-buffer (find-file-noselect file) + (smerge-ediff) + (setq-local + ediff-quit-hook + (lambda () + (let ((bufC ediff-buffer-C) + (bufS smerge-ediff-buf)) + (with-current-buffer bufS + (when (yes-or-no-p (format "Conflict resolution finished; save %s? " + buffer-file-name)) + (erase-buffer) + (insert-buffer-substring bufC) + (save-buffer)))) + (when (buffer-live-p ediff-buffer-A) (kill-buffer ediff-buffer-A)) + (when (buffer-live-p ediff-buffer-B) (kill-buffer ediff-buffer-B)) + (when (buffer-live-p ediff-buffer-C) (kill-buffer ediff-buffer-C)) + (when (buffer-live-p ediff-ancestor-buffer) + (kill-buffer ediff-ancestor-buffer)) + (let ((magit-ediff-previous-winconf smerge-ediff-windows)) + (run-hooks 'magit-ediff-quit-hook))))))) + +;;;###autoload +(defun magit-ediff-stage (file) + "Stage and unstage changes to FILE using Ediff. +FILE has to be relative to the top directory of the repository." + (interactive + (let ((files (magit-tracked-files))) + (list (magit-completing-read "Selectively stage file" files nil t nil nil + (car (member (magit-current-file) files)))))) + (magit-with-toplevel + (let* ((bufA (magit-get-revision-buffer "HEAD" file)) + (bufB (magit-get-revision-buffer "{index}" file)) + (lockB (and bufB (buffer-local-value 'buffer-read-only bufB))) + (bufC (get-file-buffer file)) + ;; Use the same encoding for all three buffers or we + ;; may end up changing the file in an unintended way. + (bufC* (or bufC (find-file-noselect file))) + (coding-system-for-read + (buffer-local-value 'buffer-file-coding-system bufC*)) + (bufA* (magit-find-file-noselect-1 "HEAD" file t)) + (bufB* (magit-find-file-index-noselect file t))) + (setf (buffer-local-value 'buffer-read-only bufB*) nil) + (magit-ediff-buffers + (bufA bufA*) + (bufB bufB*) + (bufC bufC*) + nil + (lambda () + (when (buffer-live-p ediff-buffer-B) + (when lockB + (setf (buffer-local-value 'buffer-read-only bufB) t)) + (when (buffer-modified-p ediff-buffer-B) + (with-current-buffer ediff-buffer-B + (magit-update-index)))) + (when (and (buffer-live-p ediff-buffer-C) + (buffer-modified-p ediff-buffer-C)) + (with-current-buffer ediff-buffer-C + (when (y-or-n-p (format "Save file %s? " buffer-file-name)) + (save-buffer))))))))) + +;;;###autoload +(defun magit-ediff-compare (revA revB fileA fileB) + "Compare REVA:FILEA with REVB:FILEB using Ediff. + +FILEA and FILEB have to be relative to the top directory of the +repository. If REVA or REVB is nil, then this stands for the +working tree state. + +If the region is active, use the revisions on the first and last +line of the region. With a prefix argument, instead of diffing +the revisions, choose a revision to view changes along, starting +at the common ancestor of both revisions (i.e., use a \"...\" +range)." + (interactive + (pcase-let ((`(,revA ,revB) (magit-ediff-compare--read-revisions + nil current-prefix-arg))) + (nconc (list revA revB) + (magit-ediff-read-files revA revB)))) + (magit-ediff-buffers + ((if revA (magit-get-revision-buffer revA fileA) (get-file-buffer fileA)) + (if revA (magit-find-file-noselect revA fileA) (find-file-noselect fileA))) + ((if revB (magit-get-revision-buffer revB fileB) (get-file-buffer fileB)) + (if revB (magit-find-file-noselect revB fileB) (find-file-noselect fileB))))) + +(defun magit-ediff-compare--read-revisions (&optional arg mbase) + (let ((input (or arg (magit-diff-read-range-or-commit + "Compare range or commit" + nil mbase)))) + (--if-let (magit-split-range input) + (-cons-to-list it) + (list input nil)))) + +(defun magit-ediff-read-files (revA revB &optional fileB) + "Read file in REVB, return it and the corresponding file in REVA. +When FILEB is non-nil, use this as REVB's file instead of +prompting for it." + (unless (and fileB (member fileB (magit-revision-files revB))) + (setq fileB + (or (and fileB + magit-buffer-log-files + (derived-mode-p 'magit-log-mode) + (member "--follow" magit-buffer-log-args) + (cdr (assoc fileB + (magit-renamed-files + revB + (oref (car (oref magit-root-section children)) + value))))) + (magit-read-file-choice + (format "File to compare between %s and %s" + revA (or revB "the working tree")) + (magit-changed-files revA revB) + (format "No changed files between %s and %s" + revA (or revB "the working tree")))))) + (list (or (car (member fileB (magit-revision-files revA))) + (cdr (assoc fileB (magit-renamed-files revB revA))) + (magit-read-file-choice + (format "File in %s to compare with %s in %s" + revA fileB (or revB "the working tree")) + (magit-changed-files revB revA) + (format "No files have changed between %s and %s" + revA revB))) + fileB)) + +;;;###autoload +(defun magit-ediff-dwim () + "Compare, stage, or resolve using Ediff. +This command tries to guess what file, and what commit or range +the user wants to compare, stage, or resolve using Ediff. It +might only be able to guess either the file, or range or commit, +in which case the user is asked about the other. It might not +always guess right, in which case the appropriate `magit-ediff-*' +command has to be used explicitly. If it cannot read the user's +mind at all, then it asks the user for a command to run." + (interactive) + (magit-section-case + (hunk (save-excursion + (goto-char (oref (oref it parent) start)) + (magit-ediff-dwim))) + (t + (let ((range (magit-diff--dwim)) + (file (magit-current-file)) + command revA revB) + (pcase range + ((and (guard (not magit-ediff-dwim-show-on-hunks)) + (or 'unstaged 'staged)) + (setq command (if (magit-anything-unmerged-p) + magit-ediff-dwim-resolve-function + #'magit-ediff-stage))) + ('unstaged (setq command #'magit-ediff-show-unstaged)) + ('staged (setq command #'magit-ediff-show-staged)) + (`(commit . ,value) + (setq command #'magit-ediff-show-commit) + (setq revB value)) + (`(stash . ,value) + (setq command #'magit-ediff-show-stash) + (setq revB value)) + ((pred stringp) + (pcase-let ((`(,a ,b) (magit-ediff-compare--read-revisions range))) + (setq command #'magit-ediff-compare) + (setq revA a) + (setq revB b))) + (_ + (when (derived-mode-p 'magit-diff-mode) + (pcase (magit-diff-type) + ('committed (pcase-let ((`(,a ,b) + (magit-ediff-compare--read-revisions + magit-buffer-range))) + (setq revA a) + (setq revB b))) + ((guard (not magit-ediff-dwim-show-on-hunks)) + (setq command #'magit-ediff-stage)) + ('unstaged (setq command #'magit-ediff-show-unstaged)) + ('staged (setq command #'magit-ediff-show-staged)) + ('undefined (setq command nil)) + (_ (setq command nil)))))) + (cond ((not command) + (call-interactively + (magit-read-char-case + "Failed to read your mind; do you want to " t + (?c "[c]ommit" #'magit-ediff-show-commit) + (?r "[r]ange" #'magit-ediff-compare) + (?s "[s]tage" #'magit-ediff-stage) + (?m "[m] resolve remaining conflicts" + #'magit-ediff-resolve-rest) + (?M "[M] resolve all conflicts" + #'magit-ediff-resolve-all)))) + ((eq command #'magit-ediff-compare) + (apply #'magit-ediff-compare revA revB + (magit-ediff-read-files revA revB file))) + ((eq command #'magit-ediff-show-commit) + (magit-ediff-show-commit revB)) + ((eq command #'magit-ediff-show-stash) + (magit-ediff-show-stash revB)) + (file + (funcall command file)) + (t + (call-interactively command))))))) + +;;;###autoload +(defun magit-ediff-show-staged (file) + "Show staged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository." + (interactive + (list (magit-read-file-choice "Show staged changes for file" + (magit-staged-files) + "No staged files"))) + (magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file) + (magit-find-file-noselect "HEAD" file)) + ((get-buffer (concat file ".~{index}~")) + (magit-find-file-index-noselect file t)))) + +;;;###autoload +(defun magit-ediff-show-unstaged (file) + "Show unstaged changes using Ediff. + +This only allows looking at the changes; to stage, unstage, +and discard changes using Ediff, use `magit-ediff-stage'. + +FILE must be relative to the top directory of the repository." + (interactive + (list (magit-read-file-choice "Show unstaged changes for file" + (magit-unstaged-files) + "No unstaged files"))) + (magit-ediff-buffers ((get-buffer (concat file ".~{index}~")) + (magit-find-file-index-noselect file t)) + ((get-file-buffer file) + (find-file-noselect file)))) + +;;;###autoload +(defun magit-ediff-show-working-tree (file) + "Show changes between `HEAD' and working tree using Ediff. +FILE must be relative to the top directory of the repository." + (interactive + (list (magit-read-file-choice "Show changes in file" + (magit-changed-files "HEAD") + "No changed files"))) + (magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file) + (magit-find-file-noselect "HEAD" file)) + ((get-file-buffer file) + (find-file-noselect file)))) + +;;;###autoload +(defun magit-ediff-show-commit (commit) + "Show changes introduced by COMMIT using Ediff." + (interactive (list (magit-read-branch-or-commit "Revision"))) + (let ((revA (concat commit "^")) + (revB commit)) + (apply #'magit-ediff-compare + revA revB + (magit-ediff-read-files revA revB (magit-current-file))))) + +;;;###autoload +(defun magit-ediff-show-stash (stash) + "Show changes introduced by STASH using Ediff. +`magit-ediff-show-stash-with-index' controls whether a +three-buffer Ediff is used in order to distinguish changes in the +stash that were staged." + (interactive (list (magit-read-stash "Stash"))) + (pcase-let* ((revA (concat stash "^1")) + (revB (concat stash "^2")) + (revC stash) + (`(,fileA ,fileC) (magit-ediff-read-files revA revC)) + (fileB fileC)) + (if (and magit-ediff-show-stash-with-index + (member fileA (magit-changed-files revB revA))) + (magit-ediff-buffers + ((magit-get-revision-buffer revA fileA) + (magit-find-file-noselect revA fileA)) + ((magit-get-revision-buffer revB fileB) + (magit-find-file-noselect revB fileB)) + ((magit-get-revision-buffer revC fileC) + (magit-find-file-noselect revC fileC))) + (magit-ediff-compare revA revC fileA fileC)))) + +(defun magit-ediff-cleanup-auxiliary-buffers () + (let* ((ctl-buf ediff-control-buffer) + (ctl-win (ediff-get-visible-buffer-window ctl-buf)) + (ctl-frm ediff-control-frame) + (main-frame (cond ((window-live-p ediff-window-A) + (window-frame ediff-window-A)) + ((window-live-p ediff-window-B) + (window-frame ediff-window-B))))) + (ediff-kill-buffer-carefully ediff-diff-buffer) + (ediff-kill-buffer-carefully ediff-custom-diff-buffer) + (ediff-kill-buffer-carefully ediff-fine-diff-buffer) + (ediff-kill-buffer-carefully ediff-tmp-buffer) + (ediff-kill-buffer-carefully ediff-error-buffer) + (ediff-kill-buffer-carefully ediff-msg-buffer) + (ediff-kill-buffer-carefully ediff-debug-buffer) + (when (boundp 'ediff-patch-diagnostics) + (ediff-kill-buffer-carefully ediff-patch-diagnostics)) + (cond ((and (ediff-window-display-p) + (frame-live-p ctl-frm)) + (delete-frame ctl-frm)) + ((window-live-p ctl-win) + (delete-window ctl-win))) + (ediff-kill-buffer-carefully ctl-buf) + (when (frame-live-p main-frame) + (select-frame main-frame)))) + +(defun magit-ediff-restore-previous-winconf () + (set-window-configuration magit-ediff-previous-winconf)) + +;;; _ +(provide 'magit-ediff) +;;; magit-ediff.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-extras.el b/code/elpa/magit-20220821.1819/magit-extras.el new file mode 100644 index 0000000..8308cd8 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-extras.el @@ -0,0 +1,917 @@ +;;; magit-extras.el --- Additional functionality for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; Additional functionality for Magit. + +;;; Code: + +(require 'magit) + +;; For `magit-do-async-shell-command'. +(declare-function dired-read-shell-command "dired-aux" (prompt arg files)) +;; For `magit-project-status'. +(declare-function vc-git-command "vc-git" + (buffer okstatus file-or-list &rest flags)) + +(defvar ido-exit) +(defvar ido-fallback) +(defvar project-prefix-map) +(defvar project-switch-commands) + +(defgroup magit-extras nil + "Additional functionality for Magit." + :group 'magit-extensions) + +;;; Git Tools +;;;; Git-Mergetool + +;;;###autoload (autoload 'magit-git-mergetool "magit-extras" nil t) +(transient-define-prefix magit-git-mergetool (file args &optional transient) + "Resolve conflicts in FILE using \"git mergetool --gui\". +With a prefix argument allow changing ARGS using a transient +popup. See info node `(magit) Ediffing' for information about +alternative commands." + :man-page "git-mergetool" + ["Settings" + ("-t" magit-git-mergetool:--tool) + ("=t" magit-merge.guitool) + ("=T" magit-merge.tool) + ("-r" magit-mergetool.hideResolved) + ("-b" magit-mergetool.keepBackup) + ("-k" magit-mergetool.keepTemporaries) + ("-w" magit-mergetool.writeToTemp)] + ["Actions" + (" m" "Invoke mergetool" magit-git-mergetool)] + (interactive + (if (and (not (eq transient-current-prefix 'magit-git-mergetool)) + current-prefix-arg) + (list nil nil t) + (list (magit-read-unmerged-file "Resolve") + (transient-args 'magit-git-mergetool)))) + (if transient + (transient-setup 'magit-git-mergetool) + (magit-run-git-async "mergetool" "--gui" args "--" file))) + +(transient-define-infix magit-git-mergetool:--tool () + :description "Override mergetool" + :class 'transient-option + :shortarg "-t" + :argument "--tool=" + :reader #'magit--read-mergetool) + +(transient-define-infix magit-merge.guitool () + :class 'magit--git-variable + :variable "merge.guitool" + :global t + :reader #'magit--read-mergetool) + +(transient-define-infix magit-merge.tool () + :class 'magit--git-variable + :variable "merge.tool" + :global t + :reader #'magit--read-mergetool) + +(defun magit--read-mergetool (prompt _initial-input history) + (let ((choices nil) + (lines (cdr (magit-git-lines "mergetool" "--tool-help")))) + (while (string-prefix-p "\t\t" (car lines)) + (push (substring (pop lines) 2) choices)) + (setq choices (nreverse choices)) + (magit-completing-read (or prompt "Select mergetool") + choices nil t nil history))) + +(transient-define-infix magit-mergetool.hideResolved () + :class 'magit--git-variable:boolean + :variable "mergetool.hideResolved" + :default "false" + :global t) + +(transient-define-infix magit-mergetool.keepBackup () + :class 'magit--git-variable:boolean + :variable "mergetool.keepBackup" + :default "true" + :global t) + +(transient-define-infix magit-mergetool.keepTemporaries () + :class 'magit--git-variable:boolean + :variable "mergetool.keepTemporaries" + :default "false" + :global t) + +(transient-define-infix magit-mergetool.writeToTemp () + :class 'magit--git-variable:boolean + :variable "mergetool.writeToTemp" + :default "false" + :global t) + +;;;; Git-Gui + +;;;###autoload +(defun magit-run-git-gui-blame (commit filename &optional linenum) + "Run `git gui blame' on the given FILENAME and COMMIT. +Interactively run it for the current file and the `HEAD', with a +prefix or when the current file cannot be determined let the user +choose. When the current buffer is visiting FILENAME instruct +blame to center around the line point is on." + (interactive + (let (revision filename) + (when (or current-prefix-arg + (not (setq revision "HEAD" + filename (magit-file-relative-name nil 'tracked)))) + (setq revision (magit-read-branch-or-commit "Blame from revision")) + (setq filename (magit-read-file-from-rev revision "Blame file"))) + (list revision filename + (and (equal filename + (ignore-errors + (magit-file-relative-name buffer-file-name))) + (line-number-at-pos))))) + (magit-with-toplevel + (magit-process-git 0 "gui" "blame" + (and linenum (list (format "--line=%d" linenum))) + commit + filename))) + +;;;; Gitk + +(defcustom magit-gitk-executable + (or (and (eq system-type 'windows-nt) + (let ((exe (magit-git-string + "-c" "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x" + "X" "gitk.exe"))) + (and exe (file-executable-p exe) exe))) + (executable-find "gitk") "gitk") + "The Gitk executable." + :group 'magit-extras + :set-after '(magit-git-executable) + :type 'string) + +;;;###autoload +(defun magit-run-git-gui () + "Run `git gui' for the current git repository." + (interactive) + (magit-with-toplevel (magit-process-git 0 "gui"))) + +;;;###autoload +(defun magit-run-gitk () + "Run `gitk' in the current repository." + (interactive) + (magit-process-file magit-gitk-executable nil 0)) + +;;;###autoload +(defun magit-run-gitk-branches () + "Run `gitk --branches' in the current repository." + (interactive) + (magit-process-file magit-gitk-executable nil 0 nil "--branches")) + +;;;###autoload +(defun magit-run-gitk-all () + "Run `gitk --all' in the current repository." + (interactive) + (magit-process-file magit-gitk-executable nil 0 nil "--all")) + +;;; Emacs Tools + +;;;###autoload +(defun ido-enter-magit-status () + "Drop into `magit-status' from file switching. + +This command does not work in Emacs 26.1. +See https://github.com/magit/magit/issues/3634 +and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31707. + +To make this command available use something like: + + (add-hook \\='ido-setup-hook + (lambda () + (define-key ido-completion-map + (kbd \"C-x g\") \\='ido-enter-magit-status))) + +Starting with Emacs 25.1 the Ido keymaps are defined just once +instead of every time Ido is invoked, so now you can modify it +like pretty much every other keymap: + + (define-key ido-common-completion-map + (kbd \"C-x g\") \\='ido-enter-magit-status)" + (interactive) + (setq ido-exit 'fallback) + (setq ido-fallback #'magit-status) ; for Emacs >= 26.2 + (with-no-warnings (setq fallback #'magit-status)) ; for Emacs 25 + (exit-minibuffer)) + +;;;###autoload +(defun magit-project-status () + "Run `magit-status' in the current project's root." + (interactive) + (if (fboundp 'project-root) + (magit-status-setup-buffer (project-root (project-current t))) + (user-error "`magit-project-status' requires `project' 0.3.0 or greater"))) + +(defvar magit-bind-magit-project-status t + "Whether to bind \"m\" to `magit-project-status' in `project-prefix-map'. +If so, then an entry is added to `project-switch-commands' as +well. If you want to use another key, then you must set this +to nil before loading Magit to prevent \"m\" from being bound.") + +(with-eval-after-load 'project + ;; Only more recent versions of project.el have `project-prefix-map' and + ;; `project-switch-commands', though project.el is available in Emacs 25. + (when (and magit-bind-magit-project-status + (boundp 'project-prefix-map) + ;; Only modify if it hasn't already been modified. + (equal project-switch-commands + (eval (car (get 'project-switch-commands 'standard-value)) + t))) + (define-key project-prefix-map "m" #'magit-project-status) + (add-to-list 'project-switch-commands '(magit-project-status "Magit") t))) + +;;;###autoload +(defun magit-dired-jump (&optional other-window) + "Visit file at point using Dired. +With a prefix argument, visit in another window. If there +is no file at point, then instead visit `default-directory'." + (interactive "P") + (dired-jump other-window + (and-let* ((file (magit-file-at-point))) + (expand-file-name (if (file-directory-p file) + (file-name-as-directory file) + file))))) + +;;;###autoload +(defun magit-dired-log (&optional follow) + "Show log for all marked files, or the current file." + (interactive "P") + (if-let ((topdir (magit-toplevel default-directory))) + (let ((args (car (magit-log-arguments))) + (files (compat-dired-get-marked-files + nil nil #'magit-file-tracked-p nil + "No marked file is being tracked by Git"))) + (when (and follow + (not (member "--follow" args)) + (not (cdr files))) + (push "--follow" args)) + (magit-log-setup-buffer + (list (or (magit-get-current-branch) "HEAD")) + args + (let ((default-directory topdir)) + (mapcar #'file-relative-name files)) + magit-log-buffer-file-locked)) + (magit--not-inside-repository-error))) + +;;;###autoload +(defun magit-dired-am-apply-patches (repo &optional arg) + "In Dired, apply the marked (or next ARG) files as patches. +If inside a repository, then apply in that. Otherwise prompt +for a repository." + (interactive (list (or (magit-toplevel) + (magit-read-repository t)) + current-prefix-arg)) + (let ((files (compat-dired-get-marked-files nil arg nil nil t))) + (magit-status-setup-buffer repo) + (magit-am-apply-patches files))) + +;;;###autoload +(defun magit-do-async-shell-command (file) + "Open FILE with `dired-do-async-shell-command'. +Interactively, open the file at point." + (interactive (list (or (magit-file-at-point) + (completing-read "Act on file: " + (magit-list-files))))) + (require 'dired-aux) + (dired-do-async-shell-command + (dired-read-shell-command "& on %s: " current-prefix-arg (list file)) + nil (list file))) + +;;; Shift Selection + +(defun magit--turn-on-shift-select-mode-p () + (and shift-select-mode + this-command-keys-shift-translated + (not mark-active) + (not (eq (car-safe transient-mark-mode) 'only)))) + +;;;###autoload +(defun magit-previous-line (&optional arg try-vscroll) + "Like `previous-line' but with Magit-specific shift-selection. + +Magit's selection mechanism is based on the region but selects an +area that is larger than the region. This causes `previous-line' +when invoked while holding the shift key to move up one line and +thereby select two lines. When invoked inside a hunk body this +command does not move point on the first invocation and thereby +it only selects a single line. Which inconsistency you prefer +is a matter of preference." + (declare (interactive-only + "use `forward-line' with negative argument instead.")) + (interactive "p\np") + (unless arg (setq arg 1)) + (let ((stay (or (magit-diff-inside-hunk-body-p) + (magit-section-position-in-heading-p)))) + (if (and stay (= arg 1) (magit--turn-on-shift-select-mode-p)) + (push-mark nil nil t) + (with-no-warnings + (handle-shift-selection) + (previous-line (if stay (max (1- arg) 1) arg) try-vscroll))))) + +;;;###autoload +(defun magit-next-line (&optional arg try-vscroll) + "Like `next-line' but with Magit-specific shift-selection. + +Magit's selection mechanism is based on the region but selects +an area that is larger than the region. This causes `next-line' +when invoked while holding the shift key to move down one line +and thereby select two lines. When invoked inside a hunk body +this command does not move point on the first invocation and +thereby it only selects a single line. Which inconsistency you +prefer is a matter of preference." + (declare (interactive-only forward-line)) + (interactive "p\np") + (unless arg (setq arg 1)) + (let ((stay (or (magit-diff-inside-hunk-body-p) + (magit-section-position-in-heading-p)))) + (if (and stay (= arg 1) (magit--turn-on-shift-select-mode-p)) + (push-mark nil nil t) + (with-no-warnings + (handle-shift-selection) + (next-line (if stay (max (1- arg) 1) arg) try-vscroll))))) + +;;; Clean + +;;;###autoload +(defun magit-clean (&optional arg) + "Remove untracked files from the working tree. +With a prefix argument also remove ignored files, +with two prefix arguments remove ignored files only. +\n(git clean -f -d [-x|-X])" + (interactive "p") + (when (yes-or-no-p (format "Remove %s files? " + (pcase arg + (1 "untracked") + (4 "untracked and ignored") + (_ "ignored")))) + (magit-wip-commit-before-change) + (magit-run-git "clean" "-f" "-d" (pcase arg (4 "-x") (16 "-X"))))) + +(put 'magit-clean 'disabled t) + +;;; ChangeLog + +;;;###autoload +(defun magit-generate-changelog (&optional amending) + "Insert ChangeLog entries into the current buffer. + +The entries are generated from the diff being committed. +If prefix argument, AMENDING, is non-nil, include changes +in HEAD as well as staged changes in the diff to check." + (interactive "P") + (unless (magit-commit-message-buffer) + (user-error "No commit in progress")) + (require 'diff-mode) ; `diff-add-log-current-defuns'. + (require 'vc-git) ; `vc-git-diff'. + (require 'add-log) ; `change-log-insert-entries'. + (cond + ((and (fboundp 'change-log-insert-entries) + (fboundp 'diff-add-log-current-defuns)) + (setq default-directory + (if (and (file-regular-p "gitdir") + (not (magit-git-true "rev-parse" "--is-inside-work-tree")) + (magit-git-true "rev-parse" "--is-inside-git-dir")) + (file-name-directory (magit-file-line "gitdir")) + (magit-toplevel))) + (let ((rev1 (if amending "HEAD^1" "HEAD")) + (rev2 nil)) + ;; Magit may have updated the files without notifying vc, but + ;; `diff-add-log-current-defuns' relies on vc being up-to-date. + (mapc #'vc-file-clearprops (magit-staged-files)) + (change-log-insert-entries + (with-temp-buffer + (vc-git-command (current-buffer) 1 nil + "diff-index" "--exit-code" "--patch" + (and (magit-anything-staged-p) "--cached") + rev1 "--") + ;; `diff-find-source-location' consults these vars. + (defvar diff-vc-revisions) + (setq-local diff-vc-revisions (list rev1 rev2)) + (setq-local diff-vc-backend 'Git) + (diff-add-log-current-defuns))))) + (t (user-error "`magit-generate-changelog' requires Emacs 27 or greater")))) + +;;;###autoload +(defun magit-add-change-log-entry (&optional whoami file-name other-window) + "Find change log file and add date entry and item for current change. +This differs from `add-change-log-entry' (which see) in that +it acts on the current hunk in a Magit buffer instead of on +a position in a file-visiting buffer." + (interactive (list current-prefix-arg + (prompt-for-change-log-name))) + (pcase-let ((`(,buf ,pos) (magit-diff-visit-file--noselect))) + (magit--with-temp-position buf pos + (let ((add-log-buffer-file-name-function + (lambda () + (or magit-buffer-file-name + (buffer-file-name))))) + (add-change-log-entry whoami file-name other-window))))) + +;;;###autoload +(defun magit-add-change-log-entry-other-window (&optional whoami file-name) + "Find change log file in other window and add entry and item. +This differs from `add-change-log-entry-other-window' (which see) +in that it acts on the current hunk in a Magit buffer instead of +on a position in a file-visiting buffer." + (interactive (and current-prefix-arg + (list current-prefix-arg + (prompt-for-change-log-name)))) + (magit-add-change-log-entry whoami file-name t)) + +;;; Edit Line Commit + +;;;###autoload +(defun magit-edit-line-commit (&optional type) + "Edit the commit that added the current line. + +With a prefix argument edit the commit that removes the line, +if any. The commit is determined using `git blame' and made +editable using `git rebase --interactive' if it is reachable +from `HEAD', or by checking out the commit (or a branch that +points at it) otherwise." + (interactive (list (and current-prefix-arg 'removal))) + (let* ((chunk (magit-current-blame-chunk (or type 'addition))) + (rev (oref chunk orig-rev))) + (if (string-match-p "\\`0\\{40,\\}\\'" rev) + (message "This line has not been committed yet") + (let ((rebase (magit-rev-ancestor-p rev "HEAD")) + (file (expand-file-name (oref chunk orig-file) + (magit-toplevel)))) + (if rebase + (let ((magit--rebase-published-symbol 'edit-published)) + (magit-rebase-edit-commit rev (magit-rebase-arguments))) + (magit-checkout (or (magit-rev-branch rev) rev))) + (unless (and buffer-file-name + (file-equal-p file buffer-file-name)) + (let ((blame-type (and magit-blame-mode magit-blame-type))) + (if rebase + (set-process-sentinel + magit-this-process + (lambda (process event) + (magit-sequencer-process-sentinel process event) + (when (eq (process-status process) 'exit) + (find-file file) + (when blame-type + (magit-blame--pre-blame-setup blame-type) + (magit-blame--run (magit-blame-arguments)))))) + (find-file file) + (when blame-type + (magit-blame--pre-blame-setup blame-type) + (magit-blame--run (magit-blame-arguments)))))))))) + +(put 'magit-edit-line-commit 'disabled t) + +;;;###autoload +(defun magit-diff-edit-hunk-commit (file) + "From a hunk, edit the respective commit and visit the file. + +First visit the file being modified by the hunk at the correct +location using `magit-diff-visit-file'. This actually visits a +blob. When point is on a diff header, not within an individual +hunk, then this visits the blob the first hunk is about. + +Then invoke `magit-edit-line-commit', which uses an interactive +rebase to make the commit editable, or if that is not possible +because the commit is not reachable from `HEAD' by checking out +that commit directly. This also causes the actual worktree file +to be visited. + +Neither the blob nor the file buffer are killed when finishing +the rebase. If that is undesirable, then it might be better to +use `magit-rebase-edit-command' instead of this command." + (interactive (list (magit-file-at-point t t))) + (let ((magit-diff-visit-previous-blob nil)) + (with-current-buffer + (magit-diff-visit-file--internal file nil #'pop-to-buffer-same-window) + (magit-edit-line-commit)))) + +(put 'magit-diff-edit-hunk-commit 'disabled t) + +;;; Reshelve + +(defcustom magit-reshelve-since-committer-only nil + "Whether `magit-reshelve-since' changes only the committer dates. +Otherwise the author dates are also changed." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type 'boolean) + +;;;###autoload +(defun magit-reshelve-since (rev keyid) + "Change the author and committer dates of the commits since REV. + +Ask the user for the first reachable commit whose dates should +be changed. Then read the new date for that commit. The initial +minibuffer input and the previous history element offer good +values. The next commit will be created one minute later and so +on. + +This command is only intended for interactive use and should only +be used on highly rearranged and unpublished history. + +If KEYID is non-nil, then use that to sign all reshelved commits. +Interactively use the value of the \"--gpg-sign\" option in the +list returned by `magit-rebase-arguments'." + (interactive (list nil + (transient-arg-value "--gpg-sign=" + (magit-rebase-arguments)))) + (let* ((current (or (magit-get-current-branch) + (user-error "Refusing to reshelve detached head"))) + (backup (concat "refs/original/refs/heads/" current))) + (cond + ((not rev) + (when (and (magit-ref-p backup) + (not (magit-y-or-n-p + (format "Backup ref %s already exists. Override? " backup)))) + (user-error "Abort")) + (magit-log-select + (lambda (rev) + (magit-reshelve-since rev keyid)) + "Type %p on a commit to reshelve it and the commits above it,")) + (t + (cl-flet ((adjust (time offset) + (format-time-string + "%F %T %z" + (+ (floor time) + (* offset 60) + (- (car (decode-time time))))))) + (let* ((start (concat rev "^")) + (range (concat start ".." current)) + (time-rev (adjust (float-time (string-to-number + (magit-rev-format "%at" start))) + 1)) + (time-now (adjust (float-time) + (- (string-to-number + (magit-git-string "rev-list" "--count" + range)))))) + (push time-rev magit--reshelve-history) + (let ((date (floor + (float-time + (date-to-time + (read-string "Date for first commit: " + time-now 'magit--reshelve-history)))))) + (with-environment-variables (("FILTER_BRANCH_SQUELCH_WARNING" "1")) + (magit-with-toplevel + (magit-run-git-async + "filter-branch" "--force" "--env-filter" + (format + "case $GIT_COMMIT in %s\nesac" + (mapconcat + (lambda (rev) + (prog1 + (concat + (format "%s) " rev) + (and (not magit-reshelve-since-committer-only) + (format "export GIT_AUTHOR_DATE=\"%s\"; " date)) + (format "export GIT_COMMITTER_DATE=\"%s\";;" date)) + (cl-incf date 60))) + (magit-git-lines "rev-list" "--reverse" range) + " ")) + (and keyid + (list "--commit-filter" + (format "git commit-tree --gpg-sign=%s \"$@\";" + keyid))) + range "--")) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (> (process-exit-status process) 0) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (magit-run-git "update-ref" "-d" backup))))))))))))) + +;;; Revision Stack + +(defvar magit-revision-stack nil) + +(defcustom magit-pop-revision-stack-format + '("[%N: %h] " + "%N: %cs %H\n %s\n" + "\\[\\([0-9]+\\)[]:]") + "Control how `magit-pop-revision-stack' inserts a revision. + +The command `magit-pop-revision-stack' inserts a representation +of the revision last pushed to the `magit-revision-stack' into +the current buffer. It inserts text at point and/or near the end +of the buffer, and removes the consumed revision from the stack. + +The entries on the stack have the format (HASH TOPLEVEL) and this +option has the format (POINT-FORMAT EOB-FORMAT INDEX-REGEXP), all +of which may be nil or a string (though either one of EOB-FORMAT +or POINT-FORMAT should be a string, and if INDEX-REGEXP is +non-nil, then the two formats should be too). + +First INDEX-REGEXP is used to find the previously inserted entry, +by searching backward from point. The first submatch must match +the index number. That number is incremented by one, and becomes +the index number of the entry to be inserted. If you don't want +to number the inserted revisions, then use nil for INDEX-REGEXP. + +If INDEX-REGEXP is non-nil, then both POINT-FORMAT and EOB-FORMAT +should contain \"%N\", which is replaced with the number that was +determined in the previous step. + +Both formats, if non-nil and after removing %N, are then expanded +using `git show --format=FORMAT ...' inside TOPLEVEL. + +The expansion of POINT-FORMAT is inserted at point, and the +expansion of EOB-FORMAT is inserted at the end of the buffer (if +the buffer ends with a comment, then it is inserted right before +that)." + :package-version '(magit . "3.2.0") + :group 'magit-commands + :type '(list (choice (string :tag "Insert at point format") + (cons (string :tag "Insert at point format") + (repeat (string :tag "Argument to git show"))) + (const :tag "Don't insert at point" nil)) + (choice (string :tag "Insert at eob format") + (cons (string :tag "Insert at eob format") + (repeat (string :tag "Argument to git show"))) + (const :tag "Don't insert at eob" nil)) + (choice (regexp :tag "Find index regexp") + (const :tag "Don't number entries" nil)))) + +(defcustom magit-copy-revision-abbreviated nil + "Whether to save abbreviated revision to `kill-ring' and `magit-revision-stack'." + :package-version '(magit . "3.0.0") + :group 'magit-miscellaneous + :type 'boolean) + +;;;###autoload +(defun magit-pop-revision-stack (rev toplevel) + "Insert a representation of a revision into the current buffer. + +Pop a revision from the `magit-revision-stack' and insert it into +the current buffer according to `magit-pop-revision-stack-format'. +Revisions can be put on the stack using `magit-copy-section-value' +and `magit-copy-buffer-revision'. + +If the stack is empty or with a prefix argument, instead read a +revision in the minibuffer. By using the minibuffer history this +allows selecting an item which was popped earlier or to insert an +arbitrary reference or revision without first pushing it onto the +stack. + +When reading the revision from the minibuffer, then it might not +be possible to guess the correct repository. When this command +is called inside a repository (e.g. while composing a commit +message), then that repository is used. Otherwise (e.g. while +composing an email) then the repository recorded for the top +element of the stack is used (even though we insert another +revision). If not called inside a repository and with an empty +stack, or with two prefix arguments, then read the repository in +the minibuffer too." + (interactive + (if (or current-prefix-arg (not magit-revision-stack)) + (let ((default-directory + (or (and (not (= (prefix-numeric-value current-prefix-arg) 16)) + (or (magit-toplevel) + (cadr (car magit-revision-stack)))) + (magit-read-repository)))) + (list (magit-read-branch-or-commit "Insert revision") + default-directory)) + (push (caar magit-revision-stack) magit-revision-history) + (pop magit-revision-stack))) + (if rev + (pcase-let ((`(,pnt-format ,eob-format ,idx-format) + magit-pop-revision-stack-format)) + (let ((default-directory toplevel) + (idx (and idx-format + (save-excursion + (if (re-search-backward idx-format nil t) + (number-to-string + (1+ (string-to-number (match-string 1)))) + "1")))) + pnt-args eob-args) + (when (listp pnt-format) + (setq pnt-args (cdr pnt-format)) + (setq pnt-format (car pnt-format))) + (when (listp eob-format) + (setq eob-args (cdr eob-format)) + (setq eob-format (car eob-format))) + (when pnt-format + (when idx-format + (setq pnt-format + (string-replace "%N" idx pnt-format))) + (magit-rev-insert-format pnt-format rev pnt-args) + (backward-delete-char 1)) + (when eob-format + (when idx-format + (setq eob-format + (string-replace "%N" idx eob-format))) + (save-excursion + (goto-char (point-max)) + (skip-syntax-backward ">s-") + (beginning-of-line) + (if (and comment-start (looking-at comment-start)) + (while (looking-at comment-start) + (forward-line -1)) + (forward-line) + (unless (= (current-column) 0) + (insert ?\n))) + (insert ?\n) + (magit-rev-insert-format eob-format rev eob-args) + (backward-delete-char 1))))) + (user-error "Revision stack is empty"))) + +(define-key git-commit-mode-map + (kbd "C-c C-w") #'magit-pop-revision-stack) + +;;;###autoload +(defun magit-copy-section-value (arg) + "Save the value of the current section for later use. + +Save the section value to the `kill-ring', and, provided that +the current section is a commit, branch, or tag section, push +the (referenced) revision to the `magit-revision-stack' for use +with `magit-pop-revision-stack'. + +When `magit-copy-revision-abbreviated' is non-nil, save the +abbreviated revision to the `kill-ring' and the +`magit-revision-stack'. + +When the current section is a branch or a tag, and a prefix +argument is used, then save the revision at its tip to the +`kill-ring' instead of the reference name. + +When the region is active, then save that to the `kill-ring', +like `kill-ring-save' would, instead of behaving as described +above. If a prefix argument is used and the region is within +a hunk, then strip the diff marker column and keep only either +the added or removed lines, depending on the sign of the prefix +argument." + (interactive "P") + (cond + ((and arg + (magit-section-internal-region-p) + (magit-section-match 'hunk)) + (kill-new + (thread-last (buffer-substring-no-properties + (region-beginning) + (region-end)) + (replace-regexp-in-string + (format "^\\%c.*\n?" (if (< (prefix-numeric-value arg) 0) ?+ ?-)) + "") + (replace-regexp-in-string "^[ \\+\\-]" ""))) + (deactivate-mark)) + ((use-region-p) + (call-interactively #'copy-region-as-kill)) + (t + (when-let* ((section (magit-current-section)) + (value (oref section value))) + (magit-section-case + ((branch commit module-commit tag) + (let ((default-directory default-directory) ref) + (magit-section-case + ((branch tag) + (setq ref value)) + (module-commit + (setq default-directory + (file-name-as-directory + (expand-file-name (magit-section-parent-value section) + (magit-toplevel)))))) + (setq value (magit-rev-parse + (and magit-copy-revision-abbreviated "--short") + value)) + (push (list value default-directory) magit-revision-stack) + (kill-new (message "%s" (or (and current-prefix-arg ref) + value))))) + (t (kill-new (message "%s" value)))))))) + +;;;###autoload +(defun magit-copy-buffer-revision () + "Save the revision of the current buffer for later use. + +Save the revision shown in the current buffer to the `kill-ring' +and push it to the `magit-revision-stack'. + +This command is mainly intended for use in `magit-revision-mode' +buffers, the only buffers where it is always unambiguous exactly +which revision should be saved. + +Most other Magit buffers usually show more than one revision, in +some way or another, so this command has to select one of them, +and that choice might not always be the one you think would have +been the best pick. + +In such buffers it is often more useful to save the value of +the current section instead, using `magit-copy-section-value'. + +When the region is active, then save that to the `kill-ring', +like `kill-ring-save' would, instead of behaving as described +above. + +When `magit-copy-revision-abbreviated' is non-nil, save the +abbreviated revision to the `kill-ring' and the +`magit-revision-stack'." + (interactive) + (if (use-region-p) + (call-interactively #'copy-region-as-kill) + (when-let ((rev (or magit-buffer-revision + (cl-case major-mode + (magit-diff-mode + (if (string-match "\\.\\.\\.?\\(.+\\)" + magit-buffer-range) + (match-string 1 magit-buffer-range) + magit-buffer-range)) + (magit-status-mode "HEAD"))))) + (when (magit-commit-p rev) + (setq rev (magit-rev-parse + (and magit-copy-revision-abbreviated "--short") + rev)) + (push (list rev default-directory) magit-revision-stack) + (kill-new (message "%s" rev)))))) + +;;; Buffer Switching + +;;;###autoload +(defun magit-display-repository-buffer (buffer) + "Display a Magit buffer belonging to the current Git repository. +The buffer is displayed using `magit-display-buffer', which see." + (interactive (list (magit--read-repository-buffer + "Display magit buffer: "))) + (magit-display-buffer buffer)) + +;;;###autoload +(defun magit-switch-to-repository-buffer (buffer) + "Switch to a Magit buffer belonging to the current Git repository." + (interactive (list (magit--read-repository-buffer + "Switch to magit buffer: "))) + (switch-to-buffer buffer)) + +;;;###autoload +(defun magit-switch-to-repository-buffer-other-window (buffer) + "Switch to a Magit buffer belonging to the current Git repository." + (interactive (list (magit--read-repository-buffer + "Switch to magit buffer in another window: "))) + (switch-to-buffer-other-window buffer)) + +;;;###autoload +(defun magit-switch-to-repository-buffer-other-frame (buffer) + "Switch to a Magit buffer belonging to the current Git repository." + (interactive (list (magit--read-repository-buffer + "Switch to magit buffer in another frame: "))) + (switch-to-buffer-other-frame buffer)) + +(defun magit--read-repository-buffer (prompt) + (if-let ((topdir (magit-rev-parse-safe "--show-toplevel"))) + (read-buffer + prompt (magit-get-mode-buffer 'magit-status-mode) t + (pcase-lambda (`(,_ . ,buf)) + (and buf + (with-current-buffer buf + (and (or (derived-mode-p 'magit-mode + 'magit-repolist-mode + 'magit-submodule-list-mode + 'git-rebase-mode) + (and buffer-file-name + (string-match-p git-commit-filename-regexp + buffer-file-name))) + (equal (magit-rev-parse-safe "--show-toplevel") + topdir)))))) + (user-error "Not inside a Git repository"))) + +;;; Miscellaneous + +;;;###autoload +(defun magit-abort-dwim () + "Abort current operation. +Depending on the context, this will abort a merge, a rebase, a +patch application, a cherry-pick, a revert, or a bisect." + (interactive) + (cond ((magit-merge-in-progress-p) (magit-merge-abort)) + ((magit-rebase-in-progress-p) (magit-rebase-abort)) + ((magit-am-in-progress-p) (magit-am-abort)) + ((magit-sequencer-in-progress-p) (magit-sequencer-abort)) + ((magit-bisect-in-progress-p) (magit-bisect-reset)))) + +;;; _ +(provide 'magit-extras) +;;; magit-extras.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-fetch.el b/code/elpa/magit-20220821.1819/magit-fetch.el new file mode 100644 index 0000000..6c97d18 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-fetch.el @@ -0,0 +1,199 @@ +;;; magit-fetch.el --- Download objects and refs -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements fetch commands. + +;;; Code: + +(require 'magit) + +(defvar magit-fetch-modules-jobs nil) +(make-obsolete-variable + 'magit-fetch-modules-jobs + "invoke `magit-fetch-modules' with a prefix argument instead." + "Magit 3.0.0") + +;;; Commands + +;;;###autoload (autoload 'magit-fetch "magit-fetch" nil t) +(transient-define-prefix magit-fetch () + "Fetch from another repository." + :man-page "git-fetch" + ["Arguments" + ("-p" "Prune deleted branches" ("-p" "--prune")) + ("-t" "Fetch all tags" ("-t" "--tags")) + (7 "-u" "Fetch full history" "--unshallow")] + ["Fetch from" + ("p" magit-fetch-from-pushremote) + ("u" magit-fetch-from-upstream) + ("e" "elsewhere" magit-fetch-other) + ("a" "all remotes" magit-fetch-all)] + ["Fetch" + ("o" "another branch" magit-fetch-branch) + ("r" "explicit refspec" magit-fetch-refspec) + ("m" "submodules" magit-fetch-modules)] + ["Configure" + ("C" "variables..." magit-branch-configure)]) + +(defun magit-fetch-arguments () + (transient-args 'magit-fetch)) + +(defun magit-git-fetch (remote args) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "fetch" remote args)) + +;;;###autoload (autoload 'magit-fetch-from-pushremote "magit-fetch" nil t) +(transient-define-suffix magit-fetch-from-pushremote (args) + "Fetch from the current push-remote. + +With a prefix argument or when the push-remote is either not +configured or unusable, then let the user first configure the +push-remote." + :description #'magit-fetch--pushremote-description + (interactive (list (magit-fetch-arguments))) + (let ((remote (magit-get-push-remote))) + (when (or current-prefix-arg + (not (member remote (magit-list-remotes)))) + (let ((var (magit--push-remote-variable))) + (setq remote + (magit-read-remote (format "Set %s and fetch from there" var))) + (magit-set remote var))) + (magit-git-fetch remote args))) + +(defun magit-fetch--pushremote-description () + (let* ((branch (magit-get-current-branch)) + (remote (magit-get-push-remote branch)) + (v (magit--push-remote-variable branch t))) + (cond + ((member remote (magit-list-remotes)) remote) + (remote + (format "%s, replacing invalid" v)) + (t + (format "%s, setting that" v))))) + +;;;###autoload (autoload 'magit-fetch-from-upstream "magit-fetch" nil t) +(transient-define-suffix magit-fetch-from-upstream (remote args) + "Fetch from the \"current\" remote, usually the upstream. + +If the upstream is configured for the current branch and names +an existing remote, then use that. Otherwise try to use another +remote: If only a single remote is configured, then use that. +Otherwise if a remote named \"origin\" exists, then use that. + +If no remote can be determined, then this command is not available +from the `magit-fetch' transient prefix and invoking it directly +results in an error." + :if (lambda () (magit-get-current-remote t)) + :description (lambda () (magit-get-current-remote t)) + (interactive (list (magit-get-current-remote t) + (magit-fetch-arguments))) + (unless remote + (error "The \"current\" remote could not be determined")) + (magit-git-fetch remote args)) + +;;;###autoload +(defun magit-fetch-other (remote args) + "Fetch from another repository." + (interactive (list (magit-read-remote "Fetch remote") + (magit-fetch-arguments))) + (magit-git-fetch remote args)) + +;;;###autoload +(defun magit-fetch-branch (remote branch args) + "Fetch a BRANCH from a REMOTE." + (interactive + (let ((remote (magit-read-remote-or-url "Fetch from remote or url"))) + (list remote + (magit-read-remote-branch "Fetch branch" remote) + (magit-fetch-arguments)))) + (magit-git-fetch remote (cons branch args))) + +;;;###autoload +(defun magit-fetch-refspec (remote refspec args) + "Fetch a REFSPEC from a REMOTE." + (interactive + (let ((remote (magit-read-remote-or-url "Fetch from remote or url"))) + (list remote + (magit-read-refspec "Fetch using refspec" remote) + (magit-fetch-arguments)))) + (magit-git-fetch remote (cons refspec args))) + +;;;###autoload +(defun magit-fetch-all (args) + "Fetch from all remotes." + (interactive (list (magit-fetch-arguments))) + (magit-git-fetch nil (cons "--all" args))) + +;;;###autoload +(defun magit-fetch-all-prune () + "Fetch from all remotes, and prune. +Prune remote tracking branches for branches that have been +removed on the respective remote." + (interactive) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "remote" "update" "--prune")) + +;;;###autoload +(defun magit-fetch-all-no-prune () + "Fetch from all remotes." + (interactive) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "remote" "update")) + +;;;###autoload (autoload 'magit-fetch-modules "magit-fetch" nil t) +(transient-define-prefix magit-fetch-modules (&optional transient args) + "Fetch all submodules. + +Fetching is done using \"git fetch --recurse-submodules\", which +means that the super-repository and recursively all submodules +are also fetched. + +To set and potentially save other arguments invoke this command +with a prefix argument." + :man-page "git-fetch" + :value (list "--verbose" + (cond (magit-fetch-modules-jobs + (format "--jobs=%s" magit-fetch-modules-jobs)) + (t "--jobs=4"))) + ["Arguments" + ("-v" "verbose" "--verbose") + ("-j" "number of jobs" "--jobs=" :reader transient-read-number-N+)] + ["Action" + ("m" "fetch modules" magit-fetch-modules)] + (interactive (if current-prefix-arg + (list t) + (list nil (transient-args 'magit-fetch-modules)))) + (if transient + (transient-setup 'magit-fetch-modules) + (when (magit-git-version< "2.8.0") + (when-let ((value (transient-arg-value "--jobs=" args))) + (message "Dropping --jobs; not supported by Git v%s" + (magit-git-version)) + (setq args (remove (format "--jobs=%s" value) args)))) + (magit-with-toplevel + (magit-run-git-async "fetch" "--recurse-submodules" args)))) + +;;; _ +(provide 'magit-fetch) +;;; magit-fetch.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-files.el b/code/elpa/magit-20220821.1819/magit-files.el new file mode 100644 index 0000000..c1a150f --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-files.el @@ -0,0 +1,536 @@ +;;; magit-files.el --- Finding files -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for finding blobs, staged files, +;; and Git configuration files. It also implements modes useful in +;; buffers visiting files and blobs, and the commands used by those +;; modes. + +;;; Code: + +(require 'magit) + +;;; Find Blob + +(defvar magit-find-file-hook nil) +(add-hook 'magit-find-file-hook #'magit-blob-mode) + +;;;###autoload +(defun magit-find-file (rev file) + "View FILE from REV. +Switch to a buffer visiting blob REV:FILE, creating one if none +already exists. If prior to calling this command the current +buffer and/or cursor position is about the same file, then go +to the line and column corresponding to that location." + (interactive (magit-find-file-read-args "Find file")) + (magit-find-file--internal rev file #'pop-to-buffer-same-window)) + +;;;###autoload +(defun magit-find-file-other-window (rev file) + "View FILE from REV, in another window. +Switch to a buffer visiting blob REV:FILE, creating one if none +already exists. If prior to calling this command the current +buffer and/or cursor position is about the same file, then go to +the line and column corresponding to that location." + (interactive (magit-find-file-read-args "Find file in other window")) + (magit-find-file--internal rev file #'switch-to-buffer-other-window)) + +;;;###autoload +(defun magit-find-file-other-frame (rev file) + "View FILE from REV, in another frame. +Switch to a buffer visiting blob REV:FILE, creating one if none +already exists. If prior to calling this command the current +buffer and/or cursor position is about the same file, then go to +the line and column corresponding to that location." + (interactive (magit-find-file-read-args "Find file in other frame")) + (magit-find-file--internal rev file #'switch-to-buffer-other-frame)) + +(defun magit-find-file-read-args (prompt) + (let ((pseudo-revs '("{worktree}" "{index}"))) + (if-let ((rev (magit-completing-read "Find file from revision" + (append pseudo-revs + (magit-list-refnames nil t)) + nil nil nil 'magit-revision-history + (or (magit-branch-or-commit-at-point) + (magit-get-current-branch))))) + (list rev (magit-read-file-from-rev (if (member rev pseudo-revs) + "HEAD" + rev) + prompt)) + (user-error "Nothing selected")))) + +(defun magit-find-file--internal (rev file fn) + (let ((buf (magit-find-file-noselect rev file)) + line col) + (when-let ((visited-file (magit-file-relative-name))) + (setq line (line-number-at-pos)) + (setq col (current-column)) + (cond + ((not (equal visited-file file))) + ((equal magit-buffer-revision rev)) + ((equal rev "{worktree}") + (setq line (magit-diff-visit--offset file magit-buffer-revision line))) + ((equal rev "{index}") + (setq line (magit-diff-visit--offset file nil line))) + (magit-buffer-revision + (setq line (magit-diff-visit--offset + file (concat magit-buffer-revision ".." rev) line))) + (t + (setq line (magit-diff-visit--offset file (list "-R" rev) line))))) + (funcall fn buf) + (when line + (with-current-buffer buf + (widen) + (goto-char (point-min)) + (forward-line (1- line)) + (move-to-column col))) + buf)) + +(defun magit-find-file-noselect (rev file) + "Read FILE from REV into a buffer and return the buffer. +REV is a revision or one of \"{worktree}\" or \"{index}\". +FILE must be relative to the top directory of the repository." + (magit-find-file-noselect-1 rev file)) + +(defun magit-find-file-noselect-1 (rev file &optional revert) + "Read FILE from REV into a buffer and return the buffer. +REV is a revision or one of \"{worktree}\" or \"{index}\". +FILE must be relative to the top directory of the repository. +Non-nil REVERT means to revert the buffer. If `ask-revert', +then only after asking. A non-nil value for REVERT is ignored if REV is +\"{worktree}\"." + (if (equal rev "{worktree}") + (find-file-noselect (expand-file-name file (magit-toplevel))) + (let ((topdir (magit-toplevel))) + (when (file-name-absolute-p file) + (setq file (file-relative-name file topdir))) + (with-current-buffer (magit-get-revision-buffer-create rev file) + (when (or (not magit-buffer-file-name) + (if (eq revert 'ask-revert) + (y-or-n-p (format "%s already exists; revert it? " + (buffer-name)))) + revert) + (setq magit-buffer-revision + (if (equal rev "{index}") + "{index}" + (magit-rev-format "%H" rev))) + (setq magit-buffer-refname rev) + (setq magit-buffer-file-name (expand-file-name file topdir)) + (setq default-directory + (let ((dir (file-name-directory magit-buffer-file-name))) + (if (file-exists-p dir) dir topdir))) + (setq-local revert-buffer-function #'magit-revert-rev-file-buffer) + (revert-buffer t t) + (run-hooks (if (equal rev "{index}") + 'magit-find-index-hook + 'magit-find-file-hook))) + (current-buffer))))) + +(defun magit-get-revision-buffer-create (rev file) + (magit-get-revision-buffer rev file t)) + +(defun magit-get-revision-buffer (rev file &optional create) + (funcall (if create #'get-buffer-create #'get-buffer) + (format "%s.~%s~" file (subst-char-in-string ?/ ?_ rev)))) + +(defun magit-revert-rev-file-buffer (_ignore-auto noconfirm) + (when (or noconfirm + (and (not (buffer-modified-p)) + (catch 'found + (dolist (regexp revert-without-query) + (when (string-match regexp magit-buffer-file-name) + (throw 'found t))))) + (yes-or-no-p (format "Revert buffer from Git %s? " + (if (equal magit-buffer-refname "{index}") + "index" + (concat "revision " magit-buffer-refname))))) + (let* ((inhibit-read-only t) + (default-directory (magit-toplevel)) + (file (file-relative-name magit-buffer-file-name)) + (coding-system-for-read (or coding-system-for-read 'undecided))) + (erase-buffer) + (magit-git-insert "cat-file" "-p" + (if (equal magit-buffer-refname "{index}") + (concat ":" file) + (concat magit-buffer-refname ":" file))) + (setq buffer-file-coding-system last-coding-system-used)) + (let ((buffer-file-name magit-buffer-file-name) + (after-change-major-mode-hook + (remq 'global-diff-hl-mode-enable-in-buffers + after-change-major-mode-hook))) + (delay-mode-hooks + (normal-mode t))) + (setq buffer-read-only t) + (set-buffer-modified-p nil) + (goto-char (point-min)))) + +;;; Find Index + +(defvar magit-find-index-hook nil) + +(defun magit-find-file-index-noselect (file &optional revert) + "Read FILE from the index into a buffer and return the buffer. +FILE must to be relative to the top directory of the repository." + (magit-find-file-noselect-1 "{index}" file (or revert 'ask-revert))) + +(defun magit-update-index () + "Update the index with the contents of the current buffer. +The current buffer has to be visiting a file in the index, which +is done using `magit-find-index-noselect'." + (interactive) + (let ((file (magit-file-relative-name))) + (unless (equal magit-buffer-refname "{index}") + (user-error "%s isn't visiting the index" file)) + (if (y-or-n-p (format "Update index with contents of %s" (buffer-name))) + (let ((index (make-temp-name (magit-git-dir "magit-update-index-"))) + (buffer (current-buffer))) + (when magit-wip-before-change-mode + (magit-wip-commit-before-change (list file) " before un-/stage")) + (unwind-protect + (progn + (let ((coding-system-for-write buffer-file-coding-system)) + (with-temp-file index + (insert-buffer-substring buffer))) + (magit-with-toplevel + (magit-call-git + "update-index" "--cacheinfo" + (substring (magit-git-string "ls-files" "-s" file) + 0 6) + (magit-git-string "hash-object" "-t" "blob" "-w" + (concat "--path=" file) + "--" (magit-convert-filename-for-git index)) + file))) + (ignore-errors (delete-file index))) + (set-buffer-modified-p nil) + (when magit-wip-after-apply-mode + (magit-wip-commit-after-apply (list file) " after un-/stage"))) + (message "Abort"))) + (--when-let (magit-get-mode-buffer 'magit-status-mode) + (with-current-buffer it (magit-refresh))) + t) + +;;; Find Config File + +(defun magit-find-git-config-file (filename &optional wildcards) + "Edit a file located in the current repository's git directory. + +When \".git\", located at the root of the working tree, is a +regular file, then that makes it cumbersome to open a file +located in the actual git directory. + +This command is like `find-file', except that it temporarily +binds `default-directory' to the actual git directory, while +reading the FILENAME." + (interactive + (let ((default-directory (magit-git-dir))) + (find-file-read-args "Find file: " + (confirm-nonexistent-file-or-buffer)))) + (find-file filename wildcards)) + +(defun magit-find-git-config-file-other-window (filename &optional wildcards) + "Edit a file located in the current repo's git directory, in another window. + +When \".git\", located at the root of the working tree, is a +regular file, then that makes it cumbersome to open a file +located in the actual git directory. + +This command is like `find-file-other-window', except that it +temporarily binds `default-directory' to the actual git +directory, while reading the FILENAME." + (interactive + (let ((default-directory (magit-git-dir))) + (find-file-read-args "Find file in other window: " + (confirm-nonexistent-file-or-buffer)))) + (find-file-other-window filename wildcards)) + +(defun magit-find-git-config-file-other-frame (filename &optional wildcards) + "Edit a file located in the current repo's git directory, in another frame. + +When \".git\", located at the root of the working tree, is a +regular file, then that makes it cumbersome to open a file +located in the actual git directory. + +This command is like `find-file-other-frame', except that it +temporarily binds `default-directory' to the actual git +directory, while reading the FILENAME." + (interactive + (let ((default-directory (magit-git-dir))) + (find-file-read-args "Find file in other frame: " + (confirm-nonexistent-file-or-buffer)))) + (find-file-other-frame filename wildcards)) + +;;; File Dispatch + +;;;###autoload (autoload 'magit-file-dispatch "magit" nil t) +(transient-define-prefix magit-file-dispatch () + "Invoke a Magit command that acts on the visited file. +When invoked outside a file-visiting buffer, then fall back +to `magit-dispatch'." + :info-manual "(magit) Minor Mode for Buffers Visiting Files" + ["Actions" + [("s" "Stage" magit-stage-file) + ("u" "Unstage" magit-unstage-file) + ("c" "Commit" magit-commit) + ("e" "Edit line" magit-edit-line-commit)] + [("D" "Diff..." magit-diff) + ("d" "Diff" magit-diff-buffer-file) + ("g" "Status" magit-status-here)] + [("L" "Log..." magit-log) + ("l" "Log" magit-log-buffer-file) + ("t" "Trace" magit-log-trace-definition) + (7 "M" "Merged" magit-log-merged)] + [("B" "Blame..." magit-blame) + ("b" "Blame" magit-blame-addition) + ("r" "...removal" magit-blame-removal) + ("f" "...reverse" magit-blame-reverse) + ("m" "Blame echo" magit-blame-echo) + ("q" "Quit blame" magit-blame-quit)] + [("p" "Prev blob" magit-blob-previous) + ("n" "Next blob" magit-blob-next) + ("v" "Goto blob" magit-find-file) + ("V" "Goto file" magit-blob-visit-file)] + [(5 "C-c r" "Rename file" magit-file-rename) + (5 "C-c d" "Delete file" magit-file-delete) + (5 "C-c u" "Untrack file" magit-file-untrack) + (5 "C-c c" "Checkout file" magit-file-checkout)]] + (interactive) + (transient-setup + (if (magit-file-relative-name) + 'magit-file-dispatch + 'magit-dispatch))) + +;;; Blob Mode + +(defvar magit-blob-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "p" #'magit-blob-previous) + (define-key map "n" #'magit-blob-next) + (define-key map "b" #'magit-blame-addition) + (define-key map "r" #'magit-blame-removal) + (define-key map "f" #'magit-blame-reverse) + (define-key map "q" #'magit-kill-this-buffer) + map) + "Keymap for `magit-blob-mode'.") + +(define-minor-mode magit-blob-mode + "Enable some Magit features in blob-visiting buffers. + +Currently this only adds the following key bindings. +\n\\{magit-blob-mode-map}" + :package-version '(magit . "2.3.0")) + +(defun magit-blob-next () + "Visit the next blob which modified the current file." + (interactive) + (if magit-buffer-file-name + (magit-blob-visit (or (magit-blob-successor magit-buffer-revision + magit-buffer-file-name) + magit-buffer-file-name)) + (if (buffer-file-name (buffer-base-buffer)) + (user-error "You have reached the end of time") + (user-error "Buffer isn't visiting a file or blob")))) + +(defun magit-blob-previous () + "Visit the previous blob which modified the current file." + (interactive) + (if-let ((file (or magit-buffer-file-name + (buffer-file-name (buffer-base-buffer))))) + (--if-let (magit-blob-ancestor magit-buffer-revision file) + (magit-blob-visit it) + (user-error "You have reached the beginning of time")) + (user-error "Buffer isn't visiting a file or blob"))) + +;;;###autoload +(defun magit-blob-visit-file () + "View the file from the worktree corresponding to the current blob. +When visiting a blob or the version from the index, then go to +the same location in the respective file in the working tree." + (interactive) + (if-let ((file (magit-file-relative-name))) + (magit-find-file--internal "{worktree}" file #'pop-to-buffer-same-window) + (user-error "Not visiting a blob"))) + +(defun magit-blob-visit (blob-or-file) + (if (stringp blob-or-file) + (find-file blob-or-file) + (pcase-let ((`(,rev ,file) blob-or-file)) + (magit-find-file rev file) + (apply #'message "%s (%s %s ago)" + (magit-rev-format "%s" rev) + (magit--age (magit-rev-format "%ct" rev)))))) + +(defun magit-blob-ancestor (rev file) + (let ((lines (magit-with-toplevel + (magit-git-lines "log" "-2" "--format=%H" "--name-only" + "--follow" (or rev "HEAD") "--" file)))) + (if rev (cddr lines) (butlast lines 2)))) + +(defun magit-blob-successor (rev file) + (let ((lines (magit-with-toplevel + (magit-git-lines "log" "--format=%H" "--name-only" "--follow" + "HEAD" "--" file)))) + (catch 'found + (while lines + (if (equal (nth 2 lines) rev) + (throw 'found (list (nth 0 lines) (nth 1 lines))) + (setq lines (nthcdr 2 lines))))))) + +;;; File Commands + +(defun magit-file-rename (file newname) + "Rename or move FILE to NEWNAME. +NEWNAME may be a file or directory name. If FILE isn't tracked in +Git, fallback to using `rename-file'." + (interactive + (let* ((file (magit-read-file "Rename file")) + (dir (file-name-directory file)) + (newname (read-file-name (format "Move %s to destination: " file) + (and dir (expand-file-name dir))))) + (list (expand-file-name file (magit-toplevel)) + (expand-file-name newname)))) + (let ((oldbuf (get-file-buffer file)) + (dstdir (file-name-directory newname)) + (dstfile (if (directory-name-p newname) + (concat newname (file-name-nondirectory file)) + newname))) + (when (and oldbuf (buffer-modified-p oldbuf)) + (user-error "Save %s before moving it" file)) + (when (file-exists-p dstfile) + (user-error "%s already exists" dstfile)) + (unless (file-exists-p dstdir) + (user-error "Destination directory %s does not exist" dstdir)) + (if (magit-file-tracked-p (magit-convert-filename-for-git file)) + (magit-call-git "mv" + (magit-convert-filename-for-git file) + (magit-convert-filename-for-git newname)) + (rename-file file newname current-prefix-arg)) + (when oldbuf + (with-current-buffer oldbuf + (let ((buffer-read-only buffer-read-only)) + (set-visited-file-name dstfile nil t)) + (if (fboundp 'vc-refresh-state) + (vc-refresh-state) + (with-no-warnings + (vc-find-file-hook)))))) + (magit-refresh)) + +(defun magit-file-untrack (files &optional force) + "Untrack the selected FILES or one file read in the minibuffer. + +With a prefix argument FORCE do so even when the files have +staged as well as unstaged changes." + (interactive (list (or (--if-let (magit-region-values 'file t) + (progn + (unless (magit-file-tracked-p (car it)) + (user-error "Already untracked")) + (magit-confirm-files 'untrack it "Untrack")) + (list (magit-read-tracked-file "Untrack file")))) + current-prefix-arg)) + (magit-with-toplevel + (magit-run-git "rm" "--cached" (and force "--force") "--" files))) + +(defun magit-file-delete (files &optional force) + "Delete the selected FILES or one file read in the minibuffer. + +With a prefix argument FORCE do so even when the files have +uncommitted changes. When the files aren't being tracked in +Git, then fallback to using `delete-file'." + (interactive (list (--if-let (magit-region-values 'file t) + (magit-confirm-files 'delete it "Delete") + (list (magit-read-file "Delete file"))) + current-prefix-arg)) + (if (magit-file-tracked-p (car files)) + (magit-call-git "rm" (and force "--force") "--" files) + (let ((topdir (magit-toplevel))) + (dolist (file files) + (delete-file (expand-file-name file topdir) t)))) + (magit-refresh)) + +;;;###autoload +(defun magit-file-checkout (rev file) + "Checkout FILE from REV." + (interactive + (let ((rev (magit-read-branch-or-commit + "Checkout from revision" magit-buffer-revision))) + (list rev (magit-read-file-from-rev rev "Checkout file")))) + (magit-with-toplevel + (magit-run-git "checkout" rev "--" file))) + +;;; Read File + +(defvar magit-read-file-hist nil) + +(defun magit-read-file-from-rev (rev prompt &optional default) + (let ((files (magit-revision-files rev))) + (magit-completing-read + prompt files nil t nil 'magit-read-file-hist + (car (member (or default (magit-current-file)) files))))) + +(defun magit-read-file (prompt &optional tracked-only) + (let ((choices (nconc (magit-list-files) + (unless tracked-only (magit-untracked-files))))) + (magit-completing-read + prompt choices nil t nil nil + (car (member (or (magit-section-value-if '(file submodule)) + (magit-file-relative-name nil tracked-only)) + choices))))) + +(defun magit-read-tracked-file (prompt) + (magit-read-file prompt t)) + +(defun magit-read-unmerged-file (&optional prompt) + (let ((current (magit-current-file)) + (unmerged (magit-unmerged-files))) + (unless unmerged + (user-error "There are no unresolved conflicts")) + (magit-completing-read (or prompt "Resolve file") + unmerged nil t nil nil + (car (member current unmerged))))) + +(defun magit-read-file-choice (prompt files &optional error default) + "Read file from FILES. + +If FILES has only one member, return that instead of prompting. +If FILES has no members, give a user error. ERROR can be given +to provide a more informative error. + +If DEFAULT is non-nil, use this as the default value instead of +`magit-current-file'." + (pcase (length files) + (0 (user-error (or error "No file choices"))) + (1 (car files)) + (_ (magit-completing-read + prompt files nil t nil 'magit-read-file-hist + (car (member (or default (magit-current-file)) files)))))) + +(defun magit-read-changed-file (rev-or-range prompt &optional default) + (magit-read-file-choice + prompt + (magit-changed-files rev-or-range) + default + (concat "No file changed in " rev-or-range))) + +;;; _ +(provide 'magit-files) +;;; magit-files.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-git.el b/code/elpa/magit-20220821.1819/magit-git.el new file mode 100644 index 0000000..0cc8c09 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-git.el @@ -0,0 +1,2691 @@ +;;; magit-git.el --- Git functionality -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements wrappers for various Git plumbing commands. + +;;; Code: + +(require 'magit-base) + +(require 'format-spec) + +;; From `magit-branch'. +(defvar magit-branch-prefer-remote-upstream) +(defvar magit-published-branches) + +;; From `magit-margin'. +(declare-function magit-maybe-make-margin-overlay "magit-margin" ()) + +;; From `magit-mode'. +(declare-function magit-get-mode-buffer "magit-mode" + (mode &optional value frame)) +(declare-function magit-refresh "magit-mode" ()) +(defvar magit-buffer-diff-args) +(defvar magit-buffer-file-name) +(defvar magit-buffer-log-args) +(defvar magit-buffer-log-files) +(defvar magit-buffer-refname) +(defvar magit-buffer-revision) + +;; From `magit-process'. +(declare-function magit-call-git "magit-process" (&rest args)) +(declare-function magit-process-buffer "magit-process" (&optional nodisplay)) +(declare-function magit-process-file "magit-process" + (process &optional infile buffer display &rest args)) +(declare-function magit-process-git "magit-process" (destination &rest args)) +(declare-function magit-process-insert-section "magit-process" + (pwd program args &optional errcode errlog)) +(defvar magit-this-error) +(defvar magit-process-error-message-regexps) + +(eval-when-compile + (cl-pushnew 'orig-rev eieio--known-slot-names) + (cl-pushnew 'number eieio--known-slot-names)) + +;;; Git implementations + +(defvar magit-inhibit-libgit nil + "Whether to inhibit the use of libgit.") + +(defvar magit--libgit-available-p 'unknown + "Whether libgit is available. +Use the function by the same name instead of this variable.") + +(defun magit--libgit-available-p () + (if (eq magit--libgit-available-p 'unknown) + (setq magit--libgit-available-p + (and module-file-suffix + (let ((libgit (locate-library "libgit"))) + (and libgit + (or (locate-library "libegit2") + (let ((load-path + (cons (expand-file-name + (convert-standard-filename "build") + (file-name-directory libgit)) + load-path))) + (locate-library "libegit2"))))))) + magit--libgit-available-p)) + +(defun magit-gitimpl () + "Return the Git implementation used in this repository." + (if (and (not magit-inhibit-libgit) + (not (file-remote-p default-directory)) + (magit--libgit-available-p)) + 'libgit + 'git)) + +;;; Options + +;; For now this is shared between `magit-process' and `magit-git'. +(defgroup magit-process nil + "Git and other external processes used by Magit." + :group 'magit) + +(defvar magit-git-environment + (list (format "INSIDE_EMACS=%s,magit" emacs-version)) + "Prepended to `process-environment' while running git.") + +(defcustom magit-git-output-coding-system + (and (eq system-type 'windows-nt) 'utf-8) + "Coding system for receiving output from Git. + +If non-nil, the Git config value `i18n.logOutputEncoding' should +be set via `magit-git-global-arguments' to value consistent with +this." + :package-version '(magit . "2.9.0") + :group 'magit-process + :type '(choice (coding-system :tag "Coding system to decode Git output") + (const :tag "Use system default" nil))) + +(defvar magit-git-w32-path-hack nil + "Alist of (EXE . (PATHENTRY)). +This specifies what additional PATH setting needs to be added to +the environment in order to run the non-wrapper git executables +successfully.") + +(defcustom magit-git-executable + (or (and (eq system-type 'windows-nt) + ;; Avoid the wrappers "cmd/git.exe" and "cmd/git.cmd", + ;; which are much slower than using "bin/git.exe" directly. + (and-let* ((exec (executable-find "git"))) + (ignore-errors + ;; Git for Windows 2.x provides cygpath so we can + ;; ask it for native paths. + (let* ((core-exe + (car + (process-lines + exec "-c" + "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x" + "X" "git"))) + (hack-entry (assoc core-exe magit-git-w32-path-hack)) + ;; Running the libexec/git-core executable + ;; requires some extra PATH entries. + (path-hack + (list (concat "PATH=" + (car (process-lines + exec "-c" + "alias.P=!cygpath -wp \"$PATH\"" + "P")))))) + ;; The defcustom STANDARD expression can be + ;; evaluated many times, so make sure it is + ;; idempotent. + (if hack-entry + (setcdr hack-entry path-hack) + (push (cons core-exe path-hack) magit-git-w32-path-hack)) + core-exe)))) + (and (eq system-type 'darwin) + (executable-find "git")) + "git") + "The Git executable used by Magit on the local host. +On remote machines `magit-remote-git-executable' is used instead." + :package-version '(magit . "3.2.0") + :group 'magit-process + :type 'string) + +(defcustom magit-remote-git-executable "git" + "The Git executable used by Magit on remote machines. +On the local host `magit-git-executable' is used instead. +Consider customizing `tramp-remote-path' instead of this +option." + :package-version '(magit . "3.2.0") + :group 'magit-process + :type 'string) + +(defcustom magit-git-global-arguments + `("--no-pager" "--literal-pathspecs" + "-c" "core.preloadindex=true" + "-c" "log.showSignature=false" + "-c" "color.ui=false" + "-c" "color.diff=false" + ,@(and (eq system-type 'windows-nt) + (list "-c" "i18n.logOutputEncoding=UTF-8"))) + "Global Git arguments. + +The arguments set here are used every time the git executable is +run as a subprocess. They are placed right after the executable +itself and before the git command - as in `git HERE... COMMAND +REST'. See the manpage `git(1)' for valid arguments. + +Be careful what you add here, especially if you are using Tramp +to connect to servers with ancient Git versions. Never remove +anything that is part of the default value, unless you really +know what you are doing. And think very hard before adding +something; it will be used every time Magit runs Git for any +purpose." + :package-version '(magit . "2.9.0") + :group 'magit-commands + :group 'magit-process + :type '(repeat string)) + +(defcustom magit-prefer-remote-upstream nil + "Whether to favor remote branches when reading the upstream branch. + +This controls whether commands that read a branch from the user +and then set it as the upstream branch, offer a local or a remote +branch as default completion candidate, when they have the choice. + +This affects all commands that use `magit-read-upstream-branch' +or `magit-read-starting-point', which includes most commands +that change the upstream and many that create new branches." + :package-version '(magit . "2.4.2") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-list-refs-namespaces + '("refs/heads" + "refs/remotes" + "refs/tags" + "refs/pullreqs") + "List of ref namespaces considered when reading a ref. + +This controls the order of refs returned by `magit-list-refs', +which is called by functions like `magit-list-branch-names' to +generate the collection of refs." + :package-version '(magit . "3.1.0") + :group 'magit-commands + :type '(repeat string)) + +(defcustom magit-list-refs-sortby nil + "How to sort the ref collection in the prompt. + +This affects commands that read a ref. More specifically, it +controls the order of refs returned by `magit-list-refs', which +is called by functions like `magit-list-branch-names' to generate +the collection of refs. By default, refs are sorted according to +their full refname (i.e., \"refs/...\"). + +Any value accepted by the `--sort' flag of \"git for-each-ref\" can +be used. For example, \"-creatordate\" places refs with more +recent committer or tagger dates earlier in the list. A list of +strings can also be given in order to pass multiple sort keys to +\"git for-each-ref\". + +Note that, depending on the completion framework you use, this +may not be sufficient to change the order in which the refs are +displayed. It only controls the order of the collection passed +to `magit-completing-read' or, for commands that support reading +multiple strings, `read-from-minibuffer'. The completion +framework ultimately determines how the collection is displayed." + :package-version '(magit . "2.11.0") + :group 'magit-miscellaneous + :type '(choice string (repeat string))) + +;;; Git + +(defvar magit-git-debug nil + "Whether to enable additional reporting of git errors. + +Magit basically calls git for one of these two reasons: for +side-effects or to do something with its standard output. + +When git is run for side-effects then its output, including error +messages, go into the process buffer which is shown when using \ +\\\\[magit-process]. + +When git's output is consumed in some way, then it would be too +expensive to also insert it into this buffer, but when this +option is non-nil and git returns with a non-zero exit status, +then at least its standard error is inserted into this buffer. + +This is only intended for debugging purposes. Do not enable this +permanently, that would negatively affect performance. Also note +that just because git exits with a non-zero exit status and prints +an error message that usually doesn't mean that it is an error as +far as Magit is concerned, which is another reason we usually hide +these error messages. Whether some error message is relevant in +the context of some unexpected behavior has to be judged on a case +by case basis. + +The command `magit-toggle-git-debug' changes the value of this +variable. + +Also see `magit-process-extreme-logging'.") + +(defun magit-toggle-git-debug () + "Toggle whether additional git errors are reported. +See info node `(magit)Debugging Tools' for more information." + (interactive) + (setq magit-git-debug (not magit-git-debug)) + (message "Additional reporting of Git errors %s" + (if magit-git-debug "enabled" "disabled"))) + +(defvar magit--refresh-cache nil) + +(defmacro magit--with-refresh-cache (key &rest body) + (declare (indent 1) (debug (form body))) + (let ((k (cl-gensym))) + `(if magit--refresh-cache + (let ((,k ,key)) + (--if-let (assoc ,k (cdr magit--refresh-cache)) + (progn (cl-incf (caar magit--refresh-cache)) + (cdr it)) + (cl-incf (cdar magit--refresh-cache)) + (let ((value ,(macroexp-progn body))) + (push (cons ,k value) + (cdr magit--refresh-cache)) + value))) + ,@body))) + +(defvar magit-with-editor-envvar "GIT_EDITOR" + "The environment variable exported by `magit-with-editor'. +Set this to \"GIT_SEQUENCE_EDITOR\" if you do not want to use +Emacs to edit commit messages but would like to do so to edit +rebase sequences.") + +(defmacro magit-with-editor (&rest body) + "Like `with-editor' but let-bind some more variables. +Also respect the value of `magit-with-editor-envvar'." + (declare (indent 0) (debug (body))) + `(let ((magit-process-popup-time -1) + ;; The user may have customized `shell-file-name' to + ;; something which results in `w32-shell-dos-semantics' nil + ;; (which changes the quoting style used by + ;; `shell-quote-argument'), but Git for Windows expects shell + ;; quoting in the dos style. + (shell-file-name (if (and (eq system-type 'windows-nt) + ;; If we have Cygwin mount points, + ;; the git flavor is cygwin, so dos + ;; shell quoting is probably wrong. + (not magit-cygwin-mount-points)) + "cmdproxy" + shell-file-name))) + (with-editor* magit-with-editor-envvar + ,@body))) + +(defmacro magit--with-temp-process-buffer (&rest body) + "Like `with-temp-buffer', but always propagate `process-environment'. +When that var is buffer-local in the calling buffer, it is not +propagated by `with-temp-buffer', so we explicitly ensure that +happens, so that processes will be invoked consistently. BODY is +as for that macro." + (declare (indent 0) (debug (body))) + (let ((p (cl-gensym))) + `(let ((,p process-environment)) + (with-temp-buffer + (setq-local process-environment ,p) + ,@body)))) + +(defsubst magit-git-executable () + "Return value of `magit-git-executable' or `magit-remote-git-executable'. +The variable is chosen depending on whether `default-directory' +is remote." + (if (file-remote-p default-directory) + magit-remote-git-executable + magit-git-executable)) + +(defun magit-process-git-arguments (args) + "Prepare ARGS for a function that invokes Git. + +Magit has many specialized functions for running Git; they all +pass arguments through this function before handing them to Git, +to do the following. + +* Flatten ARGS, removing nil arguments. +* Prepend `magit-git-global-arguments' to ARGS. +* On w32 systems, encode to `w32-ansi-code-page'." + (setq args (append magit-git-global-arguments (flatten-tree args))) + (if (and (eq system-type 'windows-nt) (boundp 'w32-ansi-code-page)) + ;; On w32, the process arguments *must* be encoded in the + ;; current code-page (see #3250). + (mapcar (lambda (arg) + (encode-coding-string + arg (intern (format "cp%d" w32-ansi-code-page)))) + args) + args)) + +(defun magit-git-exit-code (&rest args) + "Execute Git with ARGS, returning its exit code." + (magit-process-git nil args)) + +(defun magit-git-success (&rest args) + "Execute Git with ARGS, returning t if its exit code is 0." + (= (magit-git-exit-code args) 0)) + +(defun magit-git-failure (&rest args) + "Execute Git with ARGS, returning t if its exit code is 1." + (= (magit-git-exit-code args) 1)) + +(defun magit-git-string-p (&rest args) + "Execute Git with ARGS, returning the first line of its output. +If the exit code isn't zero or if there is no output, then return +nil. Neither of these results is considered an error; if that is +what you want, then use `magit-git-string-ng' instead. + +This is an experimental replacement for `magit-git-string', and +still subject to major changes." + (magit--with-refresh-cache (cons default-directory args) + (magit--with-temp-process-buffer + (and (zerop (magit-process-git t args)) + (not (bobp)) + (progn + (goto-char (point-min)) + (buffer-substring-no-properties (point) (line-end-position))))))) + +(defun magit-git-string-ng (&rest args) + "Execute Git with ARGS, returning the first line of its output. +If the exit code isn't zero or if there is no output, then that +is considered an error, but instead of actually signaling an +error, return nil. Additionally the output is put in the process +buffer (creating it if necessary) and the error message is shown +in the status buffer (provided it exists). + +This is an experimental replacement for `magit-git-string', and +still subject to major changes. Also see `magit-git-string-p'." + (magit--with-refresh-cache + (list default-directory 'magit-git-string-ng args) + (magit--with-temp-process-buffer + (let* ((args (magit-process-git-arguments args)) + (status (magit-process-git t args))) + (if (zerop status) + (and (not (bobp)) + (progn + (goto-char (point-min)) + (buffer-substring-no-properties + (point) (line-end-position)))) + (let ((buf (current-buffer))) + (with-current-buffer (magit-process-buffer t) + (magit-process-insert-section default-directory + magit-git-executable args + status buf))) + (when-let ((status-buf (magit-get-mode-buffer 'magit-status-mode))) + (let ((msg (magit--locate-error-message))) + (with-current-buffer status-buf + (setq magit-this-error msg)))) + nil))))) + +(defun magit-git-str (&rest args) + "Execute Git with ARGS, returning the first line of its output. +If there is no output, return nil. If the output begins with a +newline, return an empty string. Like `magit-git-string' but +ignore `magit-git-debug'." + (setq args (flatten-tree args)) + (magit--with-refresh-cache (cons default-directory args) + (magit--with-temp-process-buffer + (magit-process-git (list t nil) args) + (unless (bobp) + (goto-char (point-min)) + (buffer-substring-no-properties (point) (line-end-position)))))) + +(defun magit-git-output (&rest args) + "Execute Git with ARGS, returning its output." + (setq args (flatten-tree args)) + (magit--with-refresh-cache (cons default-directory args) + (magit--with-temp-process-buffer + (magit-process-git (list t nil) args) + (buffer-substring-no-properties (point-min) (point-max))))) + +(define-error 'magit-invalid-git-boolean "Not a Git boolean") + +(defun magit-git-true (&rest args) + "Execute Git with ARGS, returning t if it prints \"true\". +If it prints \"false\", then return nil. For any other output +signal `magit-invalid-git-boolean'." + (pcase (magit-git-output args) + ((or "true" "true\n") t) + ((or "false" "false\n") nil) + (output (signal 'magit-invalid-git-boolean (list output))))) + +(defun magit-git-false (&rest args) + "Execute Git with ARGS, returning t if it prints \"false\". +If it prints \"true\", then return nil. For any other output +signal `magit-invalid-git-boolean'." + (pcase (magit-git-output args) + ((or "true" "true\n") nil) + ((or "false" "false\n") t) + (output (signal 'magit-invalid-git-boolean (list output))))) + +(defun magit-git-config-p (variable &optional default) + "Return the boolean value of the Git variable VARIABLE. +VARIABLE has to be specified as a string. Return DEFAULT (which +defaults to nil) if VARIABLE is unset. If VARIABLE's value isn't +a boolean, then raise an error." + (let ((args (list "config" "--bool" "--default" (if default "true" "false") + variable))) + (magit--with-refresh-cache (cons default-directory args) + (magit--with-temp-process-buffer + (let ((status (magit-process-git t args)) + (output (buffer-substring (point-min) (1- (point-max))))) + (if (zerop status) + (equal output "true") + (signal 'magit-invalid-git-boolean (list output)))))))) + +(defun magit-git-insert (&rest args) + "Execute Git with ARGS, inserting its output at point. +If Git exits with a non-zero exit status, then show a message and +add a section in the respective process buffer." + (setq args (magit-process-git-arguments args)) + (if magit-git-debug + (let (log) + (unwind-protect + (progn + (setq log (make-temp-file "magit-stderr")) + (delete-file log) + (let ((exit (magit-process-git (list t log) args))) + (when (> exit 0) + (let ((msg "Git failed")) + (when (file-exists-p log) + (setq msg (with-temp-buffer + (insert-file-contents log) + (goto-char (point-max)) + (if (functionp magit-git-debug) + (funcall magit-git-debug (buffer-string)) + (magit--locate-error-message)))) + (let ((magit-git-debug nil)) + (with-current-buffer (magit-process-buffer t) + (magit-process-insert-section default-directory + magit-git-executable + args exit log)))) + (message "%s" msg))) + exit)) + (ignore-errors (delete-file log)))) + (magit-process-git (list t nil) args))) + +(defun magit--locate-error-message () + (goto-char (point-max)) + (and (run-hook-wrapped 'magit-process-error-message-regexps + (lambda (re) (re-search-backward re nil t))) + (match-string-no-properties 1))) + +(defun magit-git-string (&rest args) + "Execute Git with ARGS, returning the first line of its output. +If there is no output, return nil. If the output begins with a +newline, return an empty string." + (setq args (flatten-tree args)) + (magit--with-refresh-cache (cons default-directory args) + (magit--with-temp-process-buffer + (apply #'magit-git-insert args) + (unless (bobp) + (goto-char (point-min)) + (buffer-substring-no-properties (point) (line-end-position)))))) + +(defun magit-git-lines (&rest args) + "Execute Git with ARGS, returning its output as a list of lines. +Empty lines anywhere in the output are omitted. + +If Git exits with a non-zero exit status, then report show a +message and add a section in the respective process buffer." + (magit--with-temp-process-buffer + (apply #'magit-git-insert args) + (split-string (buffer-string) "\n" t))) + +(defun magit-git-items (&rest args) + "Execute Git with ARGS, returning its null-separated output as a list. +Empty items anywhere in the output are omitted. + +If Git exits with a non-zero exit status, then report show a +message and add a section in the respective process buffer." + (magit--with-temp-process-buffer + (apply #'magit-git-insert args) + (split-string (buffer-string) "\0" t))) + +(defun magit-git-wash (washer &rest args) + "Execute Git with ARGS, inserting washed output at point. +Actually first insert the raw output at point. If there is no +output, call `magit-cancel-section'. Otherwise temporarily narrow +the buffer to the inserted text, move to its beginning, and then +call function WASHER with ARGS as its sole argument." + (declare (indent 1)) + (let ((beg (point))) + (setq args (flatten-tree args)) + (magit-git-insert args) + (if (= (point) beg) + (magit-cancel-section) + (unless (bolp) + (insert "\n")) + (save-restriction + (narrow-to-region beg (point)) + (goto-char beg) + (funcall washer args)) + (when (or (= (point) beg) + (= (point) (1+ beg))) + (magit-cancel-section)) + (magit-maybe-make-margin-overlay)))) + +;;; Git Version + +(defconst magit--git-version-regexp + "\\`git version \\([0-9]+\\(\\.[0-9]+\\)\\{1,2\\}\\)") + +(defvar magit--host-git-version-cache nil) + +(defun magit-git-version>= (n) + "Return t if `magit-git-version's value is greater than or equal to N." + (magit--version>= (magit-git-version) n)) + +(defun magit-git-version< (n) + "Return t if `magit-git-version's value is smaller than N." + (version< (magit-git-version) n)) + +(defun magit-git-version () + "Return the Git version used for `default-directory'. +Raise an error if Git cannot be found, if it exits with a +non-zero status, or the output does not have the expected +format." + (magit--with-refresh-cache default-directory + (let ((host (file-remote-p default-directory))) + (or (cdr (assoc host magit--host-git-version-cache)) + (magit--with-temp-process-buffer + ;; Unset global arguments for ancient Git versions. + (let* ((magit-git-global-arguments nil) + (status (magit-process-git t "version")) + (output (buffer-string))) + (cond + ((not (zerop status)) + (display-warning + 'magit + (format "%S\n\nRunning \"%s --version\" failed with output:\n\n%s" + (if host + (format "Magit cannot find Git on host %S.\n +Check the value of `magit-remote-git-executable' using +`magit-debug-git-executable' and consult the info node +`(tramp)Remote programs'." host) + "Magit cannot find Git.\n +Check the values of `magit-git-executable' and `exec-path' +using `magit-debug-git-executable'.") + (magit-git-executable) + output))) + ((save-match-data + (and (string-match magit--git-version-regexp output) + (let ((version (match-string 1 output))) + (push (cons host version) + magit--host-git-version-cache) + version)))) + (t (error "Unexpected \"%s --version\" output: %S" + (magit-git-executable) + output))))))))) + +(defun magit-git-version-assert (&optional minimal who) + "Assert that the used Git version is greater than or equal to MINIMAL. +If optional MINIMAL is nil, compare with `magit--minimal-git' +instead. Optional WHO if non-nil specifies what functionality +needs at least MINIMAL, otherwise it defaults to \"Magit\"." + (when (magit-git-version< (or minimal magit--minimal-git)) + (let* ((host (file-remote-p default-directory)) + (msg (format-spec + (cond (host "\ +%w requires Git %m or greater, but on %h the version is %m. + +If multiple Git versions are installed on the host, then the +problem might be that TRAMP uses the wrong executable. + +Check the value of `magit-remote-git-executable' and consult +the info node `(tramp)Remote programs'.\n") + (t "\ +%w requires Git %m or greater, but you are using %v. + +If you have multiple Git versions installed, then check the +values of `magit-remote-git-executable' and `exec-path'.\n")) + `((?w . ,(or who "Magit")) + (?m . ,(or minimal magit--minimal-git)) + (?v . ,(magit-git-version)) + (?h . ,host))))) + (display-warning 'magit msg :error)))) + +(defun magit--safe-git-version () + "Return the Git version used for `default-directory' or an error message." + (magit--with-temp-process-buffer + (let* ((magit-git-global-arguments nil) + (status (magit-process-git t "version")) + (output (buffer-string))) + (cond ((not (zerop status)) output) + ((save-match-data + (and (string-match magit--git-version-regexp output) + (match-string 1 output)))) + (t output))))) + +(defun magit-debug-git-executable () + "Display a buffer with information about `magit-git-executable'. +Also include information about `magit-remote-git-executable'. +See info node `(magit)Debugging Tools' for more information." + (interactive) + (with-current-buffer (get-buffer-create "*magit-git-debug*") + (pop-to-buffer (current-buffer)) + (erase-buffer) + (insert (format "magit-remote-git-executable: %S\n" + magit-remote-git-executable)) + (insert (concat + (format "magit-git-executable: %S" magit-git-executable) + (and (not (file-name-absolute-p magit-git-executable)) + (format " [%S]" (executable-find magit-git-executable))) + (format " (%s)\n" (magit--safe-git-version)))) + (insert (format "exec-path: %S\n" exec-path)) + (--when-let (cl-set-difference + (-filter #'file-exists-p (remq nil (parse-colon-path + (getenv "PATH")))) + (-filter #'file-exists-p (remq nil exec-path)) + :test #'file-equal-p) + (insert (format " entries in PATH, but not in exec-path: %S\n" it))) + (dolist (execdir exec-path) + (insert (format " %s (%s)\n" execdir (car (file-attributes execdir)))) + (when (file-directory-p execdir) + (dolist (exec (directory-files + execdir t (concat + "\\`git" (regexp-opt exec-suffixes) "\\'"))) + (insert (format " %s (%s)\n" exec + (magit--safe-git-version)))))))) + +;;; Variables + +(defun magit-config-get-from-cached-list (key) + (gethash + ;; `git config --list' downcases first and last components of the key. + (let* ((key (replace-regexp-in-string "\\`[^.]+" #'downcase key t t)) + (key (replace-regexp-in-string "[^.]+\\'" #'downcase key t t))) + key) + (magit--with-refresh-cache (cons (magit-toplevel) 'config) + (let ((configs (make-hash-table :test #'equal))) + (dolist (conf (magit-git-items "config" "--list" "-z")) + (let* ((nl-pos (cl-position ?\n conf)) + (key (substring conf 0 nl-pos)) + (val (if nl-pos (substring conf (1+ nl-pos)) ""))) + (puthash key (nconc (gethash key configs) (list val)) configs))) + configs)))) + +(defun magit-get (&rest keys) + "Return the value of the Git variable specified by KEYS." + (car (last (apply #'magit-get-all keys)))) + +(defun magit-get-all (&rest keys) + "Return all values of the Git variable specified by KEYS." + (let ((magit-git-debug nil) + (arg (and (or (null (car keys)) + (string-prefix-p "--" (car keys))) + (pop keys))) + (key (mapconcat #'identity keys "."))) + (if (and magit--refresh-cache (not arg)) + (magit-config-get-from-cached-list key) + (magit-git-items "config" arg "-z" "--get-all" key)))) + +(defun magit-get-boolean (&rest keys) + "Return the boolean value of the Git variable specified by KEYS. +Also see `magit-git-config-p'." + (let ((arg (and (or (null (car keys)) + (string-prefix-p "--" (car keys))) + (pop keys))) + (key (mapconcat #'identity keys "."))) + (equal (if magit--refresh-cache + (car (last (magit-config-get-from-cached-list key))) + (magit-git-str "config" arg "--bool" key)) + "true"))) + +(defun magit-set (value &rest keys) + "Set the value of the Git variable specified by KEYS to VALUE." + (let ((arg (and (or (null (car keys)) + (string-prefix-p "--" (car keys))) + (pop keys))) + (key (mapconcat #'identity keys "."))) + (if value + (magit-git-success "config" arg key value) + (magit-git-success "config" arg "--unset" key)) + value)) + +(gv-define-setter magit-get (val &rest keys) + `(magit-set ,val ,@keys)) + +(defun magit-set-all (values &rest keys) + "Set all values of the Git variable specified by KEYS to VALUES." + (let ((arg (and (or (null (car keys)) + (string-prefix-p "--" (car keys))) + (pop keys))) + (var (mapconcat #'identity keys "."))) + (when (magit-get var) + (magit-call-git "config" arg "--unset-all" var)) + (dolist (v values) + (magit-call-git "config" arg "--add" var v)))) + +;;; Files + +(defun magit--safe-default-directory (&optional file) + (catch 'unsafe-default-dir + (let ((dir (file-name-as-directory + (expand-file-name (or file default-directory)))) + (previous nil)) + (while (not (magit-file-accessible-directory-p dir)) + (setq dir (file-name-directory (directory-file-name dir))) + (when (equal dir previous) + (throw 'unsafe-default-dir nil)) + (setq previous dir)) + dir))) + +(defmacro magit--with-safe-default-directory (file &rest body) + (declare (indent 1) (debug (form body))) + `(when-let ((default-directory (magit--safe-default-directory ,file))) + ,@body)) + +(defun magit-gitdir (&optional directory) + "Return the absolute and resolved path of the .git directory. + +If the `GIT_DIR' environment variable is define then return that. +Otherwise return the .git directory for DIRECTORY, or if that is +nil, then for `default-directory' instead. If the directory is +not located inside a Git repository, then return nil." + (let ((default-directory (or directory default-directory))) + (magit-git-dir))) + +(defun magit-git-dir (&optional path) + "Return the absolute and resolved path of the .git directory. + +If the `GIT_DIR' environment variable is define then return that. +Otherwise return the .git directory for `default-directory'. If +the directory is not located inside a Git repository, then return +nil." + (magit--with-refresh-cache (list default-directory 'magit-git-dir path) + (magit--with-safe-default-directory nil + (and-let* ((dir (magit-rev-parse-safe "--git-dir")) + (dir (file-name-as-directory (magit-expand-git-file-name dir))) + (dir (if (file-remote-p dir) + dir + (concat (file-remote-p default-directory) dir)))) + (if path (expand-file-name (convert-standard-filename path) dir) dir))))) + +(defvar magit--separated-gitdirs nil) + +(defun magit--record-separated-gitdir () + (let ((topdir (magit-toplevel)) + (gitdir (magit-git-dir))) + ;; Kludge: git-annex converts submodule gitdirs to symlinks. See #3599. + (when (file-symlink-p (directory-file-name gitdir)) + (setq gitdir (file-truename gitdir))) + ;; We want to delete the entry for `topdir' here, rather than within + ;; (unless ...), in case a `--separate-git-dir' repository was switched to + ;; the standard structure (i.e., "topdir/.git/"). + (setq magit--separated-gitdirs (cl-delete topdir + magit--separated-gitdirs + :key #'car :test #'equal)) + (unless (equal (file-name-as-directory (expand-file-name ".git" topdir)) + gitdir) + (push (cons topdir gitdir) magit--separated-gitdirs)))) + +(defun magit-toplevel (&optional directory) + "Return the absolute path to the toplevel of the current repository. + +From within the working tree or control directory of a repository +return the absolute path to the toplevel directory of the working +tree. As a special case, from within a bare repository return +the control directory instead. When called outside a repository +then return nil. + +When optional DIRECTORY is non-nil then return the toplevel for +that directory instead of the one for `default-directory'. + +Try to respect the option `find-file-visit-truename', i.e. when +the value of that option is nil, then avoid needlessly returning +the truename. When a symlink to a sub-directory of the working +tree is involved, or when called from within a sub-directory of +the gitdir or from the toplevel of a gitdir, which itself is not +located within the working tree, then it is not possible to avoid +returning the truename." + (magit--with-refresh-cache + (cons (or directory default-directory) 'magit-toplevel) + (magit--with-safe-default-directory directory + (if-let ((topdir (magit-rev-parse-safe "--show-toplevel"))) + (let (updir) + (setq topdir (magit-expand-git-file-name topdir)) + (if (and + ;; Always honor these settings. + (not find-file-visit-truename) + (not (getenv "GIT_WORK_TREE")) + ;; `--show-cdup' is the relative path to the toplevel + ;; from `(file-truename default-directory)'. Here we + ;; pretend it is relative to `default-directory', and + ;; go to that directory. Then we check whether + ;; `--show-toplevel' still returns the same value and + ;; whether `--show-cdup' now is the empty string. If + ;; both is the case, then we are at the toplevel of + ;; the same working tree, but also avoided needlessly + ;; following any symlinks. + (progn + (setq updir (file-name-as-directory + (magit-rev-parse-safe "--show-cdup"))) + (setq updir (if (file-name-absolute-p updir) + (concat (file-remote-p default-directory) updir) + (expand-file-name updir))) + (and-let* + ((default-directory updir) + (top (and (string-equal + (magit-rev-parse-safe "--show-cdup") "") + (magit-rev-parse-safe "--show-toplevel")))) + (string-equal (magit-expand-git-file-name top) topdir)))) + updir + (concat (file-remote-p default-directory) + (file-name-as-directory topdir)))) + (and-let* ((gitdir (magit-rev-parse-safe "--git-dir")) + (gitdir (file-name-as-directory + (if (file-name-absolute-p gitdir) + ;; We might have followed a symlink. + (concat (file-remote-p default-directory) + (magit-expand-git-file-name gitdir)) + (expand-file-name gitdir))))) + (if (magit-bare-repo-p) + gitdir + (let* ((link (expand-file-name "gitdir" gitdir)) + (wtree (and (file-exists-p link) + (magit-file-line link)))) + (cond + ((and wtree + ;; Ignore .git/gitdir files that result from a + ;; Git bug. See #2364. + (not (equal wtree ".git"))) + ;; Return the linked working tree. + (concat (file-remote-p default-directory) + (file-name-directory wtree))) + ;; The working directory may not be the parent directory of + ;; .git if it was set up with `git init --separate-git-dir'. + ;; See #2955. + ((car (rassoc gitdir magit--separated-gitdirs))) + (t + ;; Step outside the control directory to enter the working tree. + (file-name-directory (directory-file-name gitdir))))))))))) + +(defun magit--toplevel-safe () + (or (magit-toplevel) + (magit--not-inside-repository-error))) + +(defmacro magit-with-toplevel (&rest body) + (declare (indent defun) (debug (body))) + `(let ((default-directory (magit--toplevel-safe))) + ,@body)) + +(define-error 'magit-outside-git-repo "Not inside Git repository") +(define-error 'magit-corrupt-git-config "Corrupt Git configuration") +(define-error 'magit-git-executable-not-found + "Git executable cannot be found (see https://magit.vc/goto/e6a78ed2)") + +(defun magit--assert-usable-git () + (if (not (compat-executable-find (magit-git-executable) t)) + (signal 'magit-git-executable-not-found (magit-git-executable)) + (let ((magit-git-debug + (lambda (err) + (signal 'magit-corrupt-git-config + (format "%s: %s" default-directory err))))) + ;; This should always succeed unless there's a corrupt config + ;; (or at least a similarly severe failing state). Note that + ;; git-config's --default is avoided because it's not available + ;; until Git 2.18. + (magit-git-string "config" "--get-color" "" "reset")) + nil)) + +(defun magit--not-inside-repository-error () + (magit--assert-usable-git) + (signal 'magit-outside-git-repo default-directory)) + +(defun magit-inside-gitdir-p (&optional noerror) + "Return t if `default-directory' is below the repository directory. +If it is below the working directory, then return nil. +If it isn't below either, then signal an error unless NOERROR +is non-nil, in which case return nil." + (and (magit--assert-default-directory noerror) + ;; Below a repository directory that is not located below the + ;; working directory "git rev-parse --is-inside-git-dir" prints + ;; "false", which is wrong. + (let ((gitdir (magit-git-dir))) + (cond (gitdir (file-in-directory-p default-directory gitdir)) + (noerror nil) + (t (signal 'magit-outside-git-repo default-directory)))))) + +(defun magit-inside-worktree-p (&optional noerror) + "Return t if `default-directory' is below the working directory. +If it is below the repository directory, then return nil. +If it isn't below either, then signal an error unless NOERROR +is non-nil, in which case return nil." + (and (magit--assert-default-directory noerror) + (condition-case nil + (magit-rev-parse-true "--is-inside-work-tree") + (magit-invalid-git-boolean + (and (not noerror) + (signal 'magit-outside-git-repo default-directory)))))) + +(cl-defgeneric magit-bare-repo-p (&optional noerror) + "Return t if the current repository is bare. +If it is non-bare, then return nil. If `default-directory' +isn't below a Git repository, then signal an error unless +NOERROR is non-nil, in which case return nil." + (and (magit--assert-default-directory noerror) + (condition-case nil + (magit-rev-parse-true "--is-bare-repository") + (magit-invalid-git-boolean + (and (not noerror) + (signal 'magit-outside-git-repo default-directory)))))) + +(defun magit--assert-default-directory (&optional noerror) + (or (file-directory-p default-directory) + (and (not noerror) + (let ((exists (file-exists-p default-directory))) + (signal (if exists 'file-error 'file-missing) + (list "Running git in directory" + (if exists + "Not a directory" + "No such file or directory") + default-directory)))))) + +(defun magit-git-repo-p (directory &optional non-bare) + "Return t if DIRECTORY is a Git repository. +When optional NON-BARE is non-nil also return nil if DIRECTORY is +a bare repository." + (and (file-directory-p directory) ; Avoid archives, see #3397. + (or (file-regular-p (expand-file-name ".git" directory)) + (file-directory-p (expand-file-name ".git" directory)) + (and (not non-bare) + (file-regular-p (expand-file-name "HEAD" directory)) + (file-directory-p (expand-file-name "refs" directory)) + (file-directory-p (expand-file-name "objects" directory)))))) + +(defun magit-file-relative-name (&optional file tracked) + "Return the path of FILE relative to the repository root. + +If optional FILE is nil or omitted, return the relative path of +the file being visited in the current buffer, if any, else nil. +If the file is not inside a Git repository, then return nil. + +If TRACKED is non-nil, return the path only if it matches a +tracked file." + (unless file + (with-current-buffer (or (buffer-base-buffer) + (current-buffer)) + (setq file (or magit-buffer-file-name buffer-file-name + (and (derived-mode-p 'dired-mode) default-directory))))) + (when (and file (or (not tracked) + (magit-file-tracked-p (file-relative-name file)))) + (and-let* ((dir (magit-toplevel + (magit--safe-default-directory + (directory-file-name (file-name-directory file)))))) + (file-relative-name file dir)))) + +(defun magit-file-tracked-p (file) + (magit-git-success "ls-files" "--error-unmatch" file)) + +(defun magit-list-files (&rest args) + (apply #'magit-git-items "ls-files" "-z" "--full-name" args)) + +(defun magit-tracked-files () + (magit-list-files "--cached")) + +(defun magit-untracked-files (&optional all files) + (magit-list-files "--other" (unless all "--exclude-standard") "--" files)) + +(defun magit-modified-files (&optional nomodules files) + (magit-git-items "diff-index" "-z" "--name-only" + (and nomodules "--ignore-submodules") + (magit-headish) "--" files)) + +(defun magit-unstaged-files (&optional nomodules files) + (magit-git-items "diff-files" "-z" "--name-only" + (and nomodules "--ignore-submodules") + "--" files)) + +(defun magit-staged-files (&optional nomodules files) + (magit-git-items "diff-index" "-z" "--name-only" "--cached" + (and nomodules "--ignore-submodules") + (magit-headish) "--" files)) + +(defun magit-binary-files (&rest args) + (--mapcat (and (string-match "^-\t-\t\\(.+\\)" it) + (list (match-string 1 it))) + (apply #'magit-git-items + "diff" "-z" "--numstat" "--ignore-submodules" + args))) + +(defun magit-unmerged-files () + (magit-git-items "diff-files" "-z" "--name-only" "--diff-filter=U")) + +(defun magit-ignored-files () + (magit-git-items "ls-files" "-z" "--others" "--ignored" + "--exclude-standard" "--directory")) + +(defun magit-skip-worktree-files () + (--keep (and (and (= (aref it 0) ?S) + (substring it 2))) + (magit-list-files "-t"))) + +(defun magit-assume-unchanged-files () + (--keep (and (and (memq (aref it 0) '(?h ?s ?m ?r ?c ?k)) + (substring it 2))) + (magit-list-files "-v"))) + +(defun magit-revision-files (rev) + (magit-with-toplevel + (magit-git-items "ls-tree" "-z" "-r" "--name-only" rev))) + +(defun magit-revision-directories (rev) + "List directories that contain a tracked file in revision REV." + (magit-with-toplevel + (mapcar #'file-name-as-directory + (magit-git-items "ls-tree" "-z" "-r" "-d" "--name-only" rev)))) + +(defun magit-changed-files (rev-or-range &optional other-rev) + "Return list of files the have changed between two revisions. +If OTHER-REV is non-nil, REV-OR-RANGE should be a revision, not a +range. Otherwise, it can be any revision or range accepted by +\"git diff\" (i.e., , .., or ...)." + (magit-with-toplevel + (magit-git-items "diff" "-z" "--name-only" rev-or-range other-rev))) + +(defun magit-renamed-files (revA revB) + (mapcar (pcase-lambda (`(,_status ,fileA ,fileB)) + (cons fileA fileB)) + (seq-partition (magit-git-items "diff" "-z" "--name-status" + "--find-renames" + "--diff-filter=R" revA revB) + 3))) + +(defun magit--rev-file-name (file rev other-rev) + "For FILE, potentially renamed between REV and OTHER-REV, return name in REV. +Return nil, if FILE appears neither in REV nor OTHER-REV, +or if no rename is detected." + (or (car (member file (magit-revision-files rev))) + (and-let* ((renamed (magit-renamed-files rev other-rev))) + (car (rassoc file renamed))))) + +(defun magit-file-status (&rest args) + (magit--with-temp-process-buffer + (save-excursion (magit-git-insert "status" "-z" args)) + (let ((pos (point)) status) + (while (> (skip-chars-forward "[:print:]") 0) + (let ((x (char-after pos)) + (y (char-after (1+ pos))) + (file (buffer-substring (+ pos 3) (point)))) + (forward-char) + (if (memq x '(?R ?C)) + (progn + (setq pos (point)) + (skip-chars-forward "[:print:]") + (push (list file (buffer-substring pos (point)) x y) status) + (forward-char)) + (push (list file nil x y) status))) + (setq pos (point))) + status))) + +(defcustom magit-cygwin-mount-points + (when (eq system-type 'windows-nt) + (cl-sort (--map (if (string-match "^\\(.*\\) on \\(.*\\) type" it) + (cons (file-name-as-directory (match-string 2 it)) + (file-name-as-directory (match-string 1 it))) + (lwarn '(magit) :error + "Failed to parse Cygwin mount: %S" it)) + ;; If --exec-path is not a native Windows path, + ;; then we probably have a cygwin git. + (let ((process-environment + (append magit-git-environment process-environment))) + (and (not (string-match-p + "\\`[a-zA-Z]:" + (car (process-lines + magit-git-executable "--exec-path")))) + (ignore-errors (process-lines "mount"))))) + #'> :key (pcase-lambda (`(,cyg . ,_win)) (length cyg)))) + "Alist of (CYGWIN . WIN32) directory names. +Sorted from longest to shortest CYGWIN name." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type '(alist :key-type string :value-type directory)) + +(defun magit-expand-git-file-name (filename) + (unless (file-name-absolute-p filename) + (setq filename (expand-file-name filename))) + (if-let ((cyg:win (cl-assoc filename magit-cygwin-mount-points + :test (lambda (f cyg) (string-prefix-p cyg f))))) + (concat (cdr cyg:win) + (substring filename (length (car cyg:win)))) + filename)) + +(defun magit-convert-filename-for-git (filename) + "Convert FILENAME so that it can be passed to git. +1. If it's a absolute filename, then pass through `expand-file-name' + to replace things such as \"~/\" that Git does not understand. +2. If it's a remote filename, then remove the remote part. +3. Deal with an `windows-nt' Emacs vs. Cygwin Git incompatibility." + (if (file-name-absolute-p filename) + (if-let ((cyg:win (cl-rassoc filename magit-cygwin-mount-points + :test (lambda (f win) (string-prefix-p win f))))) + (concat (car cyg:win) + (substring filename (length (cdr cyg:win)))) + (let ((expanded (expand-file-name filename))) + (or (file-remote-p expanded 'localname) + expanded))) + filename)) + +(defun magit-decode-git-path (path) + (if (eq (aref path 0) ?\") + (decode-coding-string (read path) + (or magit-git-output-coding-system + (car default-process-coding-system)) + t) + path)) + +(defun magit-file-at-point (&optional expand assert) + (if-let ((file (magit-section-case + (file (oref it value)) + (hunk (magit-section-parent-value it))))) + (if expand + (expand-file-name file (magit-toplevel)) + file) + (when assert + (user-error "No file at point")))) + +(defun magit-current-file () + (or (magit-file-relative-name) + (magit-file-at-point) + (and (derived-mode-p 'magit-log-mode) + (car magit-buffer-log-files)))) + +;;; Predicates + +(defun magit-no-commit-p () + "Return t if there is no commit in the current Git repository." + (not (magit-rev-verify "HEAD"))) + +(defun magit-merge-commit-p (commit) + "Return t if COMMIT is a merge commit." + (length> (magit-commit-parents commit) 1)) + +(defun magit-anything-staged-p (&optional ignore-submodules &rest files) + "Return t if there are any staged changes. +If optional FILES is non-nil, then only changes to those files +are considered." + (magit-git-failure "diff" "--quiet" "--cached" + (and ignore-submodules "--ignore-submodules") + "--" files)) + +(defun magit-anything-unstaged-p (&optional ignore-submodules &rest files) + "Return t if there are any unstaged changes. +If optional FILES is non-nil, then only changes to those files +are considered." + (magit-git-failure "diff" "--quiet" + (and ignore-submodules "--ignore-submodules") + "--" files)) + +(defun magit-anything-modified-p (&optional ignore-submodules &rest files) + "Return t if there are any staged or unstaged changes. +If optional FILES is non-nil, then only changes to those files +are considered." + (or (apply #'magit-anything-staged-p ignore-submodules files) + (apply #'magit-anything-unstaged-p ignore-submodules files))) + +(defun magit-anything-unmerged-p (&rest files) + "Return t if there are any merge conflicts. +If optional FILES is non-nil, then only conflicts in those files +are considered." + (and (magit-git-string "ls-files" "--unmerged" files) t)) + +(defun magit-module-worktree-p (module) + (magit-with-toplevel + (file-exists-p (expand-file-name (expand-file-name ".git" module))))) + +(defun magit-module-no-worktree-p (module) + (not (magit-module-worktree-p module))) + +(defun magit-ignore-submodules-p (&optional return-argument) + (or (cl-find-if (lambda (arg) + (string-prefix-p "--ignore-submodules" arg)) + magit-buffer-diff-args) + (and-let* ((value (magit-get "diff.ignoreSubmodules"))) + (if return-argument + (concat "--ignore-submodules=" value) + (concat "diff.ignoreSubmodules=" value))))) + +;;; Revisions and References + +(defun magit-rev-parse (&rest args) + "Execute `git rev-parse ARGS', returning first line of output. +If there is no output, return nil." + (apply #'magit-git-string "rev-parse" args)) + +(defun magit-rev-parse-safe (&rest args) + "Execute `git rev-parse ARGS', returning first line of output. +If there is no output, return nil. Like `magit-rev-parse' but +ignore `magit-git-debug'." + (apply #'magit-git-str "rev-parse" args)) + +(defun magit-rev-parse-true (&rest args) + "Execute `git rev-parse ARGS', returning t if it prints \"true\". +If it prints \"false\", then return nil. For any other output +signal an error." + (magit-git-true "rev-parse" args)) + +(defun magit-rev-parse-false (&rest args) + "Execute `git rev-parse ARGS', returning t if it prints \"false\". +If it prints \"true\", then return nil. For any other output +signal an error." + (magit-git-false "rev-parse" args)) + +(defun magit-rev-parse-p (&rest args) + "Execute `git rev-parse ARGS', returning t if it prints \"true\". +Return t if the first (and usually only) output line is the +string \"true\", otherwise return nil." + (equal (magit-git-str "rev-parse" args) "true")) + +(defun magit-rev-verify (rev) + (magit-git-string-p "rev-parse" "--verify" rev)) + +(defun magit-commit-p (rev) + "Return full hash for REV if it names an existing commit." + (magit-rev-verify (magit--rev-dereference rev))) + +(defalias 'magit-rev-verify-commit #'magit-commit-p) + +(defalias 'magit-rev-hash #'magit-commit-p) + +(defun magit--rev-dereference (rev) + "Return a rev that forces Git to interpret REV as a commit. +If REV is nil or has the form \":/TEXT\", return REV itself." + (cond ((not rev) nil) + ((string-match-p "^:/" rev) rev) + (t (concat rev "^{commit}")))) + +(defun magit-rev-equal (a b) + "Return t if there are no differences between the commits A and B." + (magit-git-success "diff" "--quiet" a b)) + +(defun magit-rev-eq (a b) + "Return t if A and B refer to the same commit." + (let ((a (magit-commit-p a)) + (b (magit-commit-p b))) + (and a b (equal a b)))) + +(defun magit-rev-ancestor-p (a b) + "Return non-nil if commit A is an ancestor of commit B." + (magit-git-success "merge-base" "--is-ancestor" a b)) + +(defun magit-rev-head-p (rev) + (or (equal rev "HEAD") + (and rev + (not (string-search ".." rev)) + (equal (magit-rev-parse rev) + (magit-rev-parse "HEAD"))))) + +(defun magit-rev-author-p (rev) + "Return t if the user is the author of REV. +More precisely return t if `user.name' is equal to the author +name of REV and/or `user.email' is equal to the author email +of REV." + (or (equal (magit-get "user.name") (magit-rev-format "%an" rev)) + (equal (magit-get "user.email") (magit-rev-format "%ae" rev)))) + +(defun magit-rev-name (rev &optional pattern not-anchored) + "Return a symbolic name for REV using `git-name-rev'. + +PATTERN can be used to limit the result to a matching ref. +Unless NOT-ANCHORED is non-nil, the beginning of the ref must +match PATTERN. + +An anchored lookup is done using the arguments +\"--exclude=*/ --exclude=*/HEAD\" in addition to +\"--refs=\", provided at least version v2.13 of Git is +used. Older versions did not support the \"--exclude\" argument. +When \"--exclude\" cannot be used and `git-name-rev' returns a +ref that should have been excluded, then that is discarded and +this function returns nil instead. This is unfortunate because +there might be other refs that do match. To fix that, update +Git." + (if (magit-git-version< "2.13") + (and-let* + ((ref (magit-git-string "name-rev" "--name-only" "--no-undefined" + (and pattern (concat "--refs=" pattern)) + rev))) + (if (and pattern + (string-match-p "\\`refs/[^/]+/\\*\\'" pattern)) + (let ((namespace (substring pattern 0 -1))) + (and (not (or (string-suffix-p "HEAD" ref) + (and (string-match-p namespace ref) + (not (magit-rev-verify + (concat namespace ref)))))) + ref)) + ref)) + (magit-git-string "name-rev" "--name-only" "--no-undefined" + (and pattern (concat "--refs=" pattern)) + (and pattern + (not not-anchored) + (list "--exclude=*/HEAD" + (concat "--exclude=*/" pattern))) + rev))) + +(defun magit-rev-branch (rev) + (and-let* ((name (magit-rev-name rev "refs/heads/*"))) + (and (not (string-match-p "[~^]" name)) name))) + +(defun magit-get-shortname (rev) + (let* ((fn (apply-partially #'magit-rev-name rev)) + (name (or (funcall fn "refs/tags/*") + (funcall fn "refs/heads/*") + (funcall fn "refs/remotes/*")))) + (cond ((not name) + (magit-rev-parse "--short" rev)) + ((string-match "^\\(?:tags\\|remotes\\)/\\(.+\\)" name) + (if (magit-ref-ambiguous-p (match-string 1 name)) + name + (match-string 1 name))) + (t (magit-ref-maybe-qualify name))))) + +(defun magit-name-branch (rev &optional lax) + (or (magit-name-local-branch rev) + (magit-name-remote-branch rev) + (and lax (or (magit-name-local-branch rev t) + (magit-name-remote-branch rev t))))) + +(defun magit-name-local-branch (rev &optional lax) + (and-let* ((name (magit-rev-name rev "refs/heads/*"))) + (and (or lax (not (string-match-p "[~^]" name))) name))) + +(defun magit-name-remote-branch (rev &optional lax) + (and-let* ((name (magit-rev-name rev "refs/remotes/*"))) + (and (or lax (not (string-match-p "[~^]" name))) + (substring name 8)))) + +(defun magit-name-tag (rev &optional lax) + (when-let* ((name (magit-rev-name rev "refs/tags/*"))) ;debbugs#31840 + (when (string-suffix-p "^0" name) + (setq name (substring name 0 -2))) + (and (or lax (not (string-match-p "[~^]" name))) + (substring name 5)))) + +(defun magit-ref-abbrev (refname) + "Return an unambiguous abbreviation of REFNAME." + (magit-rev-parse "--verify" "--abbrev-ref" refname)) + +(defun magit-ref-fullname (refname) + "Return fully qualified refname for REFNAME. +If REFNAME is ambiguous, return nil." + (magit-rev-parse "--verify" "--symbolic-full-name" refname)) + +(defun magit-ref-ambiguous-p (refname) + (save-match-data + (if (string-match "\\`\\([^^~]+\\)\\(.*\\)" refname) + (not (magit-ref-fullname (match-string 1 refname))) + (error "%S has an unrecognized format" refname)))) + +(defun magit-ref-maybe-qualify (refname &optional prefix) + "If REFNAME is ambiguous, try to disambiguate it by prepend PREFIX to it. +Return an unambiguous refname, either REFNAME or that prefixed +with PREFIX, nil otherwise. If REFNAME has an offset suffix +such as \"~1\", then that is preserved. If optional PREFIX is +nil, then use \"heads/\". " + (if (magit-ref-ambiguous-p refname) + (let ((refname (concat (or prefix "heads/") refname))) + (and (not (magit-ref-ambiguous-p refname)) refname)) + refname)) + +(defun magit-ref-exists-p (ref) + (magit-git-success "show-ref" "--verify" ref)) + +(defun magit-ref-equal (a b) + "Return t if the refnames A and B are `equal'. +A symbolic-ref pointing to some ref, is `equal' to that ref, +as are two symbolic-refs pointing to the same ref. Refnames +may be abbreviated." + (let ((a (magit-ref-fullname a)) + (b (magit-ref-fullname b))) + (and a b (equal a b)))) + +(defun magit-ref-eq (a b) + "Return t if the refnames A and B are `eq'. +A symbolic-ref is `eq' to itself, but not to the ref it points +to, or to some other symbolic-ref that points to the same ref." + (let ((symbolic-a (magit-symbolic-ref-p a)) + (symbolic-b (magit-symbolic-ref-p b))) + (or (and symbolic-a + symbolic-b + (equal a b)) + (and (not symbolic-a) + (not symbolic-b) + (magit-ref-equal a b))))) + +(defun magit-headish () + "Return the `HEAD' or if that doesn't exist the hash of the empty tree." + (if (magit-no-commit-p) + (magit-git-string "mktree") + "HEAD")) + +(defun magit-branch-at-point () + (magit-section-case + (branch (oref it value)) + (commit (or (magit--painted-branch-at-point) + (magit-name-branch (oref it value)))))) + +(defun magit--painted-branch-at-point (&optional type) + (or (and (not (eq type 'remote)) + (memq (get-text-property (magit-point) 'font-lock-face) + (list 'magit-branch-local + 'magit-branch-current)) + (and-let* ((branch (magit-thing-at-point 'git-revision t))) + (cdr (magit-split-branch-name branch)))) + (and (not (eq type 'local)) + (memq (get-text-property (magit-point) 'font-lock-face) + (list 'magit-branch-remote + 'magit-branch-remote-head)) + (thing-at-point 'git-revision t)))) + +(defun magit-local-branch-at-point () + (magit-section-case + (branch (let ((branch (magit-ref-maybe-qualify (oref it value)))) + (when (member branch (magit-list-local-branch-names)) + branch))) + (commit (or (magit--painted-branch-at-point 'local) + (magit-name-local-branch (oref it value)))))) + +(defun magit-remote-branch-at-point () + (magit-section-case + (branch (let ((branch (oref it value))) + (when (member branch (magit-list-remote-branch-names)) + branch))) + (commit (or (magit--painted-branch-at-point 'remote) + (magit-name-remote-branch (oref it value)))))) + +(defun magit-commit-at-point () + (or (magit-section-value-if 'commit) + (magit-thing-at-point 'git-revision t) + (and-let* ((chunk (and (bound-and-true-p magit-blame-mode) + (fboundp 'magit-current-blame-chunk) + (magit-current-blame-chunk 'addition t)))) + (oref chunk orig-rev)) + (and (derived-mode-p 'magit-stash-mode + 'magit-merge-preview-mode + 'magit-revision-mode) + magit-buffer-revision))) + +(defun magit-branch-or-commit-at-point () + (or (magit-section-case + (branch (magit-ref-maybe-qualify (oref it value))) + (commit (or (magit--painted-branch-at-point) + (let ((rev (oref it value))) + (or (magit-name-branch rev) rev)))) + (tag (magit-ref-maybe-qualify (oref it value) "tags/")) + (pullreq (or (and (fboundp 'forge--pullreq-branch) + (magit-branch-p + (forge--pullreq-branch (oref it value)))) + (magit-ref-p (format "refs/pullreqs/%s" + (oref (oref it value) number)))))) + (magit-thing-at-point 'git-revision t) + (and-let* ((chunk (and (bound-and-true-p magit-blame-mode) + (fboundp 'magit-current-blame-chunk) + (magit-current-blame-chunk 'addition t)))) + (oref chunk orig-rev)) + (and magit-buffer-file-name + magit-buffer-refname) + (and (derived-mode-p 'magit-stash-mode + 'magit-merge-preview-mode + 'magit-revision-mode) + magit-buffer-revision))) + +(defun magit-tag-at-point () + (magit-section-case + (tag (oref it value)) + (commit (magit-name-tag (oref it value))))) + +(defun magit-stash-at-point () + (magit-section-value-if 'stash)) + +(defun magit-remote-at-point () + (magit-section-case + (remote (oref it value)) + ([branch remote] (magit-section-parent-value it)))) + +(defun magit-module-at-point (&optional predicate) + (when (magit-section-match 'magit-module-section) + (let ((module (oref (magit-current-section) value))) + (and (or (not predicate) + (funcall predicate module)) + module)))) + +(defun magit-get-current-branch () + "Return the refname of the currently checked out branch. +Return nil if no branch is currently checked out." + (magit-git-string "symbolic-ref" "--short" "HEAD")) + +(defvar magit-get-previous-branch-timeout 0.5 + "Maximum time to spend in `magit-get-previous-branch'. +Given as a number of seconds.") + +(defun magit-get-previous-branch () + "Return the refname of the previously checked out branch. +Return nil if no branch can be found in the `HEAD' reflog +which is different from the current branch and still exists. +The amount of time spent searching is limited by +`magit-get-previous-branch-timeout'." + (let ((t0 (float-time)) + (current (magit-get-current-branch)) + (i 1) prev) + (while (if (> (- (float-time) t0) magit-get-previous-branch-timeout) + (setq prev nil) ;; Timed out. + (and (setq prev (magit-rev-verify (format "@{-%i}" i))) + (or (not (setq prev (magit-rev-branch prev))) + (equal prev current)))) + (cl-incf i)) + prev)) + +(defun magit-set-upstream-branch (branch upstream) + "Set UPSTREAM as the upstream of BRANCH. +If UPSTREAM is nil, then unset BRANCH's upstream. +Otherwise UPSTREAM has to be an existing branch." + (if upstream + (magit-call-git "branch" "--set-upstream-to" upstream branch) + (magit-call-git "branch" "--unset-upstream" branch))) + +(defun magit-get-upstream-ref (&optional branch) + "Return the upstream branch of BRANCH as a fully qualified ref. +It BRANCH is nil, then return the upstream of the current branch, +if any, nil otherwise. If the upstream is not configured, the +configured remote is an url, or the named branch does not exist, +then return nil. I.e. return an existing local or +remote-tracking branch ref." + (and-let* ((branch (or branch (magit-get-current-branch)))) + (magit-ref-fullname (concat branch "@{upstream}")))) + +(defun magit-get-upstream-branch (&optional branch) + "Return the name of the upstream branch of BRANCH. +It BRANCH is nil, then return the upstream of the current branch +if any, nil otherwise. If the upstream is not configured, the +configured remote is an url, or the named branch does not exist, +then return nil. I.e. return the name of an existing local or +remote-tracking branch. The returned string is colorized +according to the branch type." + (magit--with-refresh-cache + (list default-directory 'magit-get-upstream-branch branch) + (and-let* ((branch (or branch (magit-get-current-branch))) + (upstream (magit-ref-abbrev (concat branch "@{upstream}")))) + (magit--propertize-face + upstream (if (equal (magit-get "branch" branch "remote") ".") + 'magit-branch-local + 'magit-branch-remote))))) + +(defun magit-get-indirect-upstream-branch (branch &optional force) + (let ((remote (magit-get "branch" branch "remote"))) + (and remote (not (equal remote ".")) + ;; The user has opted in... + (or force + (--some (if (magit-git-success "check-ref-format" "--branch" it) + (equal it branch) + (string-match-p it branch)) + magit-branch-prefer-remote-upstream)) + ;; and local BRANCH tracks a remote branch... + (let ((upstream (magit-get-upstream-branch branch))) + ;; whose upstream... + (and upstream + ;; has the same name as BRANCH... + (equal (substring upstream (1+ (length remote))) branch) + ;; and can be fast-forwarded to BRANCH. + (magit-rev-ancestor-p upstream branch) + upstream))))) + +(defun magit-get-upstream-remote (&optional branch allow-unnamed) + (and-let* ((branch (or branch (magit-get-current-branch))) + (remote (magit-get "branch" branch "remote"))) + (and (not (equal remote ".")) + (cond ((member remote (magit-list-remotes)) + (magit--propertize-face remote 'magit-branch-remote)) + ((and allow-unnamed + (string-match-p "\\(\\`.\\{0,2\\}/\\|[:@]\\)" remote)) + (magit--propertize-face remote 'bold)))))) + +(defun magit-get-unnamed-upstream (&optional branch) + (and-let* ((branch (or branch (magit-get-current-branch))) + (remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge"))) + (and (magit--unnamed-upstream-p remote merge) + (list (magit--propertize-face remote 'bold) + (magit--propertize-face merge 'magit-branch-remote))))) + +(defun magit--unnamed-upstream-p (remote merge) + (and remote (string-match-p "\\(\\`\\.\\{0,2\\}/\\|[:@]\\)" remote) + merge (string-prefix-p "refs/" merge))) + +(defun magit--valid-upstream-p (remote merge) + (and (or (equal remote ".") + (member remote (magit-list-remotes))) + (string-prefix-p "refs/" merge))) + +(defun magit-get-current-remote (&optional allow-unnamed) + (or (magit-get-upstream-remote nil allow-unnamed) + (and-let* ((remotes (magit-list-remotes)) + (remote (if (length= remotes 1) + (car remotes) + (magit-primary-remote)))) + (magit--propertize-face remote 'magit-branch-remote)))) + +(defun magit-get-push-remote (&optional branch) + (and-let* ((remote + (or (and (or branch (setq branch (magit-get-current-branch))) + (magit-get "branch" branch "pushRemote")) + (magit-get "remote.pushDefault")))) + (magit--propertize-face remote 'magit-branch-remote))) + +(defun magit-get-push-branch (&optional branch verify) + (magit--with-refresh-cache + (list default-directory 'magit-get-push-branch branch verify) + (and-let* ((branch (or branch (setq branch (magit-get-current-branch)))) + (remote (magit-get-push-remote branch)) + (target (concat remote "/" branch))) + (and (or (not verify) + (magit-rev-verify target)) + (magit--propertize-face target 'magit-branch-remote))))) + +(defun magit-get-@{push}-branch (&optional branch) + (let ((ref (magit-rev-parse "--symbolic-full-name" + (concat branch "@{push}")))) + (when (and ref (string-prefix-p "refs/remotes/" ref)) + (substring ref 13)))) + +(defun magit-get-remote (&optional branch) + (when (or branch (setq branch (magit-get-current-branch))) + (let ((remote (magit-get "branch" branch "remote"))) + (unless (equal remote ".") + remote)))) + +(defun magit-get-some-remote (&optional branch) + (or (magit-get-remote branch) + (and-let* ((main (magit-main-branch))) + (magit-get-remote main)) + (magit-primary-remote) + (car (magit-list-remotes)))) + +(defvar magit-primary-remote-names + '("upstream" "origin")) + +(defun magit-primary-remote () + "Return the primary remote. + +The primary remote is the remote that tracks the repository that +other repositories are forked from. It often is called \"origin\" +but because many people name their own fork \"origin\", using that +term would be ambiguous. Likewise we avoid the term \"upstream\" +because a branch's @{upstream} branch may be a local branch or a +branch from a remote other than the primary remote. + +If a remote exists whose name matches `magit.primaryRemote', then +that is considered the primary remote. If no remote by that name +exists, then remotes in `magit-primary-remote-names' are tried in +order and the first remote from that list that actually exists in +the current repository is considered its primary remote." + (let ((remotes (magit-list-remotes))) + (seq-find (lambda (name) + (member name remotes)) + (delete-dups + (delq nil + (cons (magit-get "magit.primaryRemote") + magit-primary-remote-names)))))) + +(defun magit-branch-merged-p (branch &optional target) + "Return non-nil if BRANCH is merged into its upstream and TARGET. + +TARGET defaults to the current branch. If `HEAD' is detached and +TARGET is nil, then always return nil. As a special case, if +TARGET is t, then return non-nil if BRANCH is merged into any one +of the other local branches. + +If, and only if, BRANCH has an upstream, then only return non-nil +if BRANCH is merged into both TARGET (as described above) as well +as into its upstream." + (and (--if-let (and (magit-branch-p branch) + (magit-get-upstream-branch branch)) + (magit-git-success "merge-base" "--is-ancestor" branch it) + t) + (if (eq target t) + (delete (magit-name-local-branch branch) + (magit-list-containing-branches branch)) + (and-let* ((target (or target (magit-get-current-branch)))) + (magit-git-success "merge-base" "--is-ancestor" branch target))))) + +(defun magit-get-tracked (refname) + "Return the remote branch tracked by the remote-tracking branch REFNAME. +The returned value has the form (REMOTE . REF), where REMOTE is +the name of a remote and REF is the ref local to the remote." + (and-let* ((ref (magit-ref-fullname refname))) + (save-match-data + (seq-some (lambda (line) + (and (string-match "\ +\\`remote\\.\\([^.]+\\)\\.fetch=\\+?\\([^:]+\\):\\(.+\\)" line) + (let ((rmt (match-string 1 line)) + (src (match-string 2 line)) + (dst (match-string 3 line))) + (and (string-match (format "\\`%s\\'" + (string-replace + "*" "\\(.+\\)" dst)) + ref) + (cons rmt (string-replace + "*" (match-string 1 ref) src)))))) + (magit-git-lines "config" "--local" "--list"))))) + +(defun magit-split-branch-name (branch) + (cond ((member branch (magit-list-local-branch-names)) + (cons "." branch)) + ((string-match "/" branch) + (or (seq-some (lambda (remote) + (and (string-match + (format "\\`\\(%s\\)/\\(.+\\)\\'" remote) + branch) + (cons (match-string 1 branch) + (match-string 2 branch)))) + (magit-list-remotes)) + (error "Invalid branch name %s" branch))))) + +(defun magit-get-current-tag (&optional rev with-distance) + "Return the closest tag reachable from REV. + +If optional REV is nil, then default to `HEAD'. +If optional WITH-DISTANCE is non-nil then return (TAG COMMITS), +if it is `dirty' return (TAG COMMIT DIRTY). COMMITS is the number +of commits in `HEAD' but not in TAG and DIRTY is t if there are +uncommitted changes, nil otherwise." + (and-let* ((str (magit-git-str "describe" "--long" "--tags" + (and (eq with-distance 'dirty) "--dirty") + rev))) + (save-match-data + (string-match + "\\(.+\\)-\\(?:0[0-9]*\\|\\([0-9]+\\)\\)-g[0-9a-z]+\\(-dirty\\)?$" str) + (if with-distance + `(,(match-string 1 str) + ,(string-to-number (or (match-string 2 str) "0")) + ,@(and (match-string 3 str) (list t))) + (match-string 1 str))))) + +(defun magit-get-next-tag (&optional rev with-distance) + "Return the closest tag from which REV is reachable. + +If optional REV is nil, then default to `HEAD'. +If no such tag can be found or if the distance is 0 (in which +case it is the current tag, not the next), return nil instead. +If optional WITH-DISTANCE is non-nil, then return (TAG COMMITS) +where COMMITS is the number of commits in TAG but not in REV." + (and-let* ((str (magit-git-str "describe" "--contains" (or rev "HEAD")))) + (save-match-data + (when (string-match "^[^^~]+" str) + (setq str (match-string 0 str)) + (unless (equal str (magit-get-current-tag rev)) + (if with-distance + (list str (car (magit-rev-diff-count str rev))) + str)))))) + +(defun magit-list-refs (&optional namespaces format sortby) + "Return list of references. + +When NAMESPACES is non-nil, list refs from these namespaces +rather than those from `magit-list-refs-namespaces'. + +FORMAT is passed to the `--format' flag of `git for-each-ref' +and defaults to \"%(refname)\". If the format is \"%(refname)\" +or \"%(refname:short)\", then drop the symbolic-ref `HEAD'. + +SORTBY is a key or list of keys to pass to the `--sort' flag of +`git for-each-ref'. When nil, use `magit-list-refs-sortby'" + (unless format + (setq format "%(refname)")) + (let ((refs (magit-git-lines "for-each-ref" + (concat "--format=" format) + (--map (concat "--sort=" it) + (pcase (or sortby magit-list-refs-sortby) + ((and val (pred stringp)) (list val)) + ((and val (pred listp)) val))) + (or namespaces magit-list-refs-namespaces)))) + (if (member format '("%(refname)" "%(refname:short)")) + (let ((case-fold-search nil)) + (--remove (string-match-p "\\(\\`\\|/\\)HEAD\\'" it) + refs)) + refs))) + +(defun magit-list-branches () + (magit-list-refs (list "refs/heads" "refs/remotes"))) + +(defun magit-list-local-branches () + (magit-list-refs "refs/heads")) + +(defun magit-list-remote-branches (&optional remote) + (magit-list-refs (concat "refs/remotes/" remote))) + +(defun magit-list-related-branches (relation &optional commit &rest args) + (--remove (string-match-p "\\(\\`(HEAD\\|HEAD -> \\)" it) + (--map (substring it 2) + (magit-git-lines "branch" args relation commit)))) + +(defun magit-list-containing-branches (&optional commit &rest args) + (magit-list-related-branches "--contains" commit args)) + +(defun magit-list-publishing-branches (&optional commit) + (--filter (magit-rev-ancestor-p (or commit "HEAD") it) + magit-published-branches)) + +(defun magit-list-merged-branches (&optional commit &rest args) + (magit-list-related-branches "--merged" commit args)) + +(defun magit-list-unmerged-branches (&optional commit &rest args) + (magit-list-related-branches "--no-merged" commit args)) + +(defun magit-list-unmerged-to-upstream-branches () + (--filter (and-let* ((upstream (magit-get-upstream-branch it))) + (member it (magit-list-unmerged-branches upstream))) + (magit-list-local-branch-names))) + +(defun magit-list-branches-pointing-at (commit) + (let ((re (format "\\`%s refs/\\(heads\\|remotes\\)/\\(.*\\)\\'" + (magit-rev-verify commit)))) + (--keep (and (string-match re it) + (let ((name (match-string 2 it))) + (and (not (string-suffix-p "HEAD" name)) + name))) + (magit-git-lines "show-ref")))) + +(defun magit-list-refnames (&optional namespaces include-special) + (nconc (magit-list-refs namespaces "%(refname:short)") + (and include-special + (magit-list-special-refnames)))) + +(defvar magit-special-refnames + '("HEAD" "ORIG_HEAD" "FETCH_HEAD" "MERGE_HEAD" "CHERRY_PICK_HEAD")) + +(defun magit-list-special-refnames () + (let ((gitdir (magit-gitdir))) + (cl-mapcan (lambda (name) + (and (file-exists-p (expand-file-name name gitdir)) + (list name))) + magit-special-refnames))) + +(defun magit-list-branch-names () + (magit-list-refnames (list "refs/heads" "refs/remotes"))) + +(defun magit-list-local-branch-names () + (magit-list-refnames "refs/heads")) + +(defun magit-list-remote-branch-names (&optional remote relative) + (if (and remote relative) + (let ((regexp (format "^refs/remotes/%s/\\(.+\\)" remote))) + (--mapcat (when (string-match regexp it) + (list (match-string 1 it))) + (magit-list-remote-branches remote))) + (magit-list-refnames (concat "refs/remotes/" remote)))) + +(defun magit-format-refs (format &rest args) + (let ((lines (magit-git-lines + "for-each-ref" (concat "--format=" format) + (or args (list "refs/heads" "refs/remotes" "refs/tags"))))) + (if (string-search "\f" format) + (--map (split-string it "\f") lines) + lines))) + +(defun magit-list-remotes () + (magit-git-lines "remote")) + +(defun magit-list-tags () + (magit-git-lines "tag")) + +(defun magit-list-stashes (&optional format) + (magit-git-lines "stash" "list" (concat "--format=" (or format "%gd")))) + +(defun magit-list-active-notes-refs () + "Return notes refs according to `core.notesRef' and `notes.displayRef'." + (magit-git-lines "for-each-ref" "--format=%(refname)" + (or (magit-get "core.notesRef") "refs/notes/commits") + (magit-get-all "notes.displayRef"))) + +(defun magit-list-notes-refnames () + (--map (substring it 6) (magit-list-refnames "refs/notes"))) + +(defun magit-remote-list-tags (remote) + (--keep (and (not (string-suffix-p "^{}" it)) + (substring it 51)) + (magit-git-lines "ls-remote" "--tags" remote))) + +(defun magit-remote-list-branches (remote) + (--keep (and (not (string-suffix-p "^{}" it)) + (substring it 52)) + (magit-git-lines "ls-remote" "--heads" remote))) + +(defun magit-remote-list-refs (remote) + (--keep (and (not (string-suffix-p "^{}" it)) + (substring it 41)) + (magit-git-lines "ls-remote" remote))) + +(defun magit-remote-head (remote) + (and-let* ((line (cl-find-if + (lambda (line) + (string-match + "\\`ref: refs/heads/\\([^\s\t]+\\)[\s\t]HEAD\\'" line)) + (magit-git-lines "ls-remote" "--symref" remote "HEAD")))) + (match-string 1 line))) + +(defun magit-list-modified-modules () + (--keep (and (string-match "\\`\\+\\([^ ]+\\) \\(.+\\) (.+)\\'" it) + (match-string 2 it)) + (magit-git-lines "submodule" "status"))) + +(defun magit-list-module-paths () + (--mapcat (and (string-match "^160000 [0-9a-z]\\{40,\\} 0\t\\(.+\\)$" it) + (list (match-string 1 it))) + (magit-git-items "ls-files" "-z" "--stage"))) + +(defun magit-list-module-names () + (mapcar #'magit-get-submodule-name (magit-list-module-paths))) + +(defun magit-get-submodule-name (path) + "Return the name of the submodule at PATH. +PATH has to be relative to the super-repository." + (magit-git-string "submodule--helper" "name" path)) + +(defun magit-list-worktrees () + (let ((remote (file-remote-p default-directory)) + worktrees worktree) + (dolist (line (let ((magit-git-global-arguments + ;; KLUDGE At least in v2.8.3 this triggers a segfault. + (remove "--no-pager" magit-git-global-arguments))) + (magit-git-lines "worktree" "list" "--porcelain"))) + (cond ((string-prefix-p "worktree" line) + (let ((path (substring line 9))) + (when remote + (setq path (concat remote path))) + ;; If the git directory is separate from the main + ;; worktree, then "git worktree" returns the git + ;; directory instead of the worktree, which isn't + ;; what it is supposed to do and not what we want. + ;; However, if the worktree has been removed, then + ;; we want to return it anway; instead of nil. + (setq path (or (magit-toplevel path) path)) + (setq worktree (list path nil nil nil)) + (push worktree worktrees))) + ((string-equal line "bare") + (let* ((default-directory (car worktree)) + (wt (and (not (magit-get-boolean "core.bare")) + (magit-get "core.worktree")))) + (if (and wt (file-exists-p (expand-file-name wt))) + (progn (setf (nth 0 worktree) (expand-file-name wt)) + (setf (nth 2 worktree) (magit-rev-parse "HEAD")) + (setf (nth 3 worktree) (magit-get-current-branch))) + (setf (nth 1 worktree) t)))) + ((string-prefix-p "HEAD" line) + (setf (nth 2 worktree) (substring line 5))) + ((string-prefix-p "branch" line) + (setf (nth 3 worktree) (substring line 18))) + ((string-equal line "detached")))) + (nreverse worktrees))) + +(defun magit-symbolic-ref-p (name) + (magit-git-success "symbolic-ref" "--quiet" name)) + +(defun magit-ref-p (rev) + (or (car (member rev (magit-list-refs "refs/"))) + (car (member rev (magit-list-refnames "refs/"))))) + +(defun magit-branch-p (rev) + (or (car (member rev (magit-list-branches))) + (car (member rev (magit-list-branch-names))))) + +(defun magit-local-branch-p (rev) + (or (car (member rev (magit-list-local-branches))) + (car (member rev (magit-list-local-branch-names))))) + +(defun magit-remote-branch-p (rev) + (or (car (member rev (magit-list-remote-branches))) + (car (member rev (magit-list-remote-branch-names))))) + +(defun magit-branch-set-face (branch) + (magit--propertize-face branch (if (magit-local-branch-p branch) + 'magit-branch-local + 'magit-branch-remote))) + +(defun magit-tag-p (rev) + (car (member rev (magit-list-tags)))) + +(defun magit-remote-p (string) + (car (member string (magit-list-remotes)))) + +(defvar magit-main-branch-names + ;; These are the names that Git suggests + ;; if `init.defaultBranch' is undefined. + '("main" "master" "trunk" "development")) + +(defun magit-main-branch () + "Return the main branch. + +If a branch exists whose name matches `init.defaultBranch', then +that is considered the main branch. If no branch by that name +exists, then the branch names in `magit-main-branch-names' are +tried in order. The first branch from that list that actually +exists in the current repository is considered its main branch." + (let ((branches (magit-list-local-branch-names))) + (seq-find (lambda (name) + (member name branches)) + (delete-dups + (delq nil + (cons (magit-get "init.defaultBranch") + magit-main-branch-names)))))) + +(defun magit-rev-diff-count (a b) + "Return the commits in A but not B and vice versa. +Return a list of two integers: (A>B B>A)." + (mapcar #'string-to-number + (split-string (magit-git-string "rev-list" + "--count" "--left-right" + (concat a "..." b)) + "\t"))) + +(defun magit-abbrev-length () + (let ((abbrev (magit-get "core.abbrev"))) + (if (and abbrev (not (equal abbrev "auto"))) + (string-to-number abbrev) + ;; Guess the length git will be using based on an example + ;; abbreviation. Actually HEAD's abbreviation might be an + ;; outlier, so use the shorter of the abbreviations for two + ;; commits. See #3034. + (if-let ((head (magit-rev-parse "--short" "HEAD")) + (head-len (length head))) + (min head-len + (--if-let (magit-rev-parse "--short" "HEAD~") + (length it) + head-len)) + ;; We're on an unborn branch, but perhaps the repository has + ;; other commits. See #4123. + (if-let ((commits (magit-git-lines "rev-list" "-n2" "--all" + "--abbrev-commit"))) + (apply #'min (mapcar #'length commits)) + ;; A commit does not exist. Fall back to the default of 7. + 7))))) + +(defun magit-abbrev-arg (&optional arg) + (format "--%s=%d" (or arg "abbrev") (magit-abbrev-length))) + +(defun magit-rev-abbrev (rev) + (magit-rev-parse (magit-abbrev-arg "short") rev)) + +(defun magit-commit-children (commit &optional args) + (mapcar #'car + (--filter (member commit (cdr it)) + (--map (split-string it " ") + (magit-git-lines + "log" "--format=%H %P" + (or args (list "--branches" "--tags" "--remotes")) + "--not" commit))))) + +(defun magit-commit-parents (commit) + (and-let* ((str (magit-git-string "rev-list" "-1" "--parents" commit))) + (cdr (split-string str)))) + +(defun magit-patch-id (rev) + (magit--with-connection-local-variables + (magit--with-temp-process-buffer + (magit-process-file + shell-file-name nil '(t nil) nil shell-command-switch + (let ((exec (shell-quote-argument (magit-git-executable)))) + (format "%s diff-tree -u %s | %s patch-id" exec rev exec))) + (car (split-string (buffer-string)))))) + +(defun magit-rev-format (format &optional rev args) + ;; Prefer `git log --no-walk' to `git show --no-patch' because it + ;; performs better in some scenarios. + (let ((str (magit-git-string "log" "--no-walk" + (concat "--format=" format) args + (if rev (magit--rev-dereference rev) "HEAD") + "--"))) + (unless (string-equal str "") + str))) + +(defun magit-rev-insert-format (format &optional rev args) + ;; Prefer `git log --no-walk' to `git show --no-patch' because it + ;; performs better in some scenarios. + (magit-git-insert "log" "--no-walk" + (concat "--format=" format) args + (if rev (magit--rev-dereference rev) "HEAD") + "--")) + +(defun magit-format-rev-summary (rev) + (when-let* ((str (magit-rev-format "%h %s" rev))) ;debbugs#31840 + (magit--put-face 0 (string-match " " str) 'magit-hash str) + str)) + +(defvar magit-ref-namespaces + '(("\\`HEAD\\'" . magit-head) + ("\\`refs/tags/\\(.+\\)" . magit-tag) + ("\\`refs/heads/\\(.+\\)" . magit-branch-local) + ("\\`refs/remotes/\\(.+\\)" . magit-branch-remote) + ("\\`refs/bisect/\\(bad\\)" . magit-bisect-bad) + ("\\`refs/bisect/\\(skip.*\\)" . magit-bisect-skip) + ("\\`refs/bisect/\\(good.*\\)" . magit-bisect-good) + ("\\`refs/stash$" . magit-refname-stash) + ("\\`refs/wip/\\(.+\\)" . magit-refname-wip) + ("\\`refs/pullreqs/\\(.+\\)" . magit-refname-pullreq) + ("\\`\\(bad\\):" . magit-bisect-bad) + ("\\`\\(skip\\):" . magit-bisect-skip) + ("\\`\\(good\\):" . magit-bisect-good) + ("\\`\\(.+\\)" . magit-refname)) + "How refs are formatted for display. + +Each entry controls how a certain type of ref is displayed, and +has the form (REGEXP . FACE). REGEXP is a regular expression +used to match full refs. The first entry whose REGEXP matches +the reference is used. + +In log and revision buffers the first regexp submatch becomes the +\"label\" that represents the ref and is propertized with FONT. +In refs buffers the displayed text is controlled by other means +and this option only controls what face is used.") + +(defun magit-format-ref-labels (string) + (save-match-data + (let ((regexp "\\(, \\|tag: \\|HEAD -> \\)") + names) + (if (and (derived-mode-p 'magit-log-mode) + (member "--simplify-by-decoration" magit-buffer-log-args)) + (let ((branches (magit-list-local-branch-names)) + (re (format "^%s/.+" (regexp-opt (magit-list-remotes))))) + (setq names + (--map (cond ((string-equal it "HEAD") it) + ((string-prefix-p "refs/" it) it) + ((member it branches) (concat "refs/heads/" it)) + ((string-match re it) (concat "refs/remotes/" it)) + (t (concat "refs/" it))) + (split-string + (string-replace "tag: " "refs/tags/" string) + regexp t)))) + (setq names (split-string string regexp t))) + (let (state head upstream tags branches remotes other combined) + (dolist (ref names) + (let* ((face (cdr (--first (string-match (car it) ref) + magit-ref-namespaces))) + (name (magit--propertize-face + (or (match-string 1 ref) ref) face))) + (cl-case face + ((magit-bisect-bad magit-bisect-skip magit-bisect-good) + (setq state name)) + (magit-head + (setq head (magit--propertize-face "@" 'magit-head))) + (magit-tag (push name tags)) + (magit-branch-local (push name branches)) + (magit-branch-remote (push name remotes)) + (t (push name other))))) + (setq remotes + (-keep + (lambda (name) + (if (string-match "\\`\\([^/]*\\)/\\(.*\\)\\'" name) + (let ((r (match-string 1 name)) + (b (match-string 2 name))) + (and (not (equal b "HEAD")) + (if (equal (concat "refs/remotes/" name) + (magit-git-string + "symbolic-ref" + (format "refs/remotes/%s/HEAD" r))) + (magit--propertize-face + name 'magit-branch-remote-head) + name))) + name)) + remotes)) + (let* ((current (magit-get-current-branch)) + (target (magit-get-upstream-branch current))) + (dolist (name branches) + (let ((push (car (member (magit-get-push-branch name) remotes)))) + (when push + (setq remotes (delete push remotes)) + (string-match "^[^/]*/" push) + (setq push (substring push 0 (match-end 0)))) + (cond + ((equal name current) + (setq head + (concat push + (magit--propertize-face + name 'magit-branch-current)))) + ((equal name target) + (setq upstream + (concat push + (magit--propertize-face + name '(magit-branch-upstream + magit-branch-local))))) + (t + (push (concat push name) combined))))) + (when (and target (not upstream)) + (if (member target remotes) + (progn + (magit--add-face-text-property + 0 (length target) 'magit-branch-upstream nil target) + (setq upstream target) + (setq remotes (delete target remotes))) + (when-let ((target (car (member target combined)))) + (magit--add-face-text-property + 0 (length target) 'magit-branch-upstream nil target) + (setq upstream target) + (setq combined (delete target combined)))))) + (mapconcat #'identity + (flatten-tree `(,state + ,head + ,upstream + ,@(nreverse tags) + ,@(nreverse combined) + ,@(nreverse remotes) + ,@other)) + " "))))) + +(defun magit-object-type (object) + (magit-git-string "cat-file" "-t" object)) + +(defmacro magit-with-blob (commit file &rest body) + (declare (indent 2) + (debug (form form body))) + `(magit--with-temp-process-buffer + (let ((buffer-file-name ,file)) + (save-excursion + (magit-git-insert "cat-file" "-p" + (concat ,commit ":" buffer-file-name))) + (decode-coding-inserted-region + (point-min) (point-max) buffer-file-name t nil nil t) + ,@body))) + +(defvar magit-tramp-process-environment nil) + +(defmacro magit-with-temp-index (tree arg &rest body) + (declare (indent 2) (debug (form form body))) + (let ((file (cl-gensym "file"))) + `(let ((magit--refresh-cache nil) + (,file (magit-convert-filename-for-git + (make-temp-name (magit-git-dir "index.magit."))))) + (unwind-protect + (magit-with-toplevel + (--when-let ,tree + (or (magit-git-success "read-tree" ,arg it + (concat "--index-output=" ,file)) + (error "Cannot read tree %s" it))) + (if (file-remote-p default-directory) + (let ((magit-tramp-process-environment + (cons (concat "GIT_INDEX_FILE=" ,file) + magit-tramp-process-environment))) + ,@body) + (with-environment-variables (("GIT_INDEX_FILE" ,file)) + ,@body))) + (ignore-errors + (delete-file (concat (file-remote-p default-directory) ,file))))))) + +(defun magit-commit-tree (message &optional tree &rest parents) + (magit-git-string "commit-tree" "--no-gpg-sign" "-m" message + (--mapcat (list "-p" it) (delq nil parents)) + (or tree + (magit-git-string "write-tree") + (error "Cannot write tree")))) + +(defun magit-commit-worktree (message &optional arg &rest other-parents) + (magit-with-temp-index "HEAD" arg + (and (magit-update-files (magit-unstaged-files)) + (apply #'magit-commit-tree message nil "HEAD" other-parents)))) + +(defun magit-update-files (files) + (magit-git-success "update-index" "--add" "--remove" "--" files)) + +(defun magit-update-ref (ref message rev &optional stashish) + (let ((magit--refresh-cache nil)) + (or (if (magit-git-version>= "2.6.0") + (zerop (magit-call-git "update-ref" "--create-reflog" + "-m" message ref rev + (or (magit-rev-verify ref) ""))) + ;; `--create-reflog' didn't exist before v2.6.0 + (let ((oldrev (magit-rev-verify ref)) + (logfile (magit-git-dir (concat "logs/" ref)))) + (unless (file-exists-p logfile) + (when oldrev + (magit-git-success "update-ref" "-d" ref oldrev)) + (make-directory (file-name-directory logfile) t) + (with-temp-file logfile) + (when (and oldrev (not stashish)) + (magit-git-success "update-ref" "-m" "enable reflog" + ref oldrev "")))) + (magit-git-success "update-ref" "-m" message ref rev + (or (magit-rev-verify ref) ""))) + (error "Cannot update %s with %s" ref rev)))) + +(defconst magit-range-re + (concat "\\`\\([^ \t]*[^.]\\)?" ; revA + "\\(\\.\\.\\.?\\)" ; range marker + "\\([^.][^ \t]*\\)?\\'")) ; revB + +(defun magit-split-range (range) + (and (string-match magit-range-re range) + (let ((beg (or (match-string 1 range) "HEAD")) + (end (or (match-string 3 range) "HEAD"))) + (cons (if (string-equal (match-string 2 range) "...") + (magit-git-string "merge-base" beg end) + beg) + end)))) + +(defun magit-hash-range (range) + (if (string-match magit-range-re range) + (concat (magit-rev-hash (match-string 1 range)) + (match-string 2 range) + (magit-rev-hash (match-string 3 range))) + (magit-rev-hash range))) + +(defvar magit-revision-faces + '(magit-hash + magit-tag + magit-branch-remote + magit-branch-remote-head + magit-branch-local + magit-branch-current + magit-branch-upstream + magit-branch-warning + magit-head + magit-refname + magit-refname-stash + magit-refname-wip + magit-refname-pullreq)) + +(put 'git-revision 'thing-at-point #'magit-thingatpt--git-revision) +(defun magit-thingatpt--git-revision () + (and-let* ((bounds + (let ((c "\s\n\t~^:?*[\\")) + (cl-letf (((get 'git-revision 'beginning-op) + (lambda () + (if (re-search-backward (format "[%s]" c) nil t) + (forward-char) + (goto-char (point-min))))) + ((get 'git-revision 'end-op) + (lambda () + (re-search-forward (format "\\=[^%s]*" c) nil t)))) + (bounds-of-thing-at-point 'git-revision)))) + (string (buffer-substring-no-properties (car bounds) (cdr bounds)))) + (and (or (and (>= (length string) 7) + (string-match-p "[a-z]" string) + (magit-commit-p string)) + (and (magit-ref-p string) + (let ((face (get-text-property (point) 'face))) + (or (not face) + (member face magit-revision-faces))))) + string))) + +;;; Completion + +(defvar magit-revision-history nil) + +(defun magit--minibuf-default-add-commit () + (let ((fn minibuffer-default-add-function)) + (lambda () + (if-let ((commit (with-selected-window (minibuffer-selected-window) + (magit-commit-at-point)))) + (cons commit (delete commit (funcall fn))) + (funcall fn))))) + +(defun magit-read-branch (prompt &optional secondary-default) + (magit-completing-read prompt (magit-list-branch-names) + nil t nil 'magit-revision-history + (or (magit-branch-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-branch-or-commit (prompt &optional secondary-default) + (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit))) + (or (magit-completing-read prompt (magit-list-refnames nil t) + nil nil nil 'magit-revision-history + (or (magit-branch-or-commit-at-point) + secondary-default + (magit-get-current-branch))) + (user-error "Nothing selected")))) + +(defun magit-read-range-or-commit (prompt &optional secondary-default) + (magit-read-range + prompt + (or (--when-let (magit-region-values '(commit branch) t) + (deactivate-mark) + (concat (car (last it)) ".." (car it))) + (magit-branch-or-commit-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-range (prompt &optional default) + (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) + (crm-separator "\\.\\.\\.?")) + (magit-completing-read-multiple* + (concat prompt ": ") + (magit-list-refnames) + nil nil nil 'magit-revision-history default nil t))) + +(defun magit-read-remote-branch + (prompt &optional remote default local-branch require-match) + (let ((choice (magit-completing-read + prompt + (-union (and local-branch + (if remote + (concat remote "/" local-branch) + (--map (concat it "/" local-branch) + (magit-list-remotes)))) + (magit-list-remote-branch-names remote t)) + nil require-match nil 'magit-revision-history default))) + (if (or remote (string-match "\\`\\([^/]+\\)/\\(.+\\)" choice)) + choice + (user-error "`%s' doesn't have the form REMOTE/BRANCH" choice)))) + +(defun magit-read-refspec (prompt remote) + (magit-completing-read prompt + (prog2 (message "Determining available refs...") + (magit-remote-list-refs remote) + (message "Determining available refs...done")))) + +(defun magit-read-local-branch (prompt &optional secondary-default) + (magit-completing-read prompt (magit-list-local-branch-names) + nil t nil 'magit-revision-history + (or (magit-local-branch-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-local-branch-or-commit (prompt) + (let ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) + (choices (nconc (magit-list-local-branch-names) + (magit-list-special-refnames))) + (commit (magit-commit-at-point))) + (when commit + (push commit choices)) + (or (magit-completing-read prompt choices + nil nil nil 'magit-revision-history + (or (magit-local-branch-at-point) commit)) + (user-error "Nothing selected")))) + +(defun magit-read-local-branch-or-ref (prompt &optional secondary-default) + (magit-completing-read prompt (nconc (magit-list-local-branch-names) + (magit-list-refs "refs/")) + nil t nil 'magit-revision-history + (or (magit-local-branch-at-point) + secondary-default + (magit-get-current-branch)))) + +(defun magit-read-other-branch + (prompt &optional exclude secondary-default no-require-match) + (let* ((current (magit-get-current-branch)) + (atpoint (magit-branch-at-point)) + (exclude (or exclude current)) + (default (or (and (not (equal atpoint exclude)) atpoint) + (and (not (equal current exclude)) current) + secondary-default + (magit-get-previous-branch)))) + (magit-completing-read prompt (delete exclude (magit-list-branch-names)) + nil (not no-require-match) + nil 'magit-revision-history default))) + +(defun magit-read-other-branch-or-commit + (prompt &optional exclude secondary-default) + (let* ((minibuffer-default-add-function (magit--minibuf-default-add-commit)) + (current (magit-get-current-branch)) + (atpoint (magit-branch-or-commit-at-point)) + (exclude (or exclude current)) + (default (or (and (not (equal atpoint exclude)) + (not (and (not current) + (magit-rev-equal atpoint "HEAD"))) + atpoint) + (and (not (equal current exclude)) current) + secondary-default + (magit-get-previous-branch)))) + (or (magit-completing-read prompt (delete exclude (magit-list-refnames)) + nil nil nil 'magit-revision-history default) + (user-error "Nothing selected")))) + +(defun magit-read-other-local-branch + (prompt &optional exclude secondary-default no-require-match) + (let* ((current (magit-get-current-branch)) + (atpoint (magit-local-branch-at-point)) + (exclude (or exclude current)) + (default (or (and (not (equal atpoint exclude)) atpoint) + (and (not (equal current exclude)) current) + secondary-default + (magit-get-previous-branch)))) + (magit-completing-read prompt + (delete exclude (magit-list-local-branch-names)) + nil (not no-require-match) + nil 'magit-revision-history default))) + +(defun magit-read-branch-prefer-other (prompt) + (let* ((current (magit-get-current-branch)) + (commit (magit-commit-at-point)) + (atrev (and commit (magit-list-branches-pointing-at commit))) + (atpoint (magit--painted-branch-at-point))) + (magit-completing-read prompt (magit-list-branch-names) + nil t nil 'magit-revision-history + (or (magit-section-value-if 'branch) + atpoint + (and (not (cdr atrev)) (car atrev)) + (--first (not (equal it current)) atrev) + (magit-get-previous-branch) + (car atrev))))) + +(defun magit-read-upstream-branch (&optional branch prompt) + "Read the upstream for BRANCH using PROMPT. +If optional BRANCH is nil, then read the upstream for the +current branch, or raise an error if no branch is checked +out. Only existing branches can be selected." + (unless branch + (setq branch (or (magit-get-current-branch) + (error "Need a branch to set its upstream")))) + (let ((branches (delete branch (magit-list-branch-names)))) + (magit-completing-read + (or prompt (format "Change upstream of %s to" branch)) + branches nil t nil 'magit-revision-history + (or (let ((r (car (member (magit-remote-branch-at-point) branches))) + (l (car (member (magit-local-branch-at-point) branches)))) + (if magit-prefer-remote-upstream (or r l) (or l r))) + (and-let* ((main (magit-main-branch))) + (let ((r (car (member (concat "origin/" main) branches))) + (l (car (member main branches)))) + (if magit-prefer-remote-upstream (or r l) (or l r)))) + (car (member (magit-get-previous-branch) branches)))))) + +(defun magit-read-starting-point (prompt &optional branch default) + (or (magit-completing-read + (concat prompt + (and branch + (if (bound-and-true-p ivy-mode) + ;; Ivy-mode strips faces from prompt. + (format " `%s'" branch) + (concat " " (magit--propertize-face + branch 'magit-branch-local)))) + " starting at") + (nconc (list "HEAD") + (magit-list-refnames) + (directory-files (magit-git-dir) nil "_HEAD\\'")) + nil nil nil 'magit-revision-history + (or default (magit--default-starting-point))) + (user-error "Nothing selected"))) + +(defun magit--default-starting-point () + (or (let ((r (magit-remote-branch-at-point)) + (l (magit-local-branch-at-point))) + (if magit-prefer-remote-upstream (or r l) (or l r))) + (magit-commit-at-point) + (magit-stash-at-point) + (magit-get-current-branch))) + +(defun magit-read-tag (prompt &optional require-match) + (magit-completing-read prompt (magit-list-tags) nil + require-match nil 'magit-revision-history + (magit-tag-at-point))) + +(defun magit-read-stash (prompt) + (let* ((atpoint (magit-stash-at-point)) + (default (and atpoint + (concat atpoint (magit-rev-format " %s" atpoint)))) + (choices (mapcar (lambda (c) + (pcase-let ((`(,rev ,msg) (split-string c "\0"))) + (concat (propertize rev 'face 'magit-hash) + " " msg))) + (magit-list-stashes "%gd%x00%s"))) + (choice (magit-completing-read prompt choices + nil t nil nil + default + (car choices)))) + (and choice + (string-match "^\\([^ ]+\\) \\(.+\\)" choice) + (substring-no-properties (match-string 1 choice))))) + +(defun magit-read-remote (prompt &optional default use-only) + (let ((remotes (magit-list-remotes))) + (if (and use-only (length= remotes 1)) + (car remotes) + (magit-completing-read prompt remotes + nil t nil nil + (or default + (magit-remote-at-point) + (magit-get-remote)))))) + +(defun magit-read-remote-or-url (prompt &optional default) + (magit-completing-read prompt + (nconc (magit-list-remotes) + (list "https://" "git://" "git@")) + nil nil nil nil + (or default + (magit-remote-at-point) + (magit-get-remote)))) + +(defun magit-read-module-path (prompt &optional predicate) + (magit-completing-read prompt (magit-list-module-paths) + predicate t nil nil + (magit-module-at-point predicate))) + +(defun magit-module-confirm (verb &optional predicate) + (let (modules) + (if current-prefix-arg + (progn + (setq modules (magit-list-module-paths)) + (when predicate + (setq modules (-filter predicate modules))) + (unless modules + (if predicate + (user-error "No modules satisfying %s available" predicate) + (user-error "No modules available")))) + (setq modules (magit-region-values 'magit-module-section)) + (when modules + (when predicate + (setq modules (-filter predicate modules))) + (unless modules + (user-error "No modules satisfying %s selected" predicate)))) + (if (length> modules 1) + (magit-confirm t nil (format "%s %%i modules" verb) nil modules) + (list (magit-read-module-path (format "%s module" verb) predicate))))) + +;;; _ +(provide 'magit-git) +;;; magit-git.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-gitignore.el b/code/elpa/magit-20220821.1819/magit-gitignore.el new file mode 100644 index 0000000..d53f149 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-gitignore.el @@ -0,0 +1,195 @@ +;;; magit-gitignore.el --- Intentionally untracked files -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements gitignore commands. + +;;; Code: + +(require 'magit) + +;;; Transient + +;;;###autoload (autoload 'magit-gitignore "magit-gitignore" nil t) +(transient-define-prefix magit-gitignore () + "Instruct Git to ignore a file or pattern." + :man-page "gitignore" + ["Gitignore" + ("t" "shared at toplevel (.gitignore)" + magit-gitignore-in-topdir) + ("s" "shared in subdirectory (path/to/.gitignore)" + magit-gitignore-in-subdir) + ("p" "privately (.git/info/exclude)" + magit-gitignore-in-gitdir) + ("g" magit-gitignore-on-system + :if (lambda () (magit-get "core.excludesfile")) + :description (lambda () + (format "privately for all repositories (%s)" + (magit-get "core.excludesfile"))))] + ["Skip worktree" + (7 "w" "do skip worktree" magit-skip-worktree) + (7 "W" "do not skip worktree" magit-no-skip-worktree)] + ["Assume unchanged" + (7 "u" "do assume unchanged" magit-assume-unchanged) + (7 "U" "do not assume unchanged" magit-no-assume-unchanged)]) + +;;; Gitignore Commands + +;;;###autoload +(defun magit-gitignore-in-topdir (rule) + "Add the Git ignore RULE to the top-level \".gitignore\" file. +Since this file is tracked, it is shared with other clones of the +repository. Also stage the file." + (interactive (list (magit-gitignore-read-pattern))) + (magit-with-toplevel + (magit--gitignore rule ".gitignore") + (magit-run-git "add" ".gitignore"))) + +;;;###autoload +(defun magit-gitignore-in-subdir (rule directory) + "Add the Git ignore RULE to a \".gitignore\" file in DIRECTORY. +Prompt the user for a directory and add the rule to the +\".gitignore\" file in that directory. Since such files are +tracked, they are shared with other clones of the repository. +Also stage the file." + (interactive (list (magit-gitignore-read-pattern) + (read-directory-name "Limit rule to files in: "))) + (magit-with-toplevel + (let ((file (expand-file-name ".gitignore" directory))) + (magit--gitignore rule file) + (magit-run-git "add" (magit-convert-filename-for-git file))))) + +;;;###autoload +(defun magit-gitignore-in-gitdir (rule) + "Add the Git ignore RULE to \"$GIT_DIR/info/exclude\". +Rules in that file only affects this clone of the repository." + (interactive (list (magit-gitignore-read-pattern))) + (magit--gitignore rule (magit-git-dir "info/exclude")) + (magit-refresh)) + +;;;###autoload +(defun magit-gitignore-on-system (rule) + "Add the Git ignore RULE to the file specified by `core.excludesFile'. +Rules that are defined in that file affect all local repositories." + (interactive (list (magit-gitignore-read-pattern))) + (magit--gitignore rule + (or (magit-get "core.excludesFile") + (error "Variable `core.excludesFile' isn't set"))) + (magit-refresh)) + +(defun magit--gitignore (rule file) + (when-let ((directory (file-name-directory file))) + (make-directory directory t)) + (with-temp-buffer + (when (file-exists-p file) + (insert-file-contents file)) + (goto-char (point-max)) + (unless (bolp) + (insert "\n")) + (insert (replace-regexp-in-string "\\(\\\\*\\)" "\\1\\1" rule)) + (insert "\n") + (write-region nil nil file))) + +(defun magit-gitignore-read-pattern () + (let* ((default (magit-current-file)) + (base (car magit-buffer-diff-files)) + (base (and base (file-directory-p base) base)) + (choices + (delete-dups + (--mapcat + (cons (concat "/" it) + (and-let* ((ext (file-name-extension it))) + (list (concat "/" (file-name-directory it) "*." ext) + (concat "*." ext)))) + (sort (nconc + (magit-untracked-files nil base) + ;; The untracked section of the status buffer lists + ;; directories containing only untracked files. + ;; Add those as candidates. + (-filter #'directory-name-p + (magit-list-files + "--other" "--exclude-standard" "--directory" + "--no-empty-directory" "--" base))) + #'string-lessp))))) + (when default + (setq default (concat "/" default)) + (unless (member default choices) + (setq default (concat "*." (file-name-extension default))) + (unless (member default choices) + (setq default nil)))) + (magit-completing-read "File or pattern to ignore" + choices nil nil nil nil default))) + +;;; Skip Worktree Commands + +;;;###autoload +(defun magit-skip-worktree (file) + "Call \"git update-index --skip-worktree -- FILE\"." + (interactive + (list (magit-read-file-choice "Skip worktree for" + (magit-with-toplevel + (cl-set-difference + (magit-list-files) + (magit-skip-worktree-files) + :test #'equal))))) + (magit-with-toplevel + (magit-run-git "update-index" "--skip-worktree" "--" file))) + +;;;###autoload +(defun magit-no-skip-worktree (file) + "Call \"git update-index --no-skip-worktree -- FILE\"." + (interactive + (list (magit-read-file-choice "Do not skip worktree for" + (magit-with-toplevel + (magit-skip-worktree-files))))) + (magit-with-toplevel + (magit-run-git "update-index" "--no-skip-worktree" "--" file))) + +;;; Assume Unchanged Commands + +;;;###autoload +(defun magit-assume-unchanged (file) + "Call \"git update-index --assume-unchanged -- FILE\"." + (interactive + (list (magit-read-file-choice "Assume file to be unchanged" + (magit-with-toplevel + (cl-set-difference + (magit-list-files) + (magit-assume-unchanged-files) + :test #'equal))))) + (magit-with-toplevel + (magit-run-git "update-index" "--assume-unchanged" "--" file))) + +;;;###autoload +(defun magit-no-assume-unchanged (file) + "Call \"git update-index --no-assume-unchanged -- FILE\"." + (interactive + (list (magit-read-file-choice "Do not assume file to be unchanged" + (magit-with-toplevel + (magit-assume-unchanged-files))))) + (magit-with-toplevel + (magit-run-git "update-index" "--no-assume-unchanged" "--" file))) + +;;; _ +(provide 'magit-gitignore) +;;; magit-gitignore.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-log.el b/code/elpa/magit-20220821.1819/magit-log.el new file mode 100644 index 0000000..eea1602 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-log.el @@ -0,0 +1,1950 @@ +;;; magit-log.el --- Inspect Git history -*- lexical-binding:t; coding:utf-8 -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for looking at Git logs, including +;; special logs like cherry-logs, as well as for selecting a commit +;; from a log. + +;;; Code: + +(require 'magit-core) +(require 'magit-diff) + +(declare-function magit-blob-visit "magit-files" (blob-or-file)) +(declare-function magit-cherry-apply "magit-sequence" (commit &optional args)) +(declare-function magit-insert-head-branch-header "magit-status" + (&optional branch)) +(declare-function magit-insert-upstream-branch-header "magit-status" + (&optional branch pull keyword)) +(declare-function magit-read-file-from-rev "magit-files" + (rev prompt &optional default)) +(declare-function magit-rebase--get-state-lines "magit-sequence" + (file)) +(declare-function magit-show-commit "magit-diff" + (arg1 &optional arg2 arg3 arg4)) +(declare-function magit-reflog-format-subject "magit-reflog" (subject)) +(defvar magit-refs-focus-column-width) +(defvar magit-refs-margin) +(defvar magit-refs-show-commit-count) +(defvar magit-buffer-margin) +(defvar magit-status-margin) +(defvar magit-status-sections-hook) + +(require 'ansi-color) +(require 'crm) +(require 'which-func) + +;;; Options +;;;; Log Mode + +(defgroup magit-log nil + "Inspect and manipulate Git history." + :link '(info-link "(magit)Logging") + :group 'magit-commands + :group 'magit-modes) + +(defcustom magit-log-mode-hook nil + "Hook run after entering Magit-Log mode." + :group 'magit-log + :type 'hook) + +(defcustom magit-log-remove-graph-args '("--follow" "--grep" "-G" "-S" "-L") + "The log arguments that cause the `--graph' argument to be dropped." + :package-version '(magit . "2.3.0") + :group 'magit-log + :type '(repeat (string :tag "Argument")) + :options '("--follow" "--grep" "-G" "-S" "-L")) + +(defcustom magit-log-revision-headers-format "\ +%+b%+N +Author: %aN <%aE> +Committer: %cN <%cE>" + "Additional format string used with the `++header' argument." + :package-version '(magit . "3.2.0") + :group 'magit-log + :type 'string) + +(defcustom magit-log-auto-more nil + "Insert more log entries automatically when moving past the last entry. +Only considered when moving past the last entry with +`magit-goto-*-section' commands." + :group 'magit-log + :type 'boolean) + +(defcustom magit-log-margin '(t age magit-log-margin-width t 18) + "Format of the margin in `magit-log-mode' buffers. + +The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). + +If INIT is non-nil, then the margin is shown initially. +STYLE controls how to format the author or committer date. + It can be one of `age' (to show the age of the commit), + `age-abbreviated' (to abbreviate the time unit to a character), + or a string (suitable for `format-time-string') to show the + actual date. Option `magit-log-margin-show-committer-date' + controls which date is being displayed. +WIDTH controls the width of the margin. This exists for forward + compatibility and currently the value should not be changed. +AUTHOR controls whether the name of the author is also shown by + default. +AUTHOR-WIDTH has to be an integer. When the name of the author + is shown, then this specifies how much space is used to do so." + :package-version '(magit . "2.9.0") + :group 'magit-log + :group 'magit-margin + :type magit-log-margin--custom-type + :initialize #'magit-custom-initialize-reset + :set (apply-partially #'magit-margin-set-variable 'magit-log-mode)) + +(defcustom magit-log-margin-show-committer-date nil + "Whether to show the committer date in the margin. + +This option only controls whether the committer date is displayed +instead of the author date. Whether some date is displayed in +the margin and whether the margin is displayed at all is +controlled by other options." + :package-version '(magit . "3.0.0") + :group 'magit-log + :group 'magit-margin + :type 'boolean) + +(defcustom magit-log-show-refname-after-summary nil + "Whether to show refnames after commit summaries. +This is useful if you use really long branch names." + :package-version '(magit . "2.2.0") + :group 'magit-log + :type 'boolean) + +(defcustom magit-log-highlight-keywords t + "Whether to highlight bracketed keywords in commit summaries." + :package-version '(magit . "2.12.0") + :group 'magit-log + :type 'boolean) + +(defcustom magit-log-header-line-function #'magit-log-header-line-sentence + "Function used to generate text shown in header line of log buffers." + :package-version '(magit . "2.12.0") + :group 'magit-log + :type '(choice (function-item magit-log-header-line-arguments) + (function-item magit-log-header-line-sentence) + function)) + +(defcustom magit-log-trace-definition-function #'magit-which-function + "Function used to determine the function at point. +This is used by the command `magit-log-trace-definition'. +You should prefer `magit-which-function' over `which-function' +because the latter may make use of Imenu's outdated cache." + :package-version '(magit . "3.0.0") + :group 'magit-log + :type '(choice (function-item magit-which-function) + (function-item which-function) + (function-item add-log-current-defun) + function)) + +(defface magit-log-graph + '((((class color) (background light)) :foreground "grey30") + (((class color) (background dark)) :foreground "grey80")) + "Face for the graph part of the log output." + :group 'magit-faces) + +(defface magit-log-author + '((((class color) (background light)) + :foreground "firebrick" + :slant normal + :weight normal) + (((class color) (background dark)) + :foreground "tomato" + :slant normal + :weight normal)) + "Face for the author part of the log output." + :group 'magit-faces) + +(defface magit-log-date + '((((class color) (background light)) + :foreground "grey30" + :slant normal + :weight normal) + (((class color) (background dark)) + :foreground "grey80" + :slant normal + :weight normal)) + "Face for the date part of the log output." + :group 'magit-faces) + +(defface magit-header-line-log-select + '((t :inherit bold)) + "Face for the `header-line' in `magit-log-select-mode'." + :group 'magit-faces) + +;;;; File Log + +(defcustom magit-log-buffer-file-locked t + "Whether `magit-log-buffer-file-quick' uses a dedicated buffer." + :package-version '(magit . "2.7.0") + :group 'magit-commands + :group 'magit-log + :type 'boolean) + +;;;; Select Mode + +(defcustom magit-log-select-show-usage 'both + "Whether to show usage information when selecting a commit from a log. +The message can be shown in the `echo-area' or the `header-line', or in +`both' places. If the value isn't one of these symbols, then it should +be nil, in which case no usage information is shown." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type '(choice (const :tag "in echo-area" echo-area) + (const :tag "in header-line" header-line) + (const :tag "in both places" both) + (const :tag "nowhere"))) + +(defcustom magit-log-select-margin + (list (nth 0 magit-log-margin) + (nth 1 magit-log-margin) + 'magit-log-margin-width t + (nth 4 magit-log-margin)) + "Format of the margin in `magit-log-select-mode' buffers. + +The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). + +If INIT is non-nil, then the margin is shown initially. +STYLE controls how to format the author or committer date. + It can be one of `age' (to show the age of the commit), + `age-abbreviated' (to abbreviate the time unit to a character), + or a string (suitable for `format-time-string') to show the + actual date. Option `magit-log-margin-show-committer-date' + controls which date is being displayed. +WIDTH controls the width of the margin. This exists for forward + compatibility and currently the value should not be changed. +AUTHOR controls whether the name of the author is also shown by + default. +AUTHOR-WIDTH has to be an integer. When the name of the author + is shown, then this specifies how much space is used to do so." + :package-version '(magit . "2.9.0") + :group 'magit-log + :group 'magit-margin + :type magit-log-margin--custom-type + :initialize #'magit-custom-initialize-reset + :set-after '(magit-log-margin) + :set (apply-partially #'magit-margin-set-variable 'magit-log-select-mode)) + +;;;; Cherry Mode + +(defcustom magit-cherry-sections-hook + '(magit-insert-cherry-headers + magit-insert-cherry-commits) + "Hook run to insert sections into the cherry buffer." + :package-version '(magit . "2.1.0") + :group 'magit-log + :type 'hook) + +(defcustom magit-cherry-margin + (list (nth 0 magit-log-margin) + (nth 1 magit-log-margin) + 'magit-log-margin-width t + (nth 4 magit-log-margin)) + "Format of the margin in `magit-cherry-mode' buffers. + +The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). + +If INIT is non-nil, then the margin is shown initially. +STYLE controls how to format the author or committer date. + It can be one of `age' (to show the age of the commit), + `age-abbreviated' (to abbreviate the time unit to a character), + or a string (suitable for `format-time-string') to show the + actual date. Option `magit-log-margin-show-committer-date' + controls which date is being displayed. +WIDTH controls the width of the margin. This exists for forward + compatibility and currently the value should not be changed. +AUTHOR controls whether the name of the author is also shown by + default. +AUTHOR-WIDTH has to be an integer. When the name of the author + is shown, then this specifies how much space is used to do so." + :package-version '(magit . "2.9.0") + :group 'magit-log + :group 'magit-margin + :type magit-log-margin--custom-type + :initialize #'magit-custom-initialize-reset + :set-after '(magit-log-margin) + :set (apply-partially #'magit-margin-set-variable 'magit-cherry-mode)) + +;;;; Log Sections + +(defcustom magit-log-section-commit-count 10 + "How many recent commits to show in certain log sections. +How many recent commits `magit-insert-recent-commits' and +`magit-insert-unpulled-from-upstream-or-recent' (provided +the upstream isn't ahead of the current branch) show." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'number) + +(defcustom magit-log-merged-commit-count 20 + "How many surrounding commits to show for `magit-log-merged'. +`magit-log-merged' will shows approximately half of this number +commits before and half after." + :package-version '(magit . "3.3.0") + :group 'magit-log + :type 'integer) + +;;; Arguments +;;;; Prefix Classes + +(defclass magit-log-prefix (transient-prefix) + ((history-key :initform 'magit-log) + (major-mode :initform 'magit-log-mode))) + +(defclass magit-log-refresh-prefix (magit-log-prefix) + ((history-key :initform 'magit-log) + (major-mode :initform nil))) + +;;;; Prefix Methods + +(cl-defmethod transient-init-value ((obj magit-log-prefix)) + (pcase-let ((`(,args ,files) + (magit-log--get-value 'magit-log-mode + magit-prefix-use-buffer-arguments))) + (unless (eq transient-current-command 'magit-dispatch) + (when-let ((file (magit-file-relative-name))) + (setq files (list file)))) + (oset obj value (if files `(("--" ,@files) ,args) args)))) + +(cl-defmethod transient-init-value ((obj magit-log-refresh-prefix)) + (oset obj value (if magit-buffer-log-files + `(("--" ,@magit-buffer-log-files) + ,magit-buffer-log-args) + magit-buffer-log-args))) + +(cl-defmethod transient-set-value ((obj magit-log-prefix)) + (magit-log--set-value obj)) + +(cl-defmethod transient-save-value ((obj magit-log-prefix)) + (magit-log--set-value obj 'save)) + +;;;; Argument Access + +(defun magit-log-arguments (&optional mode) + "Return the current log arguments." + (if (memq transient-current-command '(magit-log magit-log-refresh)) + (pcase-let ((`(,args ,alist) + (-separate #'atom (transient-get-value)))) + (list args (cdr (assoc "--" alist)))) + (magit-log--get-value (or mode 'magit-log-mode)))) + +(defun magit-log--get-value (mode &optional use-buffer-args) + (unless use-buffer-args + (setq use-buffer-args magit-direct-use-buffer-arguments)) + (let (args files) + (cond + ((and (memq use-buffer-args '(always selected current)) + (eq major-mode mode)) + (setq args magit-buffer-log-args) + (setq files magit-buffer-log-files)) + ((and (memq use-buffer-args '(always selected)) + (when-let ((buffer (magit-get-mode-buffer + mode nil + (eq use-buffer-args 'selected)))) + (setq args (buffer-local-value 'magit-buffer-log-args buffer)) + (setq files (buffer-local-value 'magit-buffer-log-files buffer)) + t))) + ((plist-member (symbol-plist mode) 'magit-log-current-arguments) + (setq args (get mode 'magit-log-current-arguments))) + ((when-let ((elt (assq (intern (format "magit-log:%s" mode)) + transient-values))) + (setq args (cdr elt)) + t)) + (t + (setq args (get mode 'magit-log-default-arguments)))) + (list args files))) + +(defun magit-log--set-value (obj &optional save) + (pcase-let* ((obj (oref obj prototype)) + (mode (or (oref obj major-mode) major-mode)) + (key (intern (format "magit-log:%s" mode))) + (`(,args ,alist) + (-separate #'atom (transient-get-value))) + (files (cdr (assoc "--" alist)))) + (put mode 'magit-log-current-arguments args) + (when save + (setf (alist-get key transient-values) args) + (transient-save-values)) + (transient--history-push obj) + (setq magit-buffer-log-args args) + (unless (derived-mode-p 'magit-log-select-mode) + (setq magit-buffer-log-files files)) + (magit-refresh))) + +;;; Commands +;;;; Prefix Commands + +;;;###autoload (autoload 'magit-log "magit-log" nil t) +(transient-define-prefix magit-log () + "Show a commit or reference log." + :man-page "git-log" + :class 'magit-log-prefix + ;; The grouping in git-log(1) appears to be guided by implementation + ;; details, so our logical grouping only follows it to an extend. + ;; Arguments that are "misplaced" here: + ;; 1. From "Commit Formatting". + ;; 2. From "Common Diff Options". + ;; 3. From unnamed first group. + ;; 4. Implemented by Magit. + ["Commit limiting" + (magit-log:-n) + (magit:--author) + (7 magit-log:--since) + (7 magit-log:--until) + (magit-log:--grep) + (7 "-i" "Search case-insensitive" ("-i" "--regexp-ignore-case")) + (7 "-I" "Invert search pattern" "--invert-grep") + (magit-log:-G) ;2 + (magit-log:-S) ;2 + (magit-log:-L) ;2 + (7 "=m" "Omit merges" "--no-merges") + (7 "=p" "First parent" "--first-parent")] + ["History simplification" + ( "-D" "Simplify by decoration" "--simplify-by-decoration") + (magit:--) + ( "-f" "Follow renames when showing single-file log" "--follow") ;3 + (6 "/s" "Only commits changing given paths" "--sparse") + (7 "/d" "Only selected commits plus meaningful history" "--dense") + (7 "/a" "Only commits existing directly on ancestry path" "--ancestry-path") + (6 "/f" "Do not prune history" "--full-history") + (7 "/m" "Prune some history" "--simplify-merges")] + ["Commit ordering" + (magit-log:--*-order) + ("-r" "Reverse order" "--reverse")] + ["Formatting" + ("-g" "Show graph" "--graph") ;1 + ("-c" "Show graph in color" "--color") ;2 + ("-d" "Show refnames" "--decorate") ;3 + ("=S" "Show signatures" "--show-signature") ;1 + ("-h" "Show header" "++header") ;4 + ("-p" "Show diffs" ("-p" "--patch")) ;2 + ("-s" "Show diffstats" "--stat")] ;2 + [["Log" + ("l" "current" magit-log-current) + ("h" "HEAD" magit-log-head) + ("u" "related" magit-log-related) + ("o" "other" magit-log-other)] + ["" + ("L" "local branches" magit-log-branches) + ("b" "all branches" magit-log-all-branches) + ("a" "all references" magit-log-all) + (7 "B" "matching branches" magit-log-matching-branches) + (7 "T" "matching tags" magit-log-matching-tags) + (7 "m" "merged" magit-log-merged)] + ["Reflog" + ("r" "current" magit-reflog-current) + ("H" "HEAD" magit-reflog-head) + ("O" "other" magit-reflog-other)] + [:if (lambda () + (and (fboundp 'magit--any-wip-mode-enabled-p) + (magit--any-wip-mode-enabled-p))) + :description "Wiplog" + ("i" "index" magit-wip-log-index) + ("w" "worktree" magit-wip-log-worktree)] + ["Other" + (5 "s" "shortlog" magit-shortlog)]]) + +;;;###autoload (autoload 'magit-log-refresh "magit-log" nil t) +(transient-define-prefix magit-log-refresh () + "Change the arguments used for the log(s) in the current buffer." + :man-page "git-log" + :class 'magit-log-refresh-prefix + [:if-mode magit-log-mode + :class transient-subgroups + ["Commit limiting" + (magit-log:-n) + (magit:--author) + (magit-log:--grep) + (7 "-i" "Search case-insensitive" ("-i" "--regexp-ignore-case")) + (7 "-I" "Invert search pattern" "--invert-grep") + (magit-log:-G) + (magit-log:-S) + (magit-log:-L)] + ["History simplification" + ( "-D" "Simplify by decoration" "--simplify-by-decoration") + (magit:--) + ( "-f" "Follow renames when showing single-file log" "--follow") ;3 + (6 "/s" "Only commits changing given paths" "--sparse") + (7 "/d" "Only selected commits plus meaningful history" "--dense") + (7 "/a" "Only commits existing directly on ancestry path" "--ancestry-path") + (6 "/f" "Do not prune history" "--full-history") + (7 "/m" "Prune some history" "--simplify-merges")] + ["Commit ordering" + (magit-log:--*-order) + ("-r" "Reverse order" "--reverse")] + ["Formatting" + ("-g" "Show graph" "--graph") + ("-c" "Show graph in color" "--color") + ("-d" "Show refnames" "--decorate") + ("=S" "Show signatures" "--show-signature") + ("-h" "Show header" "++header") + ("-p" "Show diffs" ("-p" "--patch")) + ("-s" "Show diffstats" "--stat")]] + [:if-not-mode magit-log-mode + :description "Arguments" + (magit-log:-n) + (magit-log:--*-order) + ("-g" "Show graph" "--graph") + ("-c" "Show graph in color" "--color") + ("-d" "Show refnames" "--decorate")] + [["Refresh" + ("g" "buffer" magit-log-refresh) + ("s" "buffer and set defaults" transient-set :transient nil) + ("w" "buffer and save defaults" transient-save :transient nil)] + ["Margin" + ("L" "toggle visibility" magit-toggle-margin :transient t) + ("l" "cycle style" magit-cycle-margin-style :transient t) + ("d" "toggle details" magit-toggle-margin-details) + ("x" "toggle shortstat" magit-toggle-log-margin-style)] + [:if-mode magit-log-mode + :description "Toggle" + ("b" "buffer lock" magit-toggle-buffer-lock)]] + (interactive) + (cond + ((not (eq transient-current-command 'magit-log-refresh)) + (pcase major-mode + ('magit-reflog-mode + (user-error "Cannot change log arguments in reflog buffers")) + ('magit-cherry-mode + (user-error "Cannot change log arguments in cherry buffers"))) + (transient-setup 'magit-log-refresh)) + (t + (pcase-let ((`(,args ,files) (magit-log-arguments))) + (setq magit-buffer-log-args args) + (unless (derived-mode-p 'magit-log-select-mode) + (setq magit-buffer-log-files files))) + (magit-refresh)))) + +;;;; Infix Commands + +(transient-define-argument magit-log:-n () + :description "Limit number of commits" + :class 'transient-option + ;; For historic reasons (and because it easy to guess what "-n" + ;; stands for) this is the only argument where we do not use the + ;; long argument ("--max-count"). + :shortarg "-n" + :argument "-n" + :reader #'transient-read-number-N+) + +(transient-define-argument magit:--author () + :description "Limit to author" + :class 'transient-option + :key "-A" + :argument "--author=" + :reader #'magit-transient-read-person) + +(transient-define-argument magit-log:--since () + :description "Limit to commits since" + :class 'transient-option + :key "=s" + :argument "--since=" + :reader #'transient-read-date) + +(transient-define-argument magit-log:--until () + :description "Limit to commits until" + :class 'transient-option + :key "=u" + :argument "--until=" + :reader #'transient-read-date) + +(transient-define-argument magit-log:--*-order () + :description "Order commits by" + :class 'transient-switches + :key "-o" + :argument-format "--%s-order" + :argument-regexp "\\(--\\(topo\\|author-date\\|date\\)-order\\)" + :choices '("topo" "author-date" "date")) + +(transient-define-argument magit-log:--grep () + :description "Search messages" + :class 'transient-option + :key "-F" + :argument "--grep=") + +(transient-define-argument magit-log:-G () + :description "Search changes" + :class 'transient-option + :argument "-G") + +(transient-define-argument magit-log:-S () + :description "Search occurrences" + :class 'transient-option + :argument "-S") + +(transient-define-argument magit-log:-L () + :description "Trace line evolution" + :class 'transient-option + :argument "-L" + :reader #'magit-read-file-trace) + +(defun magit-read-file-trace (&rest _ignored) + (let ((file (magit-read-file-from-rev "HEAD" "File")) + (trace (magit-read-string "Trace"))) + (concat trace ":" file))) + +;;;; Setup Commands + +(defvar magit-log-read-revs-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map crm-local-completion-map) + (define-key map "\s" #'self-insert-command) + map)) + +(defun magit-log-read-revs (&optional use-current) + (or (and use-current (and-let* ((buf (magit-get-current-branch))) (list buf))) + (let ((crm-separator "\\(\\.\\.\\.?\\|[, ]\\)") + (crm-local-completion-map magit-log-read-revs-map)) + (split-string (magit-completing-read-multiple* + "Log rev,s: " + (magit-list-refnames nil t) + nil nil nil 'magit-revision-history + (or (magit-branch-or-commit-at-point) + (unless use-current + (magit-get-previous-branch))) + nil t) + "[, ]" t)))) + +(defun magit-log-read-pattern (option) + "Read a string from the user to pass as parameter to OPTION." + (magit-read-string (format "Type a pattern to pass to %s" option))) + +;;;###autoload +(defun magit-log-current (revs &optional args files) + "Show log for the current branch. +When `HEAD' is detached or with a prefix argument show log for +one or more revs read from the minibuffer." + (interactive (cons (magit-log-read-revs t) + (magit-log-arguments))) + (magit-log-setup-buffer revs args files)) + +;;;###autoload +(defun magit-log-head (&optional args files) + "Show log for `HEAD'." + (interactive (magit-log-arguments)) + (magit-log-setup-buffer (list "HEAD") args files)) + +;;;###autoload +(defun magit-log-related (revs &optional args files) + "Show log for the current branch, its upstream and its push target. +When the upstream is a local branch, then also show its own +upstream. When `HEAD' is detached, then show log for that, the +previously checked out branch and its upstream and push-target." + (interactive + (cons (let ((current (magit-get-current-branch)) + head rebase target upstream upup) + (unless current + (setq rebase (magit-rebase--get-state-lines "head-name")) + (cond (rebase + (setq rebase (magit-ref-abbrev rebase)) + (setq current rebase) + (setq head "HEAD")) + (t (setq current (magit-get-previous-branch))))) + (cond (current + (setq current + (magit--propertize-face current'magit-branch-local)) + (setq target (magit-get-push-branch current t)) + (setq upstream (magit-get-upstream-branch current)) + (when upstream + (setq upup (and (magit-local-branch-p upstream) + (magit-get-upstream-branch upstream))))) + (t (setq head "HEAD"))) + (delq nil (list current head target upstream upup))) + (magit-log-arguments))) + (magit-log-setup-buffer revs args files)) + +;;;###autoload +(defun magit-log-other (revs &optional args files) + "Show log for one or more revs read from the minibuffer. +The user can input any revision or revisions separated by a +space, or even ranges, but only branches and tags, and a +representation of the commit at point, are available as +completion candidates." + (interactive (cons (magit-log-read-revs) + (magit-log-arguments))) + (magit-log-setup-buffer revs args files)) + +;;;###autoload +(defun magit-log-branches (&optional args files) + "Show log for all local branches and `HEAD'." + (interactive (magit-log-arguments)) + (magit-log-setup-buffer (if (magit-get-current-branch) + (list "--branches") + (list "HEAD" "--branches")) + args files)) + +;;;###autoload +(defun magit-log-matching-branches (pattern &optional args files) + "Show log for all branches matching PATTERN and `HEAD'." + (interactive (cons (magit-log-read-pattern "--branches") (magit-log-arguments))) + (magit-log-setup-buffer + (list "HEAD" (format "--branches=%s" pattern)) + args files)) + +;;;###autoload +(defun magit-log-matching-tags (pattern &optional args files) + "Show log for all tags matching PATTERN and `HEAD'." + (interactive (cons (magit-log-read-pattern "--tags") (magit-log-arguments))) + (magit-log-setup-buffer + (list "HEAD" (format "--tags=%s" pattern)) + args files)) + +;;;###autoload +(defun magit-log-all-branches (&optional args files) + "Show log for all local and remote branches and `HEAD'." + (interactive (magit-log-arguments)) + (magit-log-setup-buffer (if (magit-get-current-branch) + (list "--branches" "--remotes") + (list "HEAD" "--branches" "--remotes")) + args files)) + +;;;###autoload +(defun magit-log-all (&optional args files) + "Show log for all references and `HEAD'." + (interactive (magit-log-arguments)) + (magit-log-setup-buffer (if (magit-get-current-branch) + (list "--all") + (list "HEAD" "--all")) + args files)) + +;;;###autoload +(defun magit-log-buffer-file (&optional follow beg end) + "Show log for the blob or file visited in the current buffer. +With a prefix argument or when `--follow' is an active log +argument, then follow renames. When the region is active, +restrict the log to the lines that the region touches." + (interactive + (cons current-prefix-arg + (and (region-active-p) + (magit-file-relative-name) + (save-restriction + (widen) + (list (line-number-at-pos (region-beginning)) + (line-number-at-pos + (let ((end (region-end))) + (if (char-after end) + end + ;; Ensure that we don't get the line number + ;; of a trailing newline. + (1- end))))))))) + (require 'magit) + (if-let ((file (magit-file-relative-name))) + (magit-log-setup-buffer + (list (or magit-buffer-refname + (magit-get-current-branch) + "HEAD")) + (let ((args (car (magit-log-arguments)))) + (when (and follow (not (member "--follow" args))) + (push "--follow" args)) + (when (and (file-regular-p + (expand-file-name file (magit-toplevel))) + beg end) + (setq args (cons (format "-L%s,%s:%s" beg end file) + (cl-delete "-L" args :test + #'string-prefix-p))) + (setq file nil)) + args) + (and file (list file)) + magit-log-buffer-file-locked) + (user-error "Buffer isn't visiting a file"))) + +;;;###autoload +(defun magit-log-trace-definition (file fn rev) + "Show log for the definition at point." + (interactive (list (or (magit-file-relative-name) + (user-error "Buffer isn't visiting a file")) + (or (funcall magit-log-trace-definition-function) + (user-error "No function at point found")) + (or magit-buffer-refname + (magit-get-current-branch) + "HEAD"))) + (require 'magit) + (magit-log-setup-buffer + (list rev) + (cons (format "-L:%s%s:%s" + (string-replace ":" "\\:" (regexp-quote fn)) + (if (derived-mode-p 'lisp-mode 'emacs-lisp-mode) + ;; Git doesn't treat "-" the same way as + ;; "_", leading to false-positives such as + ;; "foo-suffix" being considered a match + ;; for "foo". Wing it. + "\\( \\|$\\)" + ;; We could use "\\b" here, but since Git + ;; already does something equivalent, that + ;; isn't necessary. + "") + file) + (cl-delete "-L" (car (magit-log-arguments)) + :test #'string-prefix-p)) + nil magit-log-buffer-file-locked)) + +(defun magit-diff-trace-definition () + "Show log for the definition at point in a diff." + (interactive) + (pcase-let ((`(,buf ,pos) (magit-diff-visit-file--noselect))) + (magit--with-temp-position buf pos + (call-interactively #'magit-log-trace-definition)))) + +;;;###autoload +(defun magit-log-merged (commit branch &optional args files) + "Show log for the merge of COMMIT into BRANCH. + +More precisely, find merge commit M that brought COMMIT into +BRANCH, and show the log of the range \"M^1..M\". If COMMIT is +directly on BRANCH, then show approximately +`magit-log-merged-commit-count' surrounding commits instead. + +This command requires git-when-merged, which is available from +https://github.com/mhagger/git-when-merged." + (interactive + (append (let ((commit (magit-read-branch-or-commit "Log merge of commit"))) + (list commit + (magit-read-other-branch "Merged into" commit))) + (magit-log-arguments))) + (unless (compat-executable-find "git-when-merged" t) + (user-error "This command requires git-when-merged (%s)" + "https://github.com/mhagger/git-when-merged")) + (let (exit m) + (with-temp-buffer + (save-excursion + (setq exit (magit-process-git t "when-merged" "-c" + (magit-abbrev-arg) + commit branch))) + (setq m (buffer-substring-no-properties (point) (line-end-position)))) + (if (zerop exit) + (magit-log-setup-buffer (list (format "%s^1..%s" m m)) + args files nil commit) + ;; Output: "". + ;; This is not the same as `string-trim'. + (setq m (string-trim-left (substring m (string-match " " m)))) + (if (equal m "Commit is directly on this branch.") + (let* ((from (format "%s~%d" commit + (/ magit-log-merged-commit-count 2))) + (to (- (car (magit-rev-diff-count branch commit)) + (/ magit-log-merged-commit-count 2))) + (to (if (<= to 0) + branch + (format "%s~%s" branch to)))) + (unless (magit-rev-verify-commit from) + (setq from (magit-git-string "rev-list" "--max-parents=0" + commit))) + (magit-log-setup-buffer (list (concat from ".." to)) + (cons "--first-parent" args) + files nil commit)) + (user-error "Could not find when %s was merged into %s: %s" + commit branch m))))) + +;;;; Limit Commands + +(defun magit-log-toggle-commit-limit () + "Toggle the number of commits the current log buffer is limited to. +If the number of commits is currently limited, then remove that +limit. Otherwise set it to 256." + (interactive) + (magit-log-set-commit-limit (lambda (&rest _) nil))) + +(defun magit-log-double-commit-limit () + "Double the number of commits the current log buffer is limited to." + (interactive) + (magit-log-set-commit-limit '*)) + +(defun magit-log-half-commit-limit () + "Half the number of commits the current log buffer is limited to." + (interactive) + (magit-log-set-commit-limit '/)) + +(defun magit-log-set-commit-limit (fn) + (let* ((val magit-buffer-log-args) + (arg (--first (string-match "^-n\\([0-9]+\\)?$" it) val)) + (num (and arg (string-to-number (match-string 1 arg)))) + (num (if num (funcall fn num 2) 256))) + (setq val (delete arg val)) + (setq magit-buffer-log-args + (if (and num (> num 0)) + (cons (format "-n%i" num) val) + val))) + (magit-refresh)) + +(defun magit-log-get-commit-limit () + (and-let* ((str (--first (string-match "^-n\\([0-9]+\\)?$" it) + magit-buffer-log-args))) + (string-to-number (match-string 1 str)))) + +;;;; Mode Commands + +(defun magit-log-bury-buffer (&optional arg) + "Bury the current buffer or the revision buffer in the same frame. +Like `magit-mode-bury-buffer' (which see) but with a negative +prefix argument instead bury the revision buffer, provided it +is displayed in the current frame." + (interactive "p") + (if (< arg 0) + (let* ((buf (magit-get-mode-buffer 'magit-revision-mode)) + (win (and buf (get-buffer-window buf (selected-frame))))) + (if win + (with-selected-window win + (with-current-buffer buf + (magit-mode-bury-buffer (> (abs arg) 1)))) + (user-error "No revision buffer in this frame"))) + (magit-mode-bury-buffer (> arg 1)))) + +;;;###autoload +(defun magit-log-move-to-parent (&optional n) + "Move to the Nth parent of the current commit." + (interactive "p") + (when (derived-mode-p 'magit-log-mode) + (when (magit-section-match 'commit) + (let* ((section (magit-current-section)) + (parent-rev (format "%s^%s" (oref section value) (or n 1)))) + (if-let ((parent-hash (magit-rev-parse "--short" parent-rev))) + (if-let ((parent (--first (equal (oref it value) + parent-hash) + (magit-section-siblings section 'next)))) + (magit-section-goto parent) + (user-error + (substitute-command-keys + (concat "Parent " parent-hash " not found. Try typing " + "\\[magit-log-double-commit-limit] first")))) + (user-error "Parent %s does not exist" parent-rev)))))) + +(defun magit-log-move-to-revision (rev) + "Read a revision and move to it in current log buffer. + +If the chosen reference or revision isn't being displayed in +the current log buffer, then inform the user about that and do +nothing else. + +If invoked outside any log buffer, then display the log buffer +of the current repository first; creating it if necessary." + (interactive (list (magit-read-branch-or-commit "In log, jump to"))) + (with-current-buffer + (cond ((derived-mode-p 'magit-log-mode) + (current-buffer)) + ((and-let* ((buf (magit-get-mode-buffer 'magit-log-mode))) + (pop-to-buffer-same-window buf))) + (t + (apply #'magit-log-all-branches (magit-log-arguments)))) + (unless (magit-log-goto-commit-section (magit-rev-abbrev rev)) + (user-error "%s isn't visible in the current log buffer" rev)))) + +;;;; Shortlog Commands + +;;;###autoload (autoload 'magit-shortlog "magit-log" nil t) +(transient-define-prefix magit-shortlog () + "Show a history summary." + :man-page "git-shortlog" + :value '("--numbered" "--summary") + ["Arguments" + ("-n" "Sort by number of commits" ("-n" "--numbered")) + ("-s" "Show commit count summary only" ("-s" "--summary")) + ("-e" "Show email addresses" ("-e" "--email")) + ("-g" "Group commits by" "--group=" + :choices ("author" "committer" "trailer:")) + (7 "-f" "Format string" "--format=") + (7 "-w" "Linewrap" "-w" :class transient-option)] + ["Shortlog" + ("s" "since" magit-shortlog-since) + ("r" "range" magit-shortlog-range)]) + +(defun magit-git-shortlog (rev args) + (let ((dir default-directory)) + (with-current-buffer (get-buffer-create "*magit-shortlog*") + (setq default-directory dir) + (setq buffer-read-only t) + (let ((inhibit-read-only t)) + (erase-buffer) + (save-excursion + (magit-git-insert "shortlog" args rev)) + (switch-to-buffer-other-window (current-buffer)))))) + +;;;###autoload +(defun magit-shortlog-since (rev args) + "Show a history summary for commits since REV." + (interactive + (list (magit-read-branch-or-commit "Shortlog since" (magit-get-current-tag)) + (transient-args 'magit-shortlog))) + (magit-git-shortlog (concat rev "..") args)) + +;;;###autoload +(defun magit-shortlog-range (rev-or-range args) + "Show a history summary for commit or range REV-OR-RANGE." + (interactive + (list (magit-read-range-or-commit "Shortlog for revision or range") + (transient-args 'magit-shortlog))) + (magit-git-shortlog rev-or-range args)) + +;;; Log Mode + +(defvar magit-log-disable-graph-hack-args + '("-G" "--grep" "--author") + "Arguments which disable the graph speedup hack.") + +(defvar magit-log-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map (kbd "C-c C-b") #'magit-go-backward) + (define-key map (kbd "C-c C-f") #'magit-go-forward) + (define-key map (kbd "C-c C-n") #'magit-log-move-to-parent) + (define-key map "j" #'magit-log-move-to-revision) + (define-key map "=" #'magit-log-toggle-commit-limit) + (define-key map "+" #'magit-log-double-commit-limit) + (define-key map "-" #'magit-log-half-commit-limit) + (define-key map "q" #'magit-log-bury-buffer) + map) + "Keymap for `magit-log-mode'.") + +(define-derived-mode magit-log-mode magit-mode "Magit Log" + "Mode for looking at Git log. + +This mode is documented in info node `(magit)Log Buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +Type \\[magit-branch] to see available branch commands. +Type \\[magit-merge] to merge the branch or commit at point. +Type \\[magit-cherry-pick] to apply the commit at point. +Type \\[magit-reset] to reset `HEAD' to the commit at point. + +\\{magit-log-mode-map}" + :group 'magit-log + (hack-dir-local-variables-non-file-buffer) + (setq magit--imenu-item-types 'commit)) + +(put 'magit-log-mode 'magit-log-default-arguments + '("--graph" "-n256" "--decorate")) + +(defun magit-log-setup-buffer (revs args files &optional locked focus) + (require 'magit) + (with-current-buffer + (magit-setup-buffer #'magit-log-mode locked + (magit-buffer-revisions revs) + (magit-buffer-log-args args) + (magit-buffer-log-files files)) + (when (if focus + (magit-log-goto-commit-section focus) + (magit-log-goto-same-commit)) + (magit-section-update-highlight)) + (current-buffer))) + +(defun magit-log-refresh-buffer () + (let ((revs magit-buffer-revisions) + (args magit-buffer-log-args) + (files magit-buffer-log-files)) + (magit-set-header-line-format + (funcall magit-log-header-line-function revs args files)) + (unless (length= files 1) + (setq args (remove "--follow" args))) + (when (and (car magit-log-remove-graph-args) + (--any-p (string-match-p + (concat "^" (regexp-opt magit-log-remove-graph-args)) it) + args)) + (setq args (remove "--graph" args))) + (unless (member "--graph" args) + (setq args (remove "--color" args))) + (when-let* ((limit (magit-log-get-commit-limit)) + (limit (* 2 limit)) ; increase odds for complete graph + (count (and (length= revs 1) + (> limit 1024) ; otherwise it's fast enough + (setq revs (car revs)) + (not (string-search ".." revs)) + (not (member revs '("--all" "--branches"))) + (-none-p (lambda (arg) + (--any-p + (string-prefix-p it arg) + magit-log-disable-graph-hack-args)) + args) + (magit-git-string "rev-list" "--count" + "--first-parent" args revs)))) + (setq revs (if (< (string-to-number count) limit) + revs + (format "%s~%s..%s" revs limit revs)))) + (magit-insert-section (logbuf) + (magit-insert-log revs args files)))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-log-mode)) + (append magit-buffer-revisions + (if (and magit-buffer-revisions magit-buffer-log-files) + (cons "--" magit-buffer-log-files) + magit-buffer-log-files))) + +(defun magit-log-header-line-arguments (revs args files) + "Return string describing some of the used arguments." + (mapconcat (lambda (arg) + (if (string-search " " arg) + (prin1 arg) + arg)) + `("git" "log" ,@args ,@revs "--" ,@files) + " ")) + +(defun magit-log-header-line-sentence (revs args files) + "Return string containing all arguments." + (concat "Commits in " + (mapconcat #'identity revs " ") + (and (member "--reverse" args) + " in reverse") + (and files (concat " touching " + (mapconcat #'identity files " "))) + (--some (and (string-prefix-p "-L" it) + (concat " " it)) + args))) + +(defun magit-insert-log (revs &optional args files) + "Insert a log section. +Do not add this to a hook variable." + (let ((magit-git-global-arguments + (remove "--literal-pathspecs" magit-git-global-arguments))) + (magit-git-wash (apply-partially #'magit-log-wash-log 'log) + "log" + (format "--format=%s%%h%%x0c%s%%x0c%s%%x0c%%aN%%x0c%s%%x0c%%s%s" + (if (and (member "--left-right" args) + (not (member "--graph" args))) + "%m " + "") + (if (member "--decorate" args) "%D" "") + (if (member "--show-signature" args) + (progn (setq args (remove "--show-signature" args)) "%G?") + "") + (if magit-log-margin-show-committer-date "%ct" "%at") + (if (member "++header" args) + (if (member "--graph" (setq args (remove "++header" args))) + (concat "\n" magit-log-revision-headers-format "\n") + (concat "\n" magit-log-revision-headers-format "\n")) + "")) + (progn + (--when-let (--first (string-match "^\\+\\+order=\\(.+\\)$" it) args) + (setq args (cons (format "--%s-order" (match-string 1 it)) + (remove it args)))) + (when (member "--decorate" args) + (setq args (cons "--decorate=full" (remove "--decorate" args)))) + (when (member "--reverse" args) + (setq args (remove "--graph" args))) + (setq args (magit-diff--maybe-add-stat-arguments args)) + args) + "--use-mailmap" "--no-prefix" revs "--" files))) + +(cl-defmethod magit-menu-common-value ((_section magit-commit-section)) + (or (magit-diff--region-range) + (oref (magit-current-section) value))) + +(defvar magit-commit-section-map + (let ((map (make-sparse-keymap))) + ;; The second remapping overrides the first but we still get two menu + ;; items, though only one of them will be available at any given time. + (magit-menu-set map [magit-visit-thing] + #'magit-diff-range "Diff %x" + '(:visible (region-active-p))) + (magit-menu-set map [magit-visit-thing] + #'magit-show-commit "Show commit %x" + '(:visible (not (region-active-p)))) + (magit-menu-set map [magit-cherry-apply] + #'magit-cherry-apply "Apply %x") + map) + "Keymap for `commit' sections.") + +(defvar magit-module-commit-section-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-commit-section-map) + map) + "Keymap for `module-commit' sections.") + +(defconst magit-log-heading-re + ;; Note: A form feed instead of a null byte is used as the delimiter + ;; because using the latter interferes with the graph prefix when + ;; ++header is used. + (concat "^" + "\\(?4:[-_/|\\*o<>. ]*\\)" ; graph + "\\(?1:[0-9a-fA-F]+\\)? " ; hash + "\\(?3:[^ \n]+\\)? " ; refs + "\\(?7:[BGUXYREN]\\)? " ; gpg + "\\(?5:[^ \n]*\\) " ; author + ;; Note: Date is optional because, prior to Git v2.19.0, + ;; `git rebase -i --root` corrupts the root's author date. + "\\(?6:[^ \n]*\\) " ; date + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-cherry-re + (concat "^" + "\\(?8:[-+]\\) " ; cherry + "\\(?1:[0-9a-fA-F]+\\) " ; hash + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-module-re + (concat "^" + "\\(?:\\(?11:[<>]\\) \\)?" ; side + "\\(?1:[0-9a-fA-F]+\\) " ; hash + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-bisect-vis-re + (concat "^" + "\\(?4:[-_/|\\*o<>. ]*\\)" ; graph + "\\(?1:[0-9a-fA-F]+\\)?\0" ; hash + "\\(?3:[^\0\n]+\\)?\0" ; refs + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-bisect-log-re + (concat "^# " + "\\(?3:[^: \n]+:\\) " ; "refs" + "\\[\\(?1:[^]\n]+\\)\\] " ; hash + "\\(?2:.*\\)$")) ; msg + +(defconst magit-log-reflog-re + (concat "^" + "\\(?1:[^\0\n]+\\)\0" ; hash + "\\(?5:[^\0\n]*\\)\0" ; author + "\\(?:\\(?:[^@\n]+@{\\(?6:[^}\n]+\\)}\0" ; date + "\\(?10:merge \\|autosave \\|restart \\|[^:\n]+: \\)?" ; refsub + "\\(?2:.*\\)?\\)\\|\0\\)$")) ; msg + +(defconst magit-reflog-subject-re + (concat "\\(?1:[^ ]+\\) ?" ; command + "\\(?2:\\(?: ?-[^ ]+\\)+\\)?" ; option + "\\(?: ?(\\(?3:[^)]+\\))\\)?")) ; type + +(defconst magit-log-stash-re + (concat "^" + "\\(?1:[^\0\n]+\\)\0" ; "hash" + "\\(?5:[^\0\n]*\\)\0" ; author + "\\(?6:[^\0\n]+\\)\0" ; date + "\\(?2:.*\\)$")) ; msg + +(defvar magit-log-count nil) + +(defvar magit-log-format-message-function #'magit-log-propertize-keywords) + +(defun magit-log-wash-log (style args) + (setq args (flatten-tree args)) + (when (and (member "--graph" args) + (member "--color" args)) + (let ((ansi-color-apply-face-function + (lambda (beg end face) + (put-text-property beg end 'font-lock-face + (or face 'magit-log-graph))))) + (ansi-color-apply-on-region (point-min) (point-max)))) + (when (eq style 'cherry) + (reverse-region (point-min) (point-max))) + (let ((magit-log-count 0)) + (when (looking-at "^\\.\\.\\.") + (magit-delete-line)) + (magit-wash-sequence (apply-partially #'magit-log-wash-rev style + (magit-abbrev-length))) + (if (derived-mode-p 'magit-log-mode 'magit-reflog-mode) + (when (eq magit-log-count (magit-log-get-commit-limit)) + (magit-insert-section (longer) + (insert-text-button + (substitute-command-keys + (format "Type \\<%s>\\[%s] to show more history" + 'magit-log-mode-map + 'magit-log-double-commit-limit)) + 'action (lambda (_button) + (magit-log-double-commit-limit)) + 'follow-link t + 'mouse-face 'magit-section-highlight))) + (insert ?\n)))) + +(cl-defun magit-log-wash-rev (style abbrev) + (when (derived-mode-p 'magit-log-mode 'magit-reflog-mode) + (cl-incf magit-log-count)) + (looking-at (pcase style + ('log magit-log-heading-re) + ('cherry magit-log-cherry-re) + ('module magit-log-module-re) + ('reflog magit-log-reflog-re) + ('stash magit-log-stash-re) + ('bisect-vis magit-log-bisect-vis-re) + ('bisect-log magit-log-bisect-log-re))) + (magit-bind-match-strings + (hash msg refs graph author date gpg cherry _ refsub side) nil + (setq msg (substring-no-properties msg)) + (when refs + (setq refs (substring-no-properties refs))) + (let ((align (or (eq style 'cherry) + (not (member "--stat" magit-buffer-log-args)))) + (non-graph-re (if (eq style 'bisect-vis) + magit-log-bisect-vis-re + magit-log-heading-re))) + (magit-delete-line) + ;; If the reflog entries have been pruned, the output of `git + ;; reflog show' includes a partial line that refers to the hash + ;; of the youngest expired reflog entry. + (when (and (eq style 'reflog) (not date)) + (cl-return-from magit-log-wash-rev t)) + (magit-insert-section section (commit hash) + (pcase style + ('stash (oset section type 'stash)) + ('module (oset section type 'module-commit)) + ('bisect-log (setq hash (magit-rev-parse "--short" hash)))) + (setq hash (propertize hash 'font-lock-face + (pcase (and gpg (aref gpg 0)) + (?G 'magit-signature-good) + (?B 'magit-signature-bad) + (?U 'magit-signature-untrusted) + (?X 'magit-signature-expired) + (?Y 'magit-signature-expired-key) + (?R 'magit-signature-revoked) + (?E 'magit-signature-error) + (?N 'magit-hash) + (_ 'magit-hash)))) + (when cherry + (when (and (derived-mode-p 'magit-refs-mode) + magit-refs-show-commit-count) + (insert (make-string (1- magit-refs-focus-column-width) ?\s))) + (insert (propertize cherry 'font-lock-face + (if (string= cherry "-") + 'magit-cherry-equivalent + 'magit-cherry-unmatched))) + (insert ?\s)) + (when side + (insert (propertize side 'font-lock-face + (if (string= side "<") + 'magit-cherry-equivalent + 'magit-cherry-unmatched))) + (insert ?\s)) + (when align + (insert hash ?\s)) + (when graph + (insert graph)) + (unless align + (insert hash ?\s)) + (when (and refs (not magit-log-show-refname-after-summary)) + (insert (magit-format-ref-labels refs) ?\s)) + (when (eq style 'reflog) + (insert (format "%-2s " (1- magit-log-count))) + (when refsub + (insert (magit-reflog-format-subject + (substring refsub 0 + (if (string-search ":" refsub) -2 -1)))))) + (when msg + (insert (funcall magit-log-format-message-function hash msg))) + (when (and refs magit-log-show-refname-after-summary) + (insert ?\s) + (insert (magit-format-ref-labels refs))) + (insert ?\n) + (when (memq style '(log reflog stash)) + (goto-char (line-beginning-position)) + (when (and refsub + (string-match "\\`\\([^ ]\\) \\+\\(..\\)\\(..\\)" date)) + (setq date (+ (string-to-number (match-string 1 date)) + (* (string-to-number (match-string 2 date)) 60 60) + (* (string-to-number (match-string 3 date)) 60)))) + (save-excursion + (backward-char) + (magit-log-format-margin hash author date))) + (when (and (eq style 'cherry) + (magit-buffer-margin-p)) + (save-excursion + (backward-char) + (apply #'magit-log-format-margin hash + (split-string (magit-rev-format "%aN%x00%ct" hash) "\0")))) + (when (and graph + (not (eobp)) + (not (looking-at non-graph-re))) + (when (looking-at "") + (magit-insert-heading) + (delete-char 1) + (magit-insert-section (commit-header) + (forward-line) + (magit-insert-heading) + (re-search-forward "") + (backward-delete-char 1) + (forward-char) + (insert ?\n)) + (delete-char 1)) + (if (looking-at "^\\(---\\|\n\s\\|\ndiff\\)") + (let ((limit (save-excursion + (and (re-search-forward non-graph-re nil t) + (match-beginning 0))))) + (unless (oref magit-insert-section--current content) + (magit-insert-heading)) + (delete-char (if (looking-at "\n") 1 4)) + (magit-diff-wash-diffs (list "--stat") limit)) + (when align + (setq align (make-string (1+ abbrev) ? ))) + (when (and (not (eobp)) (not (looking-at non-graph-re))) + (when align + (setq align (make-string (1+ abbrev) ? ))) + (while (and (not (eobp)) (not (looking-at non-graph-re))) + (when align + (save-excursion (insert align))) + (magit-make-margin-overlay) + (forward-line)) + ;; When `--format' is used and its value isn't one of the + ;; predefined formats, then `git-log' does not insert a + ;; separator line. + (save-excursion + (forward-line -1) + (looking-at "[-_/|\\*o<>. ]*")) + (setq graph (match-string 0)) + (unless (string-match-p "[/\\.]" graph) + (insert graph ?\n)))))))) + t) + +(defun magit-log-propertize-keywords (_rev msg) + (let ((boundary 0)) + (when (string-match "^\\(?:squash\\|fixup\\)! " msg boundary) + (setq boundary (match-end 0)) + (magit--put-face (match-beginning 0) (1- boundary) + 'magit-keyword-squash msg)) + (when magit-log-highlight-keywords + (while (string-match "\\[[^[]*?]" msg boundary) + (setq boundary (match-end 0)) + (magit--put-face (match-beginning 0) boundary + 'magit-keyword msg)))) + msg) + +(defun magit-log-maybe-show-more-commits (section) + "When point is at the end of a log buffer, insert more commits. + +Log buffers end with a button \"Type + to show more history\". +When the use of a section movement command puts point on that +button, then automatically show more commits, without the user +having to press \"+\". + +This function is called by `magit-section-movement-hook' and +exists mostly for backward compatibility reasons." + (when (and (eq (oref section type) 'longer) + magit-log-auto-more) + (magit-log-double-commit-limit) + (forward-line -1) + (magit-section-forward))) + +(add-hook 'magit-section-movement-hook #'magit-log-maybe-show-more-commits) + +(defvar magit--update-revision-buffer nil) + +(defun magit-log-maybe-update-revision-buffer (&optional _) + "When moving in a log or cherry buffer, update the revision buffer. +If there is no revision buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-log-mode 'magit-cherry-mode 'magit-reflog-mode) + (magit--maybe-update-revision-buffer))) + +(add-hook 'magit-section-movement-hook #'magit-log-maybe-update-revision-buffer) + +(defun magit--maybe-update-revision-buffer () + (when-let* ((commit (magit-section-value-if 'commit)) + (buffer (magit-get-mode-buffer 'magit-revision-mode nil t))) + (if magit--update-revision-buffer + (setq magit--update-revision-buffer (list commit buffer)) + (setq magit--update-revision-buffer (list commit buffer)) + (run-with-idle-timer + magit-update-other-window-delay nil + (let ((args (let ((magit-direct-use-buffer-arguments 'selected)) + (magit-show-commit--arguments)))) + (lambda () + (pcase-let ((`(,rev ,buf) magit--update-revision-buffer)) + (setq magit--update-revision-buffer nil) + (when (buffer-live-p buf) + (let ((magit-display-buffer-noselect t)) + (apply #'magit-show-commit rev args)))) + (setq magit--update-revision-buffer nil))))))) + +(defvar magit--update-blob-buffer nil) + +(defun magit-log-maybe-update-blob-buffer (&optional _) + "When moving in a log or cherry buffer, update the blob buffer. +If there is no blob buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-log-mode 'magit-cherry-mode 'magit-reflog-mode) + (magit--maybe-update-blob-buffer))) + +(defun magit--maybe-update-blob-buffer () + (when-let* ((commit (magit-section-value-if 'commit)) + (buffer (--first (with-current-buffer it + (eq revert-buffer-function + 'magit-revert-rev-file-buffer)) + (mapcar #'window-buffer (window-list))))) + (if magit--update-blob-buffer + (setq magit--update-blob-buffer (list commit buffer)) + (setq magit--update-blob-buffer (list commit buffer)) + (run-with-idle-timer + magit-update-other-window-delay nil + (lambda () + (pcase-let ((`(,rev ,buf) magit--update-blob-buffer)) + (setq magit--update-blob-buffer nil) + (when (buffer-live-p buf) + (with-selected-window (get-buffer-window buf) + (with-current-buffer buf + (save-excursion + (magit-blob-visit (list (magit-rev-parse rev) + (magit-file-relative-name + magit-buffer-file-name))))))))))))) + +(defun magit-log-goto-commit-section (rev) + (let ((abbrev (magit-rev-format "%h" rev))) + (when-let ((section (--first (equal (oref it value) abbrev) + (oref magit-root-section children)))) + (goto-char (oref section start))))) + +(defun magit-log-goto-same-commit () + (when (and magit-previous-section + (magit-section-match '(commit branch) + magit-previous-section)) + (magit-log-goto-commit-section (oref magit-previous-section value)))) + +;;; Log Margin + +(defvar-local magit-log-margin-show-shortstat nil) + +(defun magit-toggle-log-margin-style () + "Toggle between the regular and the shortstat margin style. +The shortstat style is experimental and rather slow." + (interactive) + (setq magit-log-margin-show-shortstat + (not magit-log-margin-show-shortstat)) + (magit-set-buffer-margin nil t)) + +(defun magit-log-format-margin (rev author date) + (when (magit-margin-option) + (if magit-log-margin-show-shortstat + (magit-log-format-shortstat-margin rev) + (magit-log-format-author-margin author date)))) + +(defun magit-log-format-author-margin (author date &optional previous-line) + (pcase-let ((`(,_ ,style ,width ,details ,details-width) + (or magit-buffer-margin + (symbol-value (magit-margin-option))))) + (magit-make-margin-overlay + (concat (and details + (concat (magit--propertize-face + (truncate-string-to-width + (or author "") + details-width + nil ?\s + (if (char-displayable-p ?…) "…" ">")) + 'magit-log-author) + " ")) + (magit--propertize-face + (if (stringp style) + (format-time-string + style + (seconds-to-time (string-to-number date))) + (pcase-let* ((abbr (eq style 'age-abbreviated)) + (`(,cnt ,unit) (magit--age date abbr))) + (format (format (if abbr "%%2i%%-%ic" "%%2i %%-%is") + (- width (if details (1+ details-width) 0))) + cnt unit))) + 'magit-log-date)) + previous-line))) + +(defun magit-log-format-shortstat-margin (rev) + (magit-make-margin-overlay + (if-let ((line (and rev (magit-git-string + "show" "--format=" "--shortstat" rev)))) + (if (string-match "\ +\\([0-9]+\\) files? changed, \ +\\(?:\\([0-9]+\\) insertions?(\\+)\\)?\ +\\(?:\\(?:, \\)?\\([0-9]+\\) deletions?(-)\\)?\\'" line) + (magit-bind-match-strings (files add del) line + (format + "%5s %5s%4s" + (if add + (magit--propertize-face (format "%s+" add) + 'magit-diffstat-added) + "") + (if del + (magit--propertize-face (format "%s-" del) + 'magit-diffstat-removed) + "") + files)) + "") + ""))) + +(defun magit-log-margin-width (style details details-width) + (if magit-log-margin-show-shortstat + 16 + (+ (if details (1+ details-width) 0) + (if (stringp style) + (length (format-time-string style)) + (+ 2 ; two digits + 1 ; trailing space + (if (eq style 'age-abbreviated) + 1 ; single character + (+ 1 ; gap after digits + (apply #'max (--map (max (length (nth 1 it)) + (length (nth 2 it))) + magit--age-spec))))))))) + +;;; Select Mode + +(defvar magit-log-select-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-log-mode-map) + (define-key map (kbd "C-c C-b") #'undefined) + (define-key map (kbd "C-c C-f") #'undefined) + (define-key map (kbd ".") #'magit-log-select-pick) + (define-key map (kbd "e") #'magit-log-select-pick) + (define-key map (kbd "C-c C-c") #'magit-log-select-pick) + (define-key map (kbd "q") #'magit-log-select-quit) + (define-key map (kbd "C-c C-k") #'magit-log-select-quit) + map) + "Keymap for `magit-log-select-mode'.") + +(put 'magit-log-select-pick :advertised-binding [?\C-c ?\C-c]) +(put 'magit-log-select-quit :advertised-binding [?\C-c ?\C-k]) + +(define-derived-mode magit-log-select-mode magit-log-mode "Magit Select" + "Mode for selecting a commit from history. + +This mode is documented in info node `(magit)Select from Log'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +\\\ +Type \\[magit-log-select-pick] to select the commit at point. +Type \\[magit-log-select-quit] to abort without selecting a commit." + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(put 'magit-log-select-mode 'magit-log-default-arguments + '("--graph" "-n256" "--decorate")) + +(defun magit-log-select-setup-buffer (revs args) + (magit-setup-buffer #'magit-log-select-mode nil + (magit-buffer-revisions revs) + (magit-buffer-log-args args))) + +(defun magit-log-select-refresh-buffer () + (magit-insert-section (logbuf) + (magit-insert-log magit-buffer-revisions + magit-buffer-log-args))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-log-select-mode)) + magit-buffer-revisions) + +(defvar-local magit-log-select-pick-function nil) +(defvar-local magit-log-select-quit-function nil) + +(defun magit-log-select (pick &optional msg quit branch args initial) + (declare (indent defun)) + (unless initial + (setq initial (magit-commit-at-point))) + (magit-log-select-setup-buffer + (or branch (magit-get-current-branch) "HEAD") + (append args + (car (magit-log--get-value 'magit-log-select-mode + magit-direct-use-buffer-arguments)))) + (when initial + (magit-log-goto-commit-section initial)) + (setq magit-log-select-pick-function pick) + (setq magit-log-select-quit-function quit) + (when magit-log-select-show-usage + (let ((pick (propertize (substitute-command-keys + "\\[magit-log-select-pick]") + 'font-lock-face + 'magit-header-line-key)) + (quit (propertize (substitute-command-keys + "\\[magit-log-select-quit]") + 'font-lock-face + 'magit-header-line-key))) + (setq msg (format-spec + (if msg + (if (string-suffix-p "," msg) + (concat msg " or %q to abort") + msg) + "Type %p to select commit at point, or %q to abort") + `((?p . ,pick) + (?q . ,quit))))) + (magit--add-face-text-property + 0 (length msg) 'magit-header-line-log-select t msg) + (when (memq magit-log-select-show-usage '(both header-line)) + (magit-set-header-line-format msg)) + (when (memq magit-log-select-show-usage '(both echo-area)) + (message "%s" (substring-no-properties msg))))) + +(defun magit-log-select-pick () + "Select the commit at point and act on it. +Call `magit-log-select-pick-function' with the selected +commit as argument." + (interactive) + (let ((fun magit-log-select-pick-function) + (rev (magit-commit-at-point))) + (magit-mode-bury-buffer 'kill) + (funcall fun rev))) + +(defun magit-log-select-quit () + "Abort selecting a commit, don't act on any commit. +Call `magit-log-select-quit-function' if set." + (interactive) + (let ((fun magit-log-select-quit-function)) + (magit-mode-bury-buffer 'kill) + (when fun (funcall fun)))) + +;;; Cherry Mode + +(defvar magit-cherry-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map "q" #'magit-log-bury-buffer) + (define-key map "L" #'magit-margin-settings) + map) + "Keymap for `magit-cherry-mode'.") + +(define-derived-mode magit-cherry-mode magit-mode "Magit Cherry" + "Mode for looking at commits not merged upstream. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +Type \\[magit-cherry-pick] to apply the commit at point. + +\\{magit-cherry-mode-map}" + :group 'magit-log + (hack-dir-local-variables-non-file-buffer) + (setq magit--imenu-group-types 'cherries)) + +(defun magit-cherry-setup-buffer (head upstream) + (magit-setup-buffer #'magit-cherry-mode nil + (magit-buffer-refname head) + (magit-buffer-upstream upstream) + (magit-buffer-range (concat upstream ".." head)))) + +(defun magit-cherry-refresh-buffer () + (magit-insert-section (cherry) + (magit-run-section-hook 'magit-cherry-sections-hook))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-cherry-mode)) + magit-buffer-range) + +;;;###autoload +(defun magit-cherry (head upstream) + "Show commits in a branch that are not merged in the upstream branch." + (interactive + (let ((head (magit-read-branch "Cherry head"))) + (list head (magit-read-other-branch "Cherry upstream" head + (magit-get-upstream-branch head))))) + (require 'magit) + (magit-cherry-setup-buffer head upstream)) + +(defun magit-insert-cherry-headers () + "Insert headers appropriate for `magit-cherry-mode' buffers." + (let ((branch (propertize magit-buffer-refname + 'font-lock-face 'magit-branch-local)) + (upstream (propertize magit-buffer-upstream 'font-lock-face + (if (magit-local-branch-p magit-buffer-upstream) + 'magit-branch-local + 'magit-branch-remote)))) + (magit-insert-head-branch-header branch) + (magit-insert-upstream-branch-header branch upstream "Upstream: ") + (insert ?\n))) + +(defun magit-insert-cherry-commits () + "Insert commit sections into a `magit-cherry-mode' buffer." + (magit-insert-section (cherries) + (magit-insert-heading "Cherry commits:") + (magit-git-wash (apply-partially #'magit-log-wash-log 'cherry) + "cherry" "-v" "--abbrev" + magit-buffer-upstream + magit-buffer-refname))) + +;;; Log Sections +;;;; Standard Log Sections + +(defvar magit-log-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-diff-dwim "Visit diff") + map) + "Keymap for log sections. +The classes `magit-{unpulled,unpushed,unmerged}-section' derive +from the abstract `magit-log-section' class. Accordingly this +keymap is the parent of their keymaps.") + +(defvar magit-unpulled-section-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-log-section-map) + map) + "Keymap for `unpulled' sections.") + +(cl-defmethod magit-section-ident-value ((section magit-unpulled-section)) + "\"..@{push}\" cannot be used as the value because that is +ambiguous if `push.default' does not allow a 1:1 mapping, and +many commands would fail because of that. But here that does +not matter and we need an unique value so we use that string +in the pushremote case." + (let ((value (oref section value))) + (if (equal value "..@{upstream}") value "..@{push}"))) + +(magit-define-section-jumper magit-jump-to-unpulled-from-upstream + "Unpulled from @{upstream}" unpulled "..@{upstream}") + +(defun magit-insert-unpulled-from-upstream () + "Insert commits that haven't been pulled from the upstream yet." + (when-let ((upstream (magit-get-upstream-branch))) + (magit-insert-section (unpulled "..@{upstream}" t) + (magit-insert-heading + (format (propertize "Unpulled from %s." + 'font-lock-face 'magit-section-heading) + upstream)) + (magit-insert-log "..@{upstream}" magit-buffer-log-args) + (magit-log-insert-child-count)))) + +(magit-define-section-jumper magit-jump-to-unpulled-from-pushremote + "Unpulled from " unpulled + (concat ".." (magit-get-push-branch))) + +(defun magit-insert-unpulled-from-pushremote () + "Insert commits that haven't been pulled from the push-remote yet." + (--when-let (magit-get-push-branch) + (when (magit--insert-pushremote-log-p) + (magit-insert-section (unpulled (concat ".." it) t) + (magit-insert-heading + (format (propertize "Unpulled from %s." + 'font-lock-face 'magit-section-heading) + (propertize it 'font-lock-face 'magit-branch-remote))) + (magit-insert-log (concat ".." it) magit-buffer-log-args) + (magit-log-insert-child-count))))) + +(defvar magit-unpushed-section-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-log-section-map) + map) + "Keymap for `unpushed' sections.") + +(cl-defmethod magit-section-ident-value ((section magit-unpushed-section)) + "\"..@{push}\" cannot be used as the value because that is +ambiguous if `push.default' does not allow a 1:1 mapping, and +many commands would fail because of that. But here that does +not matter and we need an unique value so we use that string +in the pushremote case." + (let ((value (oref section value))) + (if (equal value "@{upstream}..") value "@{push}.."))) + +(magit-define-section-jumper magit-jump-to-unpushed-to-upstream + "Unpushed to @{upstream}" unpushed "@{upstream}..") + +(defun magit-insert-unpushed-to-upstream-or-recent () + "Insert section showing unpushed or other recent commits. +If an upstream is configured for the current branch and it is +behind of the current branch, then show the commits that have +not yet been pushed into the upstream branch. If no upstream is +configured or if the upstream is not behind of the current branch, +then show the last `magit-log-section-commit-count' commits." + (let ((upstream (magit-get-upstream-branch))) + (if (or (not upstream) + (magit-rev-ancestor-p "HEAD" upstream)) + (magit-insert-recent-commits 'unpushed "@{upstream}..") + (magit-insert-unpushed-to-upstream)))) + +(defun magit-insert-unpushed-to-upstream () + "Insert commits that haven't been pushed to the upstream yet." + (when (magit-git-success "rev-parse" "@{upstream}") + (magit-insert-section (unpushed "@{upstream}..") + (magit-insert-heading + (format (propertize "Unmerged into %s." + 'font-lock-face 'magit-section-heading) + (magit-get-upstream-branch))) + (magit-insert-log "@{upstream}.." magit-buffer-log-args) + (magit-log-insert-child-count)))) + +(defun magit-insert-recent-commits (&optional type value) + "Insert section showing recent commits. +Show the last `magit-log-section-commit-count' commits." + (let* ((start (format "HEAD~%s" magit-log-section-commit-count)) + (range (and (magit-rev-verify start) + (concat start "..HEAD")))) + (magit-insert-section ((eval (or type 'recent)) + (or value range) + t) + (magit-insert-heading "Recent commits") + (magit-insert-log range + (cons (format "-n%d" magit-log-section-commit-count) + (--remove (string-prefix-p "-n" it) + magit-buffer-log-args)))))) + +(magit-define-section-jumper magit-jump-to-unpushed-to-pushremote + "Unpushed to " unpushed + (concat (magit-get-push-branch) "..")) + +(defun magit-insert-unpushed-to-pushremote () + "Insert commits that haven't been pushed to the push-remote yet." + (--when-let (magit-get-push-branch) + (when (magit--insert-pushremote-log-p) + (magit-insert-section (unpushed (concat it "..") t) + (magit-insert-heading + (format (propertize "Unpushed to %s." + 'font-lock-face 'magit-section-heading) + (propertize it 'font-lock-face 'magit-branch-remote))) + (magit-insert-log (concat it "..") magit-buffer-log-args) + (magit-log-insert-child-count))))) + +(defun magit--insert-pushremote-log-p () + (magit--with-refresh-cache + (cons default-directory 'magit--insert-pushremote-log-p) + (not (and (equal (magit-get-push-branch) + (magit-get-upstream-branch)) + (or (memq 'magit-insert-unpulled-from-upstream + magit-status-sections-hook) + (memq 'magit-insert-unpulled-from-upstream-or-recent + magit-status-sections-hook)))))) + +(defun magit-log-insert-child-count () + (when magit-section-show-child-count + (let ((count (length (oref magit-insert-section--current children)))) + (when (> count 0) + (when (eq count (magit-log-get-commit-limit)) + (setq count (format "%s+" count))) + (save-excursion + (goto-char (- (oref magit-insert-section--current content) 2)) + (insert (format " (%s)" count)) + (delete-char 1)))))) + +;;;; Auxiliary Log Sections + +(defun magit-insert-unpulled-cherries () + "Insert section showing unpulled commits. +Like `magit-insert-unpulled-from-upstream' but prefix each commit +which has not been applied yet (i.e. a commit with a patch-id +not shared with any local commit) with \"+\", and all others with +\"-\"." + (when (magit-git-success "rev-parse" "@{upstream}") + (magit-insert-section (unpulled "..@{upstream}") + (magit-insert-heading "Unpulled commits:") + (magit-git-wash (apply-partially #'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) + (magit-get-current-branch) "@{upstream}")))) + +(defun magit-insert-unpushed-cherries () + "Insert section showing unpushed commits. +Like `magit-insert-unpushed-to-upstream' but prefix each commit +which has not been applied to upstream yet (i.e. a commit with +a patch-id not shared with any upstream commit) with \"+\", and +all others with \"-\"." + (when (magit-git-success "rev-parse" "@{upstream}") + (magit-insert-section (unpushed "@{upstream}..") + (magit-insert-heading "Unpushed commits:") + (magit-git-wash (apply-partially #'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) "@{upstream}")))) + +;;; _ +(provide 'magit-log) +;;; magit-log.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-margin.el b/code/elpa/magit-20220821.1819/magit-margin.el new file mode 100644 index 0000000..ef3b380 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-margin.el @@ -0,0 +1,241 @@ +;;; magit-margin.el --- Margins in Magit buffers -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for showing additional information +;; in the margins of Magit buffers. Currently this is only used for +;; commits, for which the committer date or age, and optionally the +;; author name are shown. + +;;; Code: + +(require 'magit-base) +(require 'magit-transient) +(require 'magit-mode) + +;;; Options + +(defgroup magit-margin nil + "Information Magit displays in the margin. + +You can change the STYLE and AUTHOR-WIDTH of all `magit-*-margin' +options to the same values by customizing `magit-log-margin' +*before* `magit' is loaded. If you do that, then the respective +values for the other options will default to what you have set +for that variable. Likewise if you set `magit-log-margin's INIT +to nil, then that is used in the default of all other options. But +setting it to t, i.e. re-enforcing the default for that option, +does not carry to other options." + :link '(info-link "(magit)Log Margin") + :group 'magit-log) + +(defvar-local magit-buffer-margin nil) +(put 'magit-buffer-margin 'permanent-local t) + +(defvar-local magit-set-buffer-margin-refresh nil) + +(defvar magit--age-spec) + +;;; Commands + +(transient-define-prefix magit-margin-settings () + "Change what information is displayed in the margin." + :info-manual "(magit) Log Margin" + ["Margin" + ("L" "Toggle visibility" magit-toggle-margin :transient t) + ("l" "Cycle style" magit-cycle-margin-style :transient t) + ("d" "Toggle details" magit-toggle-margin-details) + ("v" "Change verbosity" magit-refs-set-show-commit-count + :if-derived magit-refs-mode)]) + +(defun magit-toggle-margin () + "Show or hide the Magit margin." + (interactive) + (unless (magit-margin-option) + (user-error "Magit margin isn't supported in this buffer")) + (setcar magit-buffer-margin (not (magit-buffer-margin-p))) + (magit-set-buffer-margin)) + +(defvar magit-margin-default-time-format nil + "See https://github.com/magit/magit/pull/4605.") + +(defun magit-cycle-margin-style () + "Cycle style used for the Magit margin." + (interactive) + (unless (magit-margin-option) + (user-error "Magit margin isn't supported in this buffer")) + ;; This is only suitable for commit margins (there are not others). + (setf (cadr magit-buffer-margin) + (pcase (cadr magit-buffer-margin) + ('age 'age-abbreviated) + ('age-abbreviated + (let ((default (or magit-margin-default-time-format + (cadr (symbol-value (magit-margin-option)))))) + (if (stringp default) default "%Y-%m-%d %H:%M "))) + (_ 'age))) + (magit-set-buffer-margin nil t)) + +(defun magit-toggle-margin-details () + "Show or hide details in the Magit margin." + (interactive) + (unless (magit-margin-option) + (user-error "Magit margin isn't supported in this buffer")) + (setf (nth 3 magit-buffer-margin) + (not (nth 3 magit-buffer-margin))) + (magit-set-buffer-margin nil t)) + +;;; Core + +(defun magit-buffer-margin-p () + (car magit-buffer-margin)) + +(defun magit-margin-option () + (pcase major-mode + ('magit-cherry-mode 'magit-cherry-margin) + ('magit-log-mode 'magit-log-margin) + ('magit-log-select-mode 'magit-log-select-margin) + ('magit-reflog-mode 'magit-reflog-margin) + ('magit-refs-mode 'magit-refs-margin) + ('magit-stashes-mode 'magit-stashes-margin) + ('magit-status-mode 'magit-status-margin) + ('forge-notifications-mode 'magit-status-margin))) + +(defun magit-set-buffer-margin (&optional reset refresh) + (when-let ((option (magit-margin-option))) + (let* ((default (symbol-value option)) + (default-width (nth 2 default))) + (when (or reset (not magit-buffer-margin)) + (setq magit-buffer-margin (copy-sequence default))) + (pcase-let ((`(,enable ,style ,_width ,details ,details-width) + magit-buffer-margin)) + (when (functionp default-width) + (setf (nth 2 magit-buffer-margin) + (funcall default-width style details details-width))) + (dolist (window (get-buffer-window-list nil nil 0)) + (with-selected-window window + (magit-set-window-margin window) + (if enable + (add-hook 'window-configuration-change-hook + #'magit-set-window-margin nil t) + (remove-hook 'window-configuration-change-hook + #'magit-set-window-margin t)))) + (when (and enable (or refresh magit-set-buffer-margin-refresh)) + (magit-refresh-buffer)))))) + +(defun magit-set-window-margin (&optional window) + (when (or window (setq window (get-buffer-window))) + (with-selected-window window + (set-window-margins + nil (car (window-margins)) + (and (magit-buffer-margin-p) + (nth 2 magit-buffer-margin)))))) + +(defun magit-make-margin-overlay (&optional string previous-line) + (if previous-line + (save-excursion + (forward-line -1) + (magit-make-margin-overlay string)) + ;; Don't put the overlay on the complete line to work around #1880. + (let ((o (make-overlay (1+ (line-beginning-position)) + (line-end-position) + nil t))) + (overlay-put o 'evaporate t) + (overlay-put o 'before-string + (propertize "o" 'display + (list (list 'margin 'right-margin) + (or string " "))))))) + +(defun magit-maybe-make-margin-overlay () + (when (or (magit-section-match + '(unpulled unpushed recent stashes local cherries) + magit-insert-section--current) + (and (eq major-mode 'magit-refs-mode) + (magit-section-match + '(remote commit tags) + magit-insert-section--current))) + (magit-make-margin-overlay nil t))) + +;;; Custom Support + +(defun magit-margin-set-variable (mode symbol value) + (set-default symbol value) + (message "Updating margins in %s buffers..." mode) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (eq major-mode mode) + (magit-set-buffer-margin t) + (magit-refresh)))) + (message "Updating margins in %s buffers...done" mode)) + +(defconst magit-log-margin--custom-type + '(list (boolean :tag "Show margin initially") + (choice :tag "Show committer" + (string :tag "date using time-format" "%Y-%m-%d %H:%M ") + (const :tag "date's age" age) + (const :tag "date's age (abbreviated)" age-abbreviated)) + (const :tag "Calculate width using magit-log-margin-width" + magit-log-margin-width) + (boolean :tag "Show author name by default") + (integer :tag "Show author name using width"))) + +;;; Time Utilities + +(defvar magit--age-spec + `((?Y "year" "years" ,(round (* 60 60 24 365.2425))) + (?M "month" "months" ,(round (* 60 60 24 30.436875))) + (?w "week" "weeks" ,(* 60 60 24 7)) + (?d "day" "days" ,(* 60 60 24)) + (?h "hour" "hours" ,(* 60 60)) + (?m "minute" "minutes" 60) + (?s "second" "seconds" 1)) + "Time units used when formatting relative commit ages. + +The value is a list of time units, beginning with the longest. +Each element has the form (CHAR UNIT UNITS SECONDS). UNIT is the +time unit, UNITS is the plural of that unit. CHAR is a character +abbreviation. And SECONDS is the number of seconds in one UNIT. + +This is defined as a variable to make it possible to use time +units for a language other than English. It is not defined +as an option, because most other parts of Magit are always in +English.") + +(defun magit--age (date &optional abbreviate) + (cl-labels ((fn (age spec) + (pcase-let ((`(,char ,unit ,units ,weight) (car spec))) + (let ((cnt (round (/ age weight 1.0)))) + (if (or (not (cdr spec)) + (>= (/ age weight) 1)) + (list cnt (cond (abbreviate char) + ((= cnt 1) unit) + (t units))) + (fn age (cdr spec))))))) + (fn (abs (- (float-time) + (if (stringp date) + (string-to-number date) + date))) + magit--age-spec))) + +;;; _ +(provide 'magit-margin) +;;; magit-margin.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-merge.el b/code/elpa/magit-20220821.1819/magit-merge.el new file mode 100644 index 0000000..ead4d21 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-merge.el @@ -0,0 +1,318 @@ +;;; magit-merge.el --- Merge functionality -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements merge commands. + +;;; Code: + +(require 'magit) +(require 'magit-diff) + +(declare-function magit-git-push "magit-push" (branch target args)) + +;;; Commands + +;;;###autoload (autoload 'magit-merge "magit" nil t) +(transient-define-prefix magit-merge () + "Merge branches." + :man-page "git-merge" + :incompatible '(("--ff-only" "--no-ff")) + ["Arguments" + :if-not magit-merge-in-progress-p + ("-f" "Fast-forward only" "--ff-only") + ("-n" "No fast-forward" "--no-ff") + (magit-merge:--strategy) + (5 magit-merge:--strategy-option) + (5 "-b" "Ignore changes in amount of whitespace" "-Xignore-space-change") + (5 "-w" "Ignore whitespace when comparing lines" "-Xignore-all-space") + (5 magit-diff:--diff-algorithm :argument "-Xdiff-algorithm=") + (5 magit:--gpg-sign)] + ["Actions" + :if-not magit-merge-in-progress-p + [("m" "Merge" magit-merge-plain) + ("e" "Merge and edit message" magit-merge-editmsg) + ("n" "Merge but don't commit" magit-merge-nocommit) + ("a" "Absorb" magit-merge-absorb)] + [("p" "Preview merge" magit-merge-preview) + "" + ("s" "Squash merge" magit-merge-squash) + ("i" "Dissolve" magit-merge-into)]] + ["Actions" + :if magit-merge-in-progress-p + ("m" "Commit merge" magit-commit-create) + ("a" "Abort merge" magit-merge-abort)]) + +(defun magit-merge-arguments () + (transient-args 'magit-merge)) + +(transient-define-argument magit-merge:--strategy () + :description "Strategy" + :class 'transient-option + ;; key for merge and rebase: "-s" + ;; key for cherry-pick and revert: "=s" + ;; shortarg for merge and rebase: "-s" + ;; shortarg for cherry-pick and revert: none + :key "-s" + :argument "--strategy=" + :choices '("resolve" "recursive" "octopus" "ours" "subtree")) + +(transient-define-argument magit-merge:--strategy-option () + :description "Strategy Option" + :class 'transient-option + :key "-X" + :argument "--strategy-option=" + :choices '("ours" "theirs" "patience")) + +;;;###autoload +(defun magit-merge-plain (rev &optional args nocommit) + "Merge commit REV into the current branch; using default message. + +Unless there are conflicts or a prefix argument is used create a +merge commit using a generic commit message and without letting +the user inspect the result. With a prefix argument pretend the +merge failed to give the user the opportunity to inspect the +merge. + +\(git merge --no-edit|--no-commit [ARGS] REV)" + (interactive (list (magit-read-other-branch-or-commit "Merge") + (magit-merge-arguments) + current-prefix-arg)) + (magit-merge-assert) + (magit-run-git-async "merge" (if nocommit "--no-commit" "--no-edit") args rev)) + +;;;###autoload +(defun magit-merge-editmsg (rev &optional args) + "Merge commit REV into the current branch; and edit message. +Perform the merge and prepare a commit message but let the user +edit it. +\n(git merge --edit --no-ff [ARGS] REV)" + (interactive (list (magit-read-other-branch-or-commit "Merge") + (magit-merge-arguments))) + (magit-merge-assert) + (cl-pushnew "--no-ff" args :test #'equal) + (apply #'magit-run-git-with-editor "merge" "--edit" + (append (delete "--ff-only" args) + (list rev)))) + +;;;###autoload +(defun magit-merge-nocommit (rev &optional args) + "Merge commit REV into the current branch; pretending it failed. +Pretend the merge failed to give the user the opportunity to +inspect the merge and change the commit message. +\n(git merge --no-commit --no-ff [ARGS] REV)" + (interactive (list (magit-read-other-branch-or-commit "Merge") + (magit-merge-arguments))) + (magit-merge-assert) + (cl-pushnew "--no-ff" args :test #'equal) + (magit-run-git-async "merge" "--no-commit" args rev)) + +;;;###autoload +(defun magit-merge-into (branch &optional args) + "Merge the current branch into BRANCH and remove the former. + +Before merging, force push the source branch to its push-remote, +provided the respective remote branch already exists, ensuring +that the respective pull-request (if any) won't get stuck on some +obsolete version of the commits that are being merged. Finally +if `forge-branch-pullreq' was used to create the merged branch, +then also remove the respective remote branch." + (interactive + (list (magit-read-other-local-branch + (format "Merge `%s' into" + (or (magit-get-current-branch) + (magit-rev-parse "HEAD"))) + nil + (and-let* ((upstream (magit-get-upstream-branch)) + (upstream (cdr (magit-split-branch-name upstream)))) + (and (magit-branch-p upstream) upstream))) + (magit-merge-arguments))) + (let ((current (magit-get-current-branch)) + (head (magit-rev-parse "HEAD"))) + (when (zerop (magit-call-git "checkout" branch)) + (if current + (magit--merge-absorb current args) + (magit-run-git-with-editor "merge" args head))))) + +;;;###autoload +(defun magit-merge-absorb (branch &optional args) + "Merge BRANCH into the current branch and remove the former. + +Before merging, force push the source branch to its push-remote, +provided the respective remote branch already exists, ensuring +that the respective pull-request (if any) won't get stuck on some +obsolete version of the commits that are being merged. Finally +if `forge-branch-pullreq' was used to create the merged branch, +then also remove the respective remote branch." + (interactive (list (magit-read-other-local-branch "Absorb branch") + (magit-merge-arguments))) + (magit--merge-absorb branch args)) + +(defun magit--merge-absorb (branch args) + (when (equal branch (magit-main-branch)) + (unless (yes-or-no-p + (format "Do you really want to merge `%s' into another branch? " + branch)) + (user-error "Abort"))) + (if-let ((target (magit-get-push-branch branch t))) + (progn + (magit-git-push branch target (list "--force-with-lease")) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (not (zerop (process-exit-status process))) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (magit--merge-absorb-1 branch args)))))) + (magit--merge-absorb-1 branch args))) + +(defun magit--merge-absorb-1 (branch args) + (if-let ((pr (magit-get "branch" branch "pullRequest"))) + (magit-run-git-async + "merge" args "-m" + (format "Merge branch '%s'%s [#%s]" + branch + (let ((current (magit-get-current-branch))) + (if (equal current (magit-main-branch)) + "" + (format " into %s" current))) + pr) + branch) + (magit-run-git-async "merge" args "--no-edit" branch)) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (> (process-exit-status process) 0) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (magit-branch-maybe-delete-pr-remote branch) + (magit-branch-unset-pushRemote branch) + (magit-run-git "branch" "-D" branch)))))) + +;;;###autoload +(defun magit-merge-squash (rev) + "Squash commit REV into the current branch; don't create a commit. +\n(git merge --squash REV)" + (interactive (list (magit-read-other-branch-or-commit "Squash"))) + (magit-merge-assert) + (magit-run-git-async "merge" "--squash" rev)) + +;;;###autoload +(defun magit-merge-preview (rev) + "Preview result of merging REV into the current branch." + (interactive (list (magit-read-other-branch-or-commit "Preview merge"))) + (magit-merge-preview-setup-buffer rev)) + +;;;###autoload +(defun magit-merge-abort () + "Abort the current merge operation. +\n(git merge --abort)" + (interactive) + (unless (file-exists-p (magit-git-dir "MERGE_HEAD")) + (user-error "No merge in progress")) + (magit-confirm 'abort-merge) + (magit-run-git-async "merge" "--abort")) + +(defun magit-checkout-stage (file arg) + "During a conflict checkout and stage side, or restore conflict." + (interactive + (let ((file (magit-completing-read "Checkout file" + (magit-tracked-files) nil nil nil + 'magit-read-file-hist + (magit-current-file)))) + (cond ((member file (magit-unmerged-files)) + (list file (magit-checkout-read-stage file))) + ((yes-or-no-p (format "Restore conflicts in %s? " file)) + (list file "--merge")) + (t + (user-error "Quit"))))) + (pcase (cons arg (cddr (car (magit-file-status file)))) + ((or `("--ours" ?D ,_) + '("--ours" ?U ?A) + `("--theirs" ,_ ?D) + '("--theirs" ?A ?U)) + (magit-run-git "rm" "--" file)) + (_ (if (equal arg "--merge") + ;; This fails if the file was deleted on one + ;; side. And we cannot do anything about it. + (magit-run-git "checkout" "--merge" "--" file) + (magit-call-git "checkout" arg "--" file) + (magit-run-git "add" "-u" "--" file))))) + +;;; Utilities + +(defun magit-merge-in-progress-p () + (file-exists-p (magit-git-dir "MERGE_HEAD"))) + +(defun magit--merge-range (&optional head) + (unless head + (setq head (magit-get-shortname + (car (magit-file-lines (magit-git-dir "MERGE_HEAD")))))) + (and head + (concat (magit-git-string "merge-base" "--octopus" "HEAD" head) + ".." head))) + +(defun magit-merge-assert () + (or (not (magit-anything-modified-p t)) + (magit-confirm 'merge-dirty + "Merging with dirty worktree is risky. Continue"))) + +(defun magit-checkout-read-stage (file) + (magit-read-char-case (format "For %s checkout: " file) t + (?o "[o]ur stage" "--ours") + (?t "[t]heir stage" "--theirs") + (?c "[c]onflict" "--merge"))) + +;;; Sections + +(defvar magit-unmerged-section-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-log-section-map) + map) + "Keymap for `unmerged' sections.") + +(defun magit-insert-merge-log () + "Insert section for the on-going merge. +Display the heads that are being merged. +If no merge is in progress, do nothing." + (when (magit-merge-in-progress-p) + (let* ((heads (mapcar #'magit-get-shortname + (magit-file-lines (magit-git-dir "MERGE_HEAD")))) + (range (magit--merge-range (car heads)))) + (magit-insert-section (unmerged range) + (magit-insert-heading + (format "Merging %s:" (mapconcat #'identity heads ", "))) + (magit-insert-log + range + (let ((args magit-buffer-log-args)) + (unless (member "--decorate=full" magit-buffer-log-args) + (push "--decorate=full" args)) + args)))))) + +;;; _ +(provide 'magit-merge) +;;; magit-merge.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-mode.el b/code/elpa/magit-20220821.1819/magit-mode.el new file mode 100644 index 0000000..73718c0 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-mode.el @@ -0,0 +1,1581 @@ +;;; magit-mode.el --- Create and refresh Magit buffers -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements the abstract major-mode `magit-mode' from +;; which almost all other Magit major-modes derive. The code in here +;; is mostly concerned with creating and refreshing Magit buffers. + +;;; Code: + +(require 'magit-base) +(require 'magit-git) + +(require 'format-spec) +(require 'help-mode) +(require 'transient) + +;;; Options + +(defcustom magit-mode-hook + '(magit-load-config-extensions) + "Hook run when entering a mode derived from Magit mode." + :package-version '(magit . "3.0.0") + :group 'magit-modes + :type 'hook + :options '(magit-load-config-extensions + bug-reference-mode)) + +(defcustom magit-setup-buffer-hook + '(magit-maybe-save-repository-buffers + magit-set-buffer-margin) + "Hook run by `magit-setup-buffer'. + +This is run right after displaying the buffer and right before +generating or updating its content. `magit-mode-hook' and other, +more specific, `magit-mode-*-hook's on the other hand are run +right before displaying the buffer. Usually one of these hooks +should be used instead of this one." + :package-version '(magit . "2.3.0") + :group 'magit-modes + :type 'hook + :options '(magit-maybe-save-repository-buffers + magit-set-buffer-margin)) + +(defcustom magit-pre-refresh-hook '(magit-maybe-save-repository-buffers) + "Hook run before refreshing in `magit-refresh'. + +This hook, or `magit-post-refresh-hook', should be used +for functions that are not tied to a particular buffer. + +To run a function with a particular buffer current, use +`magit-refresh-buffer-hook' and use `derived-mode-p' +inside your function." + :package-version '(magit . "2.4.0") + :group 'magit-refresh + :type 'hook + :options '(magit-maybe-save-repository-buffers)) + +(defcustom magit-post-refresh-hook + '(magit-auto-revert-buffers + magit-run-post-commit-hook + magit-run-post-stage-hook + magit-run-post-unstage-hook) + "Hook run after refreshing in `magit-refresh'. + +This hook, or `magit-pre-refresh-hook', should be used +for functions that are not tied to a particular buffer. + +To run a function with a particular buffer current, use +`magit-refresh-buffer-hook' and use `derived-mode-p' +inside your function." + :package-version '(magit . "2.4.0") + :group 'magit-refresh + :type 'hook + :options '(magit-auto-revert-buffers + magit-run-post-commit-hook + magit-run-post-stage-hook + magit-run-post-unstage-hook)) + +(defcustom magit-display-buffer-function #'magit-display-buffer-traditional + "The function used to display a Magit buffer. + +All Magit buffers (buffers whose major-modes derive from +`magit-mode') are displayed using `magit-display-buffer', +which in turn uses the function specified here." + :package-version '(magit . "2.3.0") + :group 'magit-buffers + :type '(radio (function-item magit-display-buffer-traditional) + (function-item magit-display-buffer-same-window-except-diff-v1) + (function-item magit-display-buffer-fullframe-status-v1) + (function-item magit-display-buffer-fullframe-status-topleft-v1) + (function-item magit-display-buffer-fullcolumn-most-v1) + (function-item display-buffer) + (function :tag "Function"))) + +(defcustom magit-pre-display-buffer-hook '(magit-save-window-configuration) + "Hook run by `magit-display-buffer' before displaying the buffer." + :package-version '(magit . "2.3.0") + :group 'magit-buffers + :type 'hook + :get #'magit-hook-custom-get + :options '(magit-save-window-configuration)) + +(defcustom magit-post-display-buffer-hook '(magit-maybe-set-dedicated) + "Hook run by `magit-display-buffer' after displaying the buffer." + :package-version '(magit . "2.3.0") + :group 'magit-buffers + :type 'hook + :get #'magit-hook-custom-get + :options '(magit-maybe-set-dedicated)) + +(defcustom magit-generate-buffer-name-function + #'magit-generate-buffer-name-default-function + "The function used to generate the name for a Magit buffer." + :package-version '(magit . "2.3.0") + :group 'magit-buffers + :type '(radio (function-item magit-generate-buffer-name-default-function) + (function :tag "Function"))) + +(defcustom magit-buffer-name-format "%x%M%v: %t%x" + "The format string used to name Magit buffers. + +The following %-sequences are supported: + +`%m' The name of the major-mode, but with the `-mode' suffix + removed. + +`%M' Like \"%m\" but abbreviate `magit-status-mode' as `magit'. + +`%v' The value the buffer is locked to, in parentheses, or an + empty string if the buffer is not locked to a value. + +`%V' Like \"%v\", but the string is prefixed with a space, unless + it is an empty string. + +`%t' The top-level directory of the working tree of the + repository, or if `magit-uniquify-buffer-names' is non-nil + an abbreviation of that. + +`%x' If `magit-uniquify-buffer-names' is nil \"*\", otherwise the + empty string. Due to limitations of the `uniquify' package, + buffer names must end with the path. + +`%T' Obsolete, use \"%t%x\" instead. Like \"%t\", but append an + asterisk if and only if `magit-uniquify-buffer-names' is nil. + +The value should always contain \"%m\" or \"%M\", \"%v\" or +\"%V\", and \"%t\" (or the obsolete \"%T\"). + +If `magit-uniquify-buffer-names' is non-nil, then the value must +end with \"%t\" or \"%t%x\" (or the obsolete \"%T\"). See issue +#2841. + +This is used by `magit-generate-buffer-name-default-function'. +If another `magit-generate-buffer-name-function' is used, then +it may not respect this option, or on the contrary it may +support additional %-sequences." + :package-version '(magit . "2.12.0") + :group 'magit-buffers + :type 'string) + +(defcustom magit-uniquify-buffer-names t + "Whether to uniquify the names of Magit buffers." + :package-version '(magit . "2.3.0") + :group 'magit-buffers + :type 'boolean) + +(defcustom magit-bury-buffer-function #'magit-mode-quit-window + "The function used to bury or kill the current Magit buffer." + :package-version '(magit . "3.2.0") + :group 'magit-buffers + :type '(radio (function-item quit-window) + (function-item magit-mode-quit-window) + (function-item magit-restore-window-configuration) + (function :tag "Function"))) + +(defcustom magit-prefix-use-buffer-arguments 'selected + "Whether certain prefix commands reuse arguments active in relevant buffer. + +This affects the transient prefix commands `magit-diff', +`magit-log' and `magit-show-refs'. + +Valid values are: + +`always': Always use the set of arguments that is currently + active in the respective buffer, provided that buffer exists + of course. +`selected': Use the set of arguments from the respective + buffer, but only if it is displayed in a window of the current + frame. This is the default. +`current': Use the set of arguments from the respective buffer, + but only if it is the current buffer. +`never': Never use the set of arguments from the respective + buffer. + +For more information see info node `(magit)Transient Arguments +and Buffer Variables'." + :package-version '(magit . "3.0.0") + :group 'magit-buffers + :group 'magit-commands + :group 'magit-diff + :group 'magit-log + :type '(choice + (const :tag "always use args from buffer" always) + (const :tag "use args from buffer if displayed in frame" selected) + (const :tag "use args from buffer if it is current" current) + (const :tag "never use args from buffer" never))) + +(defcustom magit-direct-use-buffer-arguments 'selected + "Whether certain commands reuse arguments active in relevant buffer. + +This affects certain commands such as `magit-show-commit' that +are suffixes of the diff or log transient prefix commands, but +only if they are invoked directly, i.e. *not* as a suffix. + +Valid values are: + +`always': Always use the set of arguments that is currently + active in the respective buffer, provided that buffer exists + of course. +`selected': Use the set of arguments from the respective + buffer, but only if it is displayed in a window of the current + frame. This is the default. +`current': Use the set of arguments from the respective buffer, + but only if it is the current buffer. +`never': Never use the set of arguments from the respective + buffer. + +For more information see info node `(magit)Transient Arguments +and Buffer Variables'." + :package-version '(magit . "3.0.0") + :group 'magit-buffers + :group 'magit-commands + :group 'magit-diff + :group 'magit-log + :type '(choice + (const :tag "always use args from buffer" always) + (const :tag "use args from buffer if displayed in frame" selected) + (const :tag "use args from buffer if it is current" current) + (const :tag "never use args from buffer" never))) + +(defcustom magit-region-highlight-hook '(magit-diff-update-hunk-region) + "Functions used to highlight the region. + +Each function is run with the current section as only argument +until one of them returns non-nil. If all functions return nil, +then fall back to regular region highlighting." + :package-version '(magit . "2.1.0") + :group 'magit-refresh + :type 'hook + :options '(magit-diff-update-hunk-region)) + +(defcustom magit-create-buffer-hook nil + "Normal hook run after creating a new `magit-mode' buffer." + :package-version '(magit . "2.90.0") + :group 'magit-refresh + :type 'hook) + +(defcustom magit-refresh-buffer-hook nil + "Normal hook for `magit-refresh-buffer' to run after refreshing." + :package-version '(magit . "2.1.0") + :group 'magit-refresh + :type 'hook) + +(defcustom magit-refresh-status-buffer t + "Whether the status buffer is refreshed after running git. + +When this is non-nil, then the status buffer is automatically +refreshed after running git for side-effects, in addition to the +current Magit buffer, which is always refreshed automatically. + +Only set this to nil after exhausting all other options to +improve performance." + :package-version '(magit . "2.4.0") + :group 'magit-refresh + :group 'magit-status + :type 'boolean) + +(defcustom magit-refresh-verbose nil + "Whether to revert Magit buffers verbosely." + :package-version '(magit . "2.1.0") + :group 'magit-refresh + :type 'boolean) + +(defcustom magit-save-repository-buffers t + "Whether to save file-visiting buffers when appropriate. + +If non-nil, then all modified file-visiting buffers belonging +to the current repository may be saved before running Magit +commands and before creating or refreshing Magit buffers. +If `dontask', then this is done without user intervention, for +any other non-nil value the user has to confirm each save. + +The default is t to avoid surprises, but `dontask' is the +recommended value." + :group 'magit-essentials + :group 'magit-buffers + :type '(choice (const :tag "Never" nil) + (const :tag "Ask" t) + (const :tag "Save without asking" dontask))) + +;;; Key Bindings + +(defvar magit-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-section-mode-map) + ;; Don't function-quote but make sure all commands are autoloaded. + (define-key map [C-return] 'magit-visit-thing) + (define-key map (kbd "RET") 'magit-visit-thing) + (define-key map (kbd "M-TAB") 'magit-dired-jump) + (define-key map [M-tab] 'magit-section-cycle-diffs) + (define-key map (kbd "SPC") 'magit-diff-show-or-scroll-up) + (define-key map (kbd "S-SPC") 'magit-diff-show-or-scroll-down) + (define-key map (kbd "DEL") 'magit-diff-show-or-scroll-down) + (define-key map "+" 'magit-diff-more-context) + (define-key map "-" 'magit-diff-less-context) + (define-key map "0" 'magit-diff-default-context) + (define-key map "a" 'magit-cherry-apply) + (define-key map "A" 'magit-cherry-pick) + (define-key map "b" 'magit-branch) + (define-key map "B" 'magit-bisect) + (define-key map "c" 'magit-commit) + (define-key map "C" 'magit-clone) + (define-key map "d" 'magit-diff) + (define-key map "D" 'magit-diff-refresh) + (define-key map "e" 'magit-ediff-dwim) + (define-key map "E" 'magit-ediff) + (define-key map "f" 'magit-fetch) + (define-key map "F" 'magit-pull) + (define-key map "g" 'magit-refresh) + (define-key map "G" 'magit-refresh-all) + (define-key map "h" 'magit-dispatch) + (define-key map "?" 'magit-dispatch) + (define-key map "H" 'magit-describe-section) + (define-key map "i" 'magit-gitignore) + (define-key map "I" 'magit-init) + (define-key map "j" 'magit-status-quick) + (define-key map "J" 'magit-display-repository-buffer) + (define-key map "k" 'magit-delete-thing) + (define-key map "K" 'magit-file-untrack) + (define-key map "l" 'magit-log) + (define-key map "L" 'magit-log-refresh) + (define-key map "m" 'magit-merge) + (define-key map "M" 'magit-remote) + ;; section-map "n" magit-section-forward + ;; reserved "N" forge-dispatch + (define-key map "o" 'magit-submodule) + (define-key map "O" 'magit-subtree) + ;; section-map "p" magit-section-backward + (define-key map "P" 'magit-push) + (define-key map "q" 'magit-mode-bury-buffer) + (define-key map "Q" 'magit-git-command) + (define-key map ":" 'magit-git-command) + (define-key map "r" 'magit-rebase) + (define-key map "R" 'magit-file-rename) + (define-key map "s" 'magit-stage-file) + (define-key map "S" 'magit-stage-modified) + (define-key map "t" 'magit-tag) + (define-key map "T" 'magit-notes) + (define-key map "u" 'magit-unstage-file) + (define-key map "U" 'magit-unstage-all) + (define-key map "v" 'magit-revert-no-commit) + (define-key map "V" 'magit-revert) + (define-key map "w" 'magit-am) + (define-key map "W" 'magit-patch) + (define-key map "x" 'magit-reset-quickly) + (define-key map "X" 'magit-reset) + (define-key map "y" 'magit-show-refs) + (define-key map "Y" 'magit-cherry) + (define-key map "z" 'magit-stash) + (define-key map "Z" 'magit-worktree) + (define-key map "%" 'magit-worktree) + (define-key map "$" 'magit-process-buffer) + (define-key map "!" 'magit-run) + (define-key map ">" 'magit-sparse-checkout) + (define-key map (kbd "C-c C-c") 'magit-dispatch) + (define-key map (kbd "C-c C-e") 'magit-edit-thing) + (define-key map (kbd "C-c C-o") 'magit-browse-thing) + (define-key map (kbd "C-c C-w") 'magit-browse-thing) + (define-key map (kbd "C-w") 'magit-copy-section-value) + (define-key map (kbd "M-w") 'magit-copy-buffer-revision) + (define-key map [remap previous-line] 'magit-previous-line) + (define-key map [remap next-line] 'magit-next-line) + (define-key map [remap evil-previous-line] 'evil-previous-visual-line) + (define-key map [remap evil-next-line] 'evil-next-visual-line) + map) + "Parent keymap for all keymaps of modes derived from `magit-mode'.") + +(defun magit-delete-thing () + "This is a placeholder command. +Where applicable, section-specific keymaps bind another command +which deletes the thing at point." + (interactive) + (user-error "There is no thing at point that could be deleted")) + +(defun magit-visit-thing () + "This is a placeholder command. +Where applicable, section-specific keymaps bind another command +which visits the thing at point." + (interactive) + (if (eq transient-current-command 'magit-dispatch) + (call-interactively (key-binding (this-command-keys))) + (user-error "There is no thing at point that could be visited"))) + +(defun magit-edit-thing () + "This is a placeholder command. +Where applicable, section-specific keymaps bind another command +which lets you edit the thing at point, likely in another buffer." + (interactive) + (if (eq transient-current-command 'magit-dispatch) + (call-interactively (key-binding (this-command-keys))) + (user-error "There is no thing at point that could be edited"))) + +(defun magit-browse-thing () + "This is a placeholder command. +Where applicable, section-specific keymaps bind another command +which visits the thing at point using `browse-url'." + (interactive) + (user-error "There is no thing at point that could be browsed")) + +;;;###autoload +(defun magit-info () + "Visit the Magit manual." + (interactive) + (info "magit")) + +(defvar bug-reference-map) +(with-eval-after-load 'bug-reference + (define-key bug-reference-map [remap magit-visit-thing] + 'bug-reference-push-button)) + +(easy-menu-define magit-mode-menu magit-mode-map + "Magit menu" + ;; Similar to `magit-dispatch' but exclude: + ;; - commands that are available from context menus: + ;; apply, reverse, discard, stage, unstage, + ;; cherry-pick, revert, reset, + ;; describe-section + ;; - commands that are available from submenus: + ;; git-command, ediff-dwim + ;; - and: refresh-all, status-jump, status-quick. + '("Magit" + "---" "Inspect" + [" Bisect..." magit-bisect t] + [" Cherries..." magit-cherry t] + [" Diff..." magit-diff t] + [" Ediff..." magit-ediff t] + [" Log..." magit-log t] + [" References..." magit-show-refs t] + "---" "Manipulate" + [" Commit..." magit-commit t] + [" Stash..." magit-stash t] + [" Tag..." magit-tag t] + "---" + [" Branch..." magit-branch t] + [" Remote..." magit-remote t] + "---" + [" Merge..." magit-merge t] + [" Rebase..." magit-rebase t] + "---" "Transfer" + [" Fetch..." magit-fetch t] + [" Pull..." magit-pull t] + [" Push..." magit-push t] + "---" "Setup" + [" Clone..." magit-clone t] + [" Ignore..." magit-gitignore t] + [" Init..." magit-init t] + "---" + ("Advanced" + ["Run..." magit-run t] + "---" + ["Apply patches..." magit-am t] + ["Format patches..." magit-patch t] + "---" + ["Note..." magit-notes t] + "---" + ["Submodule..." magit-submodule t] + ["Subtree..." magit-subtree t] + ["Worktree..." magit-worktree t]) + "---" + ["Show command dispatcher..." magit-dispatch t] + ["Show manual" magit-info t] + ["Show another buffer" magit-display-repository-buffer t] + "---" + ("Change buffer arguments" + ["Diff arguments" magit-diff-refresh t] + ["Log arguments" magit-log-refresh t]) + ["Refresh buffer" magit-refresh t] + ["Bury buffer" magit-mode-bury-buffer t])) + +;;; Mode + +(defun magit-load-config-extensions () + "Load Magit extensions that are defined at the Git config layer." + (dolist (ext (magit-get-all "magit.extension")) + (let ((sym (intern (format "magit-%s-mode" ext)))) + (when (fboundp sym) + (funcall sym 1))))) + +(define-derived-mode magit-mode magit-section-mode "Magit" + "Parent major mode from which Magit major modes inherit. + +Magit is documented in info node `(magit)'." + :group 'magit + (hack-dir-local-variables-non-file-buffer) + (face-remap-add-relative 'header-line 'magit-header-line) + (setq mode-line-process (magit-repository-local-get 'mode-line-process)) + (setq-local revert-buffer-function #'magit-refresh-buffer) + (setq-local bookmark-make-record-function #'magit--make-bookmark) + (setq-local imenu-create-index-function #'magit--imenu-create-index) + (setq-local isearch-filter-predicate #'magit-section--open-temporarily)) + +;;; Local Variables + +(defvar-local magit-buffer-arguments nil) +(defvar-local magit-buffer-diff-args nil) +(defvar-local magit-buffer-diff-files nil) +(defvar-local magit-buffer-diff-files-suspended nil) +(defvar-local magit-buffer-file-name nil) +(defvar-local magit-buffer-files nil) +(defvar-local magit-buffer-log-args nil) +(defvar-local magit-buffer-log-files nil) +(defvar-local magit-buffer-range nil) +(defvar-local magit-buffer-range-hashed nil) +(defvar-local magit-buffer-refname nil) +(defvar-local magit-buffer-revision nil) +(defvar-local magit-buffer-revision-hash nil) +(defvar-local magit-buffer-revisions nil) +(defvar-local magit-buffer-typearg nil) +(defvar-local magit-buffer-upstream nil) + +;; These variables are also used in file-visiting buffers. +;; Because the user may change the major-mode, they have +;; to be permanent buffer-local. +(put 'magit-buffer-file-name 'permanent-local t) +(put 'magit-buffer-refname 'permanent-local t) +(put 'magit-buffer-revision 'permanent-local t) +(put 'magit-buffer-revision-hash 'permanent-local t) + +;; `magit-status' re-enables mode function but its refresher +;; function does not reinstate this. +(put 'magit-buffer-diff-files-suspended 'permanent-local t) + +(defvar-local magit-refresh-args nil + "Obsolete. Possibly the arguments used to refresh the current buffer. +Some third-party packages might still use this, but Magit does not.") +(put 'magit-refresh-args 'permanent-local t) +(make-obsolete-variable 'magit-refresh-args nil "Magit 3.0.0") + +(defvar magit-buffer-lock-functions nil + "Obsolete buffer-locking support for third-party modes. +Implement the generic function `magit-buffer-value' for +your mode instead of adding an entry to this variable.") +(make-obsolete-variable 'magit-buffer-lock-functions nil "Magit 3.0.0") + +(cl-defgeneric magit-buffer-value () + (and-let* ((fn (cdr (assq major-mode magit-buffer-lock-functions)))) + (funcall fn (with-no-warnings magit-refresh-args)))) + +(defvar-local magit-previous-section nil) +(put 'magit-previous-section 'permanent-local t) + +(defvar-local magit--imenu-group-types nil) +(defvar-local magit--imenu-item-types nil) + +;;; Setup Buffer + +(defmacro magit-setup-buffer (mode &optional locked &rest bindings) + (declare (indent 2)) + `(magit-setup-buffer-internal + ,mode ,locked + ,(cons 'list (mapcar (pcase-lambda (`(,var ,form)) + `(list ',var ,form)) + bindings)))) + +(defun magit-setup-buffer-internal (mode locked bindings) + (let* ((value (and locked + (with-temp-buffer + (pcase-dolist (`(,var ,val) bindings) + (set (make-local-variable var) val)) + (let ((major-mode mode)) + (magit-buffer-value))))) + (buffer (magit-get-mode-buffer mode value)) + (section (and buffer (magit-current-section))) + (created (not buffer))) + (unless buffer + (setq buffer (magit-generate-new-buffer mode value))) + (with-current-buffer buffer + (setq magit-previous-section section) + (funcall mode) + (magit-xref-setup #'magit-setup-buffer-internal bindings) + (pcase-dolist (`(,var ,val) bindings) + (set (make-local-variable var) val)) + (when created + (run-hooks 'magit-create-buffer-hook))) + (magit-display-buffer buffer) + (with-current-buffer buffer + (run-hooks 'magit-setup-buffer-hook) + (magit-refresh-buffer)) + buffer)) + +(defun magit-mode-setup (mode &rest args) + "Setup up a MODE buffer using ARGS to generate its content." + (declare (obsolete magit-setup-buffer "Magit 3.0.0")) + (with-no-warnings + (magit-mode-setup-internal mode args))) + +(defun magit-mode-setup-internal (mode args &optional locked) + "Setup up a MODE buffer using ARGS to generate its content. +When optional LOCKED is non-nil, then create a buffer that is +locked to its value, which is derived from MODE and ARGS." + (declare (obsolete magit-setup-buffer "Magit 3.0.0")) + (let* ((value (and locked + (with-temp-buffer + (with-no-warnings + (setq magit-refresh-args args)) + (let ((major-mode mode)) + (magit-buffer-value))))) + (buffer (magit-get-mode-buffer mode value)) + (section (and buffer (magit-current-section))) + (created (not buffer))) + (unless buffer + (setq buffer (magit-generate-new-buffer mode value))) + (with-current-buffer buffer + (setq magit-previous-section section) + (with-no-warnings + (setq magit-refresh-args args)) + (funcall mode) + (magit-xref-setup 'magit-mode-setup-internal args) + (when created + (run-hooks 'magit-create-buffer-hook))) + (magit-display-buffer buffer) + (with-current-buffer buffer + (run-hooks 'magit-mode-setup-hook) + (magit-refresh-buffer)))) + +;;; Display Buffer + +(defvar magit-display-buffer-noselect nil + "If non-nil, then `magit-display-buffer' doesn't call `select-window'.") + +(defun magit-display-buffer (buffer &optional display-function) + "Display BUFFER in some window and maybe select it. + +If optional DISPLAY-FUNCTION is non-nil, then use that to display +the buffer. Otherwise use `magit-display-buffer-function', which +is the normal case. + +Then, unless `magit-display-buffer-noselect' is non-nil, select +the window which was used to display the buffer. + +Also run the hooks `magit-pre-display-buffer-hook' +and `magit-post-display-buffer-hook'." + (with-current-buffer buffer + (run-hooks 'magit-pre-display-buffer-hook)) + (let ((window (funcall (or display-function magit-display-buffer-function) + buffer))) + (unless magit-display-buffer-noselect + (let* ((old-frame (selected-frame)) + (new-frame (window-frame window))) + (select-window window) + (unless (eq old-frame new-frame) + (select-frame-set-input-focus new-frame))))) + (with-current-buffer buffer + (run-hooks 'magit-post-display-buffer-hook))) + +(defun magit-display-buffer-traditional (buffer) + "Display BUFFER the way this has traditionally been done." + (display-buffer + buffer (if (and (derived-mode-p 'magit-mode) + (not (memq (with-current-buffer buffer major-mode) + '(magit-process-mode + magit-revision-mode + magit-diff-mode + magit-stash-mode + magit-status-mode)))) + '(display-buffer-same-window) + nil))) ; display in another window + +(defun magit-display-buffer-same-window-except-diff-v1 (buffer) + "Display BUFFER in the selected window except for some modes. +If a buffer's `major-mode' derives from `magit-diff-mode' or +`magit-process-mode', display it in another window. Display all +other buffers in the selected window." + (display-buffer + buffer (if (with-current-buffer buffer + (derived-mode-p 'magit-diff-mode 'magit-process-mode)) + '(nil (inhibit-same-window . t)) + '(display-buffer-same-window)))) + +(defun magit--display-buffer-fullframe (buffer alist) + (when-let ((window (or (display-buffer-reuse-window buffer alist) + (display-buffer-same-window buffer alist) + (display-buffer-pop-up-window buffer alist) + (display-buffer-use-some-window buffer alist)))) + (delete-other-windows window) + window)) + +(defun magit-display-buffer-fullframe-status-v1 (buffer) + "Display BUFFER, filling entire frame if BUFFER is a status buffer. +Otherwise, behave like `magit-display-buffer-traditional'." + (if (eq (with-current-buffer buffer major-mode) + 'magit-status-mode) + (display-buffer buffer '(magit--display-buffer-fullframe)) + (magit-display-buffer-traditional buffer))) + +(defun magit--display-buffer-topleft (buffer alist) + (or (display-buffer-reuse-window buffer alist) + (when-let ((window2 (display-buffer-pop-up-window buffer alist))) + (let ((window1 (get-buffer-window)) + (buffer1 (current-buffer)) + (buffer2 (window-buffer window2)) + (w2-quit-restore (window-parameter window2 'quit-restore))) + (set-window-buffer window1 buffer2) + (set-window-buffer window2 buffer1) + (select-window window2) + ;; Swap some window state that `magit-mode-quit-window' and + ;; `quit-restore-window' inspect. + (set-window-prev-buffers window2 (cdr (window-prev-buffers window1))) + (set-window-prev-buffers window1 nil) + (set-window-parameter window2 'magit-dedicated + (window-parameter window1 'magit-dedicated)) + (set-window-parameter window1 'magit-dedicated t) + (set-window-parameter window1 'quit-restore + (list 'window 'window + (nth 2 w2-quit-restore) + (nth 3 w2-quit-restore))) + (set-window-parameter window2 'quit-restore nil) + window1)))) + +(defun magit-display-buffer-fullframe-status-topleft-v1 (buffer) + "Display BUFFER, filling entire frame if BUFFER is a status buffer. +When BUFFER derives from `magit-diff-mode' or +`magit-process-mode', try to display BUFFER to the top or left of +the current buffer rather than to the bottom or right, as +`magit-display-buffer-fullframe-status-v1' would. Whether the +split is made vertically or horizontally is determined by +`split-window-preferred-function'." + (display-buffer + buffer + (cond ((eq (with-current-buffer buffer major-mode) + 'magit-status-mode) + '(magit--display-buffer-fullframe)) + ((with-current-buffer buffer + (derived-mode-p 'magit-diff-mode 'magit-process-mode)) + '(magit--display-buffer-topleft)) + (t + '(display-buffer-same-window))))) + +(defun magit--display-buffer-fullcolumn (buffer alist) + (when-let ((window (or (display-buffer-reuse-window buffer alist) + (display-buffer-same-window buffer alist) + (display-buffer-below-selected buffer alist)))) + (delete-other-windows-vertically window) + window)) + +(defun magit-display-buffer-fullcolumn-most-v1 (buffer) + "Display BUFFER using the full column except in some cases. +For most cases where BUFFER's `major-mode' derives from +`magit-mode', display it in the selected window and grow that +window to the full height of the frame, deleting other windows in +that column as necessary. However, display BUFFER in another +window if 1) BUFFER's mode derives from `magit-process-mode', or +2) BUFFER's mode derives from `magit-diff-mode', provided that +the mode of the current buffer derives from `magit-log-mode' or +`magit-cherry-mode'." + (display-buffer + buffer + (cond ((and (or (bound-and-true-p git-commit-mode) + (derived-mode-p 'magit-log-mode + 'magit-cherry-mode + 'magit-reflog-mode)) + (with-current-buffer buffer + (derived-mode-p 'magit-diff-mode))) + nil) + ((with-current-buffer buffer + (derived-mode-p 'magit-process-mode)) + nil) + (t + '(magit--display-buffer-fullcolumn))))) + +(defun magit-maybe-set-dedicated () + "Mark the selected window as dedicated if appropriate. + +If a new window was created to display the buffer, then remember +that fact. That information is used by `magit-mode-quit-window', +to determine whether the window should be deleted when its last +Magit buffer is buried." + (let ((window (get-buffer-window (current-buffer)))) + (when (and (window-live-p window) + (not (window-prev-buffers window))) + (set-window-parameter window 'magit-dedicated t)))) + +;;; Get Buffer + +(defvar-local magit--default-directory nil + "Value of `default-directory' when buffer is generated. +This exists to prevent a let-bound `default-directory' from +tricking `magit-get-mode-buffer' or `magit-mode-get-buffers' +into thinking a buffer belongs to a repo that it doesn't.") +(put 'magit--default-directory 'permanent-local t) + +(defun magit-mode-get-buffers () + (let ((topdir (magit-toplevel))) + (--filter (with-current-buffer it + (and (derived-mode-p 'magit-mode) + (equal magit--default-directory topdir))) + (buffer-list)))) + +(defvar-local magit-buffer-locked-p nil) +(put 'magit-buffer-locked-p 'permanent-local t) + +(defun magit-get-mode-buffer (mode &optional value frame) + "Return buffer belonging to the current repository whose major-mode is MODE. + +If no such buffer exists then return nil. Multiple buffers with +the same major-mode may exist for a repository but only one can +exist that hasn't been locked to its value. Return that buffer +\(or nil if there is no such buffer) unless VALUE is non-nil, in +which case return the buffer that has been locked to that value. + +If FRAME is nil or omitted, then consider all buffers. Otherwise + only consider buffers that are displayed in some live window + on some frame. +If `all', then consider all buffers on all frames. +If `visible', then only consider buffers on all visible frames. +If `selected' or t, then only consider buffers on the selected + frame. +If a frame, then only consider buffers on that frame." + (let ((topdir (magit--toplevel-safe))) + (cl-flet* ((b (buffer) + (with-current-buffer buffer + (and (eq major-mode mode) + (equal magit--default-directory topdir) + (if value + (and magit-buffer-locked-p + (equal (magit-buffer-value) value)) + (not magit-buffer-locked-p)) + buffer))) + (w (window) + (b (window-buffer window))) + (f (frame) + (seq-some #'w (window-list frame 'no-minibuf)))) + (pcase-exhaustive frame + ('nil (seq-some #'b (buffer-list))) + ('all (seq-some #'f (frame-list))) + ('visible (seq-some #'f (visible-frame-list))) + ((or 'selected 't) (seq-some #'w (window-list (selected-frame)))) + ((guard (framep frame)) (seq-some #'w (window-list frame))))))) + +(defun magit-mode-get-buffer (mode &optional create frame value) + (declare (obsolete magit-get-mode-buffer "Magit 3.0.0")) + (when create + (error "`magit-mode-get-buffer's CREATE argument is obsolete")) + (let ((topdir (magit--toplevel-safe))) + (--first (with-current-buffer it + (and (eq major-mode mode) + (equal magit--default-directory topdir) + (if value + (and magit-buffer-locked-p + (equal (magit-buffer-value) value)) + (not magit-buffer-locked-p)))) + (if frame + (mapcar #'window-buffer + (window-list (unless (eq frame t) frame))) + (buffer-list))))) + +(defun magit-generate-new-buffer (mode &optional value directory) + (let* ((default-directory (or directory (magit--toplevel-safe))) + (name (funcall magit-generate-buffer-name-function mode value)) + (buffer (generate-new-buffer name))) + (with-current-buffer buffer + (setq magit--default-directory default-directory) + (setq magit-buffer-locked-p (and value t)) + (magit-restore-section-visibility-cache mode)) + (when magit-uniquify-buffer-names + (add-to-list 'uniquify-list-buffers-directory-modes mode) + (with-current-buffer buffer + (setq list-buffers-directory (abbreviate-file-name default-directory))) + (let ((uniquify-buffer-name-style + (if (memq uniquify-buffer-name-style '(nil forward)) + 'post-forward-angle-brackets + uniquify-buffer-name-style))) + (uniquify-rationalize-file-buffer-names + name (file-name-directory (directory-file-name default-directory)) + buffer))) + buffer)) + +(defun magit-generate-buffer-name-default-function (mode &optional value) + "Generate buffer name for a MODE buffer in the current repository. +The returned name is based on `magit-buffer-name-format' and +takes `magit-uniquify-buffer-names' and VALUE, if non-nil, into +account." + (let ((m (substring (symbol-name mode) 0 -5)) + (v (and value (format "%s" (if (listp value) value (list value))))) + (n (if magit-uniquify-buffer-names + (file-name-nondirectory + (directory-file-name default-directory)) + (abbreviate-file-name default-directory)))) + (format-spec + magit-buffer-name-format + `((?m . ,m) + (?M . ,(if (eq mode 'magit-status-mode) "magit" m)) + (?v . ,(or v "")) + (?V . ,(if v (concat " " v) "")) + (?t . ,n) + (?x . ,(if magit-uniquify-buffer-names "" "*")) + (?T . ,(if magit-uniquify-buffer-names n (concat n "*"))))))) + +;;; Buffer Lock + +(defun magit-toggle-buffer-lock () + "Lock the current buffer to its value or unlock it. + +Locking a buffer to its value prevents it from being reused to +display another value. The name of a locked buffer contains its +value, which allows telling it apart from other locked buffers +and the unlocked buffer. + +Not all Magit buffers can be locked to their values, for example +it wouldn't make sense to lock a status buffer. + +There can only be a single unlocked buffer using a certain +major-mode per repository. So when a buffer is being unlocked +and another unlocked buffer already exists for that mode and +repository, then the former buffer is instead deleted and the +latter is displayed in its place." + (interactive) + (if magit-buffer-locked-p + (if-let ((unlocked (magit-get-mode-buffer major-mode))) + (let ((locked (current-buffer))) + (switch-to-buffer unlocked nil t) + (kill-buffer locked)) + (setq magit-buffer-locked-p nil) + (rename-buffer (funcall magit-generate-buffer-name-function + major-mode))) + (if-let ((value (magit-buffer-value))) + (if-let ((locked (magit-get-mode-buffer major-mode value))) + (let ((unlocked (current-buffer))) + (switch-to-buffer locked nil t) + (kill-buffer unlocked)) + (setq magit-buffer-locked-p t) + (rename-buffer (funcall magit-generate-buffer-name-function + major-mode value))) + (user-error "Buffer has no value it could be locked to")))) + +;;; Bury Buffer + +(defun magit-mode-bury-buffer (&optional kill-buffer) + "Bury the current buffer. +With a prefix argument, kill the buffer instead. +With two prefix arguments, also kill all Magit buffers associated +with this repository. +This is done using `magit-bury-buffer-function'." + (interactive "P") + ;; Kill all associated Magit buffers when a double prefix arg is given. + (when (>= (prefix-numeric-value kill-buffer) 16) + (let ((current (current-buffer))) + (dolist (buf (magit-mode-get-buffers)) + (unless (eq buf current) + (kill-buffer buf))))) + (funcall magit-bury-buffer-function kill-buffer)) + +(defun magit-mode-quit-window (kill-buffer) + "Quit the selected window and bury its buffer. + +This behaves similar to `quit-window', but when the window +was originally created to display a Magit buffer and the +current buffer is the last remaining Magit buffer that was +ever displayed in the selected window, then delete that +window." + (if (or (one-window-p) + (--first (let ((buffer (car it))) + (and (not (eq buffer (current-buffer))) + (buffer-live-p buffer) + (or (not (window-parameter nil 'magit-dedicated)) + (with-current-buffer buffer + (derived-mode-p 'magit-mode + 'magit-process-mode))))) + (window-prev-buffers))) + (quit-window kill-buffer) + (let ((window (selected-window))) + (quit-window kill-buffer) + (when (window-live-p window) + (delete-window window))))) + +;;; Refresh Buffers + +(defvar magit-inhibit-refresh nil) + +(defun magit-refresh () + "Refresh some buffers belonging to the current repository. + +Refresh the current buffer if its major mode derives from +`magit-mode', and refresh the corresponding status buffer. + +Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." + (interactive) + (unless magit-inhibit-refresh + (unwind-protect + (let ((start (current-time)) + (magit--refresh-cache (or magit--refresh-cache + (list (cons 0 0))))) + (when magit-refresh-verbose + (message "Refreshing magit...")) + (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) + (cond ((derived-mode-p 'magit-mode) + (magit-refresh-buffer)) + ((derived-mode-p 'tabulated-list-mode) + (revert-buffer))) + (--when-let (and magit-refresh-status-buffer + (not (derived-mode-p 'magit-status-mode)) + (magit-get-mode-buffer 'magit-status-mode)) + (with-current-buffer it + (magit-refresh-buffer))) + (magit-run-hook-with-benchmark 'magit-post-refresh-hook) + (when magit-refresh-verbose + (let* ((c (caar magit--refresh-cache)) + (a (+ c (cdar magit--refresh-cache)))) + (message "Refreshing magit...done (%.3fs, cached %s/%s (%.0f%%))" + (float-time (time-subtract (current-time) start)) + c a (* (/ c (* a 1.0)) 100))))) + (run-hooks 'magit-unwind-refresh-hook)))) + +(defun magit-refresh-all () + "Refresh all buffers belonging to the current repository. + +Refresh all Magit buffers belonging to the current repository, +and revert buffers that visit files located inside the current +repository. + +Run hooks `magit-pre-refresh-hook' and `magit-post-refresh-hook'." + (interactive) + (magit-run-hook-with-benchmark 'magit-pre-refresh-hook) + (dolist (buffer (magit-mode-get-buffers)) + (with-current-buffer buffer (magit-refresh-buffer))) + (magit-run-hook-with-benchmark 'magit-post-refresh-hook)) + +(defvar-local magit-refresh-start-time nil) + +(defun magit-refresh-buffer (&rest _ignore) + "Refresh the current Magit buffer." + (setq magit-refresh-start-time (current-time)) + (let ((refresh (intern (format "%s-refresh-buffer" + (substring (symbol-name major-mode) 0 -5)))) + (magit--refresh-cache (or magit--refresh-cache (list (cons 0 0))))) + (when (functionp refresh) + (when magit-refresh-verbose + (message "Refreshing buffer `%s'..." (buffer-name))) + (let* ((buffer (current-buffer)) + (windows (cl-mapcan + (lambda (window) + (with-selected-window window + (with-current-buffer buffer + (and-let* ((section (magit-section-at))) + `(( ,window + ,section + ,@(magit-section-get-relative-position + section))))))) + ;; If it qualifies, then the selected window + ;; comes first, but we want to handle it last + ;; so that its `magit-section-movement-hook' + ;; run can override the effects of other runs. + (or (nreverse (get-buffer-window-list buffer nil t)) + (list (selected-window)))))) + (deactivate-mark) + (setq magit-section-pre-command-section nil) + (setq magit-section-highlight-overlays nil) + (setq magit-section-highlighted-sections nil) + (setq magit-section-unhighlight-sections nil) + (let ((inhibit-read-only t)) + (erase-buffer) + (save-excursion + (apply refresh (with-no-warnings magit-refresh-args)))) + (pcase-dolist (`(,window . ,args) windows) + (if (eq buffer (window-buffer window)) + (with-selected-window window + (apply #'magit-section-goto-successor args)) + (with-current-buffer buffer + (let ((magit-section-movement-hook nil)) + (apply #'magit-section-goto-successor args))))) + (run-hooks 'magit-refresh-buffer-hook) + (magit-section-update-highlight) + (set-buffer-modified-p nil)) + (when magit-refresh-verbose + (message "Refreshing buffer `%s'...done (%.3fs)" (buffer-name) + (float-time (time-subtract (current-time) + magit-refresh-start-time))))))) + +;;; Save File-Visiting Buffers + +(defvar magit--disable-save-buffers nil) + +(defun magit-pre-command-hook () + (setq magit--disable-save-buffers nil)) +(add-hook 'pre-command-hook #'magit-pre-command-hook) + +(defvar magit-after-save-refresh-buffers nil) + +(defun magit-after-save-refresh-buffers () + (dolist (buffer magit-after-save-refresh-buffers) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (magit-refresh-buffer)))) + (setq magit-after-save-refresh-buffers nil) + (remove-hook 'post-command-hook #'magit-after-save-refresh-buffers)) + +(defun magit-after-save-refresh-status () + "Refresh the status buffer of the current repository. + +This function is intended to be added to `after-save-hook'. + +If the status buffer does not exist or the file being visited in +the current buffer isn't inside the working tree of a repository, +then do nothing. + +Note that refreshing a Magit buffer is done by re-creating its +contents from scratch, which can be slow in large repositories. +If you are not satisfied with Magit's performance, then you +should obviously not add this function to that hook." + (when (and (not magit--disable-save-buffers) + (magit-inside-worktree-p t)) + (--when-let (ignore-errors (magit-get-mode-buffer 'magit-status-mode)) + (add-to-list 'magit-after-save-refresh-buffers it) + (add-hook 'post-command-hook #'magit-after-save-refresh-buffers)))) + +(defun magit-maybe-save-repository-buffers () + "Maybe save file-visiting buffers belonging to the current repository. +Do so if `magit-save-repository-buffers' is non-nil. You should +not remove this from any hooks, instead set that variable to nil +if you so desire." + (when (and magit-save-repository-buffers + (not magit--disable-save-buffers)) + (setq magit--disable-save-buffers t) + (let ((msg (current-message))) + (magit-save-repository-buffers + (eq magit-save-repository-buffers 'dontask)) + (when (and msg + (current-message) + (not (equal msg (current-message)))) + (message "%s" msg))))) + +(add-hook 'magit-pre-refresh-hook #'magit-maybe-save-repository-buffers) +(add-hook 'magit-pre-call-git-hook #'magit-maybe-save-repository-buffers) +(add-hook 'magit-pre-start-git-hook #'magit-maybe-save-repository-buffers) + +(defvar-local magit-inhibit-refresh-save nil) + +(defun magit-save-repository-buffers (&optional arg) + "Save file-visiting buffers belonging to the current repository. +After any buffer where `buffer-save-without-query' is non-nil +is saved without asking, the user is asked about each modified +buffer which visits a file in the current repository. Optional +argument (the prefix) non-nil means save all with no questions." + (interactive "P") + (when-let ((topdir (magit-rev-parse-safe "--show-toplevel"))) + (let ((remote (file-remote-p default-directory)) + (save-some-buffers-action-alist + `((?Y (lambda (buffer) + (with-current-buffer buffer + (setq buffer-save-without-query t) + (save-buffer))) + "to save the current buffer and remember choice") + (?N (lambda (buffer) + (with-current-buffer buffer + (setq magit-inhibit-refresh-save t))) + "to skip the current buffer and remember choice") + ,@save-some-buffers-action-alist))) + (save-some-buffers + arg (lambda () + (and buffer-file-name + ;; - Check whether refreshing is disabled. + (not magit-inhibit-refresh-save) + ;; - Check whether the visited file is either on the + ;; same remote as the repository, or both are on + ;; the local system. + (equal (file-remote-p buffer-file-name) remote) + ;; Delayed checks that are more expensive for remote + ;; repositories, due to the required network access. + ;; - Check whether the file is inside the repository. + (equal (magit-rev-parse-safe "--show-toplevel") topdir) + ;; - Check whether the file is actually writable. + (file-writable-p buffer-file-name))))))) + +;;; Restore Window Configuration + +(defvar magit-inhibit-save-previous-winconf nil) + +(defvar-local magit-previous-window-configuration nil) +(put 'magit-previous-window-configuration 'permanent-local t) + +(defun magit-save-window-configuration () + "Save the current window configuration. + +Later, when the buffer is buried, it may be restored by +`magit-restore-window-configuration'." + (if magit-inhibit-save-previous-winconf + (when (eq magit-inhibit-save-previous-winconf 'unset) + (setq magit-previous-window-configuration nil)) + (unless (get-buffer-window (current-buffer) (selected-frame)) + (setq magit-previous-window-configuration + (current-window-configuration))))) + +(defun magit-restore-window-configuration (&optional kill-buffer) + "Bury or kill the current buffer and restore previous window configuration." + (let ((winconf magit-previous-window-configuration) + (buffer (current-buffer)) + (frame (selected-frame))) + (quit-window kill-buffer (selected-window)) + (when (and winconf (equal frame (window-configuration-frame winconf))) + (set-window-configuration winconf) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (setq magit-previous-window-configuration nil)))))) + +;;; Buffer History + +(defun magit-go-backward () + "Move backward in current buffer's history." + (interactive) + (if help-xref-stack + (help-xref-go-back (current-buffer)) + (user-error "No previous entry in buffer's history"))) + +(defun magit-go-forward () + "Move forward in current buffer's history." + (interactive) + (if help-xref-forward-stack + (help-xref-go-forward (current-buffer)) + (user-error "No next entry in buffer's history"))) + +(defun magit-insert-xref-buttons () + "Insert xref buttons." + (when (and (not magit-buffer-locked-p) + (or help-xref-stack help-xref-forward-stack)) + (when help-xref-stack + (magit-xref-insert-button help-back-label 'magit-xref-backward)) + (when help-xref-forward-stack + (when help-xref-stack + (insert " ")) + (magit-xref-insert-button help-forward-label 'magit-xref-forward)))) + +(defun magit-xref-insert-button (label type) + (magit-insert-section (button label) + (insert-text-button label 'type type + 'help-args (list (current-buffer))))) + +(define-button-type 'magit-xref-backward + :supertype 'help-back + 'mouse-face 'magit-section-highlight + 'help-echo (purecopy "mouse-2, RET: go back to previous history entry")) + +(define-button-type 'magit-xref-forward + :supertype 'help-forward + 'mouse-face 'magit-section-highlight + 'help-echo (purecopy "mouse-2, RET: go back to next history entry")) + +(defvar magit-xref-modes + '(magit-log-mode + magit-reflog-mode + magit-diff-mode + magit-revision-mode) + "List of modes for which to insert navigation buttons.") + +(defun magit-xref-setup (fn args) + (when (memq major-mode magit-xref-modes) + (when help-xref-stack-item + (push (cons (point) help-xref-stack-item) help-xref-stack) + (setq help-xref-forward-stack nil)) + (when (called-interactively-p 'interactive) + (--when-let (nthcdr 10 help-xref-stack) + (setcdr it nil))) + (setq help-xref-stack-item + (list 'magit-xref-restore fn default-directory args)))) + +(defun magit-xref-restore (fn dir args) + (setq default-directory dir) + (funcall fn major-mode nil args) + (magit-refresh-buffer)) + +;;; Repository-Local Cache + +(defvar magit-repository-local-cache nil + "Alist mapping `magit-toplevel' paths to alists of key/value pairs.") + +(defun magit-repository-local-repository () + "Return the key for the current repository." + (or (bound-and-true-p magit--default-directory) + (magit-toplevel))) + +(defun magit-repository-local-set (key value &optional repository) + "Set the repository-local VALUE for KEY. + +Unless specified, REPOSITORY is the current buffer's repository. + +If REPOSITORY is nil (meaning there is no current repository), +then the value is not cached, and we return nil." + (let* ((repokey (or repository (magit-repository-local-repository))) + (cache (assoc repokey magit-repository-local-cache))) + ;; Don't cache values for a nil REPOSITORY, as the 'set' and 'get' + ;; calls for some KEY may happen in unrelated contexts. + (when repokey + (if cache + (let ((keyvalue (assoc key (cdr cache)))) + (if keyvalue + ;; Update pre-existing value for key. + (setcdr keyvalue value) + ;; No such key in repository-local cache. + (push (cons key value) (cdr cache)))) + ;; No cache for this repository. + (push (cons repokey (list (cons key value))) + magit-repository-local-cache))))) + +(defun magit-repository-local-exists-p (key &optional repository) + "Non-nil when a repository-local value exists for KEY. + +Return a (KEY . VALUE) cons cell. + +The KEY is matched using `equal'. + +Unless specified, REPOSITORY is the current buffer's repository." + (and-let* ((cache (assoc (or repository + (magit-repository-local-repository)) + magit-repository-local-cache))) + (assoc key (cdr cache)))) + +(defun magit-repository-local-get (key &optional default repository) + "Return the repository-local value for KEY. + +Return DEFAULT if no value for KEY exists. + +The KEY is matched using `equal'. + +Unless specified, REPOSITORY is the current buffer's repository." + (if-let ((keyvalue (magit-repository-local-exists-p key repository))) + (cdr keyvalue) + default)) + +(defun magit-repository-local-delete (key &optional repository) + "Delete the repository-local value for KEY. + +Unless specified, REPOSITORY is the current buffer's repository." + (when-let ((cache (assoc (or repository + (magit-repository-local-repository)) + magit-repository-local-cache))) + (setf cache (compat-assoc-delete-all key cache)))) + +(defmacro magit--with-repository-local-cache (key &rest body) + (declare (indent 1) (debug (form body))) + (let ((k (cl-gensym))) + `(let ((,k ,key)) + (if-let ((kv (magit-repository-local-exists-p ,k))) + (cdr kv) + (let ((v ,(macroexp-progn body))) + (magit-repository-local-set ,k v) + v))))) + +(defun magit-preserve-section-visibility-cache () + (when (derived-mode-p 'magit-status-mode 'magit-refs-mode) + (magit-repository-local-set + (cons major-mode 'magit-section-visibility-cache) + magit-section-visibility-cache))) + +(defun magit-restore-section-visibility-cache (mode) + (setq magit-section-visibility-cache + (magit-repository-local-get + (cons mode 'magit-section-visibility-cache)))) + +(defun magit-zap-caches (&optional all) + "Zap caches for the current repository. + +Remove the repository's entry from `magit-repository-local-cache', +remove the host's entry from `magit--host-git-version-cache', set +`magit-section-visibility-cache' to nil for all Magit buffers of +the repository and set `magit--libgit-available-p' to `unknown'. + +With a prefix argument or if optional ALL is non-nil, discard the +mentioned caches completely." + (interactive) + (cond (all + (setq magit-repository-local-cache nil) + (setq magit--host-git-version-cache nil) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (derived-mode-p 'magit-mode) + (setq magit-section-visibility-cache nil))))) + (t + (magit-with-toplevel + (setq magit-repository-local-cache + (cl-delete default-directory + magit-repository-local-cache + :key #'car :test #'equal)) + (setq magit--host-git-version-cache + (cl-delete (file-remote-p default-directory) + magit--host-git-version-cache + :key #'car :test #'equal))) + (dolist (buffer (magit-mode-get-buffers)) + (with-current-buffer buffer + (setq magit-section-visibility-cache nil))))) + (setq magit--libgit-available-p 'unknown)) + +;;; Imenu Support + +(defun magit--imenu-create-index () + ;; If `which-function-mode' is active, then the create-index + ;; function is called at the time the major-mode is being enabled. + ;; Modes that derive from `magit-mode' have not populated the buffer + ;; at that time yet, so we have to abort. + (and magit-root-section + (or magit--imenu-group-types + magit--imenu-item-types) + (let ((index + (cl-mapcan + (lambda (section) + (cond + (magit--imenu-group-types + (and (if (eq (car-safe magit--imenu-group-types) 'not) + (not (magit-section-match + (cdr magit--imenu-group-types) + section)) + (magit-section-match magit--imenu-group-types section)) + (and-let* ((children (oref section children))) + `((,(magit--imenu-index-name section) + ,@(mapcar (lambda (s) + (cons (magit--imenu-index-name s) + (oref s start))) + children)))))) + (magit--imenu-item-types + (and (magit-section-match magit--imenu-item-types section) + `((,(magit--imenu-index-name section) + . ,(oref section start))))))) + (oref magit-root-section children)))) + (if (and magit--imenu-group-types (symbolp magit--imenu-group-types)) + (cdar index) + index)))) + +(defun magit--imenu-index-name (section) + (let ((heading (buffer-substring-no-properties + (oref section start) + (1- (or (oref section content) + (oref section end)))))) + (save-match-data + (cond + ((and (magit-section-match [commit logbuf] section) + (string-match "[^ ]+\\([ *|]*\\).+" heading)) + (replace-match " " t t heading 1)) + ((magit-section-match + '([branch local branchbuf] [tag tags branchbuf]) section) + (oref section value)) + ((magit-section-match [branch remote branchbuf] section) + (concat (oref (oref section parent) value) "/" + (oref section value))) + ((string-match " ([0-9]+)\\'" heading) + (substring heading 0 (match-beginning 0))) + (t heading))))) + +;;; Bookmark support + +(declare-function bookmark-get-filename "bookmark" (bookmark-name-or-record)) +(declare-function bookmark-make-record-default "bookmark" + (&optional no-file no-context posn)) +(declare-function bookmark-prop-get "bookmark" (bookmark-name-or-record prop)) +(declare-function bookmark-prop-set "bookmark" (bookmark-name-or-record prop val)) + +(defun magit--make-bookmark () + "Create a bookmark for the current Magit buffer. +Input values are the major-mode's `magit-bookmark-name' method, +and the buffer-local values of the variables referenced in its +`magit-bookmark-variables' property." + (require 'bookmark) + (if (plist-member (symbol-plist major-mode) 'magit-bookmark-variables) + ;; `bookmark-make-record-default's return value does not match + ;; (NAME . ALIST), even though it is used as the default value + ;; of `bookmark-make-record-function', which states that such + ;; functions must do that. See #4356. + (let ((bookmark (cons nil (bookmark-make-record-default 'no-file)))) + (bookmark-prop-set bookmark 'handler #'magit--handle-bookmark) + (bookmark-prop-set bookmark 'mode major-mode) + (bookmark-prop-set bookmark 'filename (magit-toplevel)) + (bookmark-prop-set bookmark 'defaults (list (magit-bookmark-name))) + (dolist (var (get major-mode 'magit-bookmark-variables)) + (bookmark-prop-set bookmark var (symbol-value var))) + (bookmark-prop-set + bookmark 'magit-hidden-sections + (--keep (and (oref it hidden) + (cons (oref it type) + (if (derived-mode-p 'magit-stash-mode) + (string-replace magit-buffer-revision + magit-buffer-revision-hash + (oref it value)) + (oref it value)))) + (oref magit-root-section children))) + bookmark) + (user-error "Bookmarking is not implemented for %s buffers" major-mode))) + +(defun magit--handle-bookmark (bookmark) + "Open a bookmark created by `magit--make-bookmark'. +Call the `magit-*-setup-buffer' function of the the major-mode +with the variables' values as arguments, which were recorded by +`magit--make-bookmark'. Ignore `magit-display-buffer-function'." + (let ((buffer (let ((default-directory (bookmark-get-filename bookmark)) + (mode (bookmark-prop-get bookmark 'mode)) + (magit-display-buffer-function #'identity) + (magit-display-buffer-noselect t)) + (apply (intern (format "%s-setup-buffer" + (substring (symbol-name mode) 0 -5))) + (--map (bookmark-prop-get bookmark it) + (get mode 'magit-bookmark-variables)))))) + (set-buffer buffer) ; That is the interface we have to adhere to. + (when-let ((hidden (bookmark-prop-get bookmark 'magit-hidden-sections))) + (with-current-buffer buffer + (dolist (child (oref magit-root-section children)) + (if (member (cons (oref child type) + (oref child value)) + hidden) + (magit-section-hide child) + (magit-section-show child))))) + ;; Compatibility with `bookmark+' package. See #4356. + (when (bound-and-true-p bmkp-jump-display-function) + (funcall bmkp-jump-display-function (current-buffer))) + nil)) + +(put 'magit--handle-bookmark 'bookmark-handler-type "Magit") + +(cl-defgeneric magit-bookmark-name () + "Return name for bookmark to current buffer." + (format "%s%s" + (substring (symbol-name major-mode) 0 -5) + (if-let ((vars (get major-mode 'magit-bookmark-variables))) + (cl-mapcan (lambda (var) + (let ((val (symbol-value var))) + (if (and val (atom val)) + (list val) + val))) + vars) + ""))) + +;;; Utilities + +(defun magit-toggle-verbose-refresh () + "Toggle whether Magit refreshes buffers verbosely. +Enabling this helps figuring out which sections are bottlenecks. +The additional output can be found in the *Messages* buffer." + (interactive) + (setq magit-refresh-verbose (not magit-refresh-verbose)) + (message "%s verbose refreshing" + (if magit-refresh-verbose "Enabled" "Disabled"))) + +(defun magit-run-hook-with-benchmark (hook) + (when hook + (if magit-refresh-verbose + (let ((start (current-time))) + (message "Running %s..." hook) + (run-hooks hook) + (message "Running %s...done (%.3fs)" hook + (float-time (time-subtract (current-time) start)))) + (run-hooks hook)))) + +;;; _ +(provide 'magit-mode) +;;; magit-mode.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-notes.el b/code/elpa/magit-20220821.1819/magit-notes.el new file mode 100644 index 0000000..8df09bb --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-notes.el @@ -0,0 +1,201 @@ +;;; magit-notes.el --- Notes support -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for `git-notes'. + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-notes "magit" nil t) +(transient-define-prefix magit-notes () + "Edit notes attached to commits." + :man-page "git-notes" + ["Configure local settings" + ("c" magit-core.notesRef) + ("d" magit-notes.displayRef)] + ["Configure global settings" + ("C" magit-global-core.notesRef) + ("D" magit-global-notes.displayRef)] + ["Arguments for prune" + :if-not magit-notes-merging-p + ("-n" "Dry run" ("-n" "--dry-run"))] + ["Arguments for edit and remove" + :if-not magit-notes-merging-p + (magit-notes:--ref)] + ["Arguments for merge" + :if-not magit-notes-merging-p + (magit-notes:--strategy)] + ["Actions" + :if-not magit-notes-merging-p + ("T" "Edit" magit-notes-edit) + ("r" "Remove" magit-notes-remove) + ("m" "Merge" magit-notes-merge) + ("p" "Prune" magit-notes-prune)] + ["Actions" + :if magit-notes-merging-p + ("c" "Commit merge" magit-notes-merge-commit) + ("a" "Abort merge" magit-notes-merge-abort)]) + +(defun magit-notes-merging-p () + (let ((dir (magit-git-dir "NOTES_MERGE_WORKTREE"))) + (and (file-directory-p dir) + (directory-files dir nil "^[^.]")))) + +(transient-define-infix magit-core.notesRef () + :class 'magit--git-variable + :variable "core.notesRef" + :reader #'magit-notes-read-ref + :prompt "Set local core.notesRef") + +(transient-define-infix magit-notes.displayRef () + :class 'magit--git-variable + :variable "notes.displayRef" + :multi-value t + :reader #'magit-notes-read-refs + :prompt "Set local notes.displayRef") + +(transient-define-infix magit-global-core.notesRef () + :class 'magit--git-variable + :variable "core.notesRef" + :global t + :reader #'magit-notes-read-ref + :prompt "Set global core.notesRef") + +(transient-define-infix magit-global-notes.displayRef () + :class 'magit--git-variable + :variable "notes.displayRef" + :global t + :multi-value t + :reader #'magit-notes-read-refs + :prompt "Set global notes.displayRef") + +(transient-define-argument magit-notes:--ref () + :description "Manipulate ref" + :class 'transient-option + :key "-r" + :argument "--ref=" + :reader #'magit-notes-read-ref) + +(transient-define-argument magit-notes:--strategy () + :description "Merge strategy" + :class 'transient-option + :shortarg "-s" + :argument "--strategy=" + :choices '("manual" "ours" "theirs" "union" "cat_sort_uniq")) + +(defun magit-notes-edit (commit &optional ref) + "Edit the note attached to COMMIT. +REF is the notes ref used to store the notes. + +Interactively or when optional REF is nil use the value of Git +variable `core.notesRef' or \"refs/notes/commits\" if that is +undefined." + (interactive (magit-notes-read-args "Edit notes")) + (magit-run-git-with-editor "notes" (and ref (concat "--ref=" ref)) + "edit" commit)) + +(defun magit-notes-remove (commit &optional ref) + "Remove the note attached to COMMIT. +REF is the notes ref from which the note is removed. + +Interactively or when optional REF is nil use the value of Git +variable `core.notesRef' or \"refs/notes/commits\" if that is +undefined." + (interactive (magit-notes-read-args "Remove notes")) + (magit-run-git-with-editor "notes" (and ref (concat "--ref=" ref)) + "remove" commit)) + +(defun magit-notes-merge (ref) + "Merge the notes ref REF into the current notes ref. + +The current notes ref is the value of Git variable +`core.notesRef' or \"refs/notes/commits\" if that is undefined. + +When there are conflicts, then they have to be resolved in the +temporary worktree \".git/NOTES_MERGE_WORKTREE\". When +done use `magit-notes-merge-commit' to finish. To abort +use `magit-notes-merge-abort'." + (interactive (list (magit-read-string-ns "Merge reference"))) + (magit-run-git-with-editor "notes" "merge" ref)) + +(defun magit-notes-merge-commit () + "Commit the current notes ref merge. +Also see `magit-notes-merge'." + (interactive) + (magit-run-git-with-editor "notes" "merge" "--commit")) + +(defun magit-notes-merge-abort () + "Abort the current notes ref merge. +Also see `magit-notes-merge'." + (interactive) + (magit-run-git-with-editor "notes" "merge" "--abort")) + +(defun magit-notes-prune (&optional dry-run) + "Remove notes about unreachable commits." + (interactive (list (and (member "--dry-run" (transient-args 'magit-notes)) t))) + (when dry-run + (magit-process-buffer)) + (magit-run-git-with-editor "notes" "prune" (and dry-run "--dry-run"))) + +;;; Readers + +(defun magit-notes-read-ref (prompt _initial-input history) + (and-let* ((ref (magit-completing-read + prompt (magit-list-notes-refnames) nil nil + (and-let* ((def (magit-get "core.notesRef"))) + (if (string-prefix-p "refs/notes/" def) + (substring def 11) + def)) + history))) + (if (string-prefix-p "refs/" ref) + ref + (concat "refs/notes/" ref)))) + +(defun magit-notes-read-refs (prompt &optional _initial-input _history) + (mapcar (lambda (ref) + (if (string-prefix-p "refs/" ref) + ref + (concat "refs/notes/" ref))) + (completing-read-multiple + (concat prompt ": ") + (magit-list-notes-refnames) nil nil + (mapconcat (lambda (ref) + (if (string-prefix-p "refs/notes/" ref) + (substring ref 11) + ref)) + (magit-get-all "notes.displayRef") + ",")))) + +(defun magit-notes-read-args (prompt) + (list (magit-read-branch-or-commit prompt (magit-stash-at-point)) + (and-let* ((str (--first (string-match "^--ref=\\(.+\\)" it) + (transient-args 'magit-notes)))) + (match-string 1 str)))) + +;;; _ +(provide 'magit-notes) +;;; magit-notes.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-obsolete.el b/code/elpa/magit-20220821.1819/magit-obsolete.el new file mode 100644 index 0000000..4d27752 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-obsolete.el @@ -0,0 +1,111 @@ +;;; magit-obsolete.el --- Obsolete definitions -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library defines aliases for obsolete variables and functions. + +;;; Code: + +(require 'magit) + +;;; Obsolete since v3.0.0 + +(define-obsolete-function-alias 'magit-diff-visit-file-worktree + #'magit-diff-visit-worktree-file "Magit 3.0.0") + +(define-obsolete-function-alias 'magit-status-internal + #'magit-status-setup-buffer "Magit 3.0.0") + +(define-obsolete-variable-alias 'magit-mode-setup-hook + 'magit-setup-buffer-hook "Magit 3.0.0") + +(define-obsolete-variable-alias 'magit-branch-popup-show-variables + 'magit-branch-direct-configure "Magit 3.0.0") + +(define-obsolete-function-alias 'magit-dispatch-popup + #'magit-dispatch "Magit 3.0.0") + +(define-obsolete-function-alias 'magit-repolist-column-dirty + #'magit-repolist-column-flag "Magit 3.0.0") + +(define-obsolete-variable-alias 'magit-disable-line-numbers + 'magit-section-disable-line-numbers "Magit 3.0.0") + +(define-obsolete-variable-alias 'inhibit-magit-refresh + 'magit-inhibit-refresh "Magit 3.0.0") + +(defun magit--magit-popup-warning () + (display-warning 'magit "\ +Magit no longer uses Magit-Popup. +It now uses Transient. +See https://emacsair.me/2019/02/14/transient-0.1. + +However your configuration and/or some third-party package that +you use still depends on the `magit-popup' package. But because +`magit' no longer depends on that, `package' has removed it from +your system. + +If some package that you use still depends on `magit-popup' but +does not declare it as a dependency, then please contact its +maintainer about that and install `magit-popup' explicitly. + +If you yourself use functions that are defined in `magit-popup' +in your configuration, then the next step depends on what you use +that for. + +* If you use `magit-popup' to define your own popups but do not + modify any of Magit's old popups, then you have to install + `magit-popup' explicitly. (You can also migrate to Transient, + but there is no need to rush that.) + +* If you add additional arguments and/or actions to Magit's popups, + then you have to port that to modify the new \"transients\" instead. + See https://github.com/magit/magit/wiki/\ +Converting-popup-modifications-to-transient-modifications + +To find installed packages that still use `magit-popup' you can +use e.g. \"M-x rgrep RET magit-popup RET RET ~/.emacs.d/ RET\".")) +(cl-eval-when (eval load) + (unless (require (quote magit-popup) nil t) + (defun magit-define-popup-switch (&rest _) + (magit--magit-popup-warning)) + (defun magit-define-popup-option (&rest _) + (magit--magit-popup-warning)) + (defun magit-define-popup-variable (&rest _) + (magit--magit-popup-warning)) + (defun magit-define-popup-action (&rest _) + (magit--magit-popup-warning)) + (defun magit-define-popup-sequence-action (&rest _) + (magit--magit-popup-warning)) + (defun magit-define-popup-key (&rest _) + (magit--magit-popup-warning)) + (defun magit-define-popup-keys-deferred (&rest _) + (magit--magit-popup-warning)) + (defun magit-change-popup-key (&rest _) + (magit--magit-popup-warning)) + (defun magit-remove-popup-key (&rest _) + (magit--magit-popup-warning)))) + +;;; _ +(provide 'magit-obsolete) +;;; magit-obsolete.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-patch.el b/code/elpa/magit-20220821.1819/magit-patch.el new file mode 100644 index 0000000..c114cf2 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-patch.el @@ -0,0 +1,326 @@ +;;; magit-patch.el --- Creating and applying patches -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements patch commands. + +;;; Code: + +(require 'magit) + +;;; Options + +(defcustom magit-patch-save-arguments '(exclude "--stat") + "Control arguments used by the command `magit-patch-save'. + +`magit-patch-save' (which see) saves a diff for the changes +shown in the current buffer in a patch file. It may use the +same arguments as used in the buffer or a subset thereof, or +a constant list of arguments, depending on this option and +the prefix argument." + :package-version '(magit . "2.12.0") + :group 'magit-diff + :type '(choice (const :tag "use buffer arguments" buffer) + (cons :tag "use buffer arguments except" + (const :format "" exclude) + (repeat :format "%v%i\n" + (string :tag "Argument"))) + (repeat :tag "use constant arguments" + (string :tag "Argument")))) + +;;; Commands + +;;;###autoload (autoload 'magit-patch "magit-patch" nil t) +(transient-define-prefix magit-patch () + "Create or apply patches." + ["Actions" + [("c" "Create patches" magit-patch-create) + ("w" "Apply patches" magit-am)] + [("a" "Apply plain patch" magit-patch-apply) + ("s" "Save diff as patch" magit-patch-save)] + [("r" "Request pull" magit-request-pull)]]) + +;;;###autoload (autoload 'magit-patch-create "magit-patch" nil t) +(transient-define-prefix magit-patch-create (range args files) + "Create patches for the commits in RANGE. +When a single commit is given for RANGE, create a patch for the +changes introduced by that commit (unlike 'git format-patch' +which creates patches for all commits that are reachable from +`HEAD' but not from the specified commit)." + :man-page "git-format-patch" + :incompatible '(("--subject-prefix=" "--rfc")) + ["Mail arguments" + (6 magit-format-patch:--in-reply-to) + (6 magit-format-patch:--thread) + (6 magit-format-patch:--from) + (6 magit-format-patch:--to) + (6 magit-format-patch:--cc)] + ["Patch arguments" + (magit-format-patch:--base) + (magit-format-patch:--reroll-count) + (5 magit-format-patch:--interdiff) + (magit-format-patch:--range-diff) + (magit-format-patch:--subject-prefix) + ("C-m r " "RFC subject prefix" "--rfc") + ("C-m l " "Add cover letter" "--cover-letter") + (5 magit-format-patch:--cover-from-description) + (5 magit-format-patch:--notes) + (magit-format-patch:--output-directory)] + ["Diff arguments" + (magit-diff:-U) + (magit-diff:-M) + (magit-diff:-C) + (magit-diff:--diff-algorithm) + (magit:--) + (7 "-b" "Ignore whitespace changes" ("-b" "--ignore-space-change")) + (7 "-w" "Ignore all whitespace" ("-w" "--ignore-all-space"))] + ["Actions" + ("c" "Create patches" magit-patch-create)] + (interactive + (if (not (eq transient-current-command 'magit-patch-create)) + (list nil nil nil) + (cons (if-let ((revs (magit-region-values 'commit t))) + (concat (car (last revs)) "^.." (car revs)) + (let ((range (magit-read-range-or-commit + "Format range or commit"))) + (if (string-search ".." range) + range + (format "%s~..%s" range range)))) + (let ((args (transient-args 'magit-patch-create))) + (list (-filter #'stringp args) + (cdr (assoc "--" args))))))) + (if (not range) + (transient-setup 'magit-patch-create) + (magit-run-git "format-patch" range args "--" files) + (when (member "--cover-letter" args) + (save-match-data + (find-file + (expand-file-name + (concat (and-let* ((v (transient-arg-value "--reroll-count=" args))) + (format "v%s-" v)) + "0000-cover-letter.patch") + (let ((topdir (magit-toplevel))) + (if-let ((dir (transient-arg-value "--output-directory=" args))) + (expand-file-name dir topdir) + topdir)))))))) + +(transient-define-argument magit-format-patch:--in-reply-to () + :description "In reply to" + :class 'transient-option + :key "C-m C-r" + :argument "--in-reply-to=") + +(transient-define-argument magit-format-patch:--thread () + :description "Thread style" + :class 'transient-option + :key "C-m s " + :argument "--thread=" + :reader #'magit-format-patch-select-thread-style) + +(defun magit-format-patch-select-thread-style (&rest _ignore) + (magit-read-char-case "Thread style " t + (?d "[d]eep" "deep") + (?s "[s]hallow" "shallow"))) + +(transient-define-argument magit-format-patch:--base () + :description "Insert base commit" + :class 'transient-option + :key "C-m b " + :argument "--base=" + :reader #'magit-format-patch-select-base) + +(defun magit-format-patch-select-base (prompt initial-input history) + (or (magit-completing-read prompt (cons "auto" (magit-list-refnames)) + nil nil initial-input history "auto") + (user-error "Nothing selected"))) + +(transient-define-argument magit-format-patch:--reroll-count () + :description "Reroll count" + :class 'transient-option + :key "C-m v " + :shortarg "-v" + :argument "--reroll-count=" + :reader #'transient-read-number-N+) + +(transient-define-argument magit-format-patch:--interdiff () + :description "Insert interdiff" + :class 'transient-option + :key "C-m d i" + :argument "--interdiff=" + :reader #'magit-transient-read-revision) + +(transient-define-argument magit-format-patch:--range-diff () + :description "Insert range-diff" + :class 'transient-option + :key "C-m d r" + :argument "--range-diff=" + :reader #'magit-format-patch-select-range-diff) + +(defun magit-format-patch-select-range-diff (prompt _initial-input _history) + (magit-read-range-or-commit prompt)) + +(transient-define-argument magit-format-patch:--subject-prefix () + :description "Subject Prefix" + :class 'transient-option + :key "C-m p " + :argument "--subject-prefix=") + +(transient-define-argument magit-format-patch:--cover-from-description () + :description "Use branch description" + :class 'transient-option + :key "C-m D " + :argument "--cover-from-description=" + :reader #'magit-format-patch-select-description-mode) + +(defun magit-format-patch-select-description-mode (&rest _ignore) + (magit-read-char-case "Use description as " t + (?m "[m]essage" "message") + (?s "[s]ubject" "subject") + (?a "[a]uto" "auto") + (?n "[n]othing" "none"))) + +(transient-define-argument magit-format-patch:--notes () + :description "Insert commentary from notes" + :class 'transient-option + :key "C-m n " + :argument "--notes=" + :reader #'magit-notes-read-ref) + +(transient-define-argument magit-format-patch:--from () + :description "From" + :class 'transient-option + :key "C-m C-f" + :argument "--from=" + :reader #'magit-transient-read-person) + +(transient-define-argument magit-format-patch:--to () + :description "To" + :class 'transient-option + :key "C-m C-t" + :argument "--to=" + :reader #'magit-transient-read-person) + +(transient-define-argument magit-format-patch:--cc () + :description "CC" + :class 'transient-option + :key "C-m C-c" + :argument "--cc=" + :reader #'magit-transient-read-person) + +(transient-define-argument magit-format-patch:--output-directory () + :description "Output directory" + :class 'transient-option + :key "C-m o " + :shortarg "-o" + :argument "--output-directory=" + :reader #'transient-read-existing-directory) + +;;;###autoload (autoload 'magit-patch-apply "magit-patch" nil t) +(transient-define-prefix magit-patch-apply (file &rest args) + "Apply the patch file FILE." + :man-page "git-apply" + ["Arguments" + ("-i" "Also apply to index" "--index") + ("-c" "Only apply to index" "--cached") + ("-3" "Fall back on 3way merge" ("-3" "--3way"))] + ["Actions" + ("a" "Apply patch" magit-patch-apply)] + (interactive + (if (not (eq transient-current-command 'magit-patch-apply)) + (list nil) + (list (expand-file-name + (read-file-name "Apply patch: " + default-directory nil nil + (and-let* ((file (magit-file-at-point))) + (file-relative-name file)))) + (transient-args 'magit-patch-apply)))) + (if (not file) + (transient-setup 'magit-patch-apply) + (magit-run-git "apply" args "--" (magit-convert-filename-for-git file)))) + +;;;###autoload +(defun magit-patch-save (file &optional arg) + "Write current diff into patch FILE. + +What arguments are used to create the patch depends on the value +of `magit-patch-save-arguments' and whether a prefix argument is +used. + +If the value is the symbol `buffer', then use the same arguments +as the buffer. With a prefix argument use no arguments. + +If the value is a list beginning with the symbol `exclude', then +use the same arguments as the buffer except for those matched by +entries in the cdr of the list. The comparison is done using +`string-prefix-p'. With a prefix argument use the same arguments +as the buffer. + +If the value is a list of strings (including the empty list), +then use those arguments. With a prefix argument use the same +arguments as the buffer. + +Of course the arguments that are required to actually show the +same differences as those shown in the buffer are always used." + (interactive (list (read-file-name "Write patch file: " default-directory) + current-prefix-arg)) + (unless (derived-mode-p 'magit-diff-mode) + (user-error "Only diff buffers can be saved as patches")) + (let ((rev magit-buffer-range) + (typearg magit-buffer-typearg) + (args magit-buffer-diff-args) + (files magit-buffer-diff-files)) + (cond ((eq magit-patch-save-arguments 'buffer) + (when arg + (setq args nil))) + ((eq (car-safe magit-patch-save-arguments) 'exclude) + (unless arg + (setq args (-difference args (cdr magit-patch-save-arguments))))) + ((not arg) + (setq args magit-patch-save-arguments))) + (with-temp-file file + (magit-git-insert "diff" rev "-p" typearg args "--" files))) + (magit-refresh)) + +;;;###autoload +(defun magit-request-pull (url start end) + "Request upstream to pull from your public repository. + +URL is the url of your publicly accessible repository. +START is a commit that already is in the upstream repository. +END is the last commit, usually a branch name, which upstream +is asked to pull. START has to be reachable from that commit." + (interactive + (list (magit-get "remote" (magit-read-remote "Remote") "url") + (magit-read-branch-or-commit "Start" (magit-get-upstream-branch)) + (magit-read-branch-or-commit "End"))) + (let ((dir default-directory)) + ;; mu4e changes default-directory + (compose-mail) + (setq default-directory dir)) + (message-goto-body) + (magit-git-insert "request-pull" start url end) + (set-buffer-modified-p nil)) + +;;; _ +(provide 'magit-patch) +;;; magit-patch.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-pkg.el b/code/elpa/magit-20220821.1819/magit-pkg.el new file mode 100644 index 0000000..f874943 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-pkg.el @@ -0,0 +1,19 @@ +(define-package "magit" "20220821.1819" "A Git porcelain inside Emacs." + '((emacs "25.1") + (compat "28.1.1.2") + (dash "20210826") + (git-commit "20220222") + (magit-section "20220325") + (transient "20220325") + (with-editor "20220318")) + :commit "712be4632b0ddc7899ca90db8f9be20d90b4326f" :authors + '(("Marius Vollmer" . "marius.vollmer@gmail.com") + ("Jonas Bernoulli" . "jonas@bernoul.li")) + :maintainer + '("Jonas Bernoulli" . "jonas@bernoul.li") + :keywords + '("git" "tools" "vc") + :url "https://github.com/magit/magit") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/magit-20220821.1819/magit-process.el b/code/elpa/magit-20220821.1819/magit-process.el new file mode 100644 index 0000000..2aa8167 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-process.el @@ -0,0 +1,1223 @@ +;;; magit-process.el --- Process functionality -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements the tools used to run Git for side-effects. + +;; Note that the functions used to run Git and then consume its +;; output, are defined in `magit-git.el'. There's a bit of overlap +;; though. + +;;; Code: + +(require 'magit-base) +(require 'magit-git) +(require 'magit-mode) + +(require 'ansi-color) +(require 'with-editor) + +;;; Options + +(defcustom magit-process-connection-type (not (eq system-type 'cygwin)) + "Connection type used for the Git process. + +If nil, use pipes: this is usually more efficient, and works on Cygwin. +If t, use ptys: this enables Magit to prompt for passphrases when needed." + :group 'magit-process + :type '(choice (const :tag "pipe" nil) + (const :tag "pty" t))) + +(defcustom magit-need-cygwin-noglob + (and (eq system-type 'windows-nt) + (with-temp-buffer + (let ((process-environment + (append magit-git-environment process-environment))) + (condition-case e + (process-file magit-git-executable + nil (current-buffer) nil + "-c" "alias.echo=!echo" "echo" "x{0}") + (file-error + (lwarn 'magit-process :warning + "Could not run Git: %S" e)))) + (equal "x0\n" (buffer-string)))) + "Whether to use a workaround for Cygwin's globbing behavior. + +If non-nil, add environment variables to `process-environment' to +prevent the git.exe distributed by Cygwin and MSYS2 from +attempting to perform glob expansion when called from a native +Windows build of Emacs. See #2246." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type '(choice (const :tag "Yes" t) + (const :tag "No" nil))) + +(defcustom magit-process-popup-time -1 + "Popup the process buffer if a command takes longer than this many seconds." + :group 'magit-process + :type '(choice (const :tag "Never" -1) + (const :tag "Immediately" 0) + (integer :tag "After this many seconds"))) + +(defcustom magit-process-log-max 32 + "Maximum number of sections to keep in a process log buffer. +When adding a new section would go beyond the limit set here, +then the older half of the sections are remove. Sections that +belong to processes that are still running are never removed. +When this is nil, no sections are ever removed." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type '(choice (const :tag "Never remove old sections" nil) integer)) + +(defvar magit-process-extreme-logging nil + "Whether `magit-process-file' logs to the *Messages* buffer. + +Only intended for temporary use when you try to figure out how +Magit uses Git behind the scene. Output that normally goes to +the magit-process buffer continues to go there. Not all output +goes to either of these two buffers. + +Also see `magit-git-debug'.") + +(defcustom magit-process-error-tooltip-max-lines 20 + "The number of lines for `magit-process-error-lines' to return. + +These are displayed in a tooltip for `mode-line-process' errors. + +If `magit-process-error-tooltip-max-lines' is nil, the tooltip +displays the text of `magit-process-error-summary' instead." + :package-version '(magit . "2.12.0") + :group 'magit-process + :type '(choice (const :tag "Use summary line" nil) + integer)) + +(defcustom magit-credential-cache-daemon-socket + (--some (pcase-let ((`(,prog . ,args) (split-string it))) + (if (and prog + (string-match-p + "\\`\\(?:\\(?:/.*/\\)?git-credential-\\)?cache\\'" prog)) + (or (cl-loop for (opt val) on args + if (string= opt "--socket") + return val) + (expand-file-name "~/.git-credential-cache/socket")))) + ;; Note: `magit-process-file' is not yet defined when + ;; evaluating this form, so we use `process-lines'. + (ignore-errors + (let ((process-environment + (append magit-git-environment process-environment))) + (process-lines magit-git-executable + "config" "--get-all" "credential.helper")))) + "If non-nil, start a credential cache daemon using this socket. + +When using Git's cache credential helper in the normal way, Emacs +sends a SIGHUP to the credential daemon after the git subprocess +has exited, causing the daemon to also quit. This can be avoided +by starting the `git-credential-cache--daemon' process directly +from Emacs. + +The function `magit-maybe-start-credential-cache-daemon' takes +care of starting the daemon if necessary, using the value of this +option as the socket. If this option is nil, then it does not +start any daemon. Likewise if another daemon is already running, +then it starts no new daemon. This function has to be a member +of the hook variable `magit-credential-hook' for this to work. +If an error occurs while starting the daemon, most likely because +the necessary executable is missing, then the function removes +itself from the hook, to avoid further futile attempts." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type '(choice (file :tag "Socket") + (const :tag "Don't start a cache daemon" nil))) + +(defcustom magit-process-yes-or-no-prompt-regexp + (concat " [\[(]" + "\\([Yy]\\(?:es\\)?\\)" + "[/|]" + "\\([Nn]o?\\)" + ;; OpenSSH v8 prints this. See #3969. + "\\(?:/\\[fingerprint\\]\\)?" + "[\])] ?[?:]? ?$") + "Regexp matching Yes-or-No prompts of Git and its subprocesses." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type 'regexp) + +(defcustom magit-process-password-prompt-regexps + '("^\\(Enter \\)?[Pp]assphrase\\( for \\(RSA \\)?key '.*'\\)?: ?$" + ;; Match-group 99 is used to identify the "user@host" part. + "^\\(Enter \\)?[Pp]assword\\( for '?\\(https?://\\)?\\(?99:[^']*\\)'?\\)?: ?$" + "Please enter the passphrase for the ssh key" + "Please enter the passphrase to unlock the OpenPGP secret key" + "^.*'s password: ?$" + "^Token: $" ; For git-credential-manager-core (#4318). + "^Yubikey for .*: ?$" + "^Enter PIN for .*: ?$") + "List of regexps matching password prompts of Git and its subprocesses. +Also see `magit-process-find-password-functions'." + :package-version '(magit . "3.0.0") + :group 'magit-process + :type '(repeat (regexp))) + +(defcustom magit-process-find-password-functions nil + "List of functions to try in sequence to get a password. + +These functions may be called when git asks for a password, which +is detected using `magit-process-password-prompt-regexps'. They +are called if and only if matching the prompt resulted in the +value of the 99th submatch to be non-nil. Therefore users can +control for which prompts these functions should be called by +putting the host name in the 99th submatch, or not. + +If the functions are called, then they are called in the order +given, with the host name as only argument, until one of them +returns non-nil. If they are not called or none of them returns +non-nil, then the password is read from the user instead." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type 'hook + :options '(magit-process-password-auth-source)) + +(defcustom magit-process-username-prompt-regexps + '("^Username for '.*': ?$") + "List of regexps matching username prompts of Git and its subprocesses." + :package-version '(magit . "2.1.0") + :group 'magit-process + :type '(repeat (regexp))) + +(defcustom magit-process-prompt-functions nil + "List of functions used to forward arbitrary questions to the user. + +Magit has dedicated support for forwarding username and password +prompts and Yes-or-No questions asked by Git and its subprocesses +to the user. This can be customized using other options in the +`magit-process' customization group. + +If you encounter a new question that isn't handled by default, +then those options should be used instead of this hook. + +However subprocesses may also ask questions that differ too much +from what the code related to the above options assume, and this +hook allows users to deal with such questions explicitly. + +Each function is called with the process and the output string +as arguments until one of the functions returns non-nil. The +function is responsible for asking the user the appropriate +question using e.g. `read-char-choice' and then forwarding the +answer to the process using `process-send-string'. + +While functions such as `magit-process-yes-or-no-prompt' may not +be sufficient to handle some prompt, it may still be of benefit +to look at the implementations to gain some insights on how to +implement such functions." + :package-version '(magit . "3.0.0") + :group 'magit-process + :type 'hook) + +(defcustom magit-process-ensure-unix-line-ending t + "Whether Magit should ensure a unix coding system when talking to Git." + :package-version '(magit . "2.6.0") + :group 'magit-process + :type 'boolean) + +(defcustom magit-process-display-mode-line-error t + "Whether Magit should retain and highlight process errors in the mode line." + :package-version '(magit . "2.12.0") + :group 'magit-process + :type 'boolean) + +(defface magit-process-ok + '((t :inherit magit-section-heading :foreground "green")) + "Face for zero exit-status." + :group 'magit-faces) + +(defface magit-process-ng + '((t :inherit magit-section-heading :foreground "red")) + "Face for non-zero exit-status." + :group 'magit-faces) + +(defface magit-mode-line-process + '((t :inherit mode-line-emphasis)) + "Face for `mode-line-process' status when Git is running for side-effects." + :group 'magit-faces) + +(defface magit-mode-line-process-error + '((t :inherit error)) + "Face for `mode-line-process' error status. + +Used when `magit-process-display-mode-line-error' is non-nil." + :group 'magit-faces) + +;;; Process Mode + +(defvar magit-process-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + map) + "Keymap for `magit-process-mode'.") + +(define-derived-mode magit-process-mode magit-mode "Magit Process" + "Mode for looking at Git process output." + :group 'magit-process + (hack-dir-local-variables-non-file-buffer) + (setq magit--imenu-item-types 'process)) + +(defun magit-process-buffer (&optional nodisplay) + "Display the current repository's process buffer. + +If that buffer doesn't exist yet, then create it. +Non-interactively return the buffer and unless +optional NODISPLAY is non-nil also display it." + (interactive) + (let ((topdir (magit-toplevel))) + (unless topdir + (magit--with-safe-default-directory nil + (setq topdir default-directory) + (let (prev) + (while (not (equal topdir prev)) + (setq prev topdir) + (setq topdir (file-name-directory (directory-file-name topdir))))))) + (let ((buffer (or (--first (with-current-buffer it + (and (eq major-mode 'magit-process-mode) + (equal default-directory topdir))) + (buffer-list)) + (magit-generate-new-buffer 'magit-process-mode + nil topdir)))) + (with-current-buffer buffer + (if magit-root-section + (when magit-process-log-max + (magit-process-truncate-log)) + (magit-process-mode) + (let ((inhibit-read-only t) + (magit-insert-section--parent nil) + (magit-insert-section--oldroot nil)) + (make-local-variable 'text-property-default-nonsticky) + (magit-insert-section (processbuf) + (insert "\n"))))) + (unless nodisplay + (magit-display-buffer buffer)) + buffer))) + +(defun magit-process-kill () + "Kill the process at point." + (interactive) + (when-let ((process (magit-section-value-if 'process))) + (unless (eq (process-status process) 'run) + (user-error "Process isn't running")) + (magit-confirm 'kill-process) + (kill-process process))) + +;;; Synchronous Processes + +(defvar magit-process-raise-error nil) + +(defun magit-git (&rest args) + "Call Git synchronously in a separate process, for side-effects. + +Option `magit-git-executable' specifies the Git executable. +The arguments ARGS specify arguments to Git, they are flattened +before use. + +Process output goes into a new section in the buffer returned by +`magit-process-buffer'. If Git exits with a non-zero status, +then raise an error." + (let ((magit-process-raise-error t)) + (magit-call-git args))) + +(defun magit-run-git (&rest args) + "Call Git synchronously in a separate process, and refresh. + +Function `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The arguments ARGS specify arguments to Git, they are flattened +before use. + +After Git returns, the current buffer (if it is a Magit buffer) +as well as the current repository's status buffer are refreshed. + +Process output goes into a new section in the buffer returned by +`magit-process-buffer'." + (let ((magit--refresh-cache (list (cons 0 0)))) + (magit-call-git args) + (when (member (car args) '("init" "clone")) + ;; Creating a new repository invalidates the cache. + (setq magit--refresh-cache nil)) + (magit-refresh))) + +(defvar magit-pre-call-git-hook nil) + +(defun magit-call-git (&rest args) + "Call Git synchronously in a separate process. + +Function `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The arguments ARGS specify arguments to Git, they are flattened +before use. + +Process output goes into a new section in the buffer returned by +`magit-process-buffer'." + (run-hooks 'magit-pre-call-git-hook) + (let ((default-process-coding-system (magit--process-coding-system))) + (apply #'magit-call-process + (magit-git-executable) + (magit-process-git-arguments args)))) + +(defun magit-call-process (program &rest args) + "Call PROGRAM synchronously in a separate process. +Process output goes into a new section in the buffer returned by +`magit-process-buffer'." + (pcase-let ((`(,process-buf . ,section) + (magit-process-setup program args))) + (magit-process-finish + (let ((inhibit-read-only t)) + (apply #'magit-process-file program nil process-buf nil args)) + process-buf (current-buffer) default-directory section))) + +(defun magit-process-git (destination &rest args) + "Call Git synchronously in a separate process, returning its exit code. +DESTINATION specifies how to handle the output, like for +`call-process', except that file handlers are supported. +Enable Cygwin's \"noglob\" option during the call and +ensure unix eol conversion." + (apply #'magit-process-file + (magit-git-executable) + nil destination nil + (magit-process-git-arguments args))) + +(defun magit-process-file (process &optional infile buffer display &rest args) + "Process files synchronously in a separate process. +Identical to `process-file' but temporarily enable Cygwin's +\"noglob\" option during the call and ensure unix eol +conversion." + (when magit-process-extreme-logging + (let ((inhibit-message t)) + (message "$ %s" (magit-process--format-arguments process args)))) + (let ((process-environment (magit-process-environment)) + (default-process-coding-system (magit--process-coding-system))) + (apply #'process-file process infile buffer display args))) + +(defun magit-process-environment () + ;; The various w32 hacks are only applicable when running on the + ;; local machine. As of Emacs 25.1, a local binding of + ;; process-environment different from the top-level value affects + ;; the environment used in + ;; tramp-sh-handle-{start-file-process,process-file}. + (let ((local (not (file-remote-p default-directory)))) + (append magit-git-environment + (and local + (cdr (assoc magit-git-executable magit-git-w32-path-hack))) + (and local magit-need-cygwin-noglob + (mapcar (lambda (var) + (concat var "=" (--if-let (getenv var) + (concat it " noglob") + "noglob"))) + '("CYGWIN" "MSYS"))) + process-environment))) + +(defvar magit-this-process nil) + +(defun magit-run-git-with-input (&rest args) + "Call Git in a separate process. +ARGS is flattened and then used as arguments to Git. + +The current buffer's content is used as the process's standard +input. The buffer is assumed to be temporary and thus OK to +modify. + +Function `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The remaining arguments ARGS specify arguments to Git, they are +flattened before use." + (when (eq system-type 'windows-nt) + ;; On w32, git expects UTF-8 encoded input, ignore any user + ;; configuration telling us otherwise (see #3250). + (encode-coding-region (point-min) (point-max) 'utf-8-unix)) + (if (file-remote-p default-directory) + ;; We lack `process-file-region', so fall back to asynch + + ;; waiting in remote case. + (progn + (magit-start-git (current-buffer) args) + (while (and magit-this-process + (eq (process-status magit-this-process) 'run)) + (sleep-for 0.005))) + (run-hooks 'magit-pre-call-git-hook) + (pcase-let* ((process-environment (magit-process-environment)) + (default-process-coding-system (magit--process-coding-system)) + (flat-args (magit-process-git-arguments args)) + (`(,process-buf . ,section) + (magit-process-setup (magit-git-executable) flat-args)) + (inhibit-read-only t)) + (magit-process-finish + (apply #'call-process-region (point-min) (point-max) + (magit-git-executable) nil process-buf nil flat-args) + process-buf nil default-directory section)))) + +;;; Asynchronous Processes + +(defun magit-run-git-async (&rest args) + "Start Git, prepare for refresh, and return the process object. +ARGS is flattened and then used as arguments to Git. + +Display the command line arguments in the echo area. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. + +See `magit-start-process' for more information." + (message "Running %s %s" (magit-git-executable) + (let ((m (mapconcat #'identity (flatten-tree args) " "))) + (remove-list-of-text-properties 0 (length m) '(face) m) + m)) + (magit-start-git nil args)) + +(defun magit-run-git-with-editor (&rest args) + "Export GIT_EDITOR and start Git. +Also prepare for refresh and return the process object. +ARGS is flattened and then used as arguments to Git. + +Display the command line arguments in the echo area. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. + +See `magit-start-process' and `with-editor' for more information." + (magit--record-separated-gitdir) + (magit-with-editor (magit-run-git-async args))) + +(defun magit-run-git-sequencer (&rest args) + "Export GIT_EDITOR and start Git. +Also prepare for refresh and return the process object. +ARGS is flattened and then used as arguments to Git. + +Display the command line arguments in the echo area. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. +If the sequence stops at a commit, make the section representing +that commit the current section by moving `point' there. + +See `magit-start-process' and `with-editor' for more information." + (apply #'magit-run-git-with-editor args) + (set-process-sentinel magit-this-process #'magit-sequencer-process-sentinel) + magit-this-process) + +(defvar magit-pre-start-git-hook nil) + +(defun magit-start-git (input &rest args) + "Start Git, prepare for refresh, and return the process object. + +If INPUT is non-nil, it has to be a buffer or the name of an +existing buffer. The buffer content becomes the processes +standard input. + +Function `magit-git-executable' specifies the Git executable and +option `magit-git-global-arguments' specifies constant arguments. +The remaining arguments ARGS specify arguments to Git, they are +flattened before use. + +After Git returns some buffers are refreshed: the buffer that was +current when this function was called (if it is a Magit buffer +and still alive), as well as the respective Magit status buffer. + +See `magit-start-process' for more information." + (run-hooks 'magit-pre-start-git-hook) + (let ((default-process-coding-system (magit--process-coding-system))) + (apply #'magit-start-process (magit-git-executable) input + (magit-process-git-arguments args)))) + +(defun magit-start-process (program &optional input &rest args) + "Start PROGRAM, prepare for refresh, and return the process object. + +If optional argument INPUT is non-nil, it has to be a buffer or +the name of an existing buffer. The buffer content becomes the +processes standard input. + +The process is started using `start-file-process' and then setup +to use the sentinel `magit-process-sentinel' and the filter +`magit-process-filter'. Information required by these functions +is stored in the process object. When this function returns the +process has not started to run yet so it is possible to override +the sentinel and filter. + +After the process returns, `magit-process-sentinel' refreshes the +buffer that was current when `magit-start-process' was called (if +it is a Magit buffer and still alive), as well as the respective +Magit status buffer." + (pcase-let* + ((`(,process-buf . ,section) + (magit-process-setup program args)) + (process + (let ((process-connection-type + ;; Don't use a pty, because it would set icrnl + ;; which would modify the input (issue #20). + (and (not input) magit-process-connection-type)) + (process-environment (magit-process-environment)) + (default-process-coding-system (magit--process-coding-system))) + (apply #'start-file-process + (file-name-nondirectory program) + process-buf program args)))) + (with-editor-set-process-filter process #'magit-process-filter) + (set-process-sentinel process #'magit-process-sentinel) + (set-process-buffer process process-buf) + (when (eq system-type 'windows-nt) + ;; On w32, git expects UTF-8 encoded input, ignore any user + ;; configuration telling us otherwise. + (set-process-coding-system process nil 'utf-8-unix)) + (process-put process 'section section) + (process-put process 'command-buf (current-buffer)) + (process-put process 'default-dir default-directory) + (when magit-inhibit-refresh + (process-put process 'inhibit-refresh t)) + (oset section process process) + (with-current-buffer process-buf + (set-marker (process-mark process) (point))) + (when input + (with-current-buffer input + (process-send-region process (point-min) (point-max)) + (process-send-eof process))) + (setq magit-this-process process) + (oset section value process) + (magit-process-display-buffer process) + process)) + +(defun magit-parse-git-async (&rest args) + (setq args (magit-process-git-arguments args)) + (let ((command-buf (current-buffer)) + (process-buf (generate-new-buffer " *temp*")) + (toplevel (magit-toplevel))) + (with-current-buffer process-buf + (setq default-directory toplevel) + (let ((process + (let ((process-connection-type nil) + (process-environment (magit-process-environment)) + (default-process-coding-system + (magit--process-coding-system))) + (apply #'start-file-process "git" process-buf + (magit-git-executable) args)))) + (process-put process 'command-buf command-buf) + (process-put process 'parsed (point)) + (setq magit-this-process process) + process)))) + +;;; Process Internals + +(defun magit-process-setup (program args) + (magit-process-set-mode-line program args) + (let ((pwd default-directory) + (buf (magit-process-buffer t))) + (cons buf (with-current-buffer buf + (prog1 (magit-process-insert-section pwd program args nil nil) + (backward-char 1)))))) + +(defun magit-process-insert-section (pwd program args &optional errcode errlog) + (let ((inhibit-read-only t) + (magit-insert-section--parent magit-root-section) + (magit-insert-section--oldroot nil)) + (goto-char (1- (point-max))) + (magit-insert-section (process) + (insert (if errcode + (format "%3s " (propertize (number-to-string errcode) + 'font-lock-face 'magit-process-ng)) + "run ")) + (unless (equal (expand-file-name pwd) + (expand-file-name default-directory)) + (insert (file-relative-name pwd default-directory) ?\s)) + (insert (magit-process--format-arguments program args)) + (magit-insert-heading) + (when errlog + (if (bufferp errlog) + (insert (with-current-buffer errlog + (buffer-substring-no-properties (point-min) (point-max)))) + (insert-file-contents errlog) + (goto-char (1- (point-max))))) + (insert "\n")))) + +(defun magit-process--format-arguments (program args) + (cond + ((and args (equal program (magit-git-executable))) + (setq args (-split-at (length magit-git-global-arguments) args)) + (concat (propertize (file-name-nondirectory program) + 'font-lock-face 'magit-section-heading) + " " + (propertize (if (stringp magit-ellipsis) + magit-ellipsis + ;; For backward compatibility. + (char-to-string magit-ellipsis)) + 'font-lock-face 'magit-section-heading + 'help-echo (mapconcat #'identity (car args) " ")) + " " + (propertize (mapconcat #'shell-quote-argument (cadr args) " ") + 'font-lock-face 'magit-section-heading))) + ((and args (equal program shell-file-name)) + (propertize (cadr args) + 'font-lock-face 'magit-section-heading)) + (t + (concat (propertize (file-name-nondirectory program) + 'font-lock-face 'magit-section-heading) + " " + (propertize (mapconcat #'shell-quote-argument args " ") + 'font-lock-face 'magit-section-heading))))) + +(defun magit-process-truncate-log () + (let* ((head nil) + (tail (oref magit-root-section children)) + (count (length tail))) + (when (> (1+ count) magit-process-log-max) + (while (and (cdr tail) + (> count (/ magit-process-log-max 2))) + (let* ((inhibit-read-only t) + (section (car tail)) + (process (oref section process))) + (cond ((not process)) + ((memq (process-status process) '(exit signal)) + (delete-region (oref section start) + (1+ (oref section end))) + (cl-decf count)) + (t + (push section head)))) + (pop tail)) + (oset magit-root-section children + (nconc (reverse head) tail))))) + +(defun magit-process-sentinel (process event) + "Default sentinel used by `magit-start-process'." + (when (memq (process-status process) '(exit signal)) + (setq event (substring event 0 -1)) + (when (string-match "^finished" event) + (message (concat (capitalize (process-name process)) " finished"))) + (magit-process-finish process) + (when (eq process magit-this-process) + (setq magit-this-process nil)) + (unless (process-get process 'inhibit-refresh) + (let ((command-buf (process-get process 'command-buf))) + (if (buffer-live-p command-buf) + (with-current-buffer command-buf + (magit-refresh)) + (with-temp-buffer + (setq default-directory (process-get process 'default-dir)) + (magit-refresh))))))) + +(defun magit-sequencer-process-sentinel (process event) + "Special sentinel used by `magit-run-git-sequencer'." + (when (memq (process-status process) '(exit signal)) + (magit-process-sentinel process event) + (when-let* ((process-buf (process-buffer process)) + (- (buffer-live-p process-buf)) + (status-buf (with-current-buffer process-buf + (magit-get-mode-buffer 'magit-status-mode)))) + (with-current-buffer status-buf + (--when-let + (magit-get-section + `((commit . ,(magit-rev-parse "HEAD")) + (,(pcase (car (cadr (-split-at + (1+ (length magit-git-global-arguments)) + (process-command process)))) + ((or "rebase" "am") 'rebase-sequence) + ((or "cherry-pick" "revert") 'sequence))) + (status))) + (goto-char (oref it start)) + (magit-section-update-highlight)))))) + +(defun magit-process-filter (proc string) + "Default filter used by `magit-start-process'." + (with-current-buffer (process-buffer proc) + (let ((inhibit-read-only t)) + (goto-char (process-mark proc)) + ;; Find last ^M in string. If one was found, ignore + ;; everything before it and delete the current line. + (when-let ((ret-pos (cl-position ?\r string :from-end t))) + (cl-callf substring string (1+ ret-pos)) + (delete-region (line-beginning-position) (point))) + (insert (propertize string 'magit-section + (process-get proc 'section))) + (set-marker (process-mark proc) (point)) + ;; Make sure prompts are matched after removing ^M. + (magit-process-yes-or-no-prompt proc string) + (magit-process-username-prompt proc string) + (magit-process-password-prompt proc string) + (run-hook-with-args-until-success 'magit-process-prompt-functions + proc string)))) + +(defmacro magit-process-kill-on-abort (proc &rest body) + (declare (indent 1) (debug (form body))) + (let ((map (cl-gensym))) + `(let ((,map (make-sparse-keymap))) + (set-keymap-parent ,map minibuffer-local-map) + ;; Note: Leaving (kbd ...) unevaluated leads to the + ;; magit-process:password-prompt test failing. + (define-key ,map ,(kbd "C-g") + (lambda () + (interactive) + (ignore-errors (kill-process ,proc)) + (abort-recursive-edit))) + (let ((minibuffer-local-map ,map)) + ,@body)))) + +(defun magit-process-yes-or-no-prompt (process string) + "Forward Yes-or-No prompts to the user." + (when-let ((beg (string-match magit-process-yes-or-no-prompt-regexp string))) + (let ((max-mini-window-height 30)) + (process-send-string + process + (downcase + (concat + (match-string + (if (save-match-data + (magit-process-kill-on-abort process + (yes-or-no-p (substring string 0 beg)))) 1 2) + string) + "\n")))))) + +(defun magit-process-password-auth-source (key) + "Use `auth-source-search' to get a password. +If found, return the password. Otherwise, return nil. + +To use this function add it to the appropriate hook + (add-hook \\='magit-process-find-password-functions + \\='magit-process-password-auth-source) + +KEY typically derives from a prompt such as: + Password for \\='https://yourname@github.com\\=' +in which case it would be the string + yourname@github.com +which matches the ~/.authinfo.gpg entry + machine github.com login yourname password 12345 +or iff that is undefined, for backward compatibility + machine yourname@github.com password 12345 + +On github.com you should not use your password but a +personal access token, see [1]. For information about +the peculiarities of other forges, please consult the +respective documentation. + +After manually editing ~/.authinfo.gpg you must reset +the cache using + M-x auth-source-forget-all-cached RET + +The above will save you from having to repeatedly type +your token or password, but you might still repeatedly +be asked for your username. To prevent that, change an +URL like + https://github.com/foo/bar.git +to + https://yourname@github.com/foo/bar.git + +Instead of changing all such URLs manually, they can +be translated on the fly by doing this once + git config --global \ + url.https://yourname@github.com.insteadOf \ + https://github.com + +[1]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token." + (require 'auth-source) + (and (fboundp 'auth-source-search) + (string-match "\\`\\(.+\\)@\\([^@]+\\)\\'" key) + (let* ((user (match-string 1 key)) + (host (match-string 2 key)) + (secret + (plist-get + (car (or (auth-source-search :max 1 :host host :user user) + (auth-source-search :max 1 :host key))) + :secret))) + (if (functionp secret) + (funcall secret) + secret)))) + +(defun magit-process-git-credential-manager-core (process string) + "Authenticate using `git-credential-manager-core'. + +To use this function add it to the appropriate hook + (add-hook \\='magit-process-prompt-functions + \\='magit-process-git-credential-manager-core)" + (and (string-match "^option (enter for default): $" string) + (progn + (magit-process-buffer) + (let ((option (format "%c\n" + (read-char-choice "Option: " '(?\r ?\j ?1 ?2))))) + (insert-before-markers-and-inherit option) + (process-send-string process option))))) + +(defun magit-process-password-prompt (process string) + "Find a password based on prompt STRING and send it to git. +Use `magit-process-password-prompt-regexps' to find a known +prompt. If and only if one is found, then call functions in +`magit-process-find-password-functions' until one of them returns +the password. If all functions return nil, then read the password +from the user." + (when-let ((prompt (magit-process-match-prompt + magit-process-password-prompt-regexps string))) + (process-send-string + process (magit-process-kill-on-abort process + (concat (or (and-let* ((key (match-string 99 string))) + (run-hook-with-args-until-success + 'magit-process-find-password-functions key)) + (read-passwd prompt)) + "\n"))))) + +(defun magit-process-username-prompt (process string) + "Forward username prompts to the user." + (--when-let (magit-process-match-prompt + magit-process-username-prompt-regexps string) + (process-send-string + process (magit-process-kill-on-abort process + (concat (read-string it nil nil (user-login-name)) "\n"))))) + +(defun magit-process-match-prompt (prompts string) + "Match STRING against PROMPTS and set match data. +Return the matched string suffixed with \": \", if needed." + (when (--any-p (string-match it string) prompts) + (let ((prompt (match-string 0 string))) + (cond ((string-suffix-p ": " prompt) prompt) + ((string-suffix-p ":" prompt) (concat prompt " ")) + (t (concat prompt ": ")))))) + +(defun magit--process-coding-system () + (let ((fro (or magit-git-output-coding-system + (car default-process-coding-system))) + (to (cdr default-process-coding-system))) + (if magit-process-ensure-unix-line-ending + (cons (coding-system-change-eol-conversion fro 'unix) + (coding-system-change-eol-conversion to 'unix)) + (cons fro to)))) + +(defvar magit-credential-hook nil + "Hook run before Git needs credentials.") + +(defvar magit-credential-cache-daemon-process nil) + +(defun magit-maybe-start-credential-cache-daemon () + "Maybe start a `git-credential-cache--daemon' process. + +If such a process is already running or if the value of option +`magit-credential-cache-daemon-socket' is nil, then do nothing. +Otherwise start the process passing the value of that options +as argument." + (unless (or (not magit-credential-cache-daemon-socket) + (process-live-p magit-credential-cache-daemon-process) + (memq magit-credential-cache-daemon-process + (list-system-processes))) + (setq magit-credential-cache-daemon-process + (or (--first (let* ((attr (process-attributes it)) + (comm (cdr (assq 'comm attr))) + (user (cdr (assq 'user attr)))) + (and (string= comm "git-credential-cache--daemon") + (string= user user-login-name))) + (list-system-processes)) + (condition-case nil + (start-process "git-credential-cache--daemon" + " *git-credential-cache--daemon*" + (magit-git-executable) + "credential-cache--daemon" + magit-credential-cache-daemon-socket) + ;; Some Git implementations (e.g. Windows) won't have + ;; this program; if we fail the first time, stop trying. + ((debug error) + (remove-hook 'magit-credential-hook + #'magit-maybe-start-credential-cache-daemon))))))) + +(add-hook 'magit-credential-hook #'magit-maybe-start-credential-cache-daemon) + +(defun tramp-sh-handle-start-file-process--magit-tramp-process-environment + (fn name buffer program &rest args) + (if magit-tramp-process-environment + (apply fn name buffer + (car magit-tramp-process-environment) + (append (cdr magit-tramp-process-environment) + (cons program args))) + (apply fn name buffer program args))) + +(advice-add 'tramp-sh-handle-start-file-process :around + #'tramp-sh-handle-start-file-process--magit-tramp-process-environment) + +(defun tramp-sh-handle-process-file--magit-tramp-process-environment + (fn program &optional infile destination display &rest args) + (if magit-tramp-process-environment + (apply fn "env" infile destination display + (append magit-tramp-process-environment + (cons program args))) + (apply fn program infile destination display args))) + +(advice-add 'tramp-sh-handle-process-file :around + #'tramp-sh-handle-process-file--magit-tramp-process-environment) + +(defvar magit-mode-line-process-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd " ") + 'magit-process-buffer) + map) + "Keymap for `mode-line-process'.") + +(defun magit-process-set-mode-line (program args) + "Display the git command (sans arguments) in the mode line." + (when (equal program (magit-git-executable)) + (setq args (nthcdr (length magit-git-global-arguments) args))) + (let ((str (concat " " (propertize + (concat (file-name-nondirectory program) + (and args (concat " " (car args)))) + 'mouse-face 'highlight + 'keymap magit-mode-line-process-map + 'help-echo "mouse-1: Show process buffer" + 'font-lock-face 'magit-mode-line-process)))) + (magit-repository-local-set 'mode-line-process str) + (dolist (buf (magit-mode-get-buffers)) + (with-current-buffer buf + (setq mode-line-process str))) + (force-mode-line-update t))) + +(defun magit-process-set-mode-line-error-status (&optional error str) + "Apply an error face to the string set by `magit-process-set-mode-line'. + +If ERROR is supplied, include it in the `mode-line-process' tooltip. + +If STR is supplied, it replaces the `mode-line-process' text." + (setq str (or str (magit-repository-local-get 'mode-line-process))) + (when str + (setq error (format "%smouse-1: Show process buffer" + (if (stringp error) + (concat error "\n\n") + ""))) + (setq str (concat " " (propertize + (substring-no-properties str 1) + 'mouse-face 'highlight + 'keymap magit-mode-line-process-map + 'help-echo error + 'font-lock-face 'magit-mode-line-process-error))) + (magit-repository-local-set 'mode-line-process str) + (dolist (buf (magit-mode-get-buffers)) + (with-current-buffer buf + (setq mode-line-process str))) + (force-mode-line-update t) + ;; We remove any error status from the mode line when a magit + ;; buffer is refreshed (see `magit-refresh-buffer'), but we must + ;; ensure that we ignore any refreshes during the remainder of the + ;; current command -- otherwise a newly-set error status would be + ;; removed before it was seen. We set a flag which prevents the + ;; status from being removed prior to the next command, so that + ;; the error status is guaranteed to remain visible until then. + (let ((repokey (magit-repository-local-repository))) + ;; The following closure captures the repokey value, and is + ;; added to `pre-command-hook'. + (cl-labels ((enable-magit-process-unset-mode-line () + ;; Remove ourself from the hook variable, so + ;; that we only run once. + (remove-hook 'pre-command-hook + #'enable-magit-process-unset-mode-line) + ;; Clear the inhibit flag for the repository in + ;; which we set it. + (magit-repository-local-set + 'inhibit-magit-process-unset-mode-line nil repokey))) + ;; Set the inhibit flag until the next command is invoked. + (magit-repository-local-set + 'inhibit-magit-process-unset-mode-line t repokey) + (add-hook 'pre-command-hook + #'enable-magit-process-unset-mode-line))))) + +(defun magit-process-unset-mode-line-error-status () + "Remove any current error status from the mode line." + (let ((status (or mode-line-process + (magit-repository-local-get 'mode-line-process)))) + (when (and status + (eq (get-text-property 1 'font-lock-face status) + 'magit-mode-line-process-error)) + (magit-process-unset-mode-line)))) + +(add-hook 'magit-refresh-buffer-hook + #'magit-process-unset-mode-line-error-status) + +(defun magit-process-unset-mode-line (&optional directory) + "Remove the git command from the mode line." + (let ((default-directory (or directory default-directory))) + (unless (magit-repository-local-get 'inhibit-magit-process-unset-mode-line) + (magit-repository-local-set 'mode-line-process nil) + (dolist (buf (magit-mode-get-buffers)) + (with-current-buffer buf (setq mode-line-process nil))) + (force-mode-line-update t)))) + +(defvar magit-process-error-message-regexps + (list "^\\*ERROR\\*: Canceled by user$" + "^\\(?:error\\|fatal\\|git\\): \\(.*\\)$" + "^\\(Cannot rebase:.*\\)$")) + +(define-error 'magit-git-error "Git error") + +(defun magit-process-error-summary (process-buf section) + "A one-line error summary from the given SECTION." + (or (and (buffer-live-p process-buf) + (with-current-buffer process-buf + (and (oref section content) + (save-excursion + (goto-char (oref section end)) + (run-hook-wrapped + 'magit-process-error-message-regexps + (lambda (re) + (save-excursion + (and (re-search-backward + re (oref section start) t) + (or (match-string-no-properties 1) + (and (not magit-process-raise-error) + 'suppressed)))))))))) + "Git failed")) + +(defun magit-process-error-tooltip (process-buf section) + "Returns the text from SECTION of the PROCESS-BUF buffer. + +Limited by `magit-process-error-tooltip-max-lines'." + (and (integerp magit-process-error-tooltip-max-lines) + (> magit-process-error-tooltip-max-lines 0) + (buffer-live-p process-buf) + (with-current-buffer process-buf + (save-excursion + (goto-char (or (oref section content) + (oref section start))) + (buffer-substring-no-properties + (point) + (save-excursion + (forward-line magit-process-error-tooltip-max-lines) + (goto-char + (if (> (point) (oref section end)) + (oref section end) + (point))) + ;; Remove any trailing whitespace. + (when (re-search-backward "[^[:space:]\n]" + (oref section start) t) + (forward-char 1)) + (point))))))) + +(defvar-local magit-this-error nil) + +(defvar magit-process-finish-apply-ansi-colors nil) + +(defun magit-process-finish (arg &optional process-buf command-buf + default-dir section) + (unless (integerp arg) + (setq process-buf (process-buffer arg)) + (setq command-buf (process-get arg 'command-buf)) + (setq default-dir (process-get arg 'default-dir)) + (setq section (process-get arg 'section)) + (setq arg (process-exit-status arg))) + (when (fboundp 'dired-uncache) + (dired-uncache default-dir)) + (when (buffer-live-p process-buf) + (with-current-buffer process-buf + (let ((inhibit-read-only t) + (marker (oref section start))) + (goto-char marker) + (save-excursion + (delete-char 3) + (set-marker-insertion-type marker nil) + (insert (propertize (format "%3s" arg) + 'magit-section section + 'font-lock-face (if (= arg 0) + 'magit-process-ok + 'magit-process-ng))) + (set-marker-insertion-type marker t)) + (when magit-process-finish-apply-ansi-colors + (ansi-color-apply-on-region (oref section content) + (oref section end))) + (if (= (oref section end) + (+ (line-end-position) 2)) + (save-excursion + (goto-char (1+ (line-end-position))) + (delete-char -1) + (oset section content nil)) + (let ((buf (magit-process-buffer t))) + (when (and (= arg 0) + (not (--any-p (eq (window-buffer it) buf) + (window-list)))) + (magit-section-hide section))))))) + (if (= arg 0) + ;; Unset the `mode-line-process' value upon success. + (magit-process-unset-mode-line default-dir) + ;; Otherwise process the error. + (let ((msg (magit-process-error-summary process-buf section))) + ;; Change `mode-line-process' to an error face upon failure. + (if magit-process-display-mode-line-error + (magit-process-set-mode-line-error-status + (or (magit-process-error-tooltip process-buf section) + msg)) + (magit-process-unset-mode-line default-dir)) + ;; Either signal the error, or else display the error summary in + ;; the status buffer and with a message in the echo area. + (cond + (magit-process-raise-error + (signal 'magit-git-error (list (format "%s (in %s)" msg default-dir)))) + ((not (eq msg 'suppressed)) + (when (buffer-live-p process-buf) + (with-current-buffer process-buf + (when-let ((status-buf (magit-get-mode-buffer 'magit-status-mode))) + (with-current-buffer status-buf + (setq magit-this-error msg))))) + (message "%s ... [%s buffer %s for details]" msg + (if-let ((key (and (buffer-live-p command-buf) + (with-current-buffer command-buf + (car (where-is-internal + 'magit-process-buffer)))))) + (format "Hit %s to see" (key-description key)) + "See") + (buffer-name process-buf)))))) + arg) + +(defun magit-process-display-buffer (process) + (when (process-live-p process) + (let ((buf (process-buffer process))) + (cond ((not (buffer-live-p buf))) + ((= magit-process-popup-time 0) + (if (minibufferp) + (switch-to-buffer-other-window buf) + (pop-to-buffer buf))) + ((> magit-process-popup-time 0) + (run-with-timer magit-process-popup-time nil + (lambda (p) + (when (eq (process-status p) 'run) + (let ((buf (process-buffer p))) + (when (buffer-live-p buf) + (if (minibufferp) + (switch-to-buffer-other-window buf) + (pop-to-buffer buf)))))) + process)))))) + +(defun magit--log-action (summary line list) + (let (heading lines) + (if (cdr list) + (progn (setq heading (funcall summary list)) + (setq lines (mapcar line list))) + (setq heading (funcall line (car list)))) + (with-current-buffer (magit-process-buffer t) + (goto-char (1- (point-max))) + (let ((inhibit-read-only t)) + (magit-insert-section (message) + (magit-insert-heading (concat " * " heading)) + (when lines + (dolist (line lines) + (insert line "\n")) + (insert "\n")))) + (let ((inhibit-message t)) + (when heading + (setq lines (cons heading lines))) + (message (mapconcat #'identity lines "\n")))))) + +;;; _ +(provide 'magit-process) +;;; magit-process.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-pull.el b/code/elpa/magit-20220821.1819/magit-pull.el new file mode 100644 index 0000000..aad99a5 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-pull.el @@ -0,0 +1,165 @@ +;;; magit-pull.el --- Update local objects and refs -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements pull commands. + +;;; Code: + +(require 'magit) + +;;; Options + +(defcustom magit-pull-or-fetch nil + "Whether `magit-pull' also offers some fetch suffixes." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type 'boolean) + +;;; Commands + +;;;###autoload (autoload 'magit-pull "magit-pull" nil t) +(transient-define-prefix magit-pull () + "Pull from another repository." + :man-page "git-pull" + :incompatible '(("--ff-only" "--rebase")) + [:description + (lambda () (if magit-pull-or-fetch "Pull arguments" "Arguments")) + ("-f" "Fast-forward only" "--ff-only") + ("-r" "Rebase local commits" ("-r" "--rebase")) + ("-A" "Autostash" "--autostash" :level 7)] + [:description + (lambda () + (if-let ((branch (magit-get-current-branch))) + (concat + (propertize "Pull into " 'face 'transient-heading) + (propertize branch 'face 'magit-branch-local) + (propertize " from" 'face 'transient-heading)) + (propertize "Pull from" 'face 'transient-heading))) + ("p" magit-pull-from-pushremote) + ("u" magit-pull-from-upstream) + ("e" "elsewhere" magit-pull-branch)] + ["Fetch from" + :if-non-nil magit-pull-or-fetch + ("f" "remotes" magit-fetch-all-no-prune) + ("F" "remotes and prune" magit-fetch-all-prune)] + ["Fetch" + :if-non-nil magit-pull-or-fetch + ("o" "another branch" magit-fetch-branch) + ("s" "explicit refspec" magit-fetch-refspec) + ("m" "submodules" magit-fetch-modules)] + ["Configure" + ("r" magit-branch..rebase :if magit-get-current-branch) + ("C" "variables..." magit-branch-configure)] + (interactive) + (transient-setup 'magit-pull nil nil :scope (magit-get-current-branch))) + +(defun magit-pull-arguments () + (transient-args 'magit-pull)) + +;;;###autoload (autoload 'magit-pull-from-pushremote "magit-pull" nil t) +(transient-define-suffix magit-pull-from-pushremote (args) + "Pull from the push-remote of the current branch. + +With a prefix argument or when the push-remote is either not +configured or unusable, then let the user first configure the +push-remote." + :if #'magit-get-current-branch + :description #'magit-pull--pushbranch-description + (interactive (list (magit-pull-arguments))) + (pcase-let ((`(,branch ,remote) + (magit--select-push-remote "pull from there"))) + (run-hooks 'magit-credential-hook) + (magit-run-git-with-editor "pull" args remote branch))) + +(defun magit-pull--pushbranch-description () + ;; Also used by `magit-rebase-onto-pushremote'. + (let* ((branch (magit-get-current-branch)) + (target (magit-get-push-branch branch t)) + (remote (magit-get-push-remote branch)) + (v (magit--push-remote-variable branch t))) + (cond + (target) + ((member remote (magit-list-remotes)) + (format "%s, replacing non-existent" v)) + (remote + (format "%s, replacing invalid" v)) + (t + (format "%s, setting that" v))))) + +;;;###autoload (autoload 'magit-pull-from-upstream "magit-pull" nil t) +(transient-define-suffix magit-pull-from-upstream (args) + "Pull from the upstream of the current branch. + +With a prefix argument or when the upstream is either not +configured or unusable, then let the user first configure +the upstream." + :if #'magit-get-current-branch + :description #'magit-pull--upstream-description + (interactive (list (magit-pull-arguments))) + (let* ((branch (or (magit-get-current-branch) + (user-error "No branch is checked out"))) + (remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge"))) + (when (or current-prefix-arg + (not (or (magit-get-upstream-branch branch) + (magit--unnamed-upstream-p remote merge)))) + (magit-set-upstream-branch + branch (magit-read-upstream-branch + branch (format "Set upstream of %s and pull from there" branch))) + (setq remote (magit-get "branch" branch "remote")) + (setq merge (magit-get "branch" branch "merge"))) + (run-hooks 'magit-credential-hook) + (magit-run-git-with-editor "pull" args remote merge))) + +(defun magit-pull--upstream-description () + (and-let* ((branch (magit-get-current-branch))) + (or (magit-get-upstream-branch branch) + (let ((remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge")) + (u (magit--propertize-face "@{upstream}" 'bold))) + (cond + ((magit--unnamed-upstream-p remote merge) + (format "%s of %s" + (magit--propertize-face merge 'magit-branch-remote) + (magit--propertize-face remote 'bold))) + ((magit--valid-upstream-p remote merge) + (concat u ", replacing non-existent")) + ((or remote merge) + (concat u ", replacing invalid")) + (t + (concat u ", setting that"))))))) + +;;;###autoload +(defun magit-pull-branch (source args) + "Pull from a branch read in the minibuffer." + (interactive (list (magit-read-remote-branch "Pull" nil nil nil t) + (magit-pull-arguments))) + (run-hooks 'magit-credential-hook) + (pcase-let ((`(,remote . ,branch) + (magit-get-tracked source))) + (magit-run-git-with-editor "pull" args remote branch))) + +;;; _ +(provide 'magit-pull) +;;; magit-pull.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-push.el b/code/elpa/magit-20220821.1819/magit-push.el new file mode 100644 index 0000000..179d0f5 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-push.el @@ -0,0 +1,376 @@ +;;; magit-push.el --- Update remote objects and refs -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements push commands. + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-push "magit-push" nil t) +(transient-define-prefix magit-push () + "Push to another repository." + :man-page "git-push" + ["Arguments" + ("-f" "Force with lease" (nil "--force-with-lease")) + ("-F" "Force" ("-f" "--force")) + ("-h" "Disable hooks" "--no-verify") + ("-n" "Dry run" ("-n" "--dry-run")) + (5 "-u" "Set upstream" "--set-upstream") + (7 "-t" "Follow tags" "--follow-tags")] + [:if magit-get-current-branch + :description (lambda () + (format (propertize "Push %s to" 'face 'transient-heading) + (propertize (magit-get-current-branch) + 'face 'magit-branch-local))) + ("p" magit-push-current-to-pushremote) + ("u" magit-push-current-to-upstream) + ("e" "elsewhere" magit-push-current)] + ["Push" + [("o" "another branch" magit-push-other) + ("r" "explicit refspecs" magit-push-refspecs) + ("m" "matching branches" magit-push-matching)] + [("T" "a tag" magit-push-tag) + ("t" "all tags" magit-push-tags) + (6 "n" "a note ref" magit-push-notes-ref)]] + ["Configure" + ("C" "Set variables..." magit-branch-configure)]) + +(defun magit-push-arguments () + (transient-args 'magit-push)) + +(defun magit-git-push (branch target args) + (run-hooks 'magit-credential-hook) + ;; If the remote branch already exists, then we do not have to + ;; qualify the target, which we prefer to avoid doing because + ;; using the default namespace is wrong in obscure cases. + (pcase-let ((namespace (if (magit-get-tracked target) "" "refs/heads/")) + (`(,remote . ,target) + (magit-split-branch-name target))) + (magit-run-git-async "push" "-v" args remote + (format "%s:%s%s" branch namespace target)))) + +;;;###autoload (autoload 'magit-push-current-to-pushremote "magit-push" nil t) +(transient-define-suffix magit-push-current-to-pushremote (args) + "Push the current branch to its push-remote. + +When the push-remote is not configured, then read the push-remote +from the user, set it, and then push to it. With a prefix +argument the push-remote can be changed before pushed to it." + :if #'magit-get-current-branch + :description #'magit-push--pushbranch-description + (interactive (list (magit-push-arguments))) + (pcase-let ((`(,branch ,remote ,changed) + (magit--select-push-remote "push there"))) + (when changed + (magit-confirm 'set-and-push + (string-replace + "%" "%%" + (format "Really use \"%s\" as push-remote and push \"%s\" there" + remote branch)))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote + (format "refs/heads/%s:refs/heads/%s" + branch branch)))) ; see #3847 and #3872 + +(defun magit-push--pushbranch-description () + (let* ((branch (magit-get-current-branch)) + (target (magit-get-push-branch branch t)) + (remote (magit-get-push-remote branch)) + (v (magit--push-remote-variable branch t))) + (cond + (target) + ((member remote (magit-list-remotes)) + (format "%s, creating it" + (magit--propertize-face (concat remote "/" branch) + 'magit-branch-remote))) + (remote + (format "%s, replacing invalid" v)) + (t + (format "%s, setting that" v))))) + +;;;###autoload (autoload 'magit-push-current-to-upstream "magit-push" nil t) +(transient-define-suffix magit-push-current-to-upstream (args) + "Push the current branch to its upstream branch. + +With a prefix argument or when the upstream is either not +configured or unusable, then let the user first configure +the upstream." + :if #'magit-get-current-branch + :description #'magit-push--upstream-description + (interactive (list (magit-push-arguments))) + (let* ((branch (or (magit-get-current-branch) + (user-error "No branch is checked out"))) + (remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge"))) + (when (or current-prefix-arg + (not (or (magit-get-upstream-branch branch) + (magit--unnamed-upstream-p remote merge) + (magit--valid-upstream-p remote merge)))) + (let* ((branches (-union (--map (concat it "/" branch) + (magit-list-remotes)) + (magit-list-remote-branch-names))) + (upstream (magit-completing-read + (format "Set upstream of %s and push there" branch) + branches nil nil nil 'magit-revision-history + (or (car (member (magit-remote-branch-at-point) branches)) + (car (member "origin/master" branches))))) + (upstream* (or (magit-get-tracked upstream) + (magit-split-branch-name upstream)))) + (setq remote (car upstream*)) + (setq merge (cdr upstream*)) + (unless (string-prefix-p "refs/" merge) + ;; User selected a non-existent remote-tracking branch. + ;; It is very likely, but not certain, that this is the + ;; correct thing to do. It is even more likely that it + ;; is what the user wants to happen. + (setq merge (concat "refs/heads/" merge))) + (magit-confirm 'set-and-push + (string-replace + "%" "%%" + (format "Really use \"%s\" as upstream and push \"%s\" there" + upstream branch)))) + (cl-pushnew "--set-upstream" args :test #'equal)) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote (concat branch ":" merge)))) + +(defun magit-push--upstream-description () + (and-let* ((branch (magit-get-current-branch))) + (or (magit-get-upstream-branch branch) + (let ((remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge")) + (u (magit--propertize-face "@{upstream}" 'bold))) + (cond + ((magit--unnamed-upstream-p remote merge) + (format "%s as %s" + (magit--propertize-face remote 'bold) + (magit--propertize-face merge 'magit-branch-remote))) + ((magit--valid-upstream-p remote merge) + (format "%s creating %s" + (magit--propertize-face remote 'magit-branch-remote) + (magit--propertize-face merge 'magit-branch-remote))) + ((or remote merge) + (concat u ", creating it and replacing invalid")) + (t + (concat u ", creating it"))))))) + +;;;###autoload +(defun magit-push-current (target args) + "Push the current branch to a branch read in the minibuffer." + (interactive + (--if-let (magit-get-current-branch) + (list (magit-read-remote-branch (format "Push %s to" it) + nil nil it 'confirm) + (magit-push-arguments)) + (user-error "No branch is checked out"))) + (magit-git-push (magit-get-current-branch) target args)) + +;;;###autoload +(defun magit-push-other (source target args) + "Push an arbitrary branch or commit somewhere. +Both the source and the target are read in the minibuffer." + (interactive + (let ((source (magit-read-local-branch-or-commit "Push"))) + (list source + (magit-read-remote-branch + (format "Push %s to" source) nil + (if (magit-local-branch-p source) + (or (magit-get-push-branch source) + (magit-get-upstream-branch source)) + (and (magit-rev-ancestor-p source "HEAD") + (or (magit-get-push-branch) + (magit-get-upstream-branch)))) + source 'confirm) + (magit-push-arguments)))) + (magit-git-push source target args)) + +(defvar magit-push-refspecs-history nil) + +;;;###autoload +(defun magit-push-refspecs (remote refspecs args) + "Push one or multiple REFSPECS to a REMOTE. +Both the REMOTE and the REFSPECS are read in the minibuffer. To +use multiple REFSPECS, separate them with commas. Completion is +only available for the part before the colon, or when no colon +is used." + (interactive + (list (magit-read-remote "Push to remote") + (magit-completing-read-multiple* + "Push refspec,s: " + (cons "HEAD" (magit-list-local-branch-names)) + nil nil nil 'magit-push-refspecs-history) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote refspecs)) + +;;;###autoload +(defun magit-push-matching (remote &optional args) + "Push all matching branches to another repository. +If multiple remotes exist, then read one from the user. +If just one exists, use that without requiring confirmation." + (interactive (list (magit-read-remote "Push matching branches to" nil t) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote ":")) + +;;;###autoload +(defun magit-push-tags (remote &optional args) + "Push all tags to another repository. +If only one remote exists, then push to that. Otherwise prompt +for a remote, offering the remote configured for the current +branch as default." + (interactive (list (magit-read-remote "Push tags to remote" nil t) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote "--tags" args)) + +;;;###autoload +(defun magit-push-tag (tag remote &optional args) + "Push a tag to another repository." + (interactive + (let ((tag (magit-read-tag "Push tag"))) + (list tag (magit-read-remote (format "Push %s to remote" tag) nil t) + (magit-push-arguments)))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote tag args)) + +;;;###autoload +(defun magit-push-notes-ref (ref remote &optional args) + "Push a notes ref to another repository." + (interactive + (let ((note (magit-notes-read-ref "Push notes" nil nil))) + (list note + (magit-read-remote (format "Push %s to remote" note) nil t) + (magit-push-arguments)))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote ref args)) + +;;;###autoload (autoload 'magit-push-implicitly "magit-push" nil t) +(transient-define-suffix magit-push-implicitly (args) + "Push somewhere without using an explicit refspec. + +This command simply runs \"git push -v [ARGS]\". ARGS are the +arguments specified in the popup buffer. No explicit refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch..pushRemote', `branch..remote', +`branch..merge', and `remote..push'. + +If you add this suffix to a transient prefix without explicitly +specifying the description, then an attempt is made to predict +what this command will do. To add it use something like: + + (transient-insert-suffix \\='magit-push \"o\" + \\='(\"i\" magit-push-implicitly))" + :description #'magit-push-implicitly--desc + (interactive (list (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args)) + +(defun magit-push-implicitly--desc () + ;; This implements the logic for git push as documented. + ;; First, we resolve a remote to use based on various remote and + ;; pushRemote options. + ;; Then, we resolve the refspec to use for the remote based on push + ;; and pushDefault options. + ;; Note that the remote and refspec to push are handled separately, + ;; so it doesn't make sense to talk about "pushing to upstream". + ;; Depending on the options, you could end up pushing to the + ;; "upstream" remote but not the "upstream" branch, and vice versa. + (let* ((branch (magit-get-current-branch)) + (remote (or (magit-get-push-remote branch) + ;; Note: Avoid `magit-get-remote' because it + ;; filters out the local repo case ("."). + (magit-get "branch" branch "remote") + (let ((remotes (magit-list-remotes))) + (cond + ((and (magit-git-version>= "2.27") + (= (length remotes) 1)) + (car remotes)) + ((member "origin" remotes) "origin")))))) + (if (null remote) + "nothing (no remote)" + (let ((refspec (magit-get "remote" remote "push"))) + (if refspec + (format "to %s with refspecs %s" + (magit--propertize-face remote 'bold) + (magit--propertize-face refspec 'bold)) + (pcase (or (magit-get "push.default") "simple") + ("nothing" "nothing (due to push.default)") + ((or "current" "simple") + (format "%s to %s" + (magit--propertize-face branch 'magit-branch-current) + (magit--propertize-face (format "%s/%s" remote branch) + 'magit-branch-remote))) + ((or "upstream" "tracking") + (let ((ref (magit-get "branch" branch "merge"))) + (if ref + (format "%s to %s" + (magit--propertize-face branch 'magit-branch-current) + (cond + ((string-prefix-p "refs/heads/" ref) + (magit--propertize-face + (format "%s/%s" remote + (substring ref (length "refs/heads/"))) + 'magit-branch-remote)) + ((not (string-match "/" ref)) + (magit--propertize-face (format "%s/%s" remote ref) + 'magit-branch-remote)) + (t (format "%s as %s" + (magit--propertize-face remote 'bold) + (magit--propertize-face ref 'bold))))) + "nothing (no upstream)"))) + ("matching" (format "all matching to %s" + (magit--propertize-face remote 'bold))))))))) + +;;;###autoload (autoload 'magit-push-to-remote "magit-push" nil t) +(transient-define-suffix magit-push-to-remote (remote args) + "Push to REMOTE without using an explicit refspec. +The REMOTE is read in the minibuffer. + +This command simply runs \"git push -v [ARGS] REMOTE\". ARGS +are the arguments specified in the popup buffer. No refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch..pushRemote', `branch..remote', +`branch..merge', and `remote..push'. + +You can add this command as a suffix using something like: + + (transient-insert-suffix \\='magit-push \"o\" + \\='(\"x\" magit-push-to-remote))" + :description #'magit-push-to-remote--desc + (interactive (list (magit-read-remote "Push to remote") + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote)) + +(defun magit-push-to-remote--desc () + (format "using %s" (magit--propertize-face "git push " 'bold))) + +;;; _ +(provide 'magit-push) +;;; magit-push.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-reflog.el b/code/elpa/magit-20220821.1819/magit-reflog.el new file mode 100644 index 0000000..639a701 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-reflog.el @@ -0,0 +1,210 @@ +;;; magit-reflog.el --- Inspect ref history -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for looking at Git reflogs. + +;;; Code: + +(require 'magit-core) +(require 'magit-log) + +;;; Options + +(defcustom magit-reflog-limit 256 + "Maximal number of entries initially shown in reflog buffers. +The limit in the current buffer can be changed using \"+\" +and \"-\"." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type 'number) + +(defcustom magit-reflog-margin + (list (nth 0 magit-log-margin) + (nth 1 magit-log-margin) + 'magit-log-margin-width nil + (nth 4 magit-log-margin)) + "Format of the margin in `magit-reflog-mode' buffers. + +The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). + +If INIT is non-nil, then the margin is shown initially. +STYLE controls how to format the author or committer date. + It can be one of `age' (to show the age of the commit), + `age-abbreviated' (to abbreviate the time unit to a character), + or a string (suitable for `format-time-string') to show the + actual date. Option `magit-log-margin-show-committer-date' + controls which date is being displayed. +WIDTH controls the width of the margin. This exists for forward + compatibility and currently the value should not be changed. +AUTHOR controls whether the name of the author is also shown by + default. +AUTHOR-WIDTH has to be an integer. When the name of the author + is shown, then this specifies how much space is used to do so." + :package-version '(magit . "2.9.0") + :group 'magit-log + :group 'magit-margin + :type magit-log-margin--custom-type + :initialize #'magit-custom-initialize-reset + :set-after '(magit-log-margin) + :set (apply-partially #'magit-margin-set-variable 'magit-reflog-mode)) + +;;; Faces + +(defface magit-reflog-commit '((t :foreground "green")) + "Face for commit commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-amend '((t :foreground "magenta")) + "Face for amend commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-merge '((t :foreground "green")) + "Face for merge, checkout and branch commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-checkout '((t :foreground "blue")) + "Face for checkout commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-reset '((t :foreground "red")) + "Face for reset commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-rebase '((t :foreground "magenta")) + "Face for rebase commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-cherry-pick '((t :foreground "green")) + "Face for cherry-pick commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-remote '((t :foreground "cyan")) + "Face for pull and clone commands in reflogs." + :group 'magit-faces) + +(defface magit-reflog-other '((t :foreground "cyan")) + "Face for other commands in reflogs." + :group 'magit-faces) + +;;; Commands + +;;;###autoload +(defun magit-reflog-current () + "Display the reflog of the current branch. +If `HEAD' is detached, then show the reflog for that instead." + (interactive) + (magit-reflog-setup-buffer (or (magit-get-current-branch) "HEAD"))) + +;;;###autoload +(defun magit-reflog-other (ref) + "Display the reflog of a branch or another ref." + (interactive (list (magit-read-local-branch-or-ref "Show reflog for"))) + (magit-reflog-setup-buffer ref)) + +;;;###autoload +(defun magit-reflog-head () + "Display the `HEAD' reflog." + (interactive) + (magit-reflog-setup-buffer "HEAD")) + +;;; Mode + +(defvar magit-reflog-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-log-mode-map) + (define-key map (kbd "C-c C-n") #'undefined) + (define-key map (kbd "L") #'magit-margin-settings) + map) + "Keymap for `magit-reflog-mode'.") + +(define-derived-mode magit-reflog-mode magit-mode "Magit Reflog" + "Mode for looking at Git reflog. + +This mode is documented in info node `(magit)Reflog'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit at point. + +Type \\[magit-cherry-pick] to apply the commit at point. +Type \\[magit-reset] to reset `HEAD' to the commit at point. + +\\{magit-reflog-mode-map}" + :group 'magit-log + (hack-dir-local-variables-non-file-buffer) + (setq magit--imenu-item-types 'commit)) + +(defun magit-reflog-setup-buffer (ref) + (require 'magit) + (magit-setup-buffer #'magit-reflog-mode nil + (magit-buffer-refname ref) + (magit-buffer-log-args (list (format "-n%s" magit-reflog-limit))))) + +(defun magit-reflog-refresh-buffer () + (magit-set-header-line-format (concat "Reflog for " magit-buffer-refname)) + (magit-insert-section (reflogbuf) + (magit-git-wash (apply-partially #'magit-log-wash-log 'reflog) + "reflog" "show" "--format=%h%x00%aN%x00%gd%x00%gs" "--date=raw" + magit-buffer-log-args magit-buffer-refname "--"))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-reflog-mode)) + magit-buffer-refname) + +(defvar magit-reflog-labels + '(("commit" . magit-reflog-commit) + ("amend" . magit-reflog-amend) + ("merge" . magit-reflog-merge) + ("checkout" . magit-reflog-checkout) + ("branch" . magit-reflog-checkout) + ("reset" . magit-reflog-reset) + ("rebase" . magit-reflog-rebase) + ("cherry-pick" . magit-reflog-cherry-pick) + ("initial" . magit-reflog-commit) + ("pull" . magit-reflog-remote) + ("clone" . magit-reflog-remote) + ("autosave" . magit-reflog-commit) + ("restart" . magit-reflog-reset))) + +(defun magit-reflog-format-subject (subject) + (let* ((match (string-match magit-reflog-subject-re subject)) + (command (and match (match-string 1 subject))) + (option (and match (match-string 2 subject))) + (type (and match (match-string 3 subject))) + (label (if (string= command "commit") + (or type command) + command)) + (text (if (string= command "commit") + label + (mapconcat #'identity + (delq nil (list command option type)) + " ")))) + (format "%-16s " + (magit--propertize-face + text (or (cdr (assoc label magit-reflog-labels)) + 'magit-reflog-other))))) + +;;; _ +(provide 'magit-reflog) +;;; magit-reflog.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-refs.el b/code/elpa/magit-20220821.1819/magit-refs.el new file mode 100644 index 0000000..07b03d7 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-refs.el @@ -0,0 +1,774 @@ +;;; magit-refs.el --- Listing references -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for listing references in a buffer. + +;;; Code: + +(require 'magit) + +;;; Options + +(defgroup magit-refs nil + "Inspect and manipulate Git branches and tags." + :link '(info-link "(magit)References Buffer") + :group 'magit-modes) + +(defcustom magit-refs-mode-hook nil + "Hook run after entering Magit-Refs mode." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :type 'hook) + +(defcustom magit-refs-sections-hook + '(magit-insert-error-header + magit-insert-branch-description + magit-insert-local-branches + magit-insert-remote-branches + magit-insert-tags) + "Hook run to insert sections into a references buffer." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :type 'hook) + +(defcustom magit-refs-show-commit-count nil + "Whether to show commit counts in Magit-Refs mode buffers. + +all Show counts for branches and tags. +branch Show counts for branches only. +nil Never show counts. + +To change the value in an existing buffer use the command +`magit-refs-set-show-commit-count'." + :package-version '(magit . "2.1.0") + :group 'magit-refs + :safe (lambda (val) (memq val '(all branch nil))) + :type '(choice (const all :tag "For branches and tags") + (const branch :tag "For branches only") + (const nil :tag "Never"))) +(put 'magit-refs-show-commit-count 'safe-local-variable 'symbolp) +(put 'magit-refs-show-commit-count 'permanent-local t) + +(defcustom magit-refs-pad-commit-counts nil + "Whether to pad all counts on all sides in `magit-refs-mode' buffers. + +If this is nil, then some commit counts are displayed right next +to one of the branches that appear next to the count, without any +space in between. This might look bad if the branch name faces +look too similar to `magit-dimmed'. + +If this is non-nil, then spaces are placed on both sides of all +commit counts." + :package-version '(magit . "2.12.0") + :group 'magit-refs + :type 'boolean) + +(defvar magit-refs-show-push-remote nil + "Whether to show the push-remotes of local branches. +Also show the commits that the local branch is ahead and behind +the push-target. Unfortunately there is a bug in Git that makes +this useless (the commits ahead and behind the upstream are +shown), so this isn't enabled yet.") + +(defcustom magit-refs-show-remote-prefix nil + "Whether to show the remote prefix in lists of remote branches. + +This is redundant because the name of the remote is already shown +in the heading preceding the list of its branches." + :package-version '(magit . "2.12.0") + :group 'magit-refs + :type 'boolean) + +(defcustom magit-refs-margin + (list nil + (nth 1 magit-log-margin) + 'magit-log-margin-width nil + (nth 4 magit-log-margin)) + "Format of the margin in `magit-refs-mode' buffers. + +The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). + +If INIT is non-nil, then the margin is shown initially. +STYLE controls how to format the author or committer date. + It can be one of `age' (to show the age of the commit), + `age-abbreviated' (to abbreviate the time unit to a character), + or a string (suitable for `format-time-string') to show the + actual date. Option `magit-log-margin-show-committer-date' + controls which date is being displayed. +WIDTH controls the width of the margin. This exists for forward + compatibility and currently the value should not be changed. +AUTHOR controls whether the name of the author is also shown by + default. +AUTHOR-WIDTH has to be an integer. When the name of the author + is shown, then this specifies how much space is used to do so." + :package-version '(magit . "2.9.0") + :group 'magit-refs + :group 'magit-margin + :safe (lambda (val) (memq val '(all branch nil))) + :type magit-log-margin--custom-type + :initialize #'magit-custom-initialize-reset + :set-after '(magit-log-margin) + :set (apply-partially #'magit-margin-set-variable 'magit-refs-mode)) + +(defcustom magit-refs-margin-for-tags nil + "Whether to show information about tags in the margin. + +This is disabled by default because it is slow if there are many +tags." + :package-version '(magit . "2.9.0") + :group 'magit-refs + :group 'magit-margin + :type 'boolean) + +(defcustom magit-refs-primary-column-width (cons 16 32) + "Width of the focus column in `magit-refs-mode' buffers. + +The primary column is the column that contains the name of the +branch that the current row is about. + +If this is an integer, then the column is that many columns wide. +Otherwise it has to be a cons-cell of two integers. The first +specifies the minimal width, the second the maximal width. In that +case the actual width is determined using the length of the names +of the shown local branches. (Remote branches and tags are not +taken into account when calculating to optimal width.)" + :package-version '(magit . "2.12.0") + :group 'magit-refs + :type '(choice (integer :tag "Constant wide") + (cons :tag "Wide constrains" + (integer :tag "Minimum") + (integer :tag "Maximum")))) + +(defcustom magit-refs-focus-column-width 5 + "Width of the focus column in `magit-refs-mode' buffers. + +The focus column is the first column, which marks one +branch (usually the current branch) as the focused branch using +\"*\" or \"@\". For each other reference, this column optionally +shows how many commits it is ahead of the focused branch and \"<\", or +if it isn't ahead then the commits it is behind and \">\", or if it +isn't behind either, then a \"=\". + +This column may also display only \"*\" or \"@\" for the focused +branch, in which case this option is ignored. Use \"L v\" to +change the verbosity of this column." + :package-version '(magit . "2.12.0") + :group 'magit-refs + :type 'integer) + +(defcustom magit-refs-filter-alist nil + "Alist controlling which refs are omitted from `magit-refs-mode' buffers. + +The purpose of this option is to forgo displaying certain refs +based on their name. If you want to not display any refs of a +certain type, then you should remove the appropriate function +from `magit-refs-sections-hook' instead. + +All keys are tried in order until one matches. Then its value +is used and subsequent elements are ignored. If the value is +non-nil, then the reference is displayed, otherwise it is not. +If no element matches, then the reference is displayed. + +A key can either be a regular expression that the refname has to +match, or a function that takes the refname as only argument and +returns a boolean. A remote branch such as \"origin/master\" is +displayed as just \"master\", however for this comparison the +former is used." + :package-version '(magit . "2.12.0") + :group 'magit-refs + :type '(alist :key-type (choice :tag "Key" regexp function) + :value-type (boolean :tag "Value" + :on "show (non-nil)" + :off "omit (nil)"))) + +(defcustom magit-visit-ref-behavior nil + "Control how `magit-visit-ref' behaves in `magit-refs-mode' buffers. + +By default `magit-visit-ref' behaves like `magit-show-commit', +in all buffers, including `magit-refs-mode' buffers. When the +type of the section at point is `commit' then \"RET\" is bound to +`magit-show-commit', and when the type is either `branch' or +`tag' then it is bound to `magit-visit-ref'. + +\"RET\" is one of Magit's most essential keys and at least by +default it should behave consistently across all of Magit, +especially because users quickly learn that it does something +very harmless; it shows more information about the thing at point +in another buffer. + +However \"RET\" used to behave differently in `magit-refs-mode' +buffers, doing surprising things, some of which cannot really be +described as \"visit this thing\". If you have grown accustomed +to such inconsistent, but to you useful, behavior, then you can +restore that by adding one or more of the below symbols to the +value of this option. But keep in mind that by doing so you +don't only introduce inconsistencies, you also lose some +functionality and might have to resort to `M-x magit-show-commit' +to get it back. + +`magit-visit-ref' looks for these symbols in the order in which +they are described here. If the presence of a symbol applies to +the current situation, then the symbols that follow do not affect +the outcome. + +`focus-on-ref' + + With a prefix argument update the buffer to show commit counts + and lists of cherry commits relative to the reference at point + instead of relative to the current buffer or `HEAD'. + + Instead of adding this symbol, consider pressing \"C-u y o RET\". + +`create-branch' + + If point is on a remote branch, then create a new local branch + with the same name, use the remote branch as its upstream, and + then check out the local branch. + + Instead of adding this symbol, consider pressing \"b c RET RET\", + like you would do in other buffers. + +`checkout-any' + + Check out the reference at point. If that reference is a tag + or a remote branch, then this results in a detached `HEAD'. + + Instead of adding this symbol, consider pressing \"b b RET\", + like you would do in other buffers. + +`checkout-branch' + + Check out the local branch at point. + + Instead of adding this symbol, consider pressing \"b b RET\", + like you would do in other buffers." + :package-version '(magit . "2.9.0") + :group 'magit-refs + :group 'magit-commands + :options '(focus-on-ref create-branch checkout-any checkout-branch) + :type '(list :convert-widget custom-hook-convert-widget)) + +;;; Mode + +(defvar magit-refs-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map (kbd "C-y") #'magit-refs-set-show-commit-count) + (define-key map (kbd "L") #'magit-margin-settings) + map) + "Keymap for `magit-refs-mode'.") + +(define-derived-mode magit-refs-mode magit-mode "Magit Refs" + "Mode which lists and compares references. + +This mode is documented in info node `(magit)References Buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] or \\[magit-diff-show-or-scroll-up] \ +to visit the commit or branch at point. + +Type \\[magit-branch] to see available branch commands. +Type \\[magit-merge] to merge the branch or commit at point. +Type \\[magit-cherry-pick] to apply the commit at point. +Type \\[magit-reset] to reset `HEAD' to the commit at point. + +\\{magit-refs-mode-map}" + :group 'magit-refs + (hack-dir-local-variables-non-file-buffer) + (setq magit--imenu-group-types '(local remote tags))) + +(defun magit-refs-setup-buffer (ref args) + (magit-setup-buffer #'magit-refs-mode nil + (magit-buffer-upstream ref) + (magit-buffer-arguments args))) + +(defun magit-refs-refresh-buffer () + (setq magit-set-buffer-margin-refresh (not (magit-buffer-margin-p))) + (unless (magit-rev-verify magit-buffer-upstream) + (setq magit-refs-show-commit-count nil)) + (magit-set-header-line-format + (format "%s %s" magit-buffer-upstream + (mapconcat #'identity magit-buffer-arguments " "))) + (magit-insert-section (branchbuf) + (magit-run-section-hook 'magit-refs-sections-hook)) + (add-hook 'kill-buffer-hook #'magit-preserve-section-visibility-cache)) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-refs-mode)) + (cons magit-buffer-upstream magit-buffer-arguments)) + +;;; Commands + +;;;###autoload (autoload 'magit-show-refs "magit-refs" nil t) +(transient-define-prefix magit-show-refs (&optional transient) + "List and compare references in a dedicated buffer." + :man-page "git-branch" + :value (lambda () + (magit-show-refs-arguments magit-prefix-use-buffer-arguments)) + ["Arguments" + (magit-for-each-ref:--contains) + ("-M" "Merged" "--merged=" magit-transient-read-revision) + ("-m" "Merged to HEAD" "--merged") + ("-N" "Not merged" "--no-merged=" magit-transient-read-revision) + ("-n" "Not merged to HEAD" "--no-merged") + (magit-for-each-ref:--sort)] + ["Actions" + ("y" "Show refs, comparing them with HEAD" magit-show-refs-head) + ("c" "Show refs, comparing them with current branch" magit-show-refs-current) + ("o" "Show refs, comparing them with other branch" magit-show-refs-other) + ("r" "Show refs, changing commit count display" + magit-refs-set-show-commit-count)] + (interactive (list (or (derived-mode-p 'magit-refs-mode) + current-prefix-arg))) + (if transient + (transient-setup 'magit-show-refs) + (magit-refs-setup-buffer "HEAD" (magit-show-refs-arguments)))) + +(defun magit-show-refs-arguments (&optional use-buffer-args) + (unless use-buffer-args + (setq use-buffer-args magit-direct-use-buffer-arguments)) + (let (args) + (cond + ((eq transient-current-command 'magit-show-refs) + (setq args (transient-args 'magit-show-refs))) + ((eq major-mode 'magit-refs-mode) + (setq args magit-buffer-arguments)) + ((and (memq use-buffer-args '(always selected)) + (when-let* ((buffer (magit-get-mode-buffer ;debbugs#31840 + 'magit-refs-mode nil + (eq use-buffer-args 'selected)))) + (setq args (buffer-local-value 'magit-buffer-arguments buffer)) + t))) + (t + (setq args (alist-get 'magit-show-refs transient-values)))) + args)) + +(transient-define-argument magit-for-each-ref:--contains () + :description "Contains" + :class 'transient-option + :key "-c" + :argument "--contains=" + :reader #'magit-transient-read-revision) + +(transient-define-argument magit-for-each-ref:--sort () + :description "Sort" + :class 'transient-option + :key "-s" + :argument "--sort=" + :reader #'magit-read-ref-sort) + +(defun magit-read-ref-sort (prompt initial-input _history) + (magit-completing-read prompt + '("-committerdate" "-authordate" + "committerdate" "authordate") + nil nil initial-input)) + +;;;###autoload +(defun magit-show-refs-head (&optional args) + "List and compare references in a dedicated buffer. +Compared with `HEAD'." + (interactive (list (magit-show-refs-arguments))) + (magit-refs-setup-buffer "HEAD" args)) + +;;;###autoload +(defun magit-show-refs-current (&optional args) + "List and compare references in a dedicated buffer. +Compare with the current branch or `HEAD' if it is detached." + (interactive (list (magit-show-refs-arguments))) + (magit-refs-setup-buffer (magit-get-current-branch) args)) + +;;;###autoload +(defun magit-show-refs-other (&optional ref args) + "List and compare references in a dedicated buffer. +Compared with a branch read from the user." + (interactive (list (magit-read-other-branch "Compare with") + (magit-show-refs-arguments))) + (magit-refs-setup-buffer ref args)) + +(defun magit-refs-set-show-commit-count () + "Change for which refs the commit count is shown." + (interactive) + (setq-local magit-refs-show-commit-count + (magit-read-char-case "Show commit counts for " nil + (?a "[a]ll refs" 'all) + (?b "[b]ranches only" t) + (?n "[n]othing" nil))) + (magit-refresh)) + +(defun magit-visit-ref () + "Visit the reference or revision at point in another buffer. +If there is no revision at point or with a prefix argument prompt +for a revision. + +This command behaves just like `magit-show-commit', except if +point is on a reference in a `magit-refs-mode' buffer (a buffer +listing branches and tags), in which case the behavior may be +different, but only if you have customized the option +`magit-visit-ref-behavior' (which see). When invoked from a +menu this command always behaves like `magit-show-commit'." + (interactive) + (if (and (derived-mode-p 'magit-refs-mode) + (magit-section-match '(branch tag)) + (not (magit-menu-position))) + (let ((ref (oref (magit-current-section) value))) + (cond (current-prefix-arg + (cond ((memq 'focus-on-ref magit-visit-ref-behavior) + (magit-refs-setup-buffer ref (magit-show-refs-arguments))) + (magit-visit-ref-behavior + ;; Don't prompt for commit to visit. + (let ((current-prefix-arg nil)) + (call-interactively #'magit-show-commit))))) + ((and (memq 'create-branch magit-visit-ref-behavior) + (magit-section-match [branch remote])) + (let ((branch (cdr (magit-split-branch-name ref)))) + (if (magit-branch-p branch) + (if (magit-rev-eq branch ref) + (magit-call-git "checkout" branch) + (setq branch (propertize branch 'face 'magit-branch-local)) + (setq ref (propertize ref 'face 'magit-branch-remote)) + (pcase (prog1 (read-char-choice (format (propertize "\ +Branch %s already exists. + [c]heckout %s as-is + [r]reset %s to %s and checkout %s + [a]bort " 'face 'minibuffer-prompt) branch branch branch ref branch) + '(?c ?r ?a)) + (message "")) ; otherwise prompt sticks + (?c (magit-call-git "checkout" branch)) + (?r (magit-call-git "checkout" "-B" branch ref)) + (?a (user-error "Abort")))) + (magit-call-git "checkout" "-b" branch ref)) + (setq magit-buffer-upstream branch) + (magit-refresh))) + ((or (memq 'checkout-any magit-visit-ref-behavior) + (and (memq 'checkout-branch magit-visit-ref-behavior) + (magit-section-match [branch local]))) + (magit-call-git "checkout" ref) + (setq magit-buffer-upstream ref) + (magit-refresh)) + (t + (call-interactively #'magit-show-commit)))) + (call-interactively #'magit-show-commit))) + +;;; Sections + +(defvar magit-remote-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-delete-thing] #'magit-remote-remove "Remove %m") + (magit-menu-set map [magit-file-rename] #'magit-remote-rename "Rename %s") + map) + "Keymap for `remote' sections.") + +(defvar magit-branch-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-visit-ref "Visit commit") + (magit-menu-set map [magit-delete-thing] #'magit-branch-delete "Delete %m") + (magit-menu-set map [magit-file-rename] #'magit-branch-rename "Rename %s") + map) + "Keymap for `branch' sections.") + +(defvar magit-tag-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-visit-ref "Visit %s") + (magit-menu-set map [magit-delete-thing] #'magit-tag-delete "Delete %m") + map) + "Keymap for `tag' sections.") + +(defun magit--painted-branch-as-menu-section (section) + (and-let* ((branch (and (magit-section-match 'commit) + (magit--painted-branch-at-point)))) + (let ((dummy (magit-section :type 'branch :value branch))) + (oset dummy keymap magit-branch-section-map) + (dolist (slot '(start content hidden parent children)) + (when (slot-boundp section slot) + (setf (eieio-oref dummy slot) + (eieio-oref section slot)))) + dummy))) + +(add-hook 'magit-menu-alternative-section-hook + #'magit--painted-branch-as-menu-section) + +(defun magit-insert-branch-description () + "Insert header containing the description of the current branch. +Insert a header line with the name and description of the +current branch. The description is taken from the Git variable +`branch..description'; if that is undefined then no header +line is inserted at all." + (when-let* ((branch (magit-get-current-branch)) + (desc (magit-get "branch" branch "description")) + (desc (split-string desc "\n"))) + (when (equal (car (last desc)) "") + (setq desc (butlast desc))) + (magit-insert-section (branchdesc branch t) + (magit-insert-heading branch ": " (car desc)) + (when (cdr desc) + (insert (mapconcat #'identity (cdr desc) "\n")) + (insert "\n\n"))))) + +(defun magit-insert-tags () + "Insert sections showing all tags." + (when-let ((tags (magit-git-lines "tag" "--list" "-n" magit-buffer-arguments))) + (let ((_head (magit-rev-parse "HEAD"))) + (magit-insert-section (tags) + (magit-insert-heading "Tags:") + (dolist (tag tags) + (string-match "^\\([^ \t]+\\)[ \t]+\\([^ \t\n].*\\)?" tag) + (let ((tag (match-string 1 tag)) + (msg (match-string 2 tag))) + (when (magit-refs--insert-refname-p tag) + (magit-insert-section (tag tag t) + (magit-insert-heading + (magit-refs--format-focus-column tag 'tag) + (propertize tag 'font-lock-face 'magit-tag) + (make-string + (max 1 (- (if (consp magit-refs-primary-column-width) + (car magit-refs-primary-column-width) + magit-refs-primary-column-width) + (length tag))) + ?\s) + (and msg (magit-log-propertize-keywords nil msg))) + (when (and magit-refs-margin-for-tags (magit-buffer-margin-p)) + (magit-refs--format-margin tag)) + (magit-refs--insert-cherry-commits tag))))) + (insert ?\n) + (magit-make-margin-overlay nil t))))) + +(defun magit-insert-remote-branches () + "Insert sections showing all remote-tracking branches." + (dolist (remote (magit-list-remotes)) + (magit-insert-section (remote remote) + (magit-insert-heading + (let ((pull (magit-get "remote" remote "url")) + (push (magit-get "remote" remote "pushurl"))) + (format (propertize "Remote %s (%s):" + 'font-lock-face 'magit-section-heading) + (propertize remote 'font-lock-face 'magit-branch-remote) + (concat pull (and pull push ", ") push)))) + (let (head) + (dolist (line (magit-git-lines "for-each-ref" "--format=\ +%(symref:short)%00%(refname:short)%00%(refname)%00%(subject)" + (concat "refs/remotes/" remote) + magit-buffer-arguments)) + (pcase-let ((`(,head-branch ,branch ,ref ,msg) + (-replace "" nil (split-string line "\0")))) + (if head-branch + (progn (cl-assert (equal branch (concat remote "/HEAD"))) + (setq head head-branch)) + (when (magit-refs--insert-refname-p branch) + (magit-insert-section (branch branch t) + (let ((headp (equal branch head)) + (abbrev (if magit-refs-show-remote-prefix + branch + (substring branch (1+ (length remote)))))) + (magit-insert-heading + (magit-refs--format-focus-column branch) + (magit-refs--propertize-branch + abbrev ref (and headp 'magit-branch-remote-head)) + (make-string + (max 1 (- (if (consp magit-refs-primary-column-width) + (car magit-refs-primary-column-width) + magit-refs-primary-column-width) + (length abbrev))) + ?\s) + (and msg (magit-log-propertize-keywords nil msg)))) + (when (magit-buffer-margin-p) + (magit-refs--format-margin branch)) + (magit-refs--insert-cherry-commits branch))))))) + (insert ?\n) + (magit-make-margin-overlay nil t)))) + +(defun magit-insert-local-branches () + "Insert sections showing all local branches." + (magit-insert-section (local nil) + (magit-insert-heading "Branches:") + (dolist (line (magit-refs--format-local-branches)) + (pcase-let ((`(,branch . ,strings) line)) + (magit-insert-section + ((eval (if branch 'branch 'commit)) + (or branch (magit-rev-parse "HEAD")) + t) + (apply #'magit-insert-heading strings) + (when (magit-buffer-margin-p) + (magit-refs--format-margin branch)) + (magit-refs--insert-cherry-commits branch)))) + (insert ?\n) + (magit-make-margin-overlay nil t))) + +(defun magit-refs--format-local-branches () + (let ((lines (-keep #'magit-refs--format-local-branch + (magit-git-lines + "for-each-ref" + (concat "--format=\ +%(HEAD)%00%(refname:short)%00%(refname)%00\ +%(upstream:short)%00%(upstream)%00%(upstream:track)%00" + (if magit-refs-show-push-remote "\ +%(push:remotename)%00%(push)%00%(push:track)%00%(subject)" + "%00%00%00%(subject)")) + "refs/heads" + magit-buffer-arguments)))) + (unless (magit-get-current-branch) + (push (magit-refs--format-local-branch + (concat "*\0\0\0\0\0\0\0\0" (magit-rev-format "%s"))) + lines)) + (setq-local magit-refs-primary-column-width + (let ((def (default-value 'magit-refs-primary-column-width))) + (if (atom def) + def + (pcase-let ((`(,min . ,max) def)) + (min max (apply #'max min (mapcar #'car lines))))))) + (mapcar (pcase-lambda (`(,_ ,branch ,focus ,branch-desc ,u:ahead ,p:ahead + ,u:behind ,upstream ,p:behind ,push ,msg)) + (list branch focus branch-desc u:ahead p:ahead + (make-string (max 1 (- magit-refs-primary-column-width + (length (concat branch-desc + u:ahead + p:ahead + u:behind)))) + ?\s) + u:behind upstream p:behind push + msg)) + lines))) + +(defun magit-refs--format-local-branch (line) + (pcase-let ((`(,head ,branch ,ref ,upstream ,u:ref ,u:track + ,push ,p:ref ,p:track ,msg) + (-replace "" nil (split-string line "\0")))) + (when (or (not branch) + (magit-refs--insert-refname-p branch)) + (let* ((headp (equal head "*")) + (pushp (and push + magit-refs-show-push-remote + (magit-rev-verify p:ref) + (not (equal p:ref u:ref)))) + (branch-desc + (if branch + (magit-refs--propertize-branch + branch ref (and headp 'magit-branch-current)) + (magit--propertize-face "(detached)" 'magit-branch-warning))) + (u:ahead (and u:track + (string-match "ahead \\([0-9]+\\)" u:track) + (magit--propertize-face + (concat (and magit-refs-pad-commit-counts " ") + (match-string 1 u:track) + ">") + 'magit-dimmed))) + (u:behind (and u:track + (string-match "behind \\([0-9]+\\)" u:track) + (magit--propertize-face + (concat "<" + (match-string 1 u:track) + (and magit-refs-pad-commit-counts " ")) + 'magit-dimmed))) + (p:ahead (and pushp p:track + (string-match "ahead \\([0-9]+\\)" p:track) + (magit--propertize-face + (concat (match-string 1 p:track) + ">" + (and magit-refs-pad-commit-counts " ")) + 'magit-branch-remote))) + (p:behind (and pushp p:track + (string-match "behind \\([0-9]+\\)" p:track) + (magit--propertize-face + (concat "<" + (match-string 1 p:track) + (and magit-refs-pad-commit-counts " ")) + 'magit-dimmed)))) + (list (1+ (length (concat branch-desc u:ahead p:ahead u:behind))) + branch + (magit-refs--format-focus-column branch headp) + branch-desc u:ahead p:ahead u:behind + (and upstream + (concat (if (equal u:track "[gone]") + (magit--propertize-face upstream 'error) + (magit-refs--propertize-branch upstream u:ref)) + " ")) + (and pushp + (concat p:behind + (magit--propertize-face + push 'magit-branch-remote) + " ")) + (and msg (magit-log-propertize-keywords nil msg))))))) + +(defun magit-refs--format-focus-column (ref &optional type) + (let ((focus magit-buffer-upstream) + (width (if magit-refs-show-commit-count + magit-refs-focus-column-width + 1))) + (format + (format "%%%ss " width) + (cond ((or (equal ref focus) + (and (eq type t) + (equal focus "HEAD"))) + (magit--propertize-face (concat (if (equal focus "HEAD") "@" "*") + (make-string (1- width) ?\s)) + 'magit-section-heading)) + ((if (eq type 'tag) + (eq magit-refs-show-commit-count 'all) + magit-refs-show-commit-count) + (pcase-let ((`(,behind ,ahead) + (magit-rev-diff-count magit-buffer-upstream ref))) + (magit--propertize-face + (cond ((> ahead 0) (concat "<" (number-to-string ahead))) + ((> behind 0) (concat (number-to-string behind) ">")) + (t "=")) + 'magit-dimmed))) + (t ""))))) + +(defun magit-refs--propertize-branch (branch ref &optional head-face) + (let ((face (cdr (cl-find-if (pcase-lambda (`(,re . ,_)) + (string-match-p re ref)) + magit-ref-namespaces)))) + (magit--propertize-face + branch (if head-face (list face head-face) face)))) + +(defun magit-refs--insert-refname-p (refname) + (--if-let (-first (pcase-lambda (`(,key . ,_)) + (if (functionp key) + (funcall key refname) + (string-match-p key refname))) + magit-refs-filter-alist) + (cdr it) + t)) + +(defun magit-refs--insert-cherry-commits (ref) + (magit-insert-section-body + (let ((start (point)) + (magit-insert-section--current nil)) + (magit-git-wash (apply-partially #'magit-log-wash-log 'cherry) + "cherry" "-v" (magit-abbrev-arg) magit-buffer-upstream ref) + (if (= (point) start) + (message "No cherries for %s" ref) + (magit-make-margin-overlay nil t))))) + +(defun magit-refs--format-margin (commit) + (save-excursion + (goto-char (line-beginning-position 0)) + (let ((line (magit-rev-format "%ct%cN" commit))) + (magit-log-format-margin commit + (substring line 10) + (substring line 0 10))))) + +;;; _ +(provide 'magit-refs) +;;; magit-refs.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-remote.el b/code/elpa/magit-20220821.1819/magit-remote.el new file mode 100644 index 0000000..8c9dc1e --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-remote.el @@ -0,0 +1,368 @@ +;;; magit-remote.el --- Transfer Git commits -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements remote commands. + +;;; Code: + +(require 'magit) + +;;; Options + +(defcustom magit-remote-add-set-remote.pushDefault 'ask-if-unset + "Whether to set the value of `remote.pushDefault' after adding a remote. + +If `ask', then always ask. If `ask-if-unset', then ask, but only +if the variable isn't set already. If nil, then don't ever set. +If the value is a string, then set without asking, provided that +the name of the added remote is equal to that string and the +variable isn't already set." + :package-version '(magit . "2.4.0") + :group 'magit-commands + :type '(choice (const :tag "ask if unset" ask-if-unset) + (const :tag "always ask" ask) + (string :tag "set if named") + (const :tag "don't set"))) + +(defcustom magit-remote-direct-configure t + "Whether the command `magit-remote' shows Git variables. +When set to nil, no variables are displayed by this transient +command, instead the sub-transient `magit-remote-configure' +has to be used to view and change remote related variables." + :package-version '(magit . "2.12.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-prefer-push-default nil + "Whether to prefer `remote.pushDefault' over per-branch variables." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type 'boolean) + +;;; Commands + +;;;###autoload (autoload 'magit-remote "magit-remote" nil t) +(transient-define-prefix magit-remote (remote) + "Add, configure or remove a remote." + :man-page "git-remote" + :value '("-f") + ["Variables" + :if (lambda () + (and magit-remote-direct-configure + (oref transient--prefix scope))) + ("u" magit-remote..url) + ("U" magit-remote..fetch) + ("s" magit-remote..pushurl) + ("S" magit-remote..push) + ("O" magit-remote..tagopt)] + ["Arguments for add" + ("-f" "Fetch after add" "-f")] + ["Actions" + [("a" "Add" magit-remote-add) + ("r" "Rename" magit-remote-rename) + ("k" "Remove" magit-remote-remove)] + [("C" "Configure..." magit-remote-configure) + ("p" "Prune stale branches" magit-remote-prune) + ("P" "Prune stale refspecs" magit-remote-prune-refspecs) + (7 "z" "Unshallow remote" magit-remote-unshallow)]] + (interactive (list (magit-get-current-remote))) + (transient-setup 'magit-remote nil nil :scope remote)) + +(defun magit-read-url (prompt &optional initial-input) + (let ((url (magit-read-string-ns prompt initial-input))) + (if (string-prefix-p "~" url) + (expand-file-name url) + url))) + +;;;###autoload +(defun magit-remote-add (remote url &optional args) + "Add a remote named REMOTE and fetch it." + (interactive + (let ((origin (magit-get "remote.origin.url")) + (remote (magit-read-string-ns "Remote name"))) + (list remote + (magit-read-url + "Remote url" + (and origin + (string-match "\\([^:/]+\\)/[^/]+\\(\\.git\\)?\\'" origin) + (replace-match remote t t origin 1))) + (transient-args 'magit-remote)))) + (if (pcase (list magit-remote-add-set-remote.pushDefault + (magit-get "remote.pushDefault")) + (`(,(pred stringp) ,_) t) + ((or `(ask ,_) '(ask-if-unset nil)) + (y-or-n-p (format "Set `remote.pushDefault' to \"%s\"? " remote)))) + (progn (magit-call-git "remote" "add" args remote url) + (setf (magit-get "remote.pushDefault") remote) + (magit-refresh)) + (magit-run-git-async "remote" "add" args remote url))) + +;;;###autoload +(defun magit-remote-rename (old new) + "Rename the remote named OLD to NEW." + (interactive + (let ((remote (magit-read-remote "Rename remote"))) + (list remote (magit-read-string-ns (format "Rename %s to" remote))))) + (unless (string= old new) + (magit-call-git "remote" "rename" old new) + (magit-remote--cleanup-push-variables old new) + (magit-refresh))) + +;;;###autoload +(defun magit-remote-remove (remote) + "Delete the remote named REMOTE." + (interactive (list (magit-read-remote "Delete remote"))) + (magit-call-git "remote" "rm" remote) + (magit-remote--cleanup-push-variables remote) + (magit-refresh)) + +(defun magit-remote--cleanup-push-variables (remote &optional new-name) + (magit-with-toplevel + (when (equal (magit-get "remote.pushDefault") remote) + (magit-set new-name "remote.pushDefault")) + (dolist (var (magit-git-lines "config" "--name-only" + "--get-regexp" "^branch\.[^.]*\.pushRemote" + (format "^%s$" remote))) + (magit-call-git "config" (and (not new-name) "--unset") var new-name)))) + +(defconst magit--refspec-re "\\`\\(\\+\\)?\\([^:]+\\):\\(.*\\)\\'") + +;;;###autoload +(defun magit-remote-prune (remote) + "Remove stale remote-tracking branches for REMOTE." + (interactive (list (magit-read-remote "Prune stale branches of remote"))) + (magit-run-git-async "remote" "prune" remote)) + +;;;###autoload +(defun magit-remote-prune-refspecs (remote) + "Remove stale refspecs for REMOTE. + +A refspec is stale if there no longer exists at least one branch +on the remote that would be fetched due to that refspec. A stale +refspec is problematic because its existence causes Git to refuse +to fetch according to the remaining non-stale refspecs. + +If only stale refspecs remain, then offer to either delete the +remote or to replace the stale refspecs with the default refspec. + +Also remove the remote-tracking branches that were created due to +the now stale refspecs. Other stale branches are not removed." + (interactive (list (magit-read-remote "Prune refspecs of remote"))) + (let* ((tracking-refs (magit-list-remote-branches remote)) + (remote-refs (magit-remote-list-refs remote)) + (variable (format "remote.%s.fetch" remote)) + (refspecs (magit-get-all variable)) + stale) + (dolist (refspec refspecs) + (when (string-match magit--refspec-re refspec) + (let ((theirs (match-string 2 refspec)) + (ours (match-string 3 refspec))) + (unless (if (string-match "\\*" theirs) + (let ((re (replace-match ".*" t t theirs))) + (--some (string-match-p re it) remote-refs)) + (member theirs remote-refs)) + (push (cons refspec + (if (string-match "\\*" ours) + (let ((re (replace-match ".*" t t ours))) + (--filter (string-match-p re it) tracking-refs)) + (list (car (member ours tracking-refs))))) + stale))))) + (if (not stale) + (message "No stale refspecs for remote %S" remote) + (if (= (length stale) + (length refspecs)) + (magit-read-char-case + (format "All of %s's refspecs are stale. " remote) nil + (?s "replace with [d]efault refspec" + (magit-set-all + (list (format "+refs/heads/*:refs/remotes/%s/*" remote)) + variable)) + (?r "[r]emove remote" + (magit-call-git "remote" "rm" remote)) + (?a "or [a]abort" + (user-error "Abort"))) + (if (if (length= stale 1) + (pcase-let ((`(,refspec . ,refs) (car stale))) + (magit-confirm 'prune-stale-refspecs + (format "Prune stale refspec %s and branch %%s" refspec) + (format "Prune stale refspec %s and %%i branches" refspec) + nil refs)) + (magit-confirm 'prune-stale-refspecs nil + (format "Prune %%i stale refspecs and %i branches" + (length (cl-mapcan (lambda (s) (copy-sequence (cdr s))) + stale))) + nil + (mapcar (pcase-lambda (`(,refspec . ,refs)) + (concat refspec "\n" + (mapconcat (lambda (b) (concat " " b)) + refs "\n"))) + stale))) + (pcase-dolist (`(,refspec . ,refs) stale) + (magit-call-git "config" "--unset" variable + (regexp-quote refspec)) + (magit--log-action + (lambda (refs) + (format "Deleting %i branches" (length refs))) + (lambda (ref) + (format "Deleting branch %s (was %s)" ref + (magit-rev-parse "--short" ref))) + refs) + (dolist (ref refs) + (magit-call-git "update-ref" "-d" ref))) + (user-error "Abort"))) + (magit-refresh)))) + +;;;###autoload +(defun magit-remote-set-head (remote &optional branch) + "Set the local representation of REMOTE's default branch. +Query REMOTE and set the symbolic-ref refs/remotes//HEAD +accordingly. With a prefix argument query for the branch to be +used, which allows you to select an incorrect value if you fancy +doing that." + (interactive + (let ((remote (magit-read-remote "Set HEAD for remote"))) + (list remote + (and current-prefix-arg + (magit-read-remote-branch (format "Set %s/HEAD to" remote) + remote nil nil t))))) + (magit-run-git "remote" "set-head" remote (or branch "--auto"))) + +;;;###autoload +(defun magit-remote-unset-head (remote) + "Unset the local representation of REMOTE's default branch. +Delete the symbolic-ref \"refs/remotes//HEAD\"." + (interactive (list (magit-read-remote "Unset HEAD for remote"))) + (magit-run-git "remote" "set-head" remote "--delete")) + +;;;###autoload +(defun magit-remote-unshallow (remote) + "Convert a shallow remote into a full one. +If only a single refspec is set and it does not contain a +wildcard, then also offer to replace it with the standard +refspec." + (interactive (list (or (magit-get-current-remote) + (magit-read-remote "Delete remote")))) + (let ((refspecs (magit-get-all "remote" remote "fetch")) + (standard (format "+refs/heads/*:refs/remotes/%s/*" remote))) + (when (and (length= refspecs 1) + (not (string-search "*" (car refspecs))) + (yes-or-no-p (format "Also replace refspec %s with %s? " + (car refspecs) + standard))) + (magit-set standard "remote" remote "fetch")) + (magit-git-fetch "--unshallow" remote))) + +;;; Configure + +;;;###autoload (autoload 'magit-remote-configure "magit-remote" nil t) +(transient-define-prefix magit-remote-configure (remote) + "Configure a remote." + :man-page "git-remote" + [:description + (lambda () + (concat + (propertize "Configure " 'face 'transient-heading) + (propertize (oref transient--prefix scope) 'face 'magit-branch-remote))) + ("u" magit-remote..url) + ("U" magit-remote..fetch) + ("s" magit-remote..pushurl) + ("S" magit-remote..push) + ("O" magit-remote..tagopt)] + (interactive + (list (or (and (not current-prefix-arg) + (not (and magit-remote-direct-configure + (eq transient-current-command 'magit-remote))) + (magit-get-current-remote)) + (magit--read-remote-scope)))) + (transient-setup 'magit-remote-configure nil nil :scope remote)) + +(defun magit--read-remote-scope (&optional obj) + (magit-read-remote + (if obj + (format "Set %s for remote" + (format (oref obj variable) "")) + "Configure remote"))) + +(transient-define-infix magit-remote..url () + :class 'magit--git-variable:urls + :scope #'magit--read-remote-scope + :variable "remote.%s.url" + :multi-value t + :history-key 'magit-remote..*url) + +(transient-define-infix magit-remote..fetch () + :class 'magit--git-variable + :scope #'magit--read-remote-scope + :variable "remote.%s.fetch" + :multi-value t) + +(transient-define-infix magit-remote..pushurl () + :class 'magit--git-variable:urls + :scope #'magit--read-remote-scope + :variable "remote.%s.pushurl" + :multi-value t + :history-key 'magit-remote..*url + :seturl-arg "--push") + +(transient-define-infix magit-remote..push () + :class 'magit--git-variable + :scope #'magit--read-remote-scope + :variable "remote.%s.push") + +(transient-define-infix magit-remote..tagopt () + :class 'magit--git-variable:choices + :scope #'magit--read-remote-scope + :variable "remote.%s.tagOpt" + :choices '("--no-tags" "--tags")) + +;;; Transfer Utilities + +(defun magit--push-remote-variable (&optional branch short) + (unless branch + (setq branch (magit-get-current-branch))) + (magit--propertize-face + (if (or (not branch) magit-prefer-push-default) + (if short "pushDefault" "remote.pushDefault") + (if short "pushRemote" (format "branch.%s.pushRemote" branch))) + 'bold)) + +(defun magit--select-push-remote (prompt-suffix) + (let* ((branch (or (magit-get-current-branch) + (user-error "No branch is checked out"))) + (remote (magit-get-push-remote branch)) + (changed nil)) + (when (or current-prefix-arg + (not remote) + (not (member remote (magit-list-remotes)))) + (setq changed t) + (setq remote + (magit-read-remote (format "Set %s and %s" + (magit--push-remote-variable) + prompt-suffix))) + (setf (magit-get (magit--push-remote-variable branch)) remote)) + (list branch remote changed))) + +;;; _ +(provide 'magit-remote) +;;; magit-remote.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-repos.el b/code/elpa/magit-20220821.1819/magit-repos.el new file mode 100644 index 0000000..e7f63af --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-repos.el @@ -0,0 +1,543 @@ +;;; magit-repos.el --- Listing repositories -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for listing repositories. This +;; includes getting a Lisp list of known repositories as well as a +;; mode for listing repositories in a buffer. + +;;; Code: + +(require 'magit-core) + +(declare-function magit-status-setup-buffer "magit-status" (&optional directory)) + +(defvar x-stretch-cursor) + +;;; Options + +(defcustom magit-repository-directories nil + "List of directories that are or contain Git repositories. + +Each element has the form (DIRECTORY . DEPTH). DIRECTORY has +to be a directory or a directory file-name, a string. DEPTH, +an integer, specifies the maximum depth to look for Git +repositories. If it is 0, then only add DIRECTORY itself. + +This option controls which repositories are being listed by +`magit-list-repositories'. It also affects `magit-status' +\(which see) in potentially surprising ways." + :package-version '(magit . "3.0.0") + :group 'magit-essentials + :type '(repeat (cons directory (integer :tag "Depth")))) + +(defgroup magit-repolist nil + "List repositories in a buffer." + :link '(info-link "(magit)Repository List") + :group 'magit-modes) + +(defcustom magit-repolist-mode-hook '(hl-line-mode) + "Hook run after entering Magit-Repolist mode." + :package-version '(magit . "2.9.0") + :group 'magit-repolist + :type 'hook + :get #'magit-hook-custom-get + :options '(hl-line-mode)) + +(defcustom magit-repolist-columns + '(("Name" 25 magit-repolist-column-ident nil) + ("Version" 25 magit-repolist-column-version + ((:sort magit-repolist-version<))) + ("BU" 3 magit-repolist-column-unpushed-to-upstream + (;; (:help-echo "Local changes not in upstream") + (:right-align t) + (:sort <))) + ("Path" 99 magit-repolist-column-path nil)) + "List of columns displayed by `magit-list-repositories'. + +Each element has the form (HEADER WIDTH FORMAT PROPS). + +HEADER is the string displayed in the header. WIDTH is the width +of the column. FORMAT is a function that is called with one +argument, the repository identification (usually its basename), +and with `default-directory' bound to the toplevel of its working +tree. It has to return a string to be inserted or nil. PROPS is +an alist that supports the keys `:right-align', `:pad-right' and +`:sort'. + +The `:sort' function has a weird interface described in the +docstring of `tabulated-list--get-sort'. Alternatively `<' and +`magit-repolist-version<' can be used as those functions are +automatically replaced with functions that satisfy the interface. +Set `:sort' to nil to inhibit sorting; if unspecifed, then the +column is sortable using the default sorter. + +You may wish to display a range of numeric columns using just one +character per column and without any padding between columns, in +which case you should use an appropriat HEADER, set WIDTH to 1, +and set `:pad-right' to 0. \"+\" is substituted for numbers higher +than 9." + :package-version '(magit . "2.12.0") + :group 'magit-repolist + :type '(repeat (list :tag "Column" + (string :tag "Header Label") + (integer :tag "Column Width") + (function :tag "Inserter Function") + (repeat :tag "Properties" + (list (choice :tag "Property" + (const :right-align) + (const :pad-right) + (const :sort) + (symbol)) + (sexp :tag "Value")))))) + +(defcustom magit-repolist-column-flag-alist + '((magit-untracked-files . "N") + (magit-unstaged-files . "U") + (magit-staged-files . "S")) + "Association list of predicates and flags for `magit-repolist-column-flag'. + +Each element is of the form (FUNCTION . FLAG). Each FUNCTION is +called with no arguments, with `default-directory' bound to the +top level of a repository working tree, until one of them returns +a non-nil value. FLAG corresponding to that function is returned +as the value of `magit-repolist-column-flag'." + :package-version '(magit . "3.0.0") + :group 'magit-repolist + :type '(alist :key-type (function :tag "Predicate Function") + :value-type (string :tag "Flag"))) + +(defcustom magit-repolist-sort-key '("Path" . nil) + "Initial sort key for buffer created by `magit-list-repositories'. +If nil, no additional sorting is performed. Otherwise, this +should be a cons cell (NAME . FLIP). NAME is a string matching +one of the column names in `magit-repolist-columns'. FLIP, if +non-nil, means to invert the resulting sort." + :package-version '(magit . "3.2.0") + :group 'magit-repolist + :type '(choice (const nil) + (cons (string :tag "Column name") + (boolean :tag "Flip order")))) + +;;; List Repositories +;;;; List Commands +;;;###autoload +(defun magit-list-repositories () + "Display a list of repositories. + +Use the options `magit-repository-directories' to control which +repositories are displayed." + (interactive) + (magit-repolist-setup (default-value 'magit-repolist-columns))) + +;;;; Mode Commands + +(defun magit-repolist-status (&optional _button) + "Show the status for the repository at point." + (interactive) + (--if-let (tabulated-list-get-id) + (magit-status-setup-buffer (expand-file-name it)) + (user-error "There is no repository at point"))) + +(defun magit-repolist-mark () + "Mark a repository and move to the next line." + (interactive) + (magit-repolist--ensure-padding) + (tabulated-list-put-tag "*" t)) + +(defun magit-repolist-unmark () + "Unmark a repository and move to the next line." + (interactive) + (tabulated-list-put-tag " " t)) + +(defun magit-repolist-fetch (repos) + "Fetch all marked or listed repositories." + (interactive (list (magit-repolist--get-repos ?*))) + (run-hooks 'magit-credential-hook) + (magit-repolist--mapc (apply-partially #'magit-run-git "remote" "update") + repos "Fetching in %s...")) + +(defun magit-repolist-find-file-other-frame (repos file) + "Find a file in all marked or listed repositories." + (interactive (list (magit-repolist--get-repos ?*) + (read-string "Find file in repositories: "))) + (magit-repolist--mapc (apply-partially #'find-file-other-frame file) repos)) + +(defun magit-repolist--ensure-padding () + "Set `tabulated-list-padding' to 2, unless that is already non-zero." + (when (zerop tabulated-list-padding) + (setq tabulated-list-padding 2) + (tabulated-list-init-header) + (tabulated-list-print t))) + +(defun magit-repolist--get-repos (&optional char) + "Return marked repositories or `all' if none are marked. +If optional CHAR is non-nil, then only return repositories +marked with that character. If no repositories are marked +then ask whether to act on all repositories instead." + (or (magit-repolist--marked-repos char) + (if (magit-confirm 'repolist-all + "Nothing selected. Act on ALL displayed repositories") + 'all + (user-error "Abort")))) + +(defun magit-repolist--marked-repos (&optional char) + "Return marked repositories. +If optional CHAR is non-nil, then only return repositories +marked with that character." + (let (c list) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (setq c (char-after)) + (unless (eq c ?\s) + (if char + (when (eq c char) + (push (tabulated-list-get-id) list)) + (push (cons c (tabulated-list-get-id)) list))) + (forward-line))) + list)) + +(defun magit-repolist--mapc (fn repos &optional msg) + "Apply FN to each directory in REPOS for side effects only. +If REPOS is the symbol `all', then call FN for all displayed +repositories. When FN is called, `default-directory' is bound to +the top-level directory of the current repository. If optional +MSG is non-nil then that is displayed around each call to FN. +If it contains \"%s\" then the directory is substituted for that." + (when (eq repos 'all) + (setq repos nil) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (push (tabulated-list-get-id) repos) + (forward-line))) + (setq repos (nreverse repos))) + (let ((base default-directory) + (len (length repos)) + (i 0)) + (mapc (lambda (repo) + (let ((default-directory + (file-name-as-directory (expand-file-name repo base)))) + (if msg + (let ((msg (concat (format "(%s/%s) " (cl-incf i) len) + (format msg default-directory)))) + (message msg) + (funcall fn) + (message (concat msg "done"))) + (funcall fn)))) + repos))) + +;;;; Mode + +(defvar magit-repolist-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map tabulated-list-mode-map) + (define-key map (kbd "C-m") #'magit-repolist-status) + (define-key map (kbd "m") #'magit-repolist-mark) + (define-key map (kbd "u") #'magit-repolist-unmark) + (define-key map (kbd "f") #'magit-repolist-fetch) + (define-key map (kbd "5") #'magit-repolist-find-file-other-frame) + map) + "Local keymap for Magit-Repolist mode buffers.") + +(define-derived-mode magit-repolist-mode tabulated-list-mode "Repos" + "Major mode for browsing a list of Git repositories." + (setq-local x-stretch-cursor nil) + (setq tabulated-list-padding 0) + (add-hook 'tabulated-list-revert-hook #'magit-repolist-refresh nil t) + (setq imenu-prev-index-position-function + #'magit-repolist--imenu-prev-index-position) + (setq imenu-extract-index-name-function #'tabulated-list-get-id)) + +(defun magit-repolist-setup (columns) + (unless magit-repository-directories + (user-error "You need to customize `magit-repository-directories' %s" + "before you can list repositories")) + (with-current-buffer (get-buffer-create "*Magit Repositories*") + (magit-repolist-mode) + (setq-local magit-repolist-columns columns) + (magit-repolist-setup-1) + (magit-repolist-refresh) + (switch-to-buffer (current-buffer)))) + +(defun magit-repolist-setup-1 () + (unless tabulated-list-sort-key + (setq tabulated-list-sort-key + (pcase-let ((`(,column . ,flip) magit-repolist-sort-key)) + (cons (or (car (assoc column magit-repolist-columns)) + (caar magit-repolist-columns)) + flip)))) + (setq tabulated-list-format + (vconcat (-map-indexed + (lambda (idx column) + (pcase-let* ((`(,title ,width ,_fn ,props) column) + (sort-set (assoc :sort props)) + (sort-fn (cadr sort-set))) + (nconc (list title width + (cond ((eq sort-fn '<) + (magit-repolist-make-sorter + sort-fn #'string-to-number idx)) + ((eq sort-fn 'magit-repolist-version<) + (magit-repolist-make-sorter + sort-fn #'identity idx)) + (sort-fn sort-fn) + (sort-set nil) + (t t))) + (flatten-tree props)))) + magit-repolist-columns)))) + +(defun magit-repolist-refresh () + (setq tabulated-list-entries + (mapcar (pcase-lambda (`(,id . ,path)) + (let ((default-directory path)) + (list path + (vconcat + (mapcar (pcase-lambda (`(,title ,width ,fn ,props)) + (or (funcall fn `((:id ,id) + (:title ,title) + (:width ,width) + ,@props)) + "")) + magit-repolist-columns))))) + (magit-list-repos-uniquify + (--map (cons (file-name-nondirectory (directory-file-name it)) + it) + (magit-list-repos))))) + (message "Listing repositories...") + (tabulated-list-init-header) + (tabulated-list-print t) + (message "Listing repositories...done")) + +(defun magit-repolist--imenu-prev-index-position () + (and (not (bobp)) + (forward-line -1))) + +;;;; Columns + +(defun magit-repolist-make-sorter (sort-predicate convert-cell column-idx) + "Return a function suitable as a sorter for tabulated lists. +See `tabulated-list--get-sorter'. Given a more reasonable API +this would not be necessary and one could just use SORT-PREDICATE +directly. CONVERT-CELL can be used to turn the cell value, which +is always a string back into e.g. a number. COLUMN-IDX has to be +the index of the column that uses the returned sorter function." + (lambda (a b) + (funcall sort-predicate + (funcall convert-cell (aref (cadr a) column-idx)) + (funcall convert-cell (aref (cadr b) column-idx))))) + +(defun magit-repolist-column-ident (spec) + "Insert the identification of the repository. +Usually this is just its basename." + (cadr (assq :id spec))) + +(defun magit-repolist-column-path (_) + "Insert the absolute path of the repository." + (abbreviate-file-name default-directory)) + +(defvar magit-repolist-column-version-regexp "\ +\\(?1:-\\(?2:[0-9]*\\)\ +\\(?3:-g[a-z0-9]*\\)\\)?\ +\\(?:-\\(?4:dirty\\)\\)\ +?\\'") + +(defvar magit-repolist-column-version-resume-regexp + "\\`Resume development\\'") + +(defun magit-repolist-column-version (_) + "Insert a description of the repository's `HEAD' revision." + (and-let* ((v (or (magit-git-string "describe" "--tags" "--dirty") + ;; If there are no tags, use the date in MELPA format. + (magit-rev-format "%cd-g%h" nil + "--date=format:%Y%m%d.%H%M")))) + (save-match-data + (when (string-match magit-repolist-column-version-regexp v) + (magit--put-face (match-beginning 0) (match-end 0) 'shadow v) + (when (match-end 2) + (magit--put-face (match-beginning 2) (match-end 2) 'bold v)) + (when (match-end 4) + (magit--put-face (match-beginning 4) (match-end 4) 'error v)) + (when (and (equal (match-string 2 v) "1") + (string-match-p magit-repolist-column-version-resume-regexp + (magit-rev-format "%s"))) + (setq v (replace-match (propertize "+" 'face 'shadow) t t v 1)))) + (if (and v (string-match "\\`[0-9]" v)) + (concat " " v) + (when (and v (string-match "\\`[^0-9]+" v)) + (magit--put-face 0 (match-end 0) 'shadow v)) + v)))) + +(defun magit-repolist-version< (a b) + (save-match-data + (let ((re "[0-9]+\\(\\.[0-9]*\\)*")) + (setq a (and (string-match re a) (match-string 0 a))) + (setq b (and (string-match re b) (match-string 0 b))) + (cond ((and a b) (version< a b)) + (b nil) + (t t))))) + +(defun magit-repolist-column-branch (_) + "Insert the current branch." + (let ((branch (magit-get-current-branch))) + (if (member branch magit-main-branch-names) + (magit--propertize-face branch 'shadow) + branch))) + +(defun magit-repolist-column-upstream (_) + "Insert the upstream branch of the current branch." + (magit-get-upstream-branch)) + +(defun magit-repolist-column-flag (_) + "Insert a flag as specified by `magit-repolist-column-flag-alist'. + +By default this indicates whether there are uncommitted changes. +- N if there is at least one untracked file. +- U if there is at least one unstaged file. +- S if there is at least one staged file. +Only one letter is shown, the first that applies." + (seq-some (pcase-lambda (`(,fun . ,flag)) + (and (funcall fun) flag)) + magit-repolist-column-flag-alist)) + +(defun magit-repolist-column-flags (_) + "Insert all flags as specified by `magit-repolist-column-flag-alist'. +This is an alternative to function `magit-repolist-column-flag', +which only lists the first one found." + (mapconcat (pcase-lambda (`(,fun . ,flag)) + (if (funcall fun) flag " ")) + magit-repolist-column-flag-alist + "")) + +(defun magit-repolist-column-unpulled-from-upstream (spec) + "Insert number of upstream commits not in the current branch." + (and-let* ((br (magit-get-upstream-branch))) + (magit-repolist-insert-count (cadr (magit-rev-diff-count "HEAD" br)) spec))) + +(defun magit-repolist-column-unpulled-from-pushremote (spec) + "Insert number of commits in the push branch but not the current branch." + (and-let* ((br (magit-get-push-branch nil t))) + (magit-repolist-insert-count (cadr (magit-rev-diff-count "HEAD" br)) spec))) + +(defun magit-repolist-column-unpushed-to-upstream (spec) + "Insert number of commits in the current branch but not its upstream." + (and-let* ((br (magit-get-upstream-branch))) + (magit-repolist-insert-count (car (magit-rev-diff-count "HEAD" br)) spec))) + +(defun magit-repolist-column-unpushed-to-pushremote (spec) + "Insert number of commits in the current branch but not its push branch." + (and-let* ((br (magit-get-push-branch nil t))) + (magit-repolist-insert-count (car (magit-rev-diff-count "HEAD" br)) spec))) + +(defun magit-repolist-column-branches (spec) + "Insert number of branches." + (magit-repolist-insert-count (length (magit-list-local-branches)) + `((:normal-count 1) ,@spec))) + +(defun magit-repolist-column-stashes (spec) + "Insert number of stashes." + (magit-repolist-insert-count (length (magit-list-stashes)) spec)) + +(defun magit-repolist-insert-count (n spec) + (magit--propertize-face + (if (and (> n 9) (= (cadr (assq :width spec)) 1)) + "+" + (number-to-string n)) + (if (> n (or (cadr (assq :normal-count spec)) 0)) 'bold 'shadow))) + +;;; Read Repository + +(defun magit-read-repository (&optional read-directory-name) + "Read a Git repository in the minibuffer, with completion. + +The completion choices are the basenames of top-levels of +repositories found in the directories specified by option +`magit-repository-directories'. In case of name conflicts +the basenames are prefixed with the name of the respective +parent directories. The returned value is the actual path +to the selected repository. + +If READ-DIRECTORY-NAME is non-nil or no repositories can be +found based on the value of `magit-repository-directories', +then read an arbitrary directory using `read-directory-name' +instead." + (if-let ((repos (and (not read-directory-name) + magit-repository-directories + (magit-repos-alist)))) + (let ((reply (magit-completing-read "Git repository" repos))) + (file-name-as-directory + (or (cdr (assoc reply repos)) + (if (file-directory-p reply) + (expand-file-name reply) + (user-error "Not a repository or a directory: %s" reply))))) + (file-name-as-directory + (read-directory-name "Git repository: " + (or (magit-toplevel) default-directory))))) + +(defun magit-list-repos () + (cl-mapcan (pcase-lambda (`(,dir . ,depth)) + (magit-list-repos-1 dir depth)) + magit-repository-directories)) + +(defun magit-list-repos-1 (directory depth) + (cond ((file-readable-p (expand-file-name ".git" directory)) + (list (file-name-as-directory directory))) + ((and (> depth 0) (magit-file-accessible-directory-p directory)) + (--mapcat (and (file-directory-p it) + (magit-list-repos-1 it (1- depth))) + (directory-files directory t + directory-files-no-dot-files-regexp t))))) + +(defun magit-list-repos-uniquify (alist) + (let (result (dict (make-hash-table :test #'equal))) + (dolist (a (delete-dups alist)) + (puthash (car a) (cons (cdr a) (gethash (car a) dict)) dict)) + (maphash + (lambda (key value) + (if (length= value 1) + (push (cons key (car value)) result) + (setq result + (append result + (magit-list-repos-uniquify + (--map (cons (concat + key "\\" + (file-name-nondirectory + (directory-file-name + (substring it 0 (- (1+ (length key))))))) + it) + value)))))) + dict) + result)) + +(defun magit-repos-alist () + (magit-list-repos-uniquify + (--map (cons (file-name-nondirectory (directory-file-name it)) it) + (magit-list-repos)))) + +;;; _ +(provide 'magit-repos) +;;; magit-repos.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-reset.el b/code/elpa/magit-20220821.1819/magit-reset.el new file mode 100644 index 0000000..e201c3d --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-reset.el @@ -0,0 +1,136 @@ +;;; magit-reset.el --- Reset fuctionality -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements reset commands. + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-reset "magit" nil t) +(transient-define-prefix magit-reset () + "Reset the `HEAD', index and/or worktree to a previous state." + :man-page "git-reset" + ["Reset" + ("m" "mixed (HEAD and index)" magit-reset-mixed) + ("s" "soft (HEAD only)" magit-reset-soft) + ("h" "hard (HEAD, index and files)" magit-reset-hard) + ("k" "keep (HEAD and index, keeping uncommitted)" magit-reset-keep) + ("i" "index (only)" magit-reset-index) + ("w" "worktree (only)" magit-reset-worktree) + "" + ("f" "a file" magit-file-checkout)]) + +;;;###autoload +(defun magit-reset-mixed (commit) + "Reset the `HEAD' and index to COMMIT, but not the working tree. +\n(git reset --mixed COMMIT)" + (interactive (list (magit-reset-read-branch-or-commit "Reset %s to"))) + (magit-reset-internal "--mixed" commit)) + +;;;###autoload +(defun magit-reset-soft (commit) + "Reset the `HEAD' to COMMIT, but not the index and working tree. +\n(git reset --soft REVISION)" + (interactive (list (magit-reset-read-branch-or-commit "Soft reset %s to"))) + (magit-reset-internal "--soft" commit)) + +;;;###autoload +(defun magit-reset-hard (commit) + "Reset the `HEAD', index, and working tree to COMMIT. +\n(git reset --hard REVISION)" + (interactive (list (magit-reset-read-branch-or-commit + (concat (magit--propertize-face "Hard" 'bold) + " reset %s to")))) + (magit-reset-internal "--hard" commit)) + +;;;###autoload +(defun magit-reset-keep (commit) + "Reset the `HEAD' and index to COMMIT, while keeping uncommitted changes. +\n(git reset --keep REVISION)" + (interactive (list (magit-reset-read-branch-or-commit "Reset %s to"))) + (magit-reset-internal "--keep" commit)) + +;;;###autoload +(defun magit-reset-index (commit) + "Reset the index to COMMIT. +Keep the `HEAD' and working tree as-is, so if COMMIT refers to the +head this effectively unstages all changes. +\n(git reset COMMIT .)" + (interactive (list (magit-read-branch-or-commit "Reset index to"))) + (magit-reset-internal nil commit ".")) + +;;;###autoload +(defun magit-reset-worktree (commit) + "Reset the worktree to COMMIT. +Keep the `HEAD' and index as-is." + (interactive (list (magit-read-branch-or-commit "Reset worktree to"))) + (magit-wip-commit-before-change nil " before reset") + (magit-with-temp-index commit nil + (magit-call-git "checkout-index" "--all" "--force")) + (magit-wip-commit-after-apply nil " after reset") + (magit-refresh)) + +;;;###autoload +(defun magit-reset-quickly (commit &optional hard) + "Reset the `HEAD' and index to COMMIT, and possibly the working tree. +With a prefix argument reset the working tree otherwise don't. +\n(git reset --mixed|--hard COMMIT)" + (interactive (list (magit-reset-read-branch-or-commit + (if current-prefix-arg + (concat (magit--propertize-face "Hard" 'bold) + " reset %s to") + "Reset %s to")) + current-prefix-arg)) + (magit-reset-internal (if hard "--hard" "--mixed") commit)) + +(defun magit-reset-read-branch-or-commit (prompt) + "Prompt for and return a ref to reset HEAD to. + +PROMPT is a format string, where either the current branch name +or \"detached head\" will be substituted for %s." + (magit-read-branch-or-commit + (format prompt (or (magit-get-current-branch) "detached head")))) + +(defun magit-reset-internal (arg commit &optional path) + (when (and (not (member arg '("--hard" nil))) + (equal (magit-rev-parse commit) + (magit-rev-parse "HEAD~"))) + (with-temp-buffer + (magit-git-insert "show" "-s" "--format=%B" "HEAD") + (when git-commit-major-mode + (funcall git-commit-major-mode)) + (git-commit-setup-font-lock) + (git-commit-save-message))) + (let ((cmd (if (and (equal commit "HEAD") (not arg)) "unstage" "reset"))) + (magit-wip-commit-before-change nil (concat " before " cmd)) + (magit-run-git "reset" arg commit "--" path) + (when (equal cmd "unstage") + (magit-wip-commit-after-apply nil " after unstage")))) + +;;; _ +(provide 'magit-reset) +;;; magit-reset.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-sequence.el b/code/elpa/magit-20220821.1819/magit-sequence.el new file mode 100644 index 0000000..87cb072 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-sequence.el @@ -0,0 +1,1087 @@ +;;; magit-sequence.el --- History manipulation in Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; Support for Git commands that replay commits and help the user make +;; changes along the way. Supports `cherry-pick', `revert', `rebase', +;; `rebase--interactive' and `am'. + +;;; Code: + +(require 'magit) + +;; For `magit-rebase--todo'. +(declare-function git-rebase-current-line "git-rebase" ()) +(eval-when-compile + (cl-pushnew 'action-type eieio--known-slot-names) + (cl-pushnew 'action eieio--known-slot-names) + (cl-pushnew 'action-options eieio--known-slot-names) + (cl-pushnew 'target eieio--known-slot-names)) + +;;; Options +;;;; Faces + +(defface magit-sequence-pick + '((t :inherit default)) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-stop + '((((class color) (background light)) :foreground "DarkOliveGreen4") + (((class color) (background dark)) :foreground "DarkSeaGreen2")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-part + '((((class color) (background light)) :foreground "Goldenrod4") + (((class color) (background dark)) :foreground "LightGoldenrod2")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-head + '((((class color) (background light)) :foreground "SkyBlue4") + (((class color) (background dark)) :foreground "LightSkyBlue1")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-drop + '((((class color) (background light)) :foreground "IndianRed") + (((class color) (background dark)) :foreground "IndianRed")) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-done + '((t :inherit magit-hash)) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-onto + '((t :inherit magit-sequence-done)) + "Face used in sequence sections." + :group 'magit-faces) + +(defface magit-sequence-exec + '((t :inherit magit-hash)) + "Face used in sequence sections." + :group 'magit-faces) + +;;; Common + +;;;###autoload +(defun magit-sequencer-continue () + "Resume the current cherry-pick or revert sequence." + (interactive) + (if (magit-sequencer-in-progress-p) + (if (magit-anything-unmerged-p) + (user-error "Cannot continue due to unresolved conflicts") + (magit-run-git-sequencer + (if (magit-revert-in-progress-p) "revert" "cherry-pick") "--continue")) + (user-error "No cherry-pick or revert in progress"))) + +;;;###autoload +(defun magit-sequencer-skip () + "Skip the stopped at commit during a cherry-pick or revert sequence." + (interactive) + (if (magit-sequencer-in-progress-p) + (progn (magit-call-git "reset" "--hard") + (magit-sequencer-continue)) + (user-error "No cherry-pick or revert in progress"))) + +;;;###autoload +(defun magit-sequencer-abort () + "Abort the current cherry-pick or revert sequence. +This discards all changes made since the sequence started." + (interactive) + (if (magit-sequencer-in-progress-p) + (magit-run-git-sequencer + (if (magit-revert-in-progress-p) "revert" "cherry-pick") "--abort") + (user-error "No cherry-pick or revert in progress"))) + +(defun magit-sequencer-in-progress-p () + (or (magit-cherry-pick-in-progress-p) + (magit-revert-in-progress-p))) + +;;; Cherry-Pick + +(defvar magit-perl-executable "perl" + "The Perl executable.") + +;;;###autoload (autoload 'magit-cherry-pick "magit-sequence" nil t) +(transient-define-prefix magit-cherry-pick () + "Apply or transplant commits." + :man-page "git-cherry-pick" + :value '("--ff") + :incompatible '(("--ff" "-x")) + ["Arguments" + :if-not magit-sequencer-in-progress-p + (magit-cherry-pick:--mainline) + ("=s" magit-merge:--strategy) + ("-F" "Attempt fast-forward" "--ff") + ("-x" "Reference cherry in commit message" "-x") + ("-e" "Edit commit messages" ("-e" "--edit")) + ("-s" "Add Signed-off-by lines" ("-s" "--signoff")) + (5 magit:--gpg-sign)] + [:if-not magit-sequencer-in-progress-p + ["Apply here" + ("A" "Pick" magit-cherry-copy) + ("a" "Apply" magit-cherry-apply) + ("h" "Harvest" magit-cherry-harvest) + ("m" "Squash" magit-merge-squash)] + ["Apply elsewhere" + ("d" "Donate" magit-cherry-donate) + ("n" "Spinout" magit-cherry-spinout) + ("s" "Spinoff" magit-cherry-spinoff)]] + ["Actions" + :if magit-sequencer-in-progress-p + ("A" "Continue" magit-sequencer-continue) + ("s" "Skip" magit-sequencer-skip) + ("a" "Abort" magit-sequencer-abort)]) + +(transient-define-argument magit-cherry-pick:--mainline () + :description "Replay merge relative to parent" + :class 'transient-option + :shortarg "-m" + :argument "--mainline=" + :reader #'transient-read-number-N+) + +(defun magit-cherry-pick-read-args (prompt) + (list (or (nreverse (magit-region-values 'commit)) + (magit-read-other-branch-or-commit prompt)) + (transient-args 'magit-cherry-pick))) + +(defun magit--cherry-move-read-args (verb away fn &optional allow-detached) + (declare (indent defun)) + (let ((commits (or (nreverse (magit-region-values 'commit)) + (list (funcall (if away + #'magit-read-branch-or-commit + #'magit-read-other-branch-or-commit) + (format "%s cherry" (capitalize verb)))))) + (current (or (magit-get-current-branch) + (and allow-detached (magit-rev-parse "HEAD"))))) + (unless current + (user-error "Cannot %s cherries while HEAD is detached" verb)) + (let ((reachable (magit-rev-ancestor-p (car commits) current)) + (msg "Cannot %s cherries that %s reachable from HEAD")) + (pcase (list away reachable) + ('(nil t) (user-error msg verb "are")) + ('(t nil) (user-error msg verb "are not")))) + `(,commits + ,@(funcall fn commits) + ,(transient-args 'magit-cherry-pick)))) + +(defun magit--cherry-spinoff-read-args (verb) + (magit--cherry-move-read-args verb t + (lambda (commits) + (magit-branch-read-args + (format "Create branch from %s cherries" (length commits)) + (magit-get-upstream-branch))))) + +;;;###autoload +(defun magit-cherry-copy (commits &optional args) + "Copy COMMITS from another branch onto the current branch. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then pick all of them, +without prompting." + (interactive (magit-cherry-pick-read-args "Cherry-pick")) + (magit--cherry-pick commits args)) + +;;;###autoload +(defun magit-cherry-apply (commits &optional args) + "Apply the changes in COMMITS but do not commit them. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then apply all of them, +without prompting." + (interactive (magit-cherry-pick-read-args "Apply changes from commit")) + (magit--cherry-pick commits (cons "--no-commit" (remove "--ff" args)))) + +;;;###autoload +(defun magit-cherry-harvest (commits branch &optional args) + "Move COMMITS from another BRANCH onto the current branch. +Remove the COMMITS from BRANCH and stay on the current branch. +If a conflict occurs, then you have to fix that and finish the +process manually." + (interactive + (magit--cherry-move-read-args "harvest" nil + (lambda (commits) + (list (let ((branches (magit-list-containing-branches (car commits)))) + (pcase (length branches) + (0 nil) + (1 (car branches)) + (_ (magit-completing-read + (let ((len (length commits))) + (if (= len 1) + "Remove 1 cherry from branch" + (format "Remove %s cherries from branch" len))) + branches nil t)))))))) + (magit--cherry-move commits branch (magit-get-current-branch) args nil t)) + +;;;###autoload +(defun magit-cherry-donate (commits branch &optional args) + "Move COMMITS from the current branch onto another existing BRANCH. +Remove COMMITS from the current branch and stay on that branch. +If a conflict occurs, then you have to fix that and finish the +process manually. `HEAD' is allowed to be detached initially." + (interactive + (magit--cherry-move-read-args "donate" t + (lambda (commits) + (list (magit-read-other-branch + (let ((len (length commits))) + (if (= len 1) + "Move 1 cherry to branch" + (format "Move %s cherries to branch" len)))))) + 'allow-detached)) + (magit--cherry-move commits + (or (magit-get-current-branch) + (magit-rev-parse "HEAD")) + branch args)) + +;;;###autoload +(defun magit-cherry-spinout (commits branch start-point &optional args) + "Move COMMITS from the current branch onto a new BRANCH. +Remove COMMITS from the current branch and stay on that branch. +If a conflict occurs, then you have to fix that and finish the +process manually." + (interactive (magit--cherry-spinoff-read-args "spinout")) + (magit--cherry-move commits (magit-get-current-branch) branch args + start-point)) + +;;;###autoload +(defun magit-cherry-spinoff (commits branch start-point &optional args) + "Move COMMITS from the current branch onto a new BRANCH. +Remove COMMITS from the current branch and checkout BRANCH. +If a conflict occurs, then you have to fix that and finish +the process manually." + (interactive (magit--cherry-spinoff-read-args "spinoff")) + (magit--cherry-move commits (magit-get-current-branch) branch args + start-point t)) + +(defun magit--cherry-move (commits src dst args + &optional start-point checkout-dst) + (let ((current (magit-get-current-branch))) + (unless (magit-branch-p dst) + (let ((magit-process-raise-error t)) + (magit-call-git "branch" dst start-point)) + (--when-let (magit-get-indirect-upstream-branch start-point) + (magit-call-git "branch" "--set-upstream-to" it dst))) + (unless (equal dst current) + (let ((magit-process-raise-error t)) + (magit-call-git "checkout" dst))) + (if (not src) ; harvest only + (magit--cherry-pick commits args) + (let ((tip (car (last commits))) + (keep (concat (car commits) "^"))) + (magit--cherry-pick commits args) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (> (process-exit-status process) 0) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (cond + ((magit-rev-equal tip src) + (magit-call-git "update-ref" + "-m" (format "reset: moving to %s" keep) + (magit-ref-fullname src) + keep tip) + (if (not checkout-dst) + (magit-run-git "checkout" src) + (magit-refresh))) + (t + (magit-git "checkout" src) + (with-environment-variables + (("GIT_SEQUENCE_EDITOR" + (format "%s -i -ne '/^pick (%s)/ or print'" + magit-perl-executable + (mapconcat #'magit-rev-abbrev commits "|")))) + (magit-run-git-sequencer "rebase" "-i" keep)) + (when checkout-dst + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (> (process-exit-status process) 0) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (magit-run-git "checkout" dst)))))))))))))))) + +(defun magit--cherry-pick (commits args &optional revert) + (let ((command (if revert "revert" "cherry-pick"))) + (when (stringp commits) + (setq commits (if (string-search ".." commits) + (split-string commits "\\.\\.") + (list commits)))) + (magit-run-git-sequencer + (if revert "revert" "cherry-pick") + (pcase-let ((`(,merge ,non-merge) + (-separate #'magit-merge-commit-p commits))) + (cond + ((not merge) + (--remove (string-prefix-p "--mainline=" it) args)) + (non-merge + (user-error "Cannot %s merge and non-merge commits at once" + command)) + ((--first (string-prefix-p "--mainline=" it) args) + args) + (t + (cons (format "--mainline=%s" + (read-number "Replay merges relative to parent: ")) + args)))) + commits))) + +(defun magit-cherry-pick-in-progress-p () + ;; .git/sequencer/todo does not exist when there is only one commit left. + (or (file-exists-p (magit-git-dir "CHERRY_PICK_HEAD")) + ;; And CHERRY_PICK_HEAD does not exist when a conflict happens + ;; while picking a series of commits with --no-commit. + (when-let ((line (magit-file-line (magit-git-dir "sequencer/todo")))) + (string-prefix-p "pick" line)))) + +;;; Revert + +;;;###autoload (autoload 'magit-revert "magit-sequence" nil t) +(transient-define-prefix magit-revert () + "Revert existing commits, with or without creating new commits." + :man-page "git-revert" + :value '("--edit") + ["Arguments" + :if-not magit-sequencer-in-progress-p + (magit-cherry-pick:--mainline) + ("-e" "Edit commit message" ("-e" "--edit")) + ("-E" "Don't edit commit message" "--no-edit") + ("=s" magit-merge:--strategy) + ("-s" "Add Signed-off-by lines" ("-s" "--signoff")) + (5 magit:--gpg-sign)] + ["Actions" + :if-not magit-sequencer-in-progress-p + ("V" "Revert commit(s)" magit-revert-and-commit) + ("v" "Revert changes" magit-revert-no-commit)] + ["Actions" + :if magit-sequencer-in-progress-p + ("V" "Continue" magit-sequencer-continue) + ("s" "Skip" magit-sequencer-skip) + ("a" "Abort" magit-sequencer-abort)]) + +(defun magit-revert-read-args (prompt) + (list (or (magit-region-values 'commit) + (magit-read-branch-or-commit prompt)) + (transient-args 'magit-revert))) + +;;;###autoload +(defun magit-revert-and-commit (commit &optional args) + "Revert COMMIT by creating a new commit. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting." + (interactive (magit-revert-read-args "Revert commit")) + (magit--cherry-pick commit args t)) + +;;;###autoload +(defun magit-revert-no-commit (commit &optional args) + "Revert COMMIT by applying it in reverse to the worktree. +Prompt for a commit, defaulting to the commit at point. If +the region selects multiple commits, then revert all of them, +without prompting." + (interactive (magit-revert-read-args "Revert changes")) + (magit--cherry-pick commit (cons "--no-commit" args) t)) + +(defun magit-revert-in-progress-p () + ;; .git/sequencer/todo does not exist when there is only one commit left. + (or (file-exists-p (magit-git-dir "REVERT_HEAD")) + ;; And REVERT_HEAD does not exist when a conflict happens while + ;; reverting a series of commits with --no-commit. + (when-let ((line (magit-file-line (magit-git-dir "sequencer/todo")))) + (string-prefix-p "revert" line)))) + +;;; Patch + +;;;###autoload (autoload 'magit-am "magit-sequence" nil t) +(transient-define-prefix magit-am () + "Apply patches received by email." + :man-page "git-am" + :value '("--3way") + ["Arguments" + :if-not magit-am-in-progress-p + ("-3" "Fall back on 3way merge" ("-3" "--3way")) + (magit-apply:-p) + ("-c" "Remove text before scissors line" ("-c" "--scissors")) + ("-k" "Inhibit removal of email cruft" ("-k" "--keep")) + ("-b" "Limit removal of email cruft" "--keep-non-patch") + ("-d" "Use author date as committer date" "--committer-date-is-author-date") + ("-t" "Use current time as author date" "--ignore-date") + ("-s" "Add Signed-off-by lines" ("-s" "--signoff")) + (5 magit:--gpg-sign)] + ["Apply" + :if-not magit-am-in-progress-p + ("m" "maildir" magit-am-apply-maildir) + ("w" "patches" magit-am-apply-patches) + ("a" "plain patch" magit-patch-apply)] + ["Actions" + :if magit-am-in-progress-p + ("w" "Continue" magit-am-continue) + ("s" "Skip" magit-am-skip) + ("a" "Abort" magit-am-abort)]) + +(defun magit-am-arguments () + (transient-args 'magit-am)) + +(transient-define-argument magit-apply:-p () + :description "Remove leading slashes from paths" + :class 'transient-option + :argument "-p" + :allow-empty t + :reader #'transient-read-number-N+) + +;;;###autoload +(defun magit-am-apply-patches (&optional files args) + "Apply the patches FILES." + (interactive (list (or (magit-region-values 'file) + (list (let ((default (magit-file-at-point))) + (read-file-name + (if default + (format "Apply patch (%s): " default) + "Apply patch: ") + nil default)))) + (magit-am-arguments))) + (magit-run-git-sequencer "am" args "--" + (--map (magit-convert-filename-for-git + (expand-file-name it)) + files))) + +;;;###autoload +(defun magit-am-apply-maildir (&optional maildir args) + "Apply the patches from MAILDIR." + (interactive (list (read-file-name "Apply mbox or Maildir: ") + (magit-am-arguments))) + (magit-run-git-sequencer "am" args (magit-convert-filename-for-git + (expand-file-name maildir)))) + +;;;###autoload +(defun magit-am-continue () + "Resume the current patch applying sequence." + (interactive) + (if (magit-am-in-progress-p) + (if (magit-anything-unstaged-p t) + (error "Cannot continue due to unstaged changes") + (magit-run-git-sequencer "am" "--continue")) + (user-error "Not applying any patches"))) + +;;;###autoload +(defun magit-am-skip () + "Skip the stopped at patch during a patch applying sequence." + (interactive) + (if (magit-am-in-progress-p) + (magit-run-git-sequencer "am" "--skip") + (user-error "Not applying any patches"))) + +;;;###autoload +(defun magit-am-abort () + "Abort the current patch applying sequence. +This discards all changes made since the sequence started." + (interactive) + (if (magit-am-in-progress-p) + (magit-run-git "am" "--abort") + (user-error "Not applying any patches"))) + +(defun magit-am-in-progress-p () + (file-exists-p (magit-git-dir "rebase-apply/applying"))) + +;;; Rebase + +;;;###autoload (autoload 'magit-rebase "magit-sequence" nil t) +(transient-define-prefix magit-rebase () + "Transplant commits and/or modify existing commits." + :man-page "git-rebase" + :value '("--autostash") + ["Arguments" + :if-not magit-rebase-in-progress-p + ("-k" "Keep empty commits" "--keep-empty") + ("-p" "Preserve merges" ("-p" "--preserve-merges") + :if (lambda () (magit-git-version< "2.33.0"))) + ("-r" "Rebase merges" ("-r" "--rebase-merges=") + magit-rebase-merges-select-mode + :if (lambda () (magit-git-version>= "2.18.0"))) + (7 magit-merge:--strategy) + (7 magit-merge:--strategy-option) + (7 "=X" magit-diff:--diff-algorithm :argument "-Xdiff-algorithm=") + (7 "-f" "Force rebase" ("-f" "--force-rebase")) + ("-d" "Use author date as committer date" "--committer-date-is-author-date") + ("-t" "Use current time as author date" "--ignore-date") + ("-a" "Autosquash" "--autosquash") + ("-A" "Autostash" "--autostash") + ("-i" "Interactive" ("-i" "--interactive")) + ("-h" "Disable hooks" "--no-verify") + (7 magit-rebase:--exec) + (5 magit:--gpg-sign)] + [:if-not magit-rebase-in-progress-p + :description (lambda () + (format (propertize "Rebase %s onto" 'face 'transient-heading) + (propertize (or (magit-get-current-branch) "HEAD") + 'face 'magit-branch-local))) + ("p" magit-rebase-onto-pushremote) + ("u" magit-rebase-onto-upstream) + ("e" "elsewhere" magit-rebase-branch)] + ["Rebase" + :if-not magit-rebase-in-progress-p + [("i" "interactively" magit-rebase-interactive) + ("s" "a subset" magit-rebase-subset)] + [("m" "to modify a commit" magit-rebase-edit-commit) + ("w" "to reword a commit" magit-rebase-reword-commit) + ("k" "to remove a commit" magit-rebase-remove-commit) + ("f" "to autosquash" magit-rebase-autosquash) + (6 "t" "to change dates" magit-reshelve-since)]] + ["Actions" + :if magit-rebase-in-progress-p + ("r" "Continue" magit-rebase-continue) + ("s" "Skip" magit-rebase-skip) + ("e" "Edit" magit-rebase-edit) + ("a" "Abort" magit-rebase-abort)]) + +(transient-define-argument magit-rebase:--exec () + :description "Run command after commits" + :class 'transient-option + :shortarg "-x" + :argument "--exec=" + :reader #'read-shell-command) + +(defun magit-rebase-merges-select-mode (&rest _ignore) + (magit-read-char-case nil t + (?n "[n]o-rebase-cousins" "no-rebase-cousins") + (?r "[r]ebase-cousins" "rebase-cousins"))) + +(defun magit-rebase-arguments () + (transient-args 'magit-rebase)) + +(defun magit-git-rebase (target args) + (magit-run-git-sequencer "rebase" args target)) + +;;;###autoload (autoload 'magit-rebase-onto-pushremote "magit-sequence" nil t) +(transient-define-suffix magit-rebase-onto-pushremote (args) + "Rebase the current branch onto its push-remote branch. + +With a prefix argument or when the push-remote is either not +configured or unusable, then let the user first configure the +push-remote." + :if #'magit-get-current-branch + :description #'magit-pull--pushbranch-description + (interactive (list (magit-rebase-arguments))) + (pcase-let ((`(,branch ,remote) + (magit--select-push-remote "rebase onto that"))) + (magit-git-rebase (concat remote "/" branch) args))) + +;;;###autoload (autoload 'magit-rebase-onto-upstream "magit-sequence" nil t) +(transient-define-suffix magit-rebase-onto-upstream (args) + "Rebase the current branch onto its upstream branch. + +With a prefix argument or when the upstream is either not +configured or unusable, then let the user first configure +the upstream." + :if #'magit-get-current-branch + :description #'magit-rebase--upstream-description + (interactive (list (magit-rebase-arguments))) + (let* ((branch (or (magit-get-current-branch) + (user-error "No branch is checked out"))) + (upstream (magit-get-upstream-branch branch))) + (when (or current-prefix-arg (not upstream)) + (setq upstream + (magit-read-upstream-branch + branch (format "Set upstream of %s and rebase onto that" branch))) + (magit-set-upstream-branch branch upstream)) + (magit-git-rebase upstream args))) + +(defun magit-rebase--upstream-description () + (and-let* ((branch (magit-get-current-branch))) + (or (magit-get-upstream-branch branch) + (let ((remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge")) + (u (magit--propertize-face "@{upstream}" 'bold))) + (cond + ((magit--unnamed-upstream-p remote merge) + (concat u ", replacing unnamed")) + ((magit--valid-upstream-p remote merge) + (concat u ", replacing non-existent")) + ((or remote merge) + (concat u ", replacing invalid")) + (t + (concat u ", setting that"))))))) + +;;;###autoload +(defun magit-rebase-branch (target args) + "Rebase the current branch onto a branch read in the minibuffer. +All commits that are reachable from `HEAD' but not from the +selected branch TARGET are being rebased." + (interactive (list (magit-read-other-branch-or-commit "Rebase onto") + (magit-rebase-arguments))) + (message "Rebasing...") + (magit-git-rebase target args) + (message "Rebasing...done")) + +;;;###autoload +(defun magit-rebase-subset (newbase start args) + "Rebase a subset of the current branch's history onto a new base. +Rebase commits from START to `HEAD' onto NEWBASE. +START has to be selected from a list of recent commits." + (interactive (list (magit-read-other-branch-or-commit + "Rebase subset onto" nil + (magit-get-upstream-branch)) + nil + (magit-rebase-arguments))) + (if start + (progn (message "Rebasing...") + (magit-run-git-sequencer "rebase" "--onto" newbase start args) + (message "Rebasing...done")) + (magit-log-select + `(lambda (commit) + (magit-rebase-subset ,newbase (concat commit "^") (list ,@args))) + (concat "Type %p on a commit to rebase it " + "and commits above it onto " newbase ",")))) + +(defvar magit-rebase-interactive-include-selected t) + +(defun magit-rebase-interactive-1 + (commit args message &optional editor delay-edit-confirm noassert confirm) + (declare (indent 2)) + (when commit + (if (eq commit :merge-base) + (setq commit (--if-let (magit-get-upstream-branch) + (magit-git-string "merge-base" it "HEAD") + nil)) + (unless (magit-rev-ancestor-p commit "HEAD") + (user-error "%s isn't an ancestor of HEAD" commit)) + (if (magit-commit-parents commit) + (when (or (not (eq this-command 'magit-rebase-interactive)) + magit-rebase-interactive-include-selected) + (setq commit (concat commit "^"))) + (setq args (cons "--root" args))))) + (when (and commit (not noassert)) + (setq commit (magit-rebase-interactive-assert + commit delay-edit-confirm + (--some (string-prefix-p "--rebase-merges" it) args)))) + (if (and commit (not confirm)) + (let ((process-environment process-environment)) + (when editor + (push (concat "GIT_SEQUENCE_EDITOR=" + (if (functionp editor) + (funcall editor commit) + editor)) + process-environment)) + (magit-run-git-sequencer "rebase" "-i" args + (unless (member "--root" args) commit))) + (magit-log-select + `(lambda (commit) + ;; In some cases (currently just magit-rebase-remove-commit), "-c + ;; commentChar=#" is added to the global arguments for git. Ensure + ;; that the same happens when we chose the commit via + ;; magit-log-select, below. + (let ((magit-git-global-arguments (list ,@magit-git-global-arguments))) + (magit-rebase-interactive-1 commit (list ,@args) + ,message ,editor ,delay-edit-confirm ,noassert))) + message))) + +(defvar magit--rebase-published-symbol nil) +(defvar magit--rebase-public-edit-confirmed nil) + +(defun magit-rebase-interactive-assert + (since &optional delay-edit-confirm rebase-merges) + (let* ((commit (magit-rebase--target-commit since)) + (branches (magit-list-publishing-branches commit))) + (setq magit--rebase-public-edit-confirmed + (delete (magit-toplevel) magit--rebase-public-edit-confirmed)) + (when (and branches + (or (not delay-edit-confirm) + ;; The user might have stopped at a published commit + ;; merely to add new commits *after* it. Try not to + ;; ask users whether they really want to edit public + ;; commits, when they don't actually intend to do so. + (not (--all-p (magit-rev-equal it commit) branches)))) + (let ((m1 "Some of these commits have already been published to ") + (m2 ".\nDo you really want to modify them")) + (magit-confirm (or magit--rebase-published-symbol 'rebase-published) + (concat m1 "%s" m2) + (concat m1 "%i public branches" m2) + nil branches)) + (push (magit-toplevel) magit--rebase-public-edit-confirmed))) + (if (and (magit-git-lines "rev-list" "--merges" (concat since "..HEAD")) + (not rebase-merges)) + (magit-read-char-case "Proceed despite merge in rebase range? " nil + (?c "[c]ontinue" since) + (?s "[s]elect other" nil) + (?a "[a]bort" (user-error "Quit"))) + since)) + +(defun magit-rebase--target-commit (since) + (if (string-suffix-p "^" since) + ;; If SINCE is "REV^", then the user selected + ;; "REV", which is the first commit that will + ;; be replaced. (from^..to] <=> [from..to] + (substring since 0 -1) + ;; The "--root" argument is being used. + since)) + +;;;###autoload +(defun magit-rebase-interactive (commit args) + "Start an interactive rebase sequence." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (magit-rebase-interactive-1 commit args + "Type %p on a commit to rebase it and all commits above it," + nil t)) + +;;;###autoload +(defun magit-rebase-autosquash (args) + "Combine squash and fixup commits with their intended targets." + (interactive (list (magit-rebase-arguments))) + (magit-rebase-interactive-1 :merge-base + (nconc (list "--autosquash" "--keep-empty") args) + "Type %p on a commit to squash into it and then rebase as necessary," + "true" nil t)) + +;;;###autoload +(defun magit-rebase-edit-commit (commit args) + "Edit a single older commit using rebase." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (magit-rebase-interactive-1 commit args + "Type %p on a commit to edit it," + (apply-partially #'magit-rebase--perl-editor 'edit) + t)) + +;;;###autoload +(defun magit-rebase-reword-commit (commit args) + "Reword a single older commit using rebase." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + (magit-rebase-interactive-1 commit args + "Type %p on a commit to reword its message," + (apply-partially #'magit-rebase--perl-editor 'reword))) + +;;;###autoload +(defun magit-rebase-remove-commit (commit args) + "Remove a single older commit using rebase." + (interactive (list (magit-commit-at-point) + (magit-rebase-arguments))) + ;; magit-rebase--perl-editor assumes that the comment character is "#". + (let ((magit-git-global-arguments + (nconc (list "-c" "core.commentChar=#") + magit-git-global-arguments))) + (magit-rebase-interactive-1 commit args + "Type %p on a commit to remove it," + (apply-partially #'magit-rebase--perl-editor 'remove) + nil nil t))) + +(defun magit-rebase--perl-editor (action since) + (let ((commit (magit-rev-abbrev (magit-rebase--target-commit since)))) + (format "%s -i -p -e '++$x if not $x and s/^pick %s/%s %s/'" + magit-perl-executable + commit + (cl-case action + (edit "edit") + (remove "noop\n# pick") + (reword "reword") + (t (error "unknown action: %s" action))) + commit))) + +;;;###autoload +(defun magit-rebase-continue (&optional noedit) + "Restart the current rebasing operation. +In some cases this pops up a commit message buffer for you do +edit. With a prefix argument the old message is reused as-is." + (interactive "P") + (if (magit-rebase-in-progress-p) + (if (magit-anything-unstaged-p t) + (user-error "Cannot continue rebase with unstaged changes") + (when (and (magit-anything-staged-p) + (file-exists-p (magit-git-dir "rebase-merge")) + (not (member (magit-toplevel) + magit--rebase-public-edit-confirmed))) + (magit-commit-amend-assert + (magit-file-line (magit-git-dir "rebase-merge/orig-head")))) + (if noedit + (with-environment-variables (("GIT_EDITOR" "true")) + (magit-run-git-async (magit--rebase-resume-command) "--continue") + (set-process-sentinel magit-this-process + #'magit-sequencer-process-sentinel) + magit-this-process) + (magit-run-git-sequencer (magit--rebase-resume-command) "--continue"))) + (user-error "No rebase in progress"))) + +;;;###autoload +(defun magit-rebase-skip () + "Skip the current commit and restart the current rebase operation." + (interactive) + (unless (magit-rebase-in-progress-p) + (user-error "No rebase in progress")) + (magit-run-git-sequencer (magit--rebase-resume-command) "--skip")) + +;;;###autoload +(defun magit-rebase-edit () + "Edit the todo list of the current rebase operation." + (interactive) + (unless (magit-rebase-in-progress-p) + (user-error "No rebase in progress")) + (magit-run-git-sequencer "rebase" "--edit-todo")) + +;;;###autoload +(defun magit-rebase-abort () + "Abort the current rebase operation, restoring the original branch." + (interactive) + (unless (magit-rebase-in-progress-p) + (user-error "No rebase in progress")) + (magit-confirm 'abort-rebase "Abort this rebase") + (magit-run-git (magit--rebase-resume-command) "--abort")) + +(defun magit-rebase-in-progress-p () + "Return t if a rebase is in progress." + (or (file-exists-p (magit-git-dir "rebase-merge")) + (file-exists-p (magit-git-dir "rebase-apply/onto")))) + +(defun magit--rebase-resume-command () + (if (file-exists-p (magit-git-dir "rebase-recursive")) "rbr" "rebase")) + +(defun magit-rebase--get-state-lines (file) + (and (magit-rebase-in-progress-p) + (magit-file-line + (magit-git-dir + (concat (if (file-directory-p (magit-git-dir "rebase-merge")) + "rebase-merge/" + "rebase-apply/") + file))))) + +;;; Sections + +(defun magit-insert-sequencer-sequence () + "Insert section for the on-going cherry-pick or revert sequence. +If no such sequence is in progress, do nothing." + (let ((picking (magit-cherry-pick-in-progress-p))) + (when (or picking (magit-revert-in-progress-p)) + (magit-insert-section (sequence) + (magit-insert-heading (if picking "Cherry Picking" "Reverting")) + (when-let ((lines + (cdr (magit-file-lines (magit-git-dir "sequencer/todo"))))) + (dolist (line (nreverse lines)) + (when (string-match + "^\\(pick\\|revert\\) \\([^ ]+\\) \\(.*\\)$" line) + (magit-bind-match-strings (cmd hash msg) line + (magit-insert-section (commit hash) + (insert (propertize cmd 'font-lock-face 'magit-sequence-pick) + " " (propertize hash 'font-lock-face 'magit-hash) + " " msg "\n")))))) + (magit-sequence-insert-sequence + (magit-file-line (magit-git-dir (if picking + "CHERRY_PICK_HEAD" + "REVERT_HEAD"))) + (magit-file-line (magit-git-dir "sequencer/head"))) + (insert "\n"))))) + +(defun magit-insert-am-sequence () + "Insert section for the on-going patch applying sequence. +If no such sequence is in progress, do nothing." + (when (magit-am-in-progress-p) + (magit-insert-section (rebase-sequence) + (magit-insert-heading "Applying patches") + (let ((patches (nreverse (magit-rebase-patches))) + patch commit) + (while patches + (setq patch (pop patches)) + (setq commit (magit-commit-p + (cadr (split-string (magit-file-line patch))))) + (cond ((and commit patches) + (magit-sequence-insert-commit + "pick" commit 'magit-sequence-pick)) + (patches + (magit-sequence-insert-am-patch + "pick" patch 'magit-sequence-pick)) + (commit + (magit-sequence-insert-sequence commit "ORIG_HEAD")) + (t + (magit-sequence-insert-am-patch + "stop" patch 'magit-sequence-stop) + (magit-sequence-insert-sequence nil "ORIG_HEAD"))))) + (insert ?\n)))) + +(defun magit-sequence-insert-am-patch (type patch face) + (magit-insert-section (file patch) + (let ((title + (with-temp-buffer + (insert-file-contents patch nil nil 4096) + (unless (re-search-forward "^Subject: " nil t) + (goto-char (point-min))) + (buffer-substring (point) (line-end-position))))) + (insert (propertize type 'font-lock-face face) + ?\s (propertize (file-name-nondirectory patch) + 'font-lock-face 'magit-hash) + ?\s title + ?\n)))) + +(defun magit-insert-rebase-sequence () + "Insert section for the on-going rebase sequence. +If no such sequence is in progress, do nothing." + (when (magit-rebase-in-progress-p) + (let* ((interactive (file-directory-p (magit-git-dir "rebase-merge"))) + (dir (if interactive "rebase-merge/" "rebase-apply/")) + (name (thread-first (concat dir "head-name") + magit-git-dir + magit-file-line)) + (onto (thread-first (concat dir "onto") + magit-git-dir + magit-file-line)) + (onto (or (magit-rev-name onto name) + (magit-rev-name onto "refs/heads/*") onto)) + (name (or (magit-rev-name name "refs/heads/*") name))) + (magit-insert-section (rebase-sequence) + (magit-insert-heading (format "Rebasing %s onto %s" name onto)) + (if interactive + (magit-rebase-insert-merge-sequence onto) + (magit-rebase-insert-apply-sequence onto)) + (insert ?\n))))) + +(defun magit-rebase--todo () + "Return `git-rebase-action' instances for remaining rebase actions. +These are ordered in that the same way they'll be sorted in the +status buffer (i.e. the reverse of how they will be applied)." + (let ((comment-start (or (magit-get "core.commentChar") "#")) + lines) + (with-temp-buffer + (insert-file-contents (magit-git-dir "rebase-merge/git-rebase-todo")) + (while (not (eobp)) + (let ((ln (git-rebase-current-line))) + (when (oref ln action-type) + (push ln lines))) + (forward-line))) + lines)) + +(defun magit-rebase-insert-merge-sequence (onto) + (dolist (line (magit-rebase--todo)) + (with-slots (action-type action action-options target) line + (pcase action-type + ('commit + (magit-sequence-insert-commit action target 'magit-sequence-pick)) + ((or (or `exec `label) + (and `merge (guard (not action-options)))) + (insert (propertize action 'font-lock-face 'magit-sequence-onto) "\s" + (propertize target 'font-lock-face 'git-rebase-label) "\n")) + ('merge + (if-let ((hash (and (string-match "-[cC] \\([^ ]+\\)" action-options) + (match-string 1 action-options)))) + (magit-insert-section (commit hash) + (magit-insert-heading + (propertize "merge" 'font-lock-face 'magit-sequence-pick) + "\s" + (magit-format-rev-summary hash) "\n")) + (error "failed to parse merge message hash")))))) + (magit-sequence-insert-sequence + (magit-file-line (magit-git-dir "rebase-merge/stopped-sha")) + onto + (and-let* ((lines (magit-file-lines (magit-git-dir "rebase-merge/done")))) + (cadr (split-string (car (last lines))))))) + +(defun magit-rebase-insert-apply-sequence (onto) + (let ((rewritten + (--map (car (split-string it)) + (magit-file-lines (magit-git-dir "rebase-apply/rewritten")))) + (stop (magit-file-line (magit-git-dir "rebase-apply/original-commit")))) + (dolist (patch (nreverse (cdr (magit-rebase-patches)))) + (let ((hash (cadr (split-string (magit-file-line patch))))) + (unless (or (member hash rewritten) + (equal hash stop)) + (magit-sequence-insert-commit "pick" hash 'magit-sequence-pick))))) + (magit-sequence-insert-sequence + (magit-file-line (magit-git-dir "rebase-apply/original-commit")) + onto)) + +(defun magit-rebase-patches () + (directory-files (magit-git-dir "rebase-apply") t "^[0-9]\\{4\\}$")) + +(defun magit-sequence-insert-sequence (stop onto &optional orig) + (let ((head (magit-rev-parse "HEAD")) done) + (setq onto (if onto (magit-rev-parse onto) head)) + (setq done (magit-git-lines "log" "--format=%H" (concat onto "..HEAD"))) + (when (and stop (not (member (magit-rev-parse stop) done))) + (let ((id (magit-patch-id stop))) + (--if-let (--first (equal (magit-patch-id it) id) done) + (setq stop it) + (cond + ((--first (magit-rev-equal it stop) done) + ;; The commit's testament has been executed. + (magit-sequence-insert-commit "void" stop 'magit-sequence-drop)) + ;; The faith of the commit is still undecided... + ((magit-anything-unmerged-p) + ;; ...and time travel isn't for the faint of heart. + (magit-sequence-insert-commit "join" stop 'magit-sequence-part)) + ((magit-anything-modified-p t) + ;; ...and the dust hasn't settled yet... + (magit-sequence-insert-commit + (let* ((magit--refresh-cache nil) + (staged (magit-commit-tree "oO" nil "HEAD")) + (unstaged (magit-commit-worktree "oO" "--reset"))) + (cond + ;; ...but we could end up at the same tree just by committing. + ((or (magit-rev-equal staged stop) + (magit-rev-equal unstaged stop)) "goal") + ;; ...but the changes are still there, untainted. + ((or (equal (magit-patch-id staged) id) + (equal (magit-patch-id unstaged) id)) "same") + ;; ...and some changes are gone and/or others were added. + (t "work"))) + stop 'magit-sequence-part)) + ;; The commit is definitely gone... + ((--first (magit-rev-equal it stop) done) + ;; ...but all of its changes are still in effect. + (magit-sequence-insert-commit "poof" stop 'magit-sequence-drop)) + (t + ;; ...and some changes are gone and/or other changes were added. + (magit-sequence-insert-commit "gone" stop 'magit-sequence-drop))) + (setq stop nil)))) + (dolist (rev done) + (apply #'magit-sequence-insert-commit + (cond ((equal rev stop) + ;; ...but its reincarnation lives on. + ;; Or it didn't die in the first place. + (list (if (and (equal rev head) + (equal (magit-patch-id rev) + (magit-patch-id orig))) + "stop" ; We haven't done anything yet. + "like") ; There are new commits. + rev (if (equal rev head) + 'magit-sequence-head + 'magit-sequence-stop))) + ((equal rev head) + (list "done" rev 'magit-sequence-head)) + (t + (list "done" rev 'magit-sequence-done))))) + (magit-sequence-insert-commit "onto" onto + (if (equal onto head) + 'magit-sequence-head + 'magit-sequence-onto)))) + +(defun magit-sequence-insert-commit (type hash face) + (magit-insert-section (commit hash) + (magit-insert-heading + (propertize type 'font-lock-face face) "\s" + (magit-format-rev-summary hash) "\n"))) + +;;; _ +(provide 'magit-sequence) +;;; magit-sequence.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-sparse-checkout.el b/code/elpa/magit-20220821.1819/magit-sparse-checkout.el new file mode 100644 index 0000000..e9f761d --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-sparse-checkout.el @@ -0,0 +1,170 @@ +;;; magit-sparse-checkout.el --- Sparse checkout support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Kyle Meyer +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library provides an interface to the `git sparse-checkout' +;; command. It's been possible to define sparse checkouts since Git +;; v1.7.0 by adding patterns to $GIT_DIR/info/sparse-checkout and +;; calling `git read-tree -mu HEAD' to update the index and working +;; tree. However, Git v2.25 introduced the `git sparse-checkout' +;; command along with "cone mode", which restricts the possible +;; patterns to directories to provide better performance. +;; +;; The goal of this library is to support the `git sparse-checkout' +;; command operating in cone mode. + +;;; Code: + +(require 'magit) + +;;; Utilities + +(defun magit-sparse-checkout-enabled-p () + "Return non-nil if working tree is a sparse checkout." + (magit-get-boolean "core.sparsecheckout")) + +(defun magit-sparse-checkout--assert-version () + ;; Older versions of Git have the ability to define sparse checkout + ;; patterns in .git/info/sparse-checkout, but the sparse-checkout + ;; command isn't available until 2.25.0. + (when (magit-git-version< "2.25.0") + (user-error "`git sparse-checkout' not available until Git v2.25"))) + +(defun magit-sparse-checkout--auto-enable () + (if (magit-sparse-checkout-enabled-p) + (unless (magit-get-boolean "core.sparsecheckoutcone") + (user-error + "Magit's sparse checkout functionality requires cone mode")) + ;; Note: Don't use `magit-sparse-checkout-enable' because it's + ;; asynchronous. + (magit-run-git "sparse-checkout" "init" "--cone"))) + +(defun magit-sparse-checkout-directories () + "Return directories that are recursively included in the sparse checkout. +See the `git sparse-checkout' manpage for details about +\"recursive\" versus \"parent\" directories in cone mode." + (and (magit-get-boolean "core.sparsecheckoutcone") + (mapcar #'file-name-as-directory + (magit-git-lines "sparse-checkout" "list")))) + +;;; Commands + +;;;###autoload (autoload 'magit-sparse-checkout "magit-sparse-checkout" nil t) +(transient-define-prefix magit-sparse-checkout () + "Create and manage sparse checkouts." + :man-page "git-sparse-checkout" + ["Arguments for enabling" + :if-not magit-sparse-checkout-enabled-p + ("-i" "Use sparse index" "--sparse-index")] + ["Actions" + [:if-not magit-sparse-checkout-enabled-p + ("e" "Enable sparse checkout" magit-sparse-checkout-enable)] + [:if magit-sparse-checkout-enabled-p + ("d" "Disable sparse checkout" magit-sparse-checkout-disable) + ("r" "Reapply rules" magit-sparse-checkout-reapply)] + [("s" "Set directories" magit-sparse-checkout-set) + ("a" "Add directories" magit-sparse-checkout-add)]]) + +;;;###autoload +(defun magit-sparse-checkout-enable (&optional args) + "Convert the working tree to a sparse checkout." + (interactive (list (transient-args 'magit-sparse-checkout))) + (magit-sparse-checkout--assert-version) + (magit-run-git-async "sparse-checkout" "init" "--cone" args)) + +;;;###autoload +(defun magit-sparse-checkout-set (directories) + "Restrict working tree to DIRECTORIES. +To extend rather than override the currently configured +directories, call `magit-sparse-checkout-add' instead." + (interactive + (list (magit-completing-read-multiple* + "Include these directories: " + ;; Note: Given that the appeal of sparse checkouts is + ;; dealing with very large trees, listing all subdirectories + ;; may need to be reconsidered. + (magit-revision-directories "HEAD")))) + (magit-sparse-checkout--assert-version) + (magit-sparse-checkout--auto-enable) + (magit-run-git-async "sparse-checkout" "set" directories)) + +;;;###autoload +(defun magit-sparse-checkout-add (directories) + "Add DIRECTORIES to the working tree. +To override rather than extend the currently configured +directories, call `magit-sparse-checkout-set' instead." + (interactive + (list (magit-completing-read-multiple* + "Add these directories: " + ;; Same performance note as in `magit-sparse-checkout-set', + ;; but even more so given the additional processing. + (seq-remove + (let ((re (concat + "\\`" + (regexp-opt (magit-sparse-checkout-directories))))) + (lambda (d) (string-match-p re d))) + (magit-revision-directories "HEAD"))))) + (magit-sparse-checkout--assert-version) + (magit-sparse-checkout--auto-enable) + (magit-run-git-async "sparse-checkout" "add" directories)) + +;;;###autoload +(defun magit-sparse-checkout-reapply () + "Reapply the sparse checkout rules to the working tree. +Some operations such as merging or rebasing may need to check out +files that aren't included in the sparse checkout. Call this +command to reset to the sparse checkout state." + (interactive) + (magit-sparse-checkout--assert-version) + (magit-run-git-async "sparse-checkout" "reapply")) + +;;;###autoload +(defun magit-sparse-checkout-disable () + "Convert sparse checkout to full checkout. +Note that disabling the sparse checkout does not clear the +configured directories. Call `magit-sparse-checkout-enable' to +restore the previous sparse checkout." + (interactive) + (magit-sparse-checkout--assert-version) + (magit-run-git-async "sparse-checkout" "disable")) + +;;; Miscellaneous + +(defun magit-sparse-checkout-insert-header () + "Insert header line with sparse checkout information. +This header is not inserted by default. To enable it, add it to +`magit-status-headers-hook'." + (when (magit-sparse-checkout-enabled-p) + (insert (propertize (format "%-10s" "Sparse! ") + 'font-lock-face 'magit-section-heading)) + (insert + (let ((dirs (magit-sparse-checkout-directories))) + (pcase (length dirs) + (0 "top-level directory") + (1 (car dirs)) + (n (format "%d directories" n))))) + (insert ?\n))) + +;;; _ +(provide 'magit-sparse-checkout) +;;; magit-sparse-checkout.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-stash.el b/code/elpa/magit-20220821.1819/magit-stash.el new file mode 100644 index 0000000..8aee060 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-stash.el @@ -0,0 +1,566 @@ +;;; magit-stash.el --- Stash support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; Support for Git stashes. + +;;; Code: + +(require 'magit) +(require 'magit-reflog) +(require 'magit-sequence) + +;;; Options + +(defgroup magit-stash nil + "List stashes and show stash diffs." + :group 'magit-modes) + +;;;; Diff options + +(defcustom magit-stash-sections-hook + '(magit-insert-stash-notes + magit-insert-stash-worktree + magit-insert-stash-index + magit-insert-stash-untracked) + "Hook run to insert sections into stash diff buffers." + :package-version '(magit . "2.1.0") + :group 'magit-stash + :type 'hook) + +;;;; Log options + +(defcustom magit-stashes-margin + (list (nth 0 magit-log-margin) + (nth 1 magit-log-margin) + 'magit-log-margin-width nil + (nth 4 magit-log-margin)) + "Format of the margin in `magit-stashes-mode' buffers. + +The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). + +If INIT is non-nil, then the margin is shown initially. +STYLE controls how to format the author or committer date. + It can be one of `age' (to show the age of the commit), + `age-abbreviated' (to abbreviate the time unit to a character), + or a string (suitable for `format-time-string') to show the + actual date. Option `magit-log-margin-show-committer-date' + controls which date is being displayed. +WIDTH controls the width of the margin. This exists for forward + compatibility and currently the value should not be changed. +AUTHOR controls whether the name of the author is also shown by + default. +AUTHOR-WIDTH has to be an integer. When the name of the author + is shown, then this specifies how much space is used to do so." + :package-version '(magit . "2.9.0") + :group 'magit-stash + :group 'magit-margin + :type magit-log-margin--custom-type + :initialize #'magit-custom-initialize-reset + :set-after '(magit-log-margin) + :set (apply-partially #'magit-margin-set-variable 'magit-stashes-mode)) + +;;; Commands + +;;;###autoload (autoload 'magit-stash "magit-stash" nil t) +(transient-define-prefix magit-stash () + "Stash uncommitted changes." + :man-page "git-stash" + ["Arguments" + ("-u" "Also save untracked files" ("-u" "--include-untracked")) + ("-a" "Also save untracked and ignored files" ("-a" "--all"))] + [["Stash" + ("z" "both" magit-stash-both) + ("i" "index" magit-stash-index) + ("w" "worktree" magit-stash-worktree) + ("x" "keeping index" magit-stash-keep-index) + ("P" "push" magit-stash-push :level 5)] + ["Snapshot" + ("Z" "both" magit-snapshot-both) + ("I" "index" magit-snapshot-index) + ("W" "worktree" magit-snapshot-worktree) + ("r" "to wip ref" magit-wip-commit)] + ["Use" + ("a" "Apply" magit-stash-apply) + ("p" "Pop" magit-stash-pop) + ("k" "Drop" magit-stash-drop)] + ["Inspect" + ("l" "List" magit-stash-list) + ("v" "Show" magit-stash-show)] + ["Transform" + ("b" "Branch" magit-stash-branch) + ("B" "Branch here" magit-stash-branch-here) + ("f" "Format patch" magit-stash-format-patch)]]) + +(defun magit-stash-arguments () + (transient-args 'magit-stash)) + +;;;###autoload +(defun magit-stash-both (message &optional include-untracked) + "Create a stash of the index and working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive + (progn (when (and (magit-merge-in-progress-p) + (not (magit-y-or-n-p "\ +Stashing and resetting during a merge conflict. \ +Applying the resulting stash won't restore the merge state. \ +Proceed anyway? "))) + (user-error "Abort")) + (magit-stash-read-args))) + (magit-stash-save message t t include-untracked t)) + +;;;###autoload +(defun magit-stash-index (message) + "Create a stash of the index only. +Unstaged and untracked changes are not stashed. The stashed +changes are applied in reverse to both the index and the +worktree. This command can fail when the worktree is not clean. +Applying the resulting stash has the inverse effect." + (interactive (list (magit-stash-read-message))) + (magit-stash-save message t nil nil t 'worktree)) + +;;;###autoload +(defun magit-stash-worktree (message &optional include-untracked) + "Create a stash of unstaged changes in the working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-stash-read-args)) + (magit-stash-save message nil t include-untracked t 'index)) + +;;;###autoload +(defun magit-stash-keep-index (message &optional include-untracked) + "Create a stash of the index and working tree, keeping index intact. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-stash-read-args)) + (magit-stash-save message t t include-untracked t 'index)) + +(defun magit-stash-read-args () + (list (magit-stash-read-message) + (magit-stash-read-untracked))) + +(defun magit-stash-read-untracked () + (let ((prefix (prefix-numeric-value current-prefix-arg)) + (args (magit-stash-arguments))) + (cond ((or (= prefix 16) (member "--all" args)) 'all) + ((or (= prefix 4) (member "--include-untracked" args)) t)))) + +(defun magit-stash-read-message () + (let* ((default (format "On %s: " + (or (magit-get-current-branch) "(no branch)"))) + (input (magit-read-string "Stash message" default))) + (if (equal input default) + (concat default (magit-rev-format "%h %s")) + input))) + +;;;###autoload +(defun magit-snapshot-both (&optional include-untracked) + "Create a snapshot of the index and working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-snapshot-read-args)) + (magit-snapshot-save t t include-untracked t)) + +;;;###autoload +(defun magit-snapshot-index () + "Create a snapshot of the index only. +Unstaged and untracked changes are not stashed." + (interactive) + (magit-snapshot-save t nil nil t)) + +;;;###autoload +(defun magit-snapshot-worktree (&optional include-untracked) + "Create a snapshot of unstaged changes in the working tree. +Untracked files are included according to infix arguments. +One prefix argument is equivalent to `--include-untracked' +while two prefix arguments are equivalent to `--all'." + (interactive (magit-snapshot-read-args)) + (magit-snapshot-save nil t include-untracked t)) + +(defun magit-snapshot-read-args () + (list (magit-stash-read-untracked))) + +(defun magit-snapshot-save (index worktree untracked &optional refresh) + (magit-stash-save (concat "WIP on " (magit-stash-summary)) + index worktree untracked refresh t)) + +;;;###autoload (autoload 'magit-stash-push "magit-stash" nil t) +(transient-define-prefix magit-stash-push (&optional transient args) + "Create stash using \"git stash push\". + +This differs from Magit's other stashing commands, which don't +use \"git stash\" and are generally more flexible but don't allow +specifying a list of files to be stashed." + :man-page "git-stash" + ["Arguments" + (magit:-- :reader ,(-rpartial #'magit-read-files + #'magit-modified-files)) + ("-u" "Also save untracked files" ("-u" "--include-untracked")) + ("-a" "Also save untracked and ignored files" ("-a" "--all")) + ("-k" "Keep index" ("-k" "--keep-index")) + ("-K" "Don't keep index" "--no-keep-index")] + ["Actions" + ("P" "push" magit-stash-push)] + (interactive (if (eq transient-current-command 'magit-stash-push) + (list nil (transient-args 'magit-stash-push)) + (list t))) + (if transient + (transient-setup 'magit-stash-push) + (magit-run-git "stash" "push" args))) + +;;;###autoload +(defun magit-stash-apply (stash) + "Apply a stash to the working tree. +Try to preserve the stash index. If that fails because there +are staged changes, apply without preserving the stash index." + (interactive (list (magit-read-stash "Apply stash"))) + (if (= (magit-call-git "stash" "apply" "--index" stash) 0) + (magit-refresh) + (magit-run-git "stash" "apply" stash))) + +;;;###autoload +(defun magit-stash-pop (stash) + "Apply a stash to the working tree and remove it from stash list. +Try to preserve the stash index. If that fails because there +are staged changes, apply without preserving the stash index +and forgo removing the stash." + (interactive (list (magit-read-stash "Pop stash"))) + (if (= (magit-call-git "stash" "apply" "--index" stash) 0) + (magit-stash-drop stash) + (magit-run-git "stash" "apply" stash))) + +;;;###autoload +(defun magit-stash-drop (stash) + "Remove a stash from the stash list. +When the region is active offer to drop all contained stashes." + (interactive + (list (--if-let (magit-region-values 'stash) + (magit-confirm 'drop-stashes nil "Drop %i stashes" nil it) + (magit-read-stash "Drop stash")))) + (dolist (stash (if (listp stash) + (nreverse (prog1 stash (setq stash (car stash)))) + (list stash))) + (message "Deleted refs/%s (was %s)" stash + (magit-rev-parse "--short" stash)) + (magit-call-git "rev-parse" stash) + (magit-call-git "stash" "drop" stash)) + (magit-refresh)) + +;;;###autoload +(defun magit-stash-clear (ref) + "Remove all stashes saved in REF's reflog by deleting REF." + (interactive (let ((ref (or (magit-section-value-if 'stashes) "refs/stash"))) + (magit-confirm t (format "Drop all stashes in %s" ref)) + (list ref))) + (magit-run-git "update-ref" "-d" ref)) + +;;;###autoload +(defun magit-stash-branch (stash branch) + "Create and checkout a new BRANCH from STASH." + (interactive (list (magit-read-stash "Branch stash") + (magit-read-string-ns "Branch name"))) + (magit-run-git "stash" "branch" branch stash)) + +;;;###autoload +(defun magit-stash-branch-here (stash branch) + "Create and checkout a new BRANCH and apply STASH. +The branch is created using `magit-branch-and-checkout', using the +current branch or `HEAD' as the start-point." + (interactive (list (magit-read-stash "Branch stash") + (magit-read-string-ns "Branch name"))) + (let ((magit-inhibit-refresh t)) + (magit-branch-and-checkout branch (or (magit-get-current-branch) "HEAD"))) + (magit-stash-apply stash)) + +;;;###autoload +(defun magit-stash-format-patch (stash) + "Create a patch from STASH" + (interactive (list (magit-read-stash "Create patch from stash"))) + (with-temp-file (magit-rev-format "0001-%f.patch" stash) + (magit-git-insert "stash" "show" "-p" stash)) + (magit-refresh)) + +;;; Plumbing + +(defun magit-stash-save (message index worktree untracked + &optional refresh keep noerror ref) + (if (or (and index (magit-staged-files t)) + (and worktree (magit-unstaged-files t)) + (and untracked (magit-untracked-files (eq untracked 'all)))) + (magit-with-toplevel + (magit-stash-store message (or ref "refs/stash") + (magit-stash-create message index worktree untracked)) + (if (eq keep 'worktree) + (with-temp-buffer + (magit-git-insert "diff" "--cached" "--no-ext-diff") + (magit-run-git-with-input + "apply" "--reverse" "--cached" "--ignore-space-change" "-") + (magit-run-git-with-input + "apply" "--reverse" "--ignore-space-change" "-")) + (unless (eq keep t) + (if (eq keep 'index) + (magit-call-git "checkout" "--" ".") + (magit-call-git "reset" "--hard" "HEAD" "--")) + (when untracked + (magit-call-git "clean" "--force" "-d" + (and (eq untracked 'all) "-x"))))) + (when refresh + (magit-refresh))) + (unless noerror + (user-error "No %s changes to save" (cond ((not index) "unstaged") + ((not worktree) "staged") + (t "local")))))) + +(defun magit-stash-store (message ref commit) + (magit-update-ref ref message commit t)) + +(defun magit-stash-create (message index worktree untracked) + (unless (magit-rev-parse "--verify" "HEAD") + (error "You do not have the initial commit yet")) + (let ((magit-git-global-arguments (nconc (list "-c" "commit.gpgsign=false") + magit-git-global-arguments)) + (default-directory (magit-toplevel)) + (summary (magit-stash-summary)) + (head "HEAD")) + (when (and worktree (not index)) + (setq head (or (magit-commit-tree "pre-stash index" nil "HEAD") + (error "Cannot save the current index state")))) + (or (setq index (magit-commit-tree (concat "index on " summary) nil head)) + (error "Cannot save the current index state")) + (and untracked + (setq untracked (magit-untracked-files (eq untracked 'all))) + (setq untracked (magit-with-temp-index nil nil + (or (and (magit-update-files untracked) + (magit-commit-tree + (concat "untracked files on " summary))) + (error "Cannot save the untracked files"))))) + (magit-with-temp-index index "-m" + (when worktree + (or (magit-update-files (magit-git-items "diff" "-z" "--name-only" head)) + (error "Cannot save the current worktree state"))) + (or (magit-commit-tree message nil head index untracked) + (error "Cannot save the current worktree state"))))) + +(defun magit-stash-summary () + (concat (or (magit-get-current-branch) "(no branch)") + ": " (magit-rev-format "%h %s"))) + +;;; Sections + +(defvar magit-stashes-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-stash-list "List %t") + (magit-menu-set map [magit-delete-thing] #'magit-stash-clear "Clear %t") + map) + "Keymap for `stashes' section.") + +(defvar magit-stash-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-stash-show "Visit %v") + (magit-menu-set map [magit-delete-thing] #'magit-stash-drop "Delete %M") + (magit-menu-set map [magit-cherry-apply] #'magit-stash-apply "Apply %M") + (magit-menu-set map [magit-cherry-pick] #'magit-stash-pop "Pop %M") + map) + "Keymap for `stash' sections.") + +(magit-define-section-jumper magit-jump-to-stashes + "Stashes" stashes "refs/stash") + +(cl-defun magit-insert-stashes (&optional (ref "refs/stash") + (heading "Stashes:")) + "Insert `stashes' section showing reflog for \"refs/stash\". +If optional REF is non-nil, show reflog for that instead. +If optional HEADING is non-nil, use that as section heading +instead of \"Stashes:\"." + (let ((verified (magit-rev-verify ref)) + (autostash (magit-rebase--get-state-lines "autostash"))) + (when (or autostash verified) + (magit-insert-section (stashes ref) + (magit-insert-heading heading) + (when autostash + (pcase-let ((`(,author ,date ,msg) + (split-string + (car (magit-git-lines + "show" "-q" "--format=%aN%x00%at%x00%s" + autostash)) + "\0"))) + (magit-insert-section (stash autostash) + (insert (propertize "AUTOSTASH" 'font-lock-face 'magit-hash)) + (insert " " msg "\n") + (save-excursion + (backward-char) + (magit-log-format-margin autostash author date))))) + (if verified + (magit-git-wash (apply-partially #'magit-log-wash-log 'stash) + "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" ref) + (insert ?\n) + (save-excursion + (backward-char) + (magit-make-margin-overlay))))))) + +;;; List Stashes + +;;;###autoload +(defun magit-stash-list () + "List all stashes in a buffer." + (interactive) + (magit-stashes-setup-buffer)) + +(define-derived-mode magit-stashes-mode magit-reflog-mode "Magit Stashes" + "Mode for looking at lists of stashes." + :group 'magit-log + (hack-dir-local-variables-non-file-buffer)) + +(defun magit-stashes-setup-buffer () + (magit-setup-buffer #'magit-stashes-mode nil + (magit-buffer-refname "refs/stash"))) + +(defun magit-stashes-refresh-buffer () + (magit-insert-section (stashesbuf) + (magit-insert-heading (if (equal magit-buffer-refname "refs/stash") + "Stashes:" + (format "Stashes [%s]:" magit-buffer-refname))) + (magit-git-wash (apply-partially #'magit-log-wash-log 'stash) + "reflog" "--format=%gd%x00%aN%x00%at%x00%gs" magit-buffer-refname))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-stashes-mode)) + magit-buffer-refname) + +(defvar magit--update-stash-buffer nil) + +(defun magit-stashes-maybe-update-stash-buffer (&optional _) + "When moving in the stashes buffer, update the stash buffer. +If there is no stash buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-stashes-mode) + (magit--maybe-update-stash-buffer))) + +(defun magit--maybe-update-stash-buffer () + (when-let* ((stash (magit-section-value-if 'stash)) + (buffer (magit-get-mode-buffer 'magit-stash-mode nil t))) + (if magit--update-stash-buffer + (setq magit--update-stash-buffer (list stash buffer)) + (setq magit--update-stash-buffer (list stash buffer)) + (run-with-idle-timer + magit-update-other-window-delay nil + (let ((args (with-current-buffer buffer + (let ((magit-direct-use-buffer-arguments 'selected)) + (magit-show-commit--arguments))))) + (lambda () + (pcase-let ((`(,stash ,buf) magit--update-stash-buffer)) + (setq magit--update-stash-buffer nil) + (when (buffer-live-p buf) + (let ((magit-display-buffer-noselect t)) + (apply #'magit-stash-show stash args)))) + (setq magit--update-stash-buffer nil))))))) + +;;; Show Stash + +;;;###autoload +(defun magit-stash-show (stash &optional args files) + "Show all diffs of a stash in a buffer." + (interactive (cons (or (and (not current-prefix-arg) + (magit-stash-at-point)) + (magit-read-stash "Show stash")) + (pcase-let ((`(,args ,files) + (magit-diff-arguments 'magit-stash-mode))) + (list (delete "--stat" args) files)))) + (magit-stash-setup-buffer stash args files)) + +(define-derived-mode magit-stash-mode magit-diff-mode "Magit Stash" + "Mode for looking at individual stashes." + :group 'magit-diff + (hack-dir-local-variables-non-file-buffer) + (setq magit--imenu-group-types '(commit))) + +(defun magit-stash-setup-buffer (stash args files) + (magit-setup-buffer #'magit-stash-mode nil + (magit-buffer-revision stash) + (magit-buffer-range (format "%s^..%s" stash stash)) + (magit-buffer-diff-args args) + (magit-buffer-diff-files files))) + +(defun magit-stash-refresh-buffer () + (magit-set-header-line-format + (concat (capitalize magit-buffer-revision) " " + (propertize (magit-rev-format "%s" magit-buffer-revision) + 'font-lock-face + (list :weight 'normal :foreground + (face-attribute 'default :foreground))))) + (setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision)) + (magit-insert-section (stash) + (magit-run-section-hook 'magit-stash-sections-hook))) + +(cl-defmethod magit-buffer-value (&context (major-mode magit-stash-mode)) + magit-buffer-revision) + +(defun magit-stash-insert-section (commit range message &optional files) + (magit-insert-section (commit commit) + (magit-insert-heading message) + (magit--insert-diff "diff" range "-p" "--no-prefix" magit-buffer-diff-args + "--" (or files magit-buffer-diff-files)))) + +(defun magit-insert-stash-notes () + "Insert section showing notes for a stash. +This shows the notes for stash@{N} but not for the other commits +that make up the stash." + (magit-insert-section section (note) + (magit-insert-heading "Notes") + (magit-git-insert "notes" "show" magit-buffer-revision) + (if (= (point) + (oref section content)) + (magit-cancel-section) + (insert "\n")))) + +(defun magit-insert-stash-index () + "Insert section showing staged changes of the stash." + (magit-stash-insert-section + (format "%s^2" magit-buffer-revision) + (format "%s^..%s^2" magit-buffer-revision magit-buffer-revision) + "Staged")) + +(defun magit-insert-stash-worktree () + "Insert section showing unstaged changes of the stash." + (magit-stash-insert-section + magit-buffer-revision + (format "%s^2..%s" magit-buffer-revision magit-buffer-revision) + "Unstaged")) + +(defun magit-insert-stash-untracked () + "Insert section showing the untracked files commit of the stash." + (let ((stash magit-buffer-revision) + (rev (concat magit-buffer-revision "^3"))) + (when (magit-rev-verify rev) + (magit-stash-insert-section (format "%s^3" stash) + (format "%s^..%s^3" stash stash) + "Untracked files" + (magit-git-items "ls-tree" "-z" "--name-only" + "-r" "--full-tree" rev))))) + +;;; _ +(provide 'magit-stash) +;;; magit-stash.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-status.el b/code/elpa/magit-20220821.1819/magit-status.el new file mode 100644 index 0000000..186828a --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-status.el @@ -0,0 +1,824 @@ +;;; magit-status.el --- The grand overview -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements the status buffer. + +;;; Code: + +(require 'magit) + +;;; Options + +(defgroup magit-status nil + "Inspect and manipulate Git repositories." + :link '(info-link "(magit)Status Buffer") + :group 'magit-modes) + +(defcustom magit-status-mode-hook nil + "Hook run after entering Magit-Status mode." + :group 'magit-status + :type 'hook) + +(defcustom magit-status-headers-hook + '(magit-insert-error-header + magit-insert-diff-filter-header + magit-insert-head-branch-header + magit-insert-upstream-branch-header + magit-insert-push-branch-header + magit-insert-tags-header) + "Hook run to insert headers into the status buffer. + +This hook is run by `magit-insert-status-headers', which in turn +has to be a member of `magit-status-sections-hook' to be used at +all." + :package-version '(magit . "2.1.0") + :group 'magit-status + :type 'hook + :options '(magit-insert-error-header + magit-insert-diff-filter-header + magit-insert-repo-header + magit-insert-remote-header + magit-insert-head-branch-header + magit-insert-upstream-branch-header + magit-insert-push-branch-header + magit-insert-tags-header)) + +(defcustom magit-status-sections-hook + '(magit-insert-status-headers + magit-insert-merge-log + magit-insert-rebase-sequence + magit-insert-am-sequence + magit-insert-sequencer-sequence + magit-insert-bisect-output + magit-insert-bisect-rest + magit-insert-bisect-log + magit-insert-untracked-files + magit-insert-unstaged-changes + magit-insert-staged-changes + magit-insert-stashes + magit-insert-unpushed-to-pushremote + magit-insert-unpushed-to-upstream-or-recent + magit-insert-unpulled-from-pushremote + magit-insert-unpulled-from-upstream) + "Hook run to insert sections into a status buffer." + :package-version '(magit . "2.12.0") + :group 'magit-status + :type 'hook) + +(defcustom magit-status-initial-section '(1) + "The section point is placed on when a status buffer is created. + +When such a buffer is merely being refreshed or being shown again +after it was merely buried, then this option has no effect. + +If this is nil, then point remains on the very first section as +usual. Otherwise it has to be a list of integers and section +identity lists. The members of that list are tried in order +until a matching section is found. + +An integer means to jump to the nth section, 1 for example +jumps over the headings. To get a section's \"identity list\" +use \\[universal-argument] \\[magit-describe-section-briefly]. + +If, for example, you want to jump to the commits that haven't +been pulled from the upstream, or else the second section, then +use: (((unpulled . \"..@{upstream}\") (status)) 1). + +See option `magit-section-initial-visibility-alist' for how to +control the initial visibility of the jumped to section." + :package-version '(magit . "2.90.0") + :group 'magit-status + :type '(choice (const :tag "as usual" nil) + (repeat (choice (number :tag "nth top-level section") + (sexp :tag "section identity"))))) + +(defcustom magit-status-goto-file-position nil + "Whether to go to position corresponding to file position. + +If this is non-nil and the current buffer is visiting a file, +then `magit-status' tries to go to the position in the status +buffer that corresponds to the position in the file-visiting +buffer. This jumps into either the diff of unstaged changes +or the diff of staged changes. + +If the previously current buffer does not visit a file, or if +the file has neither unstaged nor staged changes then this has +no effect. + +The command `magit-status-here' tries to go to that position, +regardless of the value of this option." + :package-version '(magit . "3.0.0") + :group 'magit-status + :type 'boolean) + +(defcustom magit-status-show-hashes-in-headers nil + "Whether headers in the status buffer show hashes. +The functions which respect this option are +`magit-insert-head-branch-header', +`magit-insert-upstream-branch-header', and +`magit-insert-push-branch-header'." + :package-version '(magit . "2.4.0") + :group 'magit-status + :type 'boolean) + +(defcustom magit-status-margin + (list nil + (nth 1 magit-log-margin) + 'magit-log-margin-width nil + (nth 4 magit-log-margin)) + "Format of the margin in `magit-status-mode' buffers. + +The value has the form (INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH). + +If INIT is non-nil, then the margin is shown initially. +STYLE controls how to format the author or committer date. + It can be one of `age' (to show the age of the commit), + `age-abbreviated' (to abbreviate the time unit to a character), + or a string (suitable for `format-time-string') to show the + actual date. Option `magit-log-margin-show-committer-date' + controls which date is being displayed. +WIDTH controls the width of the margin. This exists for forward + compatibility and currently the value should not be changed. +AUTHOR controls whether the name of the author is also shown by + default. +AUTHOR-WIDTH has to be an integer. When the name of the author + is shown, then this specifies how much space is used to do so." + :package-version '(magit . "2.9.0") + :group 'magit-status + :group 'magit-margin + :type magit-log-margin--custom-type + :initialize #'magit-custom-initialize-reset + :set-after '(magit-log-margin) + :set (apply-partially #'magit-margin-set-variable 'magit-status-mode)) + +(defcustom magit-status-use-buffer-arguments 'selected + "Whether `magit-status' reuses arguments when the buffer already exists. + +This option has no effect when merely refreshing the status +buffer using `magit-refresh'. + +Valid values are: + +`always': Always use the set of arguments that is currently + active in the status buffer, provided that buffer exists + of course. +`selected': Use the set of arguments from the status + buffer, but only if it is displayed in a window of the + current frame. This is the default. +`current': Use the set of arguments from the status buffer, + but only if it is the current buffer. +`never': Never use the set of arguments from the status + buffer." + :package-version '(magit . "3.0.0") + :group 'magit-buffers + :group 'magit-commands + :type '(choice + (const :tag "always use args from buffer" always) + (const :tag "use args from buffer if displayed in frame" selected) + (const :tag "use args from buffer if it is current" current) + (const :tag "never use args from buffer" never))) + +;;; Commands + +;;;###autoload +(defun magit-init (directory) + "Initialize a Git repository, then show its status. + +If the directory is below an existing repository, then the user +has to confirm that a new one should be created inside. If the +directory is the root of the existing repository, then the user +has to confirm that it should be reinitialized. + +Non-interactively DIRECTORY is (re-)initialized unconditionally." + (interactive + (let ((directory (file-name-as-directory + (expand-file-name + (read-directory-name "Create repository in: "))))) + (when-let ((toplevel (magit-toplevel directory))) + (setq toplevel (expand-file-name toplevel)) + (unless (y-or-n-p (if (file-equal-p toplevel directory) + (format "Reinitialize existing repository %s? " + directory) + (format "%s is a repository. Create another in %s? " + toplevel directory))) + (user-error "Abort"))) + (list directory))) + ;; `git init' does not understand the meaning of "~"! + (magit-call-git "init" (magit-convert-filename-for-git + (expand-file-name directory))) + (magit-status-setup-buffer directory)) + +;;;###autoload +(defun magit-status (&optional directory cache) + "Show the status of the current Git repository in a buffer. + +If the current directory isn't located within a Git repository, +then prompt for an existing repository or an arbitrary directory, +depending on option `magit-repository-directories', and show the +status of the selected repository instead. + +* If that option specifies any existing repositories, then offer + those for completion and show the status buffer for the + selected one. + +* Otherwise read an arbitrary directory using regular file-name + completion. If the selected directory is the top-level of an + existing working tree, then show the status buffer for that. + +* Otherwise offer to initialize the selected directory as a new + repository. After creating the repository show its status + buffer. + +These fallback behaviors can also be forced using one or more +prefix arguments: + +* With two prefix arguments (or more precisely a numeric prefix + value of 16 or greater) read an arbitrary directory and act on + it as described above. The same could be accomplished using + the command `magit-init'. + +* With a single prefix argument read an existing repository, or + if none can be found based on `magit-repository-directories', + then fall back to the same behavior as with two prefix + arguments." + (interactive + (let ((magit--refresh-cache (list (cons 0 0)))) + (list (and (or current-prefix-arg (not (magit-toplevel))) + (progn (magit--assert-usable-git) + (magit-read-repository + (>= (prefix-numeric-value current-prefix-arg) 16)))) + magit--refresh-cache))) + (let ((magit--refresh-cache (or cache (list (cons 0 0))))) + (if directory + (let ((toplevel (magit-toplevel directory))) + (setq directory (file-name-as-directory + (expand-file-name directory))) + (if (and toplevel (file-equal-p directory toplevel)) + (magit-status-setup-buffer directory) + (when (y-or-n-p + (if toplevel + (format "%s is a repository. Create another in %s? " + toplevel directory) + (format "Create repository in %s? " directory))) + ;; Creating a new repository invalidates cached values. + (setq magit--refresh-cache nil) + (magit-init directory)))) + (magit-status-setup-buffer default-directory)))) + +(put 'magit-status 'interactive-only 'magit-status-setup-buffer) + +;;;###autoload +(defalias 'magit #'magit-status + "An alias for `magit-status' for better discoverability. + +Instead of invoking this alias for `magit-status' using +\"M-x magit RET\", you should bind a key to `magit-status' +and read the info node `(magit)Getting Started', which +also contains other useful hints.") + +;;;###autoload +(defun magit-status-here () + "Like `magit-status' but with non-nil `magit-status-goto-file-position'." + (interactive) + (let ((magit-status-goto-file-position t)) + (call-interactively #'magit-status))) + +(put 'magit-status-here 'interactive-only 'magit-status-setup-buffer) + +;;;###autoload +(defun magit-status-quick () + "Show the status of the current Git repository, maybe without refreshing. + +If the status buffer of the current Git repository exists but +isn't being displayed in the selected frame, then display it +without refreshing it. + +If the status buffer is being displayed in the selected frame, +then also refresh it. + +Prefix arguments have the same meaning as for `magit-status', +and additionally cause the buffer to be refresh. + +To use this function instead of `magit-status', add this to your +init file: (global-set-key (kbd \"C-x g\") \\='magit-status-quick)." + (interactive) + (if-let ((buffer + (and (not current-prefix-arg) + (not (magit-get-mode-buffer 'magit-status-mode nil 'selected)) + (magit-get-mode-buffer 'magit-status-mode)))) + (magit-display-buffer buffer) + (call-interactively #'magit-status))) + +;;; Mode + +(defvar magit-status-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-mode-map) + (define-key map "j" #'magit-status-jump) + (define-key map [remap dired-jump] #'magit-dired-jump) + map) + "Keymap for `magit-status-mode'.") + +(transient-define-prefix magit-status-jump () + "In a Magit-Status buffer, jump to a section." + ["Jump to" + [("z " "Stashes" magit-jump-to-stashes + :if (lambda () (memq 'magit-insert-stashes magit-status-sections-hook))) + ("t " "Tracked" magit-jump-to-tracked + :if (lambda () (memq 'magit-insert-tracked-files magit-status-sections-hook))) + ("n " "Untracked" magit-jump-to-untracked + :if (lambda () (memq 'magit-insert-untracked-files magit-status-sections-hook))) + ("u " "Unstaged" magit-jump-to-unstaged + :if (lambda () (memq 'magit-insert-unstaged-changes magit-status-sections-hook))) + ("s " "Staged" magit-jump-to-staged + :if (lambda () (memq 'magit-insert-staged-changes magit-status-sections-hook)))] + [("fu" "Unpulled from upstream" magit-jump-to-unpulled-from-upstream + :if (lambda () (memq 'magit-insert-unpulled-from-upstream magit-status-sections-hook))) + ("fp" "Unpulled from pushremote" magit-jump-to-unpulled-from-pushremote + :if (lambda () (memq 'magit-insert-unpulled-from-pushremote magit-status-sections-hook))) + ("pu" magit-jump-to-unpushed-to-upstream + :if (lambda () + (or (memq 'magit-insert-unpushed-to-upstream-or-recent magit-status-sections-hook) + (memq 'magit-insert-unpushed-to-upstream magit-status-sections-hook))) + :description (lambda () + (let ((upstream (magit-get-upstream-branch))) + (if (or (not upstream) + (magit-rev-ancestor-p "HEAD" upstream)) + "Recent commits" + "Unmerged into upstream")))) + ("pp" "Unpushed to pushremote" magit-jump-to-unpushed-to-pushremote + :if (lambda () (memq 'magit-insert-unpushed-to-pushremote magit-status-sections-hook))) + ("a " "Assumed unstaged" magit-jump-to-assume-unchanged + :if (lambda () (memq 'magit-insert-assume-unchanged-files magit-status-sections-hook))) + ("w " "Skip worktree" magit-jump-to-skip-worktree + :if (lambda () (memq 'magit-insert-skip-worktree-files magit-status-sections-hook)))] + [("i" "Using Imenu" imenu)]]) + +(define-derived-mode magit-status-mode magit-mode "Magit" + "Mode for looking at Git status. + +This mode is documented in info node `(magit)Status Buffer'. + +\\\ +Type \\[magit-refresh] to refresh the current buffer. +Type \\[magit-section-toggle] to expand or hide the section at point. +Type \\[magit-visit-thing] to visit the change or commit at point. + +Type \\[magit-dispatch] to invoke major commands. + +Staging and applying changes is documented in info node +`(magit)Staging and Unstaging' and info node `(magit)Applying'. + +\\Type \ +\\[magit-apply] to apply the change at point, \ +\\[magit-stage] to stage, +\\[magit-unstage] to unstage, \ +\\[magit-discard] to discard, or \ +\\[magit-reverse] to reverse it. + +\\\ +Type \\[magit-commit] to create a commit. + +\\{magit-status-mode-map}" + :group 'magit-status + (hack-dir-local-variables-non-file-buffer) + (when magit-status-initial-section + (add-hook 'magit-refresh-buffer-hook + #'magit-status-goto-initial-section nil t)) + (setq magit--imenu-group-types '(not branch commit))) + +(put 'magit-status-mode 'magit-diff-default-arguments + '("--no-ext-diff")) +(put 'magit-status-mode 'magit-log-default-arguments + '("-n256" "--decorate")) + +;;;###autoload +(defun magit-status-setup-buffer (&optional directory) + (unless directory + (setq directory default-directory)) + (when (file-remote-p directory) + (magit-git-version-assert)) + (let* ((default-directory directory) + (d (magit-diff--get-value 'magit-status-mode + magit-status-use-buffer-arguments)) + (l (magit-log--get-value 'magit-status-mode + magit-status-use-buffer-arguments)) + (file (and magit-status-goto-file-position + (magit-file-relative-name))) + (line (and file (line-number-at-pos))) + (col (and file (current-column))) + (buf (magit-setup-buffer #'magit-status-mode nil + (magit-buffer-diff-args (nth 0 d)) + (magit-buffer-diff-files (nth 1 d)) + (magit-buffer-log-args (nth 0 l)) + (magit-buffer-log-files (nth 1 l))))) + (when file + (with-current-buffer buf + (let ((staged (magit-get-section '((staged) (status))))) + (if (and staged + (cadr (magit-diff--locate-hunk file line staged))) + (magit-diff--goto-position file line col staged) + (let ((unstaged (magit-get-section '((unstaged) (status))))) + (unless (and unstaged + (magit-diff--goto-position file line col unstaged)) + (when staged + (magit-diff--goto-position file line col staged)))))))) + buf)) + +(defun magit-status-refresh-buffer () + (magit-git-exit-code "update-index" "--refresh") + (magit-insert-section (status) + (magit-run-section-hook 'magit-status-sections-hook))) + +(defun magit-status-goto-initial-section () + "Jump to the section specified by `magit-status-initial-section'." + (when-let ((section + (--some (if (integerp it) + (nth (1- it) + (magit-section-siblings (magit-current-section) + 'next)) + (magit-get-section it)) + magit-status-initial-section))) + (goto-char (oref section start)) + (when-let ((vis (cdr (assq 'magit-status-initial-section + magit-section-initial-visibility-alist)))) + (if (eq vis 'hide) + (magit-section-hide section) + (magit-section-show section)))) + (remove-hook 'magit-refresh-buffer-hook + #'magit-status-goto-initial-section t)) + +(defun magit-status-maybe-update-revision-buffer (&optional _) + "When moving in the status buffer, update the revision buffer. +If there is no revision buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-status-mode) + (magit--maybe-update-revision-buffer))) + +(defun magit-status-maybe-update-stash-buffer (&optional _) + "When moving in the status buffer, update the stash buffer. +If there is no stash buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-status-mode) + (magit--maybe-update-stash-buffer))) + +(defun magit-status-maybe-update-blob-buffer (&optional _) + "When moving in the status buffer, update the blob buffer. +If there is no blob buffer in the same frame, then do nothing." + (when (derived-mode-p 'magit-status-mode) + (magit--maybe-update-blob-buffer))) + +;;; Sections +;;;; Special Headers + +(defun magit-insert-status-headers () + "Insert header sections appropriate for `magit-status-mode' buffers. +The sections are inserted by running the functions on the hook +`magit-status-headers-hook'." + (if (magit-rev-verify "HEAD") + (magit-insert-headers 'magit-status-headers-hook) + (insert "In the beginning there was darkness\n\n"))) + +(defvar magit-error-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] + #'magit-process-buffer "Visit process output") + map) + "Keymap for `error' sections.") + +(defun magit-insert-error-header () + "Insert the message about the Git error that just occurred. + +This function is only aware of the last error that occur when Git +was run for side-effects. If, for example, an error occurs while +generating a diff, then that error won't be inserted. Refreshing +the status buffer causes this section to disappear again." + (when magit-this-error + (magit-insert-section (error 'git) + (insert (propertize (format "%-10s" "GitError! ") + 'font-lock-face 'magit-section-heading)) + (insert (propertize magit-this-error 'font-lock-face 'error)) + (when-let ((key (car (where-is-internal 'magit-process-buffer)))) + (insert (format " [Type `%s' for details]" (key-description key)))) + (insert ?\n)) + (setq magit-this-error nil))) + +(defun magit-insert-diff-filter-header () + "Insert a header line showing the effective diff filters." + (let ((ignore-modules (magit-ignore-submodules-p))) + (when (or ignore-modules + magit-buffer-diff-files) + (insert (propertize (format "%-10s" "Filter! ") + 'font-lock-face 'magit-section-heading)) + (when ignore-modules + (insert ignore-modules) + (when magit-buffer-diff-files + (insert " -- "))) + (when magit-buffer-diff-files + (insert (mapconcat #'identity magit-buffer-diff-files " "))) + (insert ?\n)))) + +;;;; Reference Headers + +(defun magit-insert-head-branch-header (&optional branch) + "Insert a header line about the current branch. +If `HEAD' is detached, then insert information about that commit +instead. The optional BRANCH argument is for internal use only." + (let ((branch (or branch (magit-get-current-branch))) + (output (magit-rev-format "%h %s" (or branch "HEAD")))) + (string-match "^\\([^ ]+\\) \\(.*\\)" output) + (magit-bind-match-strings (commit summary) output + (when (equal summary "") + (setq summary "(no commit message)")) + (if branch + (magit-insert-section (branch branch) + (insert (format "%-10s" "Head: ")) + (when magit-status-show-hashes-in-headers + (insert (propertize commit 'font-lock-face 'magit-hash) ?\s)) + (insert (propertize branch 'font-lock-face 'magit-branch-local)) + (insert ?\s) + (insert (funcall magit-log-format-message-function branch summary)) + (insert ?\n)) + (magit-insert-section (commit commit) + (insert (format "%-10s" "Head: ")) + (insert (propertize commit 'font-lock-face 'magit-hash)) + (insert ?\s) + (insert (funcall magit-log-format-message-function nil summary)) + (insert ?\n)))))) + +(defun magit-insert-upstream-branch-header (&optional branch upstream keyword) + "Insert a header line about the upstream of the current branch. +If no branch is checked out, then insert nothing. The optional +arguments are for internal use only." + (when-let ((branch (or branch (magit-get-current-branch)))) + (let ((remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge")) + (rebase (magit-get "branch" branch "rebase"))) + (when (or remote merge) + (unless upstream + (setq upstream (magit-get-upstream-branch branch))) + (magit-insert-section (branch upstream) + (pcase rebase + ("true") + ("false" (setq rebase nil)) + (_ (setq rebase (magit-get-boolean "pull.rebase")))) + (insert (format "%-10s" (or keyword (if rebase "Rebase: " "Merge: ")))) + (insert + (if upstream + (concat (and magit-status-show-hashes-in-headers + (concat (propertize (magit-rev-format "%h" upstream) + 'font-lock-face 'magit-hash) + " ")) + upstream " " + (funcall magit-log-format-message-function upstream + (funcall magit-log-format-message-function nil + (or (magit-rev-format "%s" upstream) + "(no commit message)")))) + (cond + ((magit--unnamed-upstream-p remote merge) + (concat (propertize merge 'font-lock-face 'magit-branch-remote) + " from " + (propertize remote 'font-lock-face 'bold))) + ((magit--valid-upstream-p remote merge) + (if (equal remote ".") + (concat + (propertize merge 'font-lock-face 'magit-branch-local) " " + (propertize "does not exist" + 'font-lock-face 'magit-branch-warning)) + (format + "%s %s %s" + (propertize merge 'font-lock-face 'magit-branch-remote) + (propertize "does not exist on" + 'font-lock-face 'magit-branch-warning) + (propertize remote 'font-lock-face 'magit-branch-remote)))) + (t + (propertize "invalid upstream configuration" + 'font-lock-face 'magit-branch-warning))))) + (insert ?\n)))))) + +(defun magit-insert-push-branch-header () + "Insert a header line about the branch the current branch is pushed to." + (when-let* ((branch (magit-get-current-branch)) + (target (magit-get-push-branch branch))) + (magit-insert-section (branch target) + (insert (format "%-10s" "Push: ")) + (insert + (if (magit-rev-verify target) + (concat (and magit-status-show-hashes-in-headers + (concat (propertize (magit-rev-format "%h" target) + 'font-lock-face 'magit-hash) + " ")) + target " " + (funcall magit-log-format-message-function target + (funcall magit-log-format-message-function nil + (or (magit-rev-format "%s" target) + "(no commit message)")))) + (let ((remote (magit-get-push-remote branch))) + (if (magit-remote-p remote) + (concat target " " + (propertize "does not exist" + 'font-lock-face 'magit-branch-warning)) + (concat remote " " + (propertize "remote does not exist" + 'font-lock-face 'magit-branch-warning)))))) + (insert ?\n)))) + +(defun magit-insert-tags-header () + "Insert a header line about the current and/or next tag." + (let* ((this-tag (magit-get-current-tag nil t)) + (next-tag (magit-get-next-tag nil t)) + (this-cnt (cadr this-tag)) + (next-cnt (cadr next-tag)) + (this-tag (car this-tag)) + (next-tag (car next-tag)) + (both-tags (and this-tag next-tag t))) + (when (or this-tag next-tag) + (magit-insert-section (tag (or this-tag next-tag)) + (insert (format "%-10s" (if both-tags "Tags: " "Tag: "))) + (cl-flet ((insert-count (tag count face) + (insert (concat (propertize tag 'font-lock-face 'magit-tag) + (and (> count 0) + (format " (%s)" + (propertize + (format "%s" count) + 'font-lock-face face))))))) + (when this-tag (insert-count this-tag this-cnt 'magit-branch-local)) + (when both-tags (insert ", ")) + (when next-tag (insert-count next-tag next-cnt 'magit-tag))) + (insert ?\n))))) + +;;;; Auxiliary Headers + +(defun magit-insert-user-header () + "Insert a header line about the current user." + (let ((name (magit-get "user.name")) + (email (magit-get "user.email"))) + (when (and name email) + (magit-insert-section (user name) + (insert (format "%-10s" "User: ")) + (insert (propertize name 'font-lock-face 'magit-log-author)) + (insert " <" email ">\n"))))) + +(defun magit-insert-repo-header () + "Insert a header line showing the path to the repository top-level." + (let ((topdir (magit-toplevel))) + (magit-insert-section (repo topdir) + (insert (format "%-10s%s\n" "Repo: " (abbreviate-file-name topdir)))))) + +(defun magit-insert-remote-header () + "Insert a header line about the remote of the current branch. + +If no remote is configured for the current branch, then fall back +showing the \"origin\" remote, or if that does not exist the first +remote in alphabetic order." + (when-let* ((name (magit-get-some-remote)) + ;; Under certain configurations it's possible for + ;; url to be nil, when name is not, see #2858. + (url (magit-get "remote" name "url"))) + (magit-insert-section (remote name) + (insert (format "%-10s" "Remote: ")) + (insert (propertize name 'font-lock-face 'magit-branch-remote) ?\s) + (insert url ?\n)))) + +;;;; File Sections + +(defvar magit-untracked-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-stage-file] #'magit-stage "Stage files") + (magit-menu-set map [magit-delete-thing] #'magit-discard "Discard files") + map) + "Keymap for the `untracked' section.") + +(magit-define-section-jumper magit-jump-to-untracked "Untracked files" untracked) + +(defun magit-insert-untracked-files () + "Maybe insert a list or tree of untracked files. + +Do so depending on the value of `status.showUntrackedFiles'. +Note that even if the value is `all', Magit still initially +only shows directories. But the directory sections can then +be expanded using \"TAB\". + +If the first element of `magit-buffer-diff-files' is a +directory, then limit the list to files below that. The value +value of that variable can be set using \"D -- DIRECTORY RET g\"." + (let* ((show (or (magit-get "status.showUntrackedFiles") "normal")) + (base (car magit-buffer-diff-files)) + (base (and base (file-directory-p base) base))) + (unless (equal show "no") + (if (equal show "all") + (when-let ((files (magit-untracked-files nil base))) + (magit-insert-section (untracked) + (magit-insert-heading "Untracked files:") + (magit-insert-files files base) + (insert ?\n))) + (when-let ((files + (--mapcat (and (eq (aref it 0) ??) + (list (substring it 3))) + (magit-git-items "status" "-z" "--porcelain" + (magit-ignore-submodules-p t) + "--" base)))) + (magit-insert-section (untracked) + (magit-insert-heading "Untracked files:") + (dolist (file files) + (magit-insert-section (file file) + (insert (propertize file 'font-lock-face 'magit-filename) ?\n))) + (insert ?\n))))))) + +(magit-define-section-jumper magit-jump-to-tracked "Tracked files" tracked) + +(defun magit-insert-tracked-files () + "Insert a tree of tracked files. + +If the first element of `magit-buffer-diff-files' is a +directory, then limit the list to files below that. The value +value of that variable can be set using \"D -- DIRECTORY RET g\"." + (when-let ((files (magit-list-files))) + (let* ((base (car magit-buffer-diff-files)) + (base (and base (file-directory-p base) base))) + (magit-insert-section (tracked nil t) + (magit-insert-heading "Tracked files:") + (magit-insert-files files base) + (insert ?\n))))) + +(defun magit-insert-ignored-files () + "Insert a tree of ignored files. + +If the first element of `magit-buffer-diff-files' is a +directory, then limit the list to files below that. The value +of that variable can be set using \"D -- DIRECTORY RET g\"." + (when-let ((files (magit-ignored-files))) + (let* ((base (car magit-buffer-diff-files)) + (base (and base (file-directory-p base) base))) + (magit-insert-section (tracked nil t) + (magit-insert-heading "Ignored files:") + (magit-insert-files files base) + (insert ?\n))))) + +(magit-define-section-jumper magit-jump-to-skip-worktree "Skip-worktree files" skip-worktree) + +(defun magit-insert-skip-worktree-files () + "Insert a tree of skip-worktree files. + +If the first element of `magit-buffer-diff-files' is a +directory, then limit the list to files below that. The value +of that variable can be set using \"D -- DIRECTORY RET g\"." + (when-let ((files (magit-skip-worktree-files))) + (let* ((base (car magit-buffer-diff-files)) + (base (and base (file-directory-p base) base))) + (magit-insert-section (skip-worktree nil t) + (magit-insert-heading "Skip-worktree files:") + (magit-insert-files files base) + (insert ?\n))))) + +(magit-define-section-jumper magit-jump-to-assume-unchanged "Assume-unchanged files" assume-unchanged) + +(defun magit-insert-assume-unchanged-files () + "Insert a tree of files that are assumed to be unchanged. + +If the first element of `magit-buffer-diff-files' is a +directory, then limit the list to files below that. The value +of that variable can be set using \"D -- DIRECTORY RET g\"." + (when-let ((files (magit-assume-unchanged-files))) + (let* ((base (car magit-buffer-diff-files)) + (base (and base (file-directory-p base) base))) + (magit-insert-section (assume-unchanged nil t) + (magit-insert-heading "Assume-unchanged files:") + (magit-insert-files files base) + (insert ?\n))))) + +(defun magit-insert-files (files directory) + (while (and files (string-prefix-p (or directory "") (car files))) + (let ((dir (file-name-directory (car files)))) + (if (equal dir directory) + (let ((file (pop files))) + (magit-insert-section (file file) + (insert (propertize file 'font-lock-face 'magit-filename) ?\n))) + (magit-insert-section (file dir t) + (insert (propertize dir 'file 'magit-filename) ?\n) + (magit-insert-heading) + (setq files (magit-insert-files files dir)))))) + files) + +;;; _ +(provide 'magit-status) +;;; magit-status.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-submodule.el b/code/elpa/magit-20220821.1819/magit-submodule.el new file mode 100644 index 0000000..7abaa84 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-submodule.el @@ -0,0 +1,723 @@ +;;; magit-submodule.el --- Submodule support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Code: + +(require 'magit) + +(defvar x-stretch-cursor) + +;;; Options + +(defcustom magit-module-sections-hook + '(magit-insert-modules-overview + magit-insert-modules-unpulled-from-upstream + magit-insert-modules-unpulled-from-pushremote + magit-insert-modules-unpushed-to-upstream + magit-insert-modules-unpushed-to-pushremote) + "Hook run by `magit-insert-modules'. + +That function isn't part of `magit-status-sections-hook's default +value, so you have to add it yourself for this hook to have any +effect." + :package-version '(magit . "2.11.0") + :group 'magit-status + :type 'hook) + +(defcustom magit-module-sections-nested t + "Whether `magit-insert-modules' wraps inserted sections. + +If this is non-nil, then only a single top-level section +is inserted. If it is nil, then all sections listed in +`magit-module-sections-hook' become top-level sections." + :package-version '(magit . "2.11.0") + :group 'magit-status + :type 'boolean) + +(defcustom magit-submodule-list-mode-hook '(hl-line-mode) + "Hook run after entering Magit-Submodule-List mode." + :package-version '(magit . "2.9.0") + :group 'magit-repolist + :type 'hook + :get 'magit-hook-custom-get + :options '(hl-line-mode)) + +(defcustom magit-submodule-list-columns + '(("Path" 25 magit-modulelist-column-path nil) + ("Version" 25 magit-repolist-column-version + ((:sort magit-repolist-version<))) + ("Branch" 20 magit-repolist-column-branch nil) + ("BU" 3 magit-repolist-column-unpushed-to-upstream + ((:right-align t) + (:sort <))) + ("BP" 3 magit-repolist-column-unpushed-to-pushremote + ((:right-align t) + (:sort <))) + ("B" 3 magit-repolist-column-branches + ((:right-align t) + (:sort <))) + ("S" 3 magit-repolist-column-stashes + ((:right-align t) + (:sort <)))) + "List of columns displayed by `magit-list-submodules'. + +Each element has the form (HEADER WIDTH FORMAT PROPS). + +HEADER is the string displayed in the header. WIDTH is the width +of the column. FORMAT is a function that is called with one +argument, the repository identification (usually its basename), +and with `default-directory' bound to the toplevel of its working +tree. It has to return a string to be inserted or nil. PROPS is +an alist that supports the keys `:right-align', `:pad-right' and +`:sort'. + +The `:sort' function has a weird interface described in the +docstring of `tabulated-list--get-sort'. Alternatively `<' and +`magit-repolist-version<' can be used as those functions are +automatically replaced with functions that satisfy the interface. +Set `:sort' to nil to inhibit sorting; if unspecifed, then the +column is sortable using the default sorter. + +You may wish to display a range of numeric columns using just one +character per column and without any padding between columns, in +which case you should use an appropriat HEADER, set WIDTH to 1, +and set `:pad-right' to 0. \"+\" is substituted for numbers higher +than 9." + :package-version '(magit . "2.8.0") + :group 'magit-repolist + :type `(repeat (list :tag "Column" + (string :tag "Header Label") + (integer :tag "Column Width") + (function :tag "Inserter Function") + (repeat :tag "Properties" + (list (choice :tag "Property" + (const :right-align) + (const :pad-right) + (const :sort) + (symbol)) + (sexp :tag "Value")))))) + +(defcustom magit-submodule-list-sort-key '("Path" . nil) + "Initial sort key for buffer created by `magit-list-submodules'. +If nil, no additional sorting is performed. Otherwise, this +should be a cons cell (NAME . FLIP). NAME is a string matching +one of the column names in `magit-submodule-list-columns'. FLIP, +if non-nil, means to invert the resulting sort." + :package-version '(magit . "3.2.0") + :group 'magit-repolist + :type '(choice (const nil) + (cons (string :tag "Column name") + (boolean :tag "Flip order")))) + +(defvar magit-submodule-list-format-path-functions nil) + +(defcustom magit-submodule-remove-trash-gitdirs nil + "Whether `magit-submodule-remove' offers to trash module gitdirs. + +If this is nil, then that command does not offer to do so unless +a prefix argument is used. When this is t, then it does offer to +do so even without a prefix argument. + +In both cases the action still has to be confirmed unless that is +disabled using the option `magit-no-confirm'. Doing the latter +and also setting this variable to t will lead to tears." + :package-version '(magit . "2.90.0") + :group 'magit-commands + :type 'boolean) + +;;; Popup + +;;;###autoload (autoload 'magit-submodule "magit-submodule" nil t) +(transient-define-prefix magit-submodule () + "Act on a submodule." + :man-page "git-submodule" + ["Arguments" + ("-f" "Force" ("-f" "--force")) + ("-r" "Recursive" "--recursive") + ("-N" "Do not fetch" ("-N" "--no-fetch")) + ("-C" "Checkout tip" "--checkout") + ("-R" "Rebase onto tip" "--rebase") + ("-M" "Merge tip" "--merge") + ("-U" "Use upstream tip" "--remote")] + ["One module actions" + ("a" magit-submodule-add) + ("r" magit-submodule-register) + ("p" magit-submodule-populate) + ("u" magit-submodule-update) + ("s" magit-submodule-synchronize) + ("d" magit-submodule-unpopulate) + ("k" "Remove" magit-submodule-remove)] + ["All modules actions" + ("l" "List all modules" magit-list-submodules) + ("f" "Fetch all modules" magit-fetch-modules)]) + +(defun magit-submodule-arguments (&rest filters) + (--filter (and (member it filters) it) + (transient-args 'magit-submodule))) + +(defclass magit--git-submodule-suffix (transient-suffix) + ()) + +(cl-defmethod transient-format-description ((obj magit--git-submodule-suffix)) + (let ((value (delq nil (mapcar #'transient-infix-value transient--suffixes)))) + (replace-regexp-in-string + "\\[--[^]]+\\]" + (lambda (match) + (format (propertize "[%s]" 'face 'transient-inactive-argument) + (mapconcat (lambda (arg) + (propertize arg 'face + (if (member arg value) + 'transient-argument + 'transient-inactive-argument))) + (save-match-data + (split-string (substring match 1 -1) "|")) + (propertize "|" 'face 'transient-inactive-argument)))) + (cl-call-next-method obj)))) + +;;;###autoload (autoload 'magit-submodule-add "magit-submodule" nil t) +(transient-define-suffix magit-submodule-add (url &optional path name args) + "Add the repository at URL as a module. + +Optional PATH is the path to the module relative to the root of +the superproject. If it is nil, then the path is determined +based on the URL. Optional NAME is the name of the module. If +it is nil, then PATH also becomes the name." + :class 'magit--git-submodule-suffix + :description "Add git submodule add [--force]" + (interactive + (magit-with-toplevel + (let* ((url (magit-read-string-ns "Add submodule (remote url)")) + (path (let ((read-file-name-function + (if (or (eq read-file-name-function 'ido-read-file-name) + (advice-function-member-p + 'ido-read-file-name + read-file-name-function)) + ;; The Ido variant doesn't work properly here. + #'read-file-name-default + read-file-name-function))) + (directory-file-name + (file-relative-name + (read-directory-name + "Add submodules at path: " nil nil nil + (and (string-match "\\([^./]+\\)\\(\\.git\\)?$" url) + (match-string 1 url)))))))) + (list url + (directory-file-name path) + (magit-submodule-read-name-for-path path) + (magit-submodule-arguments "--force"))))) + (magit-submodule-add-1 url path name args)) + +(defun magit-submodule-add-1 (url &optional path name args) + (magit-with-toplevel + (magit-submodule--maybe-reuse-gitdir name path) + (magit-run-git-async "submodule" "add" + (and name (list "--name" name)) + args "--" url path) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (if (> (process-exit-status process) 0) + (magit-process-sentinel process event) + (process-put process 'inhibit-refresh t) + (magit-process-sentinel process event) + (when (magit-git-version>= "2.12.0") + (magit-call-git "submodule" "absorbgitdirs" path)) + (magit-refresh))))))) + +;;;###autoload +(defun magit-submodule-read-name-for-path (path &optional prefer-short) + (let* ((path (directory-file-name (file-relative-name path))) + (name (file-name-nondirectory path))) + (push (if prefer-short path name) minibuffer-history) + (magit-read-string-ns + "Submodule name" nil (cons 'minibuffer-history 2) + (or (--keep (pcase-let ((`(,var ,val) (split-string it "="))) + (and (equal val path) + (cadr (split-string var "\\.")))) + (magit-git-lines "config" "--list" "-f" ".gitmodules")) + (if prefer-short name path))))) + +;;;###autoload (autoload 'magit-submodule-register "magit-submodule" nil t) +(transient-define-suffix magit-submodule-register (modules) + "Register MODULES. + +With a prefix argument act on all suitable modules. Otherwise, +if the region selects modules, then act on those. Otherwise, if +there is a module at point, then act on that. Otherwise read a +single module from the user." + ;; This command and the underlying "git submodule init" do NOT + ;; "initialize" modules. They merely "register" modules in the + ;; super-projects $GIT_DIR/config file, the purpose of which is to + ;; allow users to change such values before actually initializing + ;; the modules. + :description "Register git submodule init" + (interactive + (list (magit-module-confirm "Register" 'magit-module-no-worktree-p))) + (magit-with-toplevel + (magit-run-git-async "submodule" "init" "--" modules))) + +;;;###autoload (autoload 'magit-submodule-populate "magit-submodule" nil t) +(transient-define-suffix magit-submodule-populate (modules) + "Create MODULES working directories, checking out the recorded commits. + +With a prefix argument act on all suitable modules. Otherwise, +if the region selects modules, then act on those. Otherwise, if +there is a module at point, then act on that. Otherwise read a +single module from the user." + ;; This is the command that actually "initializes" modules. + ;; A module is initialized when it has a working directory, + ;; a gitlink, and a .gitmodules entry. + :description "Populate git submodule update --init" + (interactive + (list (magit-module-confirm "Populate" 'magit-module-no-worktree-p))) + (magit-with-toplevel + (magit-run-git-async "submodule" "update" "--init" "--" modules))) + +;;;###autoload (autoload 'magit-submodule-update "magit-submodule" nil t) +(transient-define-suffix magit-submodule-update (modules args) + "Update MODULES by checking out the recorded commits. + +With a prefix argument act on all suitable modules. Otherwise, +if the region selects modules, then act on those. Otherwise, if +there is a module at point, then act on that. Otherwise read a +single module from the user." + ;; Unlike `git-submodule's `update' command ours can only update + ;; "initialized" modules by checking out other commits but not + ;; "initialize" modules by creating the working directories. + ;; To do the latter we provide the "setup" command. + :class 'magit--git-submodule-suffix + :description "Update git submodule update [--force] [--no-fetch] + [--remote] [--recursive] [--checkout|--rebase|--merge]" + (interactive + (list (magit-module-confirm "Update" 'magit-module-worktree-p) + (magit-submodule-arguments + "--force" "--remote" "--recursive" "--checkout" "--rebase" "--merge" + "--no-fetch"))) + (magit-with-toplevel + (magit-run-git-async "submodule" "update" args "--" modules))) + +;;;###autoload (autoload 'magit-submodule-synchronize "magit-submodule" nil t) +(transient-define-suffix magit-submodule-synchronize (modules args) + "Synchronize url configuration of MODULES. + +With a prefix argument act on all suitable modules. Otherwise, +if the region selects modules, then act on those. Otherwise, if +there is a module at point, then act on that. Otherwise read a +single module from the user." + :class 'magit--git-submodule-suffix + :description "Synchronize git submodule sync [--recursive]" + (interactive + (list (magit-module-confirm "Synchronize" 'magit-module-worktree-p) + (magit-submodule-arguments "--recursive"))) + (magit-with-toplevel + (magit-run-git-async "submodule" "sync" args "--" modules))) + +;;;###autoload (autoload 'magit-submodule-unpopulate "magit-submodule" nil t) +(transient-define-suffix magit-submodule-unpopulate (modules args) + "Remove working directories of MODULES. + +With a prefix argument act on all suitable modules. Otherwise, +if the region selects modules, then act on those. Otherwise, if +there is a module at point, then act on that. Otherwise read a +single module from the user." + ;; Even though a package is "uninitialized" (it has no worktree) + ;; the super-projects $GIT_DIR/config may never-the-less set the + ;; module's url. This may happen if you `deinit' and then `init' + ;; to register (NOT initialize). Because the purpose of `deinit' + ;; is to remove the working directory AND to remove the url, this + ;; command does not limit itself to modules that have no working + ;; directory. + :class 'magit--git-submodule-suffix + :description "Unpopulate git submodule deinit [--force]" + (interactive + (list (magit-module-confirm "Unpopulate") + (magit-submodule-arguments "--force"))) + (magit-with-toplevel + (magit-run-git-async "submodule" "deinit" args "--" modules))) + +;;;###autoload +(defun magit-submodule-remove (modules args trash-gitdirs) + "Unregister MODULES and remove their working directories. + +For safety reasons, do not remove the gitdirs and if a module has +uncommitted changes, then do not remove it at all. If a module's +gitdir is located inside the working directory, then move it into +the gitdir of the superproject first. + +With the \"--force\" argument offer to remove dirty working +directories and with a prefix argument offer to delete gitdirs. +Both actions are very dangerous and have to be confirmed. There +are additional safety precautions in place, so you might be able +to recover from making a mistake here, but don't count on it." + (interactive + (list (if-let ((modules (magit-region-values 'magit-module-section t))) + (magit-confirm 'remove-modules nil "Remove %i modules" nil modules) + (list (magit-read-module-path "Remove module"))) + (magit-submodule-arguments "--force") + current-prefix-arg)) + (when (magit-git-version< "2.12.0") + (error "This command requires Git v2.12.0")) + (when magit-submodule-remove-trash-gitdirs + (setq trash-gitdirs t)) + (magit-with-toplevel + (when-let + ((modified + (-filter (lambda (module) + (let ((default-directory (file-name-as-directory + (expand-file-name module)))) + (and (cddr (directory-files default-directory)) + (magit-anything-modified-p)))) + modules))) + (if (member "--force" args) + (if (magit-confirm 'remove-dirty-modules + "Remove dirty module %s" + "Remove %i dirty modules" + t modified) + (dolist (module modified) + (let ((default-directory (file-name-as-directory + (expand-file-name module)))) + (magit-git "stash" "push" + "-m" "backup before removal of this module"))) + (setq modules (cl-set-difference modules modified :test #'equal))) + (if (cdr modified) + (message "Omitting %s modules with uncommitted changes: %s" + (length modified) + (mapconcat #'identity modified ", ")) + (message "Omitting module %s, it has uncommitted changes" + (car modified))) + (setq modules (cl-set-difference modules modified :test #'equal)))) + (when modules + (let ((alist + (and trash-gitdirs + (--map (split-string it "\0") + (magit-git-lines "submodule" "foreach" "-q" + "printf \"$sm_path\\0$name\n\""))))) + (magit-git "submodule" "absorbgitdirs" "--" modules) + (magit-git "submodule" "deinit" args "--" modules) + (magit-git "rm" args "--" modules) + (when (and trash-gitdirs + (magit-confirm 'trash-module-gitdirs + "Trash gitdir of module %s" + "Trash gitdirs of %i modules" + t modules)) + (dolist (module modules) + (if-let ((name (cadr (assoc module alist)))) + ;; Disregard if `magit-delete-by-moving-to-trash' + ;; is nil. Not doing so would be too dangerous. + (delete-directory (magit-git-dir + (convert-standard-filename + (concat "modules/" name))) + t t) + (error "BUG: Weird module name and/or path for %s" module))))) + (magit-refresh)))) + +;;; Sections + +;;;###autoload +(defun magit-insert-modules () + "Insert submodule sections. +Hook `magit-module-sections-hook' controls which module sections +are inserted, and option `magit-module-sections-nested' controls +whether they are wrapped in an additional section." + (when-let ((modules (magit-list-module-paths))) + (if magit-module-sections-nested + (magit-insert-section (modules nil t) + (magit-insert-heading + (format "%s (%s)" + (propertize "Modules" + 'font-lock-face 'magit-section-heading) + (length modules))) + (magit-insert-section-body + (magit--insert-modules))) + (magit--insert-modules)))) + +(defun magit--insert-modules (&optional _section) + (magit-run-section-hook 'magit-module-sections-hook)) + +;;;###autoload +(defun magit-insert-modules-overview () + "Insert sections for all modules. +For each section insert the path and the output of `git describe --tags', +or, failing that, the abbreviated HEAD commit hash." + (when-let ((modules (magit-list-module-paths))) + (magit-insert-section (modules nil t) + (magit-insert-heading + (format "%s (%s)" + (propertize "Modules overview" + 'font-lock-face 'magit-section-heading) + (length modules))) + (magit-insert-section-body + (magit--insert-modules-overview))))) + +(defvar magit-modules-overview-align-numbers t) + +(defun magit--insert-modules-overview (&optional _section) + (magit-with-toplevel + (let* ((modules (magit-list-module-paths)) + (path-format (format "%%-%is " + (min (apply #'max (mapcar #'length modules)) + (/ (window-width) 2)))) + (branch-format (format "%%-%is " (min 25 (/ (window-width) 3))))) + (dolist (module modules) + (let ((default-directory + (expand-file-name (file-name-as-directory module)))) + (magit-insert-section (magit-module-section module t) + (insert (propertize (format path-format module) + 'font-lock-face 'magit-diff-file-heading)) + (if (not (file-exists-p ".git")) + (insert "(unpopulated)") + (insert (format + branch-format + (--if-let (magit-get-current-branch) + (propertize it 'font-lock-face 'magit-branch-local) + (propertize "(detached)" 'font-lock-face 'warning)))) + (--if-let (magit-git-string "describe" "--tags") + (progn (when (and magit-modules-overview-align-numbers + (string-match-p "\\`[0-9]" it)) + (insert ?\s)) + (insert (propertize it 'font-lock-face 'magit-tag))) + (--when-let (magit-rev-format "%h") + (insert (propertize it 'font-lock-face 'magit-hash))))) + (insert ?\n)))))) + (insert ?\n)) + +(defvar magit-modules-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [remap magit-visit-thing] + #'magit-list-submodules "List %t") + map) + "Keymap for `modules' sections.") + +(defvar magit-module-section-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-j") #'magit-submodule-visit) + (define-key map [C-return] #'magit-submodule-visit) + (magit-menu-set map [magit-visit-thing] + #'magit-submodule-visit "Visit %s") + (magit-menu-set map [magit-stage-file] + #'magit-stage "Stage %T" + '(:visible (eq (magit-diff-type) 'unstaged))) + (magit-menu-set map [magit-unstage-file] + #'magit-unstage "Unstage %T" + '(:visible (eq (magit-diff-type) 'staged))) + (define-key-after map [separator-magit-submodule] menu-bar-separator) + (magit-menu-set map [magit-submodule] #'magit-submodule "Module commands...") + map) + "Keymap for `module' sections.") + +(defun magit-submodule-visit (module &optional other-window) + "Visit MODULE by calling `magit-status' on it. +Offer to initialize MODULE if it's not checked out yet. +With a prefix argument, visit in another window." + (interactive (list (or (magit-section-value-if 'module) + (magit-read-module-path "Visit module")) + current-prefix-arg)) + (magit-with-toplevel + (let ((path (expand-file-name module))) + (cond + ((file-exists-p (expand-file-name ".git" module)) + (magit-diff-visit-directory path other-window)) + ((y-or-n-p (format "Initialize submodule '%s' first?" module)) + (magit-run-git-async "submodule" "update" "--init" "--" module) + (set-process-sentinel + magit-this-process + (lambda (process event) + (let ((magit-process-raise-error t)) + (magit-process-sentinel process event)) + (when (and (eq (process-status process) 'exit) + (= (process-exit-status process) 0)) + (magit-diff-visit-directory path other-window))))) + ((file-exists-p path) + (dired-jump other-window (concat path "/."))))))) + +;;;###autoload +(defun magit-insert-modules-unpulled-from-upstream () + "Insert sections for modules that haven't been pulled from the upstream. +These sections can be expanded to show the respective commits." + (magit--insert-modules-logs "Modules unpulled from @{upstream}" + 'modules-unpulled-from-upstream + "HEAD..@{upstream}")) + +;;;###autoload +(defun magit-insert-modules-unpulled-from-pushremote () + "Insert sections for modules that haven't been pulled from the push-remote. +These sections can be expanded to show the respective commits." + (magit--insert-modules-logs "Modules unpulled from @{push}" + 'modules-unpulled-from-pushremote + "HEAD..@{push}")) + +;;;###autoload +(defun magit-insert-modules-unpushed-to-upstream () + "Insert sections for modules that haven't been pushed to the upstream. +These sections can be expanded to show the respective commits." + (magit--insert-modules-logs "Modules unmerged into @{upstream}" + 'modules-unpushed-to-upstream + "@{upstream}..HEAD")) + +;;;###autoload +(defun magit-insert-modules-unpushed-to-pushremote () + "Insert sections for modules that haven't been pushed to the push-remote. +These sections can be expanded to show the respective commits." + (magit--insert-modules-logs "Modules unpushed to @{push}" + 'modules-unpushed-to-pushremote + "@{push}..HEAD")) + +(defun magit--insert-modules-logs (heading type range) + "For internal use, don't add to a hook." + (unless (magit-ignore-submodules-p) + (when-let ((modules (magit-list-module-paths))) + (magit-insert-section section ((eval type) nil t) + (string-match "\\`\\(.+\\) \\([^ ]+\\)\\'" heading) + (magit-insert-heading + (propertize (match-string 1 heading) + 'font-lock-face 'magit-section-heading) + " " + (propertize (match-string 2 heading) + 'font-lock-face 'magit-branch-remote) + ":") + (magit-with-toplevel + (dolist (module modules) + (when (magit-module-worktree-p module) + (let ((default-directory + (expand-file-name (file-name-as-directory module)))) + (when (magit-file-accessible-directory-p default-directory) + (magit-insert-section sec (magit-module-section module t) + (magit-insert-heading + (propertize module + 'font-lock-face 'magit-diff-file-heading) + ":") + (oset sec range range) + (magit-git-wash + (apply-partially #'magit-log-wash-log 'module) + "-c" "push.default=current" "log" "--oneline" range) + (when (> (point) + (oref sec content)) + (delete-char -1)))))))) + (if (> (point) + (oref section content)) + (insert ?\n) + (magit-cancel-section)))))) + +;;; List + +;;;###autoload +(defun magit-list-submodules () + "Display a list of the current repository's submodules." + (interactive) + (magit-submodule-list-setup magit-submodule-list-columns)) + +(defvar magit-submodule-list-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map magit-repolist-mode-map) + map) + "Local keymap for Magit-Submodule-List mode buffers.") + +(define-derived-mode magit-submodule-list-mode tabulated-list-mode "Modules" + "Major mode for browsing a list of Git submodules." + :group 'magit-repolist-mode + (setq-local x-stretch-cursor nil) + (setq tabulated-list-padding 0) + (add-hook 'tabulated-list-revert-hook #'magit-submodule-list-refresh nil t) + (setq imenu-prev-index-position-function + #'magit-repolist--imenu-prev-index-position) + (setq imenu-extract-index-name-function #'tabulated-list-get-id)) + +(defvar-local magit-submodule-list-predicate nil) + +(defun magit-submodule-list-setup (columns &optional predicate) + (magit-display-buffer + (or (magit-get-mode-buffer 'magit-submodule-list-mode) + (magit-generate-new-buffer 'magit-submodule-list-mode))) + (magit-submodule-list-mode) + (setq-local magit-repolist-columns columns) + (setq-local magit-repolist-sort-key magit-submodule-list-sort-key) + (setq-local magit-submodule-list-predicate predicate) + (magit-repolist-setup-1) + (magit-submodule-list-refresh)) + +(defun magit-submodule-list-refresh () + (setq tabulated-list-entries + (-keep (lambda (module) + (let ((default-directory + (expand-file-name (file-name-as-directory module)))) + (and (file-exists-p ".git") + (or (not magit-submodule-list-predicate) + (funcall magit-submodule-list-predicate module)) + (list module + (vconcat + (mapcar (pcase-lambda (`(,title ,width ,fn ,props)) + (or (funcall fn `((:path ,module) + (:title ,title) + (:width ,width) + ,@props)) + "")) + magit-repolist-columns)))))) + (magit-list-module-paths))) + (message "Listing submodules...") + (tabulated-list-init-header) + (tabulated-list-print t) + (message "Listing submodules...done")) + +(defun magit-modulelist-column-path (spec) + "Insert the relative path of the submodule." + (let ((path (cadr (assq :path spec)))) + (or (run-hook-with-args-until-success + 'magit-submodule-list-format-path-functions path) + path))) + +;;; Utilities + +(defun magit-submodule--maybe-reuse-gitdir (name path) + (let ((gitdir + (magit-git-dir (convert-standard-filename (concat "modules/" name))))) + (when (and (file-exists-p gitdir) + (not (file-exists-p path))) + (pcase (read-char-choice + (concat + gitdir " already exists.\n" + "Type [u] to use the existing gitdir and create the working tree\n" + " [r] to rename the existing gitdir and clone again\n" + " [t] to trash the existing gitdir and clone again\n" + " [C-g] to abort ") + '(?u ?r ?t)) + (?u (magit-submodule--restore-worktree (expand-file-name path) gitdir)) + (?r (rename-file gitdir (concat gitdir "-" + (format-time-string "%F-%T")))) + (?t (delete-directory gitdir t t)))))) + +(defun magit-submodule--restore-worktree (worktree gitdir) + (make-directory worktree t) + (with-temp-file (expand-file-name ".git" worktree) + (insert "gitdir: " (file-relative-name gitdir worktree) "\n")) + (let ((default-directory worktree)) + (magit-call-git "reset" "--hard" "HEAD" "--"))) + +;;; _ +(provide 'magit-submodule) +;;; magit-submodule.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-subtree.el b/code/elpa/magit-20220821.1819/magit-subtree.el new file mode 100644 index 0000000..fec2aff --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-subtree.el @@ -0,0 +1,181 @@ +;;; magit-subtree.el --- Subtree support for Magit -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-subtree "magit-subtree" nil t) +(transient-define-prefix magit-subtree () + "Import or export subtrees." + :man-page "git-subtree" + ["Actions" + ("i" "Import" magit-subtree-import) + ("e" "Export" magit-subtree-export)]) + +;;;###autoload (autoload 'magit-subtree-import "magit-subtree" nil t) +(transient-define-prefix magit-subtree-import () + "Import subtrees." + :man-page "git-subtree" + ["Arguments" + (magit-subtree:--prefix) + (magit-subtree:--message) + ("-s" "Squash" "--squash")] + ["Actions" + [("a" "Add" magit-subtree-add) + ("c" "Add commit" magit-subtree-add-commit)] + [("m" "Merge" magit-subtree-merge) + ("f" "Pull" magit-subtree-pull)]]) + +;;;###autoload (autoload 'magit-subtree-export "magit-subtree" nil t) +(transient-define-prefix magit-subtree-export () + "Export subtrees." + :man-page "git-subtree" + ["Arguments" + (magit-subtree:--prefix) + (magit-subtree:--annotate) + (magit-subtree:--branch) + (magit-subtree:--onto) + ("-i" "Ignore joins" "--ignore-joins") + ("-j" "Rejoin" "--rejoin")] + ["Actions" + ("p" "Push" magit-subtree-push) + ("s" "Split" magit-subtree-split)]) + +(transient-define-argument magit-subtree:--prefix () + :description "Prefix" + :class 'transient-option + :shortarg "-P" + :argument "--prefix=" + :reader #'magit-subtree-read-prefix) + +(defun magit-subtree-read-prefix (prompt &optional default _history) + (let* ((insert-default-directory nil) + (topdir (magit-toplevel)) + (prefix (read-directory-name (concat prompt ": ") topdir default))) + (if (file-name-absolute-p prefix) + ;; At least `ido-mode's variant is not compatible. + (if (string-prefix-p topdir prefix) + (file-relative-name prefix topdir) + (user-error "%s isn't inside the repository at %s" prefix topdir)) + prefix))) + +(transient-define-argument magit-subtree:--message () + :description "Message" + :class 'transient-option + :shortarg "-m" + :argument "--message=") + +(transient-define-argument magit-subtree:--annotate () + :description "Annotate" + :class 'transient-option + :key "-a" + :argument "--annotate=") + +(transient-define-argument magit-subtree:--branch () + :description "Branch" + :class 'transient-option + :shortarg "-b" + :argument "--branch=") + +(transient-define-argument magit-subtree:--onto () + :description "Onto" + :class 'transient-option + :key "-o" + :argument "--onto=" + :reader #'magit-transient-read-revision) + +(defun magit-subtree-prefix (transient prompt) + (--if-let (--first (string-prefix-p "--prefix=" it) + (transient-args transient)) + (substring it 9) + (magit-subtree-read-prefix prompt))) + +(defun magit-subtree-arguments (transient) + (--remove (string-prefix-p "--prefix=" it) + (transient-args transient))) + +(defun magit-git-subtree (subcmd prefix &rest args) + (magit-run-git-async "subtree" subcmd (concat "--prefix=" prefix) args)) + +;;;###autoload +(defun magit-subtree-add (prefix repository ref args) + "Add REF from REPOSITORY as a new subtree at PREFIX." + (interactive + (cons (magit-subtree-prefix 'magit-subtree-import "Add subtree") + (let ((remote (magit-read-remote-or-url "From repository"))) + (list remote + (magit-read-refspec "Ref" remote) + (magit-subtree-arguments 'magit-subtree-import))))) + (magit-git-subtree "add" prefix args repository ref)) + +;;;###autoload +(defun magit-subtree-add-commit (prefix commit args) + "Add COMMIT as a new subtree at PREFIX." + (interactive + (list (magit-subtree-prefix 'magit-subtree-import "Add subtree") + (magit-read-string-ns "Commit") + (magit-subtree-arguments 'magit-subtree-import))) + (magit-git-subtree "add" prefix args commit)) + +;;;###autoload +(defun magit-subtree-merge (prefix commit args) + "Merge COMMIT into the PREFIX subtree." + (interactive + (list (magit-subtree-prefix 'magit-subtree-import "Merge into subtree") + (magit-read-string-ns "Commit") + (magit-subtree-arguments 'magit-subtree-import))) + (magit-git-subtree "merge" prefix args commit)) + +;;;###autoload +(defun magit-subtree-pull (prefix repository ref args) + "Pull REF from REPOSITORY into the PREFIX subtree." + (interactive + (cons (magit-subtree-prefix 'magit-subtree-import "Pull into subtree") + (let ((remote (magit-read-remote-or-url "From repository"))) + (list remote + (magit-read-refspec "Ref" remote) + (magit-subtree-arguments 'magit-subtree-import))))) + (magit-git-subtree "pull" prefix args repository ref)) + +;;;###autoload +(defun magit-subtree-push (prefix repository ref args) + "Extract the history of the subtree PREFIX and push it to REF on REPOSITORY." + (interactive (list (magit-subtree-prefix 'magit-subtree-export "Push subtree") + (magit-read-remote-or-url "To repository") + (magit-read-string-ns "To reference") + (magit-subtree-arguments 'magit-subtree-export))) + (magit-git-subtree "push" prefix args repository ref)) + +;;;###autoload +(defun magit-subtree-split (prefix commit args) + "Extract the history of the subtree PREFIX." + (interactive (list (magit-subtree-prefix 'magit-subtree-export "Split subtree") + (magit-read-string-ns "Commit") + (magit-subtree-arguments 'magit-subtree-export))) + (magit-git-subtree "split" prefix args commit)) + +;;; _ +(provide 'magit-subtree) +;;; magit-subtree.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-tag.el b/code/elpa/magit-20220821.1819/magit-tag.el new file mode 100644 index 0000000..f127f13 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-tag.el @@ -0,0 +1,245 @@ +;;; magit-tag.el --- Tag functionality -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements tag commands. + +;;; Code: + +(require 'magit) + +;; For `magit-tag-delete'. +(defvar helm-comp-read-use-marked) + +;;; Commands + +;;;###autoload (autoload 'magit-tag "magit" nil t) +(transient-define-prefix magit-tag () + "Create or delete a tag." + :man-page "git-tag" + ["Arguments" + ("-f" "Force" ("-f" "--force")) + ("-a" "Annotate" ("-a" "--annotate")) + ("-s" "Sign" ("-s" "--sign")) + (magit-tag:--local-user)] + [["Create" + ("t" "tag" magit-tag-create) + ("r" "release" magit-tag-release)] + ["Do" + ("k" "delete" magit-tag-delete) + ("p" "prune" magit-tag-prune)]]) + +(defun magit-tag-arguments () + (transient-args 'magit-tag)) + +(transient-define-argument magit-tag:--local-user () + :description "Sign as" + :class 'transient-option + :shortarg "-u" + :argument "--local-user=" + :reader #'magit-read-gpg-signing-key + :history-key 'magit:--gpg-sign) + +;;;###autoload +(defun magit-tag-create (name rev &optional args) + "Create a new tag with the given NAME at REV. +With a prefix argument annotate the tag. +\n(git tag [--annotate] NAME REV)" + (interactive (list (magit-read-tag "Tag name") + (magit-read-branch-or-commit "Place tag on") + (let ((args (magit-tag-arguments))) + (when current-prefix-arg + (cl-pushnew "--annotate" args)) + args))) + (magit-run-git-with-editor "tag" args name rev)) + +;;;###autoload +(defun magit-tag-delete (tags) + "Delete one or more tags. +If the region marks multiple tags (and nothing else), then offer +to delete those, otherwise prompt for a single tag to be deleted, +defaulting to the tag at point. +\n(git tag -d TAGS)" + (interactive (list (--if-let (magit-region-values 'tag) + (magit-confirm t nil "Delete %i tags" nil it) + (let ((helm-comp-read-use-marked t)) + (magit-read-tag "Delete tag" t))))) + (magit-run-git "tag" "-d" tags)) + +;;;###autoload +(defun magit-tag-prune (tags remote-tags remote) + "Offer to delete tags missing locally from REMOTE, and vice versa." + (interactive + (let* ((remote (magit-read-remote "Prune tags using remote")) + (tags (magit-list-tags)) + (rtags (prog2 (message "Determining remote tags...") + (magit-remote-list-tags remote) + (message "Determining remote tags...done"))) + (ltags (-difference tags rtags)) + (rtags (-difference rtags tags))) + (unless (or ltags rtags) + (message "Same tags exist locally and remotely")) + (unless (magit-confirm t + "Delete %s locally" + "Delete %i tags locally" + 'noabort ltags) + (setq ltags nil)) + (unless (magit-confirm t + "Delete %s from remote" + "Delete %i tags from remote" + 'noabort rtags) + (setq rtags nil)) + (list ltags rtags remote))) + (when tags + (magit-call-git "tag" "-d" tags)) + (when remote-tags + (magit-run-git-async "push" remote (--map (concat ":" it) remote-tags)))) + +(defvar magit-tag-version-regexp-alist + '(("^[-._+ ]?snapshot\\.?$" . -4) + ("^[-._+]$" . -4) + ("^[-._+ ]?\\(cvs\\|git\\|bzr\\|svn\\|hg\\|darcs\\)\\.?$" . -4) + ("^[-._+ ]?unknown\\.?$" . -4) + ("^[-._+ ]?alpha\\.?$" . -3) + ("^[-._+ ]?beta\\.?$" . -2) + ("^[-._+ ]?\\(pre\\|rc\\)\\.?$" . -1)) + "Overrides `version-regexp-alist' for `magit-tag-release'. +See also `magit-release-tag-regexp'.") + +(defvar magit-release-tag-regexp "\\`\ +\\(?1:\\(?:v\\(?:ersion\\)?\\|r\\(?:elease\\)?\\)?[-_]?\\)?\ +\\(?2:[0-9]+\\(?:\\.[0-9]+\\)*\ +\\(?:-[a-zA-Z0-9-]+\\(?:\\.[a-zA-Z0-9-]+\\)*\\)?\\)\\'" + "Regexp used by `magit-tag-release' to parse release tags. + +The first submatch must match the prefix, if any. The second +submatch must match the version string. + +If this matches versions that are not dot separated numbers, +then `magit-tag-version-regexp-alist' has to contain entries +for the separators allowed here.") + +(defvar magit-release-commit-regexp "\\`Release version \\(.+\\)\\'" + "Regexp used by `magit-tag-release' to parse release commit messages. +The first submatch must match the version string.") + +;;;###autoload +(defun magit-tag-release (tag msg &optional args) + "Create a release tag for `HEAD'. + +Assume that release tags match `magit-release-tag-regexp'. + +If `HEAD's message matches `magit-release-commit-regexp', then +base the tag on the version string specified by that. Otherwise +prompt for the name of the new tag using the highest existing +tag as initial input and leaving it to the user to increment the +desired part of the version string. + +If `--annotate' is enabled, then prompt for the message of the +new tag. Base the proposed tag message on the message of the +highest tag, provided that that contains the corresponding +version string and substituting the new version string for that. +Otherwise propose something like \"Foo-Bar 1.2.3\", given, for +example, a TAG \"v1.2.3\" and a repository located at something +like \"/path/to/foo-bar\"." + (interactive + (save-match-data + (pcase-let* + ((`(,pver ,ptag ,pmsg) (car (magit--list-releases))) + (msg (magit-rev-format "%s")) + (ver (and (string-match magit-release-commit-regexp msg) + (match-string 1 msg))) + (_ (and (not ver) + (require (quote sisyphus) nil t) + (string-match magit-release-commit-regexp + (magit-rev-format "%s" ptag)) + (user-error "Use `sisyphus-create-release' first"))) + (tag (cond + ((not ptag) + (read-string "Create first release tag: " + (if (string-match-p "\\`[0-9]" ver) + (concat "v" ver) + ver))) + (ver + (concat (and (string-match magit-release-tag-regexp ptag) + (match-string 1 ptag)) + ver)) + (t + (read-string + (format "Create release tag (previous was %s): " ptag) + ptag)))) + (ver (and (string-match magit-release-tag-regexp tag) + (match-string 2 tag))) + (args (magit-tag-arguments))) + (list tag + (and (member "--annotate" args) + (read-string + (format "Message for %S: " tag) + (cond ((and pver (string-match (regexp-quote pver) pmsg)) + (replace-match ver t t pmsg)) + ((and ptag (string-match (regexp-quote ptag) pmsg)) + (replace-match tag t t pmsg)) + (t (format "%s %s" + (capitalize + (file-name-nondirectory + (directory-file-name (magit-toplevel)))) + ver))))) + args)))) + (magit-run-git-async "tag" args (and msg (list "-m" msg)) tag) + (set-process-sentinel + magit-this-process + (lambda (process event) + (when (memq (process-status process) '(exit signal)) + (magit-process-sentinel process event) + (magit-refs-setup-buffer "HEAD" (magit-show-refs-arguments)))))) + +(defun magit--list-releases () + "Return a list of releases. +The list is ordered, beginning with the highest release. +Each release element has the form (VERSION TAG MESSAGE). +`magit-release-tag-regexp' is used to determine whether +a tag qualifies as a release tag." + (save-match-data + (mapcar + #'cdr + (nreverse + (cl-sort (cl-mapcan + (lambda (line) + (and (string-match " +" line) + (let ((tag (substring line 0 (match-beginning 0))) + (msg (substring line (match-end 0)))) + (and (string-match magit-release-tag-regexp tag) + (let ((ver (match-string 2 tag)) + (version-regexp-alist + magit-tag-version-regexp-alist)) + (list (list (version-to-list ver) + ver tag msg))))))) + ;; Cannot rely on "--sort=-version:refname" because + ;; that gets confused if the version prefix has changed. + (magit-git-lines "tag" "-n")) + ;; The inverse of this function does not exist. + #'version-list-< :key #'car))))) + +;;; _ +(provide 'magit-tag) +;;; magit-tag.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-transient.el b/code/elpa/magit-20220821.1819/magit-transient.el new file mode 100644 index 0000000..d7bca89 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-transient.el @@ -0,0 +1,220 @@ +;;; magit-transient.el --- Support for transients -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements Magit-specific prefix and suffix classes, +;; and their methods. + +;;; Code: + +(require 'magit-git) +(require 'magit-mode) +(require 'magit-process) + +(require 'transient) + +;;; Classes + +(defclass magit--git-variable (transient-variable) + ((scope :initarg :scope) + (global :initarg :global :initform nil))) + +(defclass magit--git-variable:choices (magit--git-variable) + ((choices :initarg :choices) + (fallback :initarg :fallback :initform nil) + (default :initarg :default :initform nil))) + +(defclass magit--git-variable:boolean (magit--git-variable:choices) + ((choices :initarg :choices :initform '("true" "false")))) + +(defclass magit--git-variable:urls (magit--git-variable) + ((seturl-arg :initarg :seturl-arg :initform nil))) + +;;; Methods +;;;; Init + +(cl-defmethod transient-init-scope ((obj magit--git-variable)) + (oset obj scope + (cond (transient--prefix + (oref transient--prefix scope)) + ((slot-boundp obj 'scope) + (funcall (oref obj scope) obj))))) + +(cl-defmethod transient-init-value ((obj magit--git-variable)) + (let ((variable (format (oref obj variable) + (oref obj scope))) + (arg (if (oref obj global) "--global" "--local"))) + (oset obj variable variable) + (oset obj value + (cond ((oref obj multi-value) + (magit-get-all arg variable)) + (t + (magit-get arg variable)))))) + +(cl-defmethod transient-init-value ((obj magit--git-variable:boolean)) + (let ((variable (format (oref obj variable) + (oref obj scope))) + (arg (if (oref obj global) "--global" "--local"))) + (oset obj variable variable) + (oset obj value (if (magit-get-boolean arg variable) "true" "false")))) + +;;;; Read + +(cl-defmethod transient-infix-read :around ((obj magit--git-variable:urls)) + (transient--with-emergency-exit + (transient--with-suspended-override + (mapcar (lambda (url) + (if (string-prefix-p "~" url) + (expand-file-name url) + url)) + (cl-call-next-method obj))))) + +(cl-defmethod transient-infix-read ((obj magit--git-variable:choices)) + (let ((choices (oref obj choices))) + (when (functionp choices) + (setq choices (funcall choices))) + (if-let ((value (oref obj value))) + (cadr (member value choices)) + (car choices)))) + +;;;; Readers + +(defun magit-transient-read-person (prompt initial-input history) + (magit-completing-read + prompt + (mapcar (lambda (line) + (save-excursion + (and (string-match "\\`[\s\t]+[0-9]+\t" line) + (list (substring line (match-end 0)))))) + (magit-git-lines "shortlog" "-n" "-s" "-e" "HEAD")) + nil nil initial-input history)) + +(defun magit-transient-read-revision (prompt initial-input history) + (or (magit-completing-read prompt (cons "HEAD" (magit-list-refnames)) + nil nil initial-input history + (or (magit-branch-or-commit-at-point) + (magit-get-current-branch))) + (user-error "Nothing selected"))) + +;;;; Set + +(cl-defmethod transient-infix-set ((obj magit--git-variable) value) + (let ((variable (oref obj variable)) + (arg (if (oref obj global) "--global" "--local"))) + (oset obj value value) + (if (oref obj multi-value) + (magit-set-all value arg variable) + (magit-set value arg variable)) + (magit-refresh) + (unless (or value transient--prefix) + (message "Unset %s" variable)))) + +(cl-defmethod transient-infix-set ((obj magit--git-variable:urls) values) + (let ((previous (oref obj value)) + (seturl (oref obj seturl-arg)) + (remote (oref transient--prefix scope))) + (oset obj value values) + (dolist (v (-difference values previous)) + (magit-call-git "remote" "set-url" seturl "--add" remote v)) + (dolist (v (-difference previous values)) + (magit-call-git "remote" "set-url" seturl "--delete" remote + (concat "^" (regexp-quote v) "$"))) + (magit-refresh))) + +;;;; Draw + +(cl-defmethod transient-format-description ((obj magit--git-variable)) + (or (oref obj description) + (oref obj variable))) + +(cl-defmethod transient-format-value ((obj magit--git-variable)) + (if-let ((value (oref obj value))) + (if (oref obj multi-value) + (if (cdr value) + (mapconcat (lambda (v) + (concat "\n " + (propertize v 'face 'transient-value))) + value "") + (propertize (car value) 'face 'transient-value)) + (propertize (car (split-string value "\n")) + 'face 'transient-value)) + (propertize "unset" 'face 'transient-inactive-value))) + +(cl-defmethod transient-format-value ((obj magit--git-variable:choices)) + (let* ((variable (oref obj variable)) + (choices (oref obj choices)) + (globalp (oref obj global)) + (value nil) + (global (magit-git-string "config" "--global" variable)) + (defaultp (oref obj default)) + (default (if (functionp defaultp) (funcall defaultp obj) defaultp)) + (fallback (oref obj fallback)) + (fallback (and fallback + (and-let* ((val (magit-get fallback))) + (concat fallback ":" val))))) + (if (not globalp) + (setq value (magit-git-string "config" "--local" variable)) + (setq value global) + (setq global nil)) + (when (functionp choices) + (setq choices (funcall choices))) + (concat + (propertize "[" 'face 'transient-inactive-value) + (mapconcat (lambda (choice) + (propertize choice 'face (if (equal choice value) + (if (member choice choices) + 'transient-value + 'font-lock-warning-face) + 'transient-inactive-value))) + (if (and value (not (member value choices))) + (cons value choices) + choices) + (propertize "|" 'face 'transient-inactive-value)) + (and (or global fallback default) + (concat + (propertize "|" 'face 'transient-inactive-value) + (cond (global + (propertize (concat "global:" global) + 'face (cond (value + 'transient-inactive-value) + ((member global choices) + 'transient-value) + (t + 'font-lock-warning-face)))) + (fallback + (propertize fallback + 'face (if value + 'transient-inactive-value + 'transient-value))) + (default + (propertize (if (functionp defaultp) + (concat "dwim:" default) + (concat "default:" default)) + 'face (if value + 'transient-inactive-value + 'transient-value)))))) + (propertize "]" 'face 'transient-inactive-value)))) + +;;; _ +(provide 'magit-transient) +;;; magit-transient.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-wip.el b/code/elpa/magit-20220821.1819/magit-wip.el new file mode 100644 index 0000000..ec6b0cc --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-wip.el @@ -0,0 +1,453 @@ +;;; magit-wip.el --- Commit snapshots to work-in-progress refs -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library defines tree global modes which automatically commit +;; snapshots to branch-specific work-in-progress refs before and after +;; making changes, and two commands which can be used to do so on +;; demand. + +;;; Code: + +(require 'magit-core) +(require 'magit-log) + +;;; Options + +(defgroup magit-wip nil + "Automatically commit to work-in-progress refs." + :link '(info-link "(magit)Wip Modes") + :group 'magit-modes + :group 'magit-essentials) + +(defgroup magit-wip-legacy nil + "It is better to not use these modes individually." + :link '(info-link "(magit)Legacy Wip Modes") + :group 'magit-wip) + +(defcustom magit-wip-mode-lighter " Wip" + "Lighter for Magit-Wip mode." + :package-version '(magit . "2.90.0") + :group 'magit-wip + :type 'string) + +(defcustom magit-wip-after-save-local-mode-lighter "" + "Lighter for Magit-Wip-After-Save-Local mode." + :package-version '(magit . "2.1.0") + :group 'magit-wip-legacy + :type 'string) + +(defcustom magit-wip-after-apply-mode-lighter "" + "Lighter for Magit-Wip-After-Apply mode." + :package-version '(magit . "2.1.0") + :group 'magit-wip-legacy + :type 'string) + +(defcustom magit-wip-before-change-mode-lighter "" + "Lighter for Magit-Wip-Before-Change mode." + :package-version '(magit . "2.1.0") + :group 'magit-wip-legacy + :type 'string) + +(defcustom magit-wip-initial-backup-mode-lighter "" + "Lighter for Magit-Wip-Initial Backup mode." + :package-version '(magit . "2.1.0") + :group 'magit-wip-legacy + :type 'string) + +(defcustom magit-wip-merge-branch nil + "Whether to merge the current branch into its wip ref. + +If non-nil and the current branch has new commits, then it is +merged into the wip ref before creating a new wip commit. This +makes it easier to inspect wip history and the wip commits are +never garbage collected. + +If nil and the current branch has new commits, then the wip ref +is reset to the tip of the branch before creating a new wip +commit. With this setting wip commits are eventually garbage +collected. This is currently the default." + :package-version '(magit . "2.90.0") + :group 'magit-wip + :type 'boolean) + +(defcustom magit-wip-namespace "refs/wip/" + "Namespace used for work-in-progress refs. +The wip refs are named \"index/\" +and \"wtree/\". When snapshots +are created while the `HEAD' is detached then \"HEAD\" +is used as `branch-ref'." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :type 'string) + +;;; Modes + +;;;###autoload +(define-minor-mode magit-wip-mode + "Save uncommitted changes to work-in-progress refs. + +Whenever appropriate (i.e. when dataloss would be a possibility +otherwise) this mode causes uncommitted changes to be committed +to dedicated work-in-progress refs. + +For historic reasons this mode is implemented on top of four +other `magit-wip-*' modes, which can also be used individually, +if you want finer control over when the wip refs are updated; +but that is discouraged." + :package-version '(magit . "2.90.0") + :lighter magit-wip-mode-lighter + :global t + (let ((arg (if magit-wip-mode 1 -1))) + (magit-wip-after-save-mode arg) + (magit-wip-after-apply-mode arg) + (magit-wip-before-change-mode arg) + (magit-wip-initial-backup-mode arg))) + +(define-minor-mode magit-wip-after-save-local-mode + "After saving, also commit to a worktree work-in-progress ref. + +After saving the current file-visiting buffer this mode also +commits the changes to the worktree work-in-progress ref for +the current branch. + +This mode should be enabled globally by turning on the globalized +variant `magit-wip-after-save-mode'." + :package-version '(magit . "2.1.0") + :lighter magit-wip-after-save-local-mode-lighter + (if magit-wip-after-save-local-mode + (if (and buffer-file-name (magit-inside-worktree-p t)) + (add-hook 'after-save-hook #'magit-wip-commit-buffer-file t t) + (setq magit-wip-after-save-local-mode nil) + (user-error "Need a worktree and a file")) + (remove-hook 'after-save-hook #'magit-wip-commit-buffer-file t))) + +(defun magit-wip-after-save-local-mode-turn-on () + (and buffer-file-name + (magit-inside-worktree-p t) + (magit-file-tracked-p buffer-file-name) + (magit-wip-after-save-local-mode))) + +;;;###autoload +(define-globalized-minor-mode magit-wip-after-save-mode + magit-wip-after-save-local-mode magit-wip-after-save-local-mode-turn-on + :package-version '(magit . "2.1.0") + :group 'magit-wip) + +(defun magit-wip-commit-buffer-file (&optional msg) + "Commit visited file to a worktree work-in-progress ref. + +Also see `magit-wip-after-save-mode' which calls this function +automatically whenever a buffer visiting a tracked file is saved." + (interactive) + (--when-let (magit-wip-get-ref) + (magit-with-toplevel + (let ((file (file-relative-name buffer-file-name))) + (magit-wip-commit-worktree + it (list file) + (format (cond (msg) + ((called-interactively-p 'any) + "wip-save %s after save") + (t + "autosave %s after save")) + file)))))) + +;;;###autoload +(define-minor-mode magit-wip-after-apply-mode + "Commit to work-in-progress refs. + +After applying a change using any \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected files to the current wip refs. For each branch there +may be two wip refs; one contains snapshots of the files as found +in the worktree and the other contains snapshots of the entries +in the index." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :lighter magit-wip-after-apply-mode-lighter + :global t) + +(defun magit-wip-commit-after-apply (&optional files msg) + (when magit-wip-after-apply-mode + (magit-wip-commit files msg))) + +;;;###autoload +(define-minor-mode magit-wip-before-change-mode + "Commit to work-in-progress refs before certain destructive changes. + +Before invoking a revert command or an \"apply variant\" +command (apply, stage, unstage, discard, and reverse) commit the +affected tracked files to the current wip refs. For each branch +there may be two wip refs; one contains snapshots of the files +as found in the worktree and the other contains snapshots of the +entries in the index. + +Only changes to files which could potentially be affected by the +command which is about to be called are committed." + :package-version '(magit . "2.1.0") + :group 'magit-wip + :lighter magit-wip-before-change-mode-lighter + :global t) + +(defun magit-wip-commit-before-change (&optional files msg) + (when magit-wip-before-change-mode + (magit-with-toplevel + (magit-wip-commit files msg)))) + +(define-minor-mode magit-wip-initial-backup-mode + "Before saving a buffer for the first time, commit to a wip ref." + :package-version '(magit . "2.90.0") + :group 'magit-wip + :lighter magit-wip-initial-backup-mode-lighter + :global t + (if magit-wip-initial-backup-mode + (add-hook 'before-save-hook #'magit-wip-commit-initial-backup) + (remove-hook 'before-save-hook #'magit-wip-commit-initial-backup))) + +(defun magit--any-wip-mode-enabled-p () + "Return non-nil if any global wip mode is enabled." + (or magit-wip-mode + magit-wip-after-save-mode + magit-wip-after-apply-mode + magit-wip-before-change-mode + magit-wip-initial-backup-mode)) + +(defvar-local magit-wip-buffer-backed-up nil) +(put 'magit-wip-buffer-backed-up 'permanent-local t) + +;;;###autoload +(defun magit-wip-commit-initial-backup () + "Before saving, commit current file to a worktree wip ref. + +The user has to add this function to `before-save-hook'. + +Commit the current state of the visited file before saving the +current buffer to that file. This backs up the same version of +the file as `backup-buffer' would, but stores the backup in the +worktree wip ref, which is also used by the various Magit Wip +modes, instead of in a backup file as `backup-buffer' would. + +This function ignores the variables that affect `backup-buffer' +and can be used along-side that function, which is recommended +because this function only backs up files that are tracked in +a Git repository." + (when (and (not magit-wip-buffer-backed-up) + buffer-file-name + (magit-inside-worktree-p t) + (magit-file-tracked-p buffer-file-name)) + (let ((magit-save-repository-buffers nil)) + (magit-wip-commit-buffer-file "autosave %s before save")) + (setq magit-wip-buffer-backed-up t))) + +;;; Core + +(defun magit-wip-commit (&optional files msg) + "Commit all tracked files to the work-in-progress refs. + +Interactively, commit all changes to all tracked files using +a generic commit message. With a prefix-argument the commit +message is read in the minibuffer. + +Non-interactively, only commit changes to FILES using MSG as +commit message." + (interactive (list nil (if current-prefix-arg + (magit-read-string "Wip commit message") + "wip-save tracked files"))) + (--when-let (magit-wip-get-ref) + (magit-wip-commit-index it files msg) + (magit-wip-commit-worktree it files msg))) + +(defun magit-wip-commit-index (ref files msg) + (let* ((wipref (magit--wip-index-ref ref)) + (parent (magit-wip-get-parent ref wipref)) + (tree (magit-git-string "write-tree"))) + (magit-wip-update-wipref ref wipref tree parent files msg "index"))) + +(defun magit-wip-commit-worktree (ref files msg) + (when (or (not files) + ;; `update-index' will either ignore (before Git v2.32.0) + ;; or fail when passed directories (relevant for the + ;; untracked files code paths). + (setq files (seq-remove #'file-directory-p files))) + (let* ((wipref (magit--wip-wtree-ref ref)) + (parent (magit-wip-get-parent ref wipref)) + (tree (magit-with-temp-index parent (list "--reset" "-i") + (if files + ;; Note: `update-index' is used instead of `add' + ;; because `add' will fail if a file is already + ;; deleted in the temporary index. + (magit-call-git + "update-index" "--add" "--remove" + (and (magit-git-version>= "2.25.0") + "--ignore-skip-worktree-entries") + "--" files) + (magit-with-toplevel + (magit-call-git "add" "-u" "."))) + (magit-git-string "write-tree")))) + (magit-wip-update-wipref ref wipref tree parent files msg "worktree")))) + +(defun magit-wip-update-wipref (ref wipref tree parent files msg start-msg) + (cond + ((and (not (equal parent wipref)) + (or (not magit-wip-merge-branch) + (not (magit-rev-verify wipref)))) + (setq start-msg (concat "start autosaving " start-msg)) + (magit-update-ref wipref start-msg + (magit-git-string "commit-tree" "--no-gpg-sign" + "-p" parent "-m" start-msg + (concat parent "^{tree}"))) + (setq parent wipref)) + ((and magit-wip-merge-branch + (or (not (magit-rev-ancestor-p ref wipref)) + (not (magit-rev-ancestor-p + (concat (magit-git-string "log" "--format=%H" + "-1" "--merges" wipref) + "^2") + ref)))) + (setq start-msg (format "merge %s into %s" ref start-msg)) + (magit-update-ref wipref start-msg + (magit-git-string "commit-tree" "--no-gpg-sign" + "-p" wipref "-p" ref + "-m" start-msg + (concat ref "^{tree}"))) + (setq parent wipref))) + (when (magit-git-failure "diff-tree" "--quiet" parent tree "--" files) + (unless (and msg (not (= (aref msg 0) ?\s))) + (let ((len (length files))) + (setq msg (concat + (cond ((= len 0) "autosave tracked files") + ((> len 1) (format "autosave %s files" len)) + (t (concat "autosave " + (file-relative-name (car files) + (magit-toplevel))))) + msg)))) + (magit-update-ref wipref msg + (magit-git-string "commit-tree" "--no-gpg-sign" + "-p" parent "-m" msg tree)))) + +(defun magit-wip-get-ref () + (let ((ref (or (magit-git-string "symbolic-ref" "HEAD") "HEAD"))) + (and (magit-rev-verify ref) + ref))) + +(defun magit-wip-get-parent (ref wipref) + (if (and (magit-rev-verify wipref) + (equal (magit-git-string "merge-base" wipref ref) + (magit-rev-verify ref))) + wipref + ref)) + +(defun magit--wip-index-ref (&optional ref) + (magit--wip-ref "index/" ref)) + +(defun magit--wip-wtree-ref (&optional ref) + (magit--wip-ref "wtree/" ref)) + +(defun magit--wip-ref (namespace &optional ref) + (concat magit-wip-namespace namespace + (or (and ref (string-prefix-p "refs/" ref) ref) + (and-let* ((branch (and (not (equal ref "HEAD")) + (or ref (magit-get-current-branch))))) + (concat "refs/heads/" branch)) + "HEAD"))) + +(defun magit-wip-maybe-add-commit-hook () + (when (and magit-wip-merge-branch + (magit-wip-any-enabled-p)) + (add-hook 'git-commit-post-finish-hook #'magit-wip-commit nil t))) + +(defun magit-wip-any-enabled-p () + (or magit-wip-mode + magit-wip-after-save-local-mode + magit-wip-after-save-mode + magit-wip-after-apply-mode + magit-wip-before-change-mode + magit-wip-initial-backup-mode)) + +;;; Log + +(defun magit-wip-log-index (args files) + "Show log for the index wip ref of the current branch." + (interactive (magit-log-arguments)) + (magit-log-setup-buffer (list (magit--wip-index-ref)) args files)) + +(defun magit-wip-log-worktree (args files) + "Show log for the worktree wip ref of the current branch." + (interactive (magit-log-arguments)) + (magit-log-setup-buffer (list (magit--wip-wtree-ref)) args files)) + +(defun magit-wip-log-current (branch args files count) + "Show log for the current branch and its wip refs. +With a negative prefix argument only show the worktree wip ref. +The absolute numeric value of the prefix argument controls how +many \"branches\" of each wip ref are shown." + (interactive + (nconc (list (or (magit-get-current-branch) "HEAD")) + (magit-log-arguments) + (list (prefix-numeric-value current-prefix-arg)))) + (magit-wip-log branch args files count)) + +(defun magit-wip-log (branch args files count) + "Show log for a branch and its wip refs. +With a negative prefix argument only show the worktree wip ref. +The absolute numeric value of the prefix argument controls how +many \"branches\" of each wip ref are shown." + (interactive + (nconc (list (magit-completing-read + "Log branch and its wip refs" + (-snoc (magit-list-local-branch-names) "HEAD") + nil t nil 'magit-revision-history + (or (magit-branch-at-point) + (magit-get-current-branch) + "HEAD"))) + (magit-log-arguments) + (list (prefix-numeric-value current-prefix-arg)))) + (magit-log-setup-buffer (nconc (list branch) + (magit-wip-log-get-tips + (magit--wip-wtree-ref branch) + (abs count)) + (and (>= count 0) + (magit-wip-log-get-tips + (magit--wip-index-ref branch) + (abs count)))) + args files)) + +(defun magit-wip-log-get-tips (wipref count) + (and-let* ((reflog (magit-git-lines "reflog" wipref))) + (let (tips) + (while (and reflog (> count 1)) + ;; "start autosaving ..." is the current message, but it used + ;; to be "restart autosaving ...", and those messages may + ;; still be around (e.g., if gc.reflogExpire is to "never"). + (setq reflog (cl-member "^[^ ]+ [^:]+: \\(?:re\\)?start autosaving" + reflog :test #'string-match-p)) + (when (and (cadr reflog) + (string-match "^[^ ]+ \\([^:]+\\)" (cadr reflog))) + (push (match-string 1 (cadr reflog)) tips)) + (setq reflog (cddr reflog)) + (cl-decf count)) + (cons wipref (nreverse tips))))) + +;;; _ +(provide 'magit-wip) +;;; magit-wip.el ends here diff --git a/code/elpa/magit-20220821.1819/magit-worktree.el b/code/elpa/magit-20220821.1819/magit-worktree.el new file mode 100644 index 0000000..4a94c5b --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit-worktree.el @@ -0,0 +1,191 @@ +;;; magit-worktree.el --- Worktree support -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;;; Commentary: + +;; This library implements support for `git-worktree'. + +;;; Code: + +(require 'magit) + +;;; Options + +(defcustom magit-worktree-read-directory-name-function #'read-directory-name + "Function used to read a directory for worktree commands. +This is called with one argument, the prompt, and can be used +to e.g. use a base directory other than `default-directory'. +Used by `magit-worktree-checkout' and `magit-worktree-branch'." + :package-version '(magit . "3.0.0") + :group 'magit-commands + :type 'function) + +;;; Commands + +;;;###autoload (autoload 'magit-worktree "magit-worktree" nil t) +(transient-define-prefix magit-worktree () + "Act on a worktree." + :man-page "git-worktree" + [["Create new" + ("b" "worktree" magit-worktree-checkout) + ("c" "branch and worktree" magit-worktree-branch)] + ["Commands" + ("m" "Move worktree" magit-worktree-move) + ("k" "Delete worktree" magit-worktree-delete) + ("g" "Visit worktree" magit-worktree-status)]]) + +;;;###autoload +(defun magit-worktree-checkout (path branch) + "Checkout BRANCH in a new worktree at PATH." + (interactive + (let ((branch (magit-read-branch-or-commit "Checkout"))) + (list (funcall magit-worktree-read-directory-name-function + (format "Checkout %s in new worktree: " branch)) + branch))) + (magit-run-git "worktree" "add" (magit--expand-worktree path) branch) + (magit-diff-visit-directory path)) + +;;;###autoload +(defun magit-worktree-branch (path branch start-point &optional force) + "Create a new BRANCH and check it out in a new worktree at PATH." + (interactive + `(,(funcall magit-worktree-read-directory-name-function + "Create worktree: ") + ,@(magit-branch-read-args "Create and checkout branch") + ,current-prefix-arg)) + (magit-run-git "worktree" "add" (if force "-B" "-b") + branch (magit--expand-worktree path) start-point) + (magit-diff-visit-directory path)) + +;;;###autoload +(defun magit-worktree-move (worktree path) + "Move WORKTREE to PATH." + (interactive + (list (magit-completing-read "Move worktree" + (cdr (magit-list-worktrees)) + nil t nil nil + (magit-section-value-if 'worktree)) + (funcall magit-worktree-read-directory-name-function + "Move worktree to: "))) + (if (file-directory-p (expand-file-name ".git" worktree)) + (user-error "You may not move the main working tree") + (let ((preexisting-directory (file-directory-p path))) + (when (and (zerop (magit-call-git "worktree" "move" worktree + (magit--expand-worktree path))) + (not (file-exists-p default-directory)) + (derived-mode-p 'magit-status-mode)) + (kill-buffer) + (magit-diff-visit-directory + (if preexisting-directory + (concat (file-name-as-directory path) + (file-name-nondirectory worktree)) + path))) + (magit-refresh)))) + +(defun magit-worktree-delete (worktree) + "Delete a worktree, defaulting to the worktree at point. +The primary worktree cannot be deleted." + (interactive + (list (magit-completing-read "Delete worktree" + (cdr (magit-list-worktrees)) + nil t nil nil + (magit-section-value-if 'worktree)))) + (if (file-directory-p (expand-file-name ".git" worktree)) + (user-error "Deleting %s would delete the shared .git directory" worktree) + (let ((primary (file-name-as-directory (caar (magit-list-worktrees))))) + (magit-confirm-files (if magit-delete-by-moving-to-trash 'trash 'delete) + (list "worktree")) + (when (file-exists-p worktree) + (let ((delete-by-moving-to-trash magit-delete-by-moving-to-trash)) + (delete-directory worktree t magit-delete-by-moving-to-trash))) + (if (file-exists-p default-directory) + (magit-run-git "worktree" "prune") + (let ((default-directory primary)) + (magit-run-git "worktree" "prune")) + (when (derived-mode-p 'magit-status-mode) + (kill-buffer) + (magit-status-setup-buffer primary)))))) + +(defun magit-worktree-status (worktree) + "Show the status for the worktree at point. +If there is no worktree at point, then read one in the +minibuffer. If the worktree at point is the one whose +status is already being displayed in the current buffer, +then show it in Dired instead." + (interactive + (list (or (magit-section-value-if 'worktree) + (magit-completing-read + "Show status for worktree" + (cl-delete (directory-file-name (magit-toplevel)) + (magit-list-worktrees) + :test #'equal :key #'car))))) + (magit-diff-visit-directory worktree)) + +(defun magit--expand-worktree (path) + (magit-convert-filename-for-git (expand-file-name path))) + +;;; Sections + +(defvar magit-worktree-section-map + (let ((map (make-sparse-keymap))) + (magit-menu-set map [magit-visit-thing] #'magit-worktree-status "Visit %s") + (magit-menu-set map [magit-delete-thing] #'magit-worktree-delete "Delete %m") + (define-key-after map [separator-magit-worktree] menu-bar-separator) + (magit-menu-set map [magit-worktree ] #'magit-worktree "Worktree commands...") + map) + "Keymap for `worktree' sections.") + +(defun magit-insert-worktrees () + "Insert sections for all worktrees. +If there is only one worktree, then insert nothing." + (let ((worktrees (magit-list-worktrees))) + (when (length> worktrees 1) + (magit-insert-section (worktrees) + (magit-insert-heading "Worktrees:") + (let* ((cols + (mapcar + (pcase-lambda (`(,path ,barep ,commit ,branch)) + (cons (cond + (branch (propertize + branch 'font-lock-face + (if (equal branch (magit-get-current-branch)) + 'magit-branch-current + 'magit-branch-local))) + (commit (propertize (magit-rev-abbrev commit) + 'font-lock-face 'magit-hash)) + (barep "(bare)")) + path)) + worktrees)) + (align (1+ (-max (--map (string-width (car it)) cols))))) + (pcase-dolist (`(,head . ,path) cols) + (magit-insert-section (worktree path) + (insert head) + (insert (make-string (- align (length head)) ?\s)) + (insert (let ((r (file-relative-name path)) + (a (abbreviate-file-name path))) + (if (< (string-width r) (string-width a)) r a))) + (insert ?\n)))) + (insert ?\n))))) + +;;; _ +(provide 'magit-worktree) +;;; magit-worktree.el ends here diff --git a/code/elpa/magit-20220821.1819/magit.el b/code/elpa/magit-20220821.1819/magit.el new file mode 100644 index 0000000..b45c866 --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit.el @@ -0,0 +1,751 @@ +;;; magit.el --- A Git porcelain inside Emacs -*- lexical-binding:t; coding:utf-8 -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Marius Vollmer +;; Jonas Bernoulli +;; Maintainer: Jonas Bernoulli +;; Kyle Meyer +;; Former-Maintainers: +;; Nicolas Dudebout +;; Noam Postavsky +;; Peter J. Weisberg +;; Phil Jackson +;; Rémi Vanicat +;; Yann Hodique + +;; Homepage: https://github.com/magit/magit +;; Keywords: git tools vc + +;; Package-Version: 3.3.0-git +;; Package-Requires: ( +;; (emacs "25.1") +;; (compat "28.1.1.2") +;; (dash "2.19.1") +;; (git-commit "3.3.0") +;; (magit-section "3.3.0") +;; (transient "0.3.6") +;; (with-editor "3.0.5")) + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;; You should have received a copy of the AUTHORS.md file, which +;; lists all contributors. If not, see https://magit.vc/authors. + +;;; Commentary: + +;; Magit is a text-based Git user interface that puts an unmatched focus +;; on streamlining workflows. Commands are invoked using short mnemonic +;; key sequences that take the cursor’s position in the highly actionable +;; interface into account to provide context-sensitive behavior. + +;; With Magit you can do nearly everything that you can do when using Git +;; on the command-line, but at greater speed and while taking advantage +;; of advanced features that previously seemed too daunting to use on a +;; daily basis. Many users will find that by using Magit they can become +;; more effective Git user. + +;;; Code: + +(require 'magit-core) +(require 'magit-diff) +(require 'magit-log) +(require 'magit-wip) +(require 'magit-apply) +(require 'magit-repos) +(require 'git-commit) + +(require 'format-spec) +(require 'package nil t) ; used in `magit-version' +(require 'with-editor) + +;; For `magit:--gpg-sign' +(declare-function epg-list-keys "epg" (context &optional name mode)) +(declare-function epg-decode-dn "epg" (alist)) + +;;; Options + +(defcustom magit-openpgp-default-signing-key nil + "Fingerprint of your default Openpgp key used for signing. +If the specified primary key has signing capacity then it is used +as the value of the `--gpg-sign' argument without prompting, even +when other such keys exist. To be able to select another key you +must then use a prefix argument." + :package-version '(magit . "3.4.0") + :group 'magit-commands + :type 'string) + +;;; Faces + +(defface magit-header-line + '((t :inherit magit-section-heading)) + "Face for the `header-line' in some Magit modes. +Note that some modes, such as `magit-log-select-mode', have their +own faces for the `header-line', or for parts of the +`header-line'." + :group 'magit-faces) + +(defface magit-header-line-key + '((t :inherit font-lock-builtin-face)) + "Face for keys in the `header-line'." + :group 'magit-faces) + +(defface magit-dimmed + '((((class color) (background light)) :foreground "grey50") + (((class color) (background dark)) :foreground "grey50")) + "Face for text that shouldn't stand out." + :group 'magit-faces) + +(defface magit-hash + '((((class color) (background light)) :foreground "grey60") + (((class color) (background dark)) :foreground "grey40")) + "Face for the commit object name in the log output." + :group 'magit-faces) + +(defface magit-tag + '((((class color) (background light)) :foreground "Goldenrod4") + (((class color) (background dark)) :foreground "LightGoldenrod2")) + "Face for tag labels shown in log buffer." + :group 'magit-faces) + +(defface magit-branch-remote + '((((class color) (background light)) :foreground "DarkOliveGreen4") + (((class color) (background dark)) :foreground "DarkSeaGreen2")) + "Face for remote branch head labels shown in log buffer." + :group 'magit-faces) + +(defface magit-branch-remote-head + '((((supports (:box t))) :inherit magit-branch-remote :box t) + (t :inherit magit-branch-remote :inverse-video t)) + "Face for current branch." + :group 'magit-faces) + +(defface magit-branch-local + '((((class color) (background light)) :foreground "SkyBlue4") + (((class color) (background dark)) :foreground "LightSkyBlue1")) + "Face for local branches." + :group 'magit-faces) + +(defface magit-branch-current + '((((supports (:box t))) :inherit magit-branch-local :box t) + (t :inherit magit-branch-local :inverse-video t)) + "Face for current branch." + :group 'magit-faces) + +(defface magit-branch-upstream + '((t :slant italic)) + "Face for upstream branch. +This face is only used in logs and it gets combined + with `magit-branch-local', `magit-branch-remote' +and/or `magit-branch-remote-head'." + :group 'magit-faces) + +(defface magit-branch-warning + '((t :inherit warning)) + "Face for warning about (missing) branch." + :group 'magit-faces) + +(defface magit-head + '((((class color) (background light)) :inherit magit-branch-local) + (((class color) (background dark)) :inherit magit-branch-local)) + "Face for the symbolic ref `HEAD'." + :group 'magit-faces) + +(defface magit-refname + '((((class color) (background light)) :foreground "grey30") + (((class color) (background dark)) :foreground "grey80")) + "Face for refnames without a dedicated face." + :group 'magit-faces) + +(defface magit-refname-stash + '((t :inherit magit-refname)) + "Face for stash refnames." + :group 'magit-faces) + +(defface magit-refname-wip + '((t :inherit magit-refname)) + "Face for wip refnames." + :group 'magit-faces) + +(defface magit-refname-pullreq + '((t :inherit magit-refname)) + "Face for pullreq refnames." + :group 'magit-faces) + +(defface magit-keyword + '((t :inherit font-lock-string-face)) + "Face for parts of commit messages inside brackets." + :group 'magit-faces) + +(defface magit-keyword-squash + '((t :inherit font-lock-warning-face)) + "Face for squash! and fixup! keywords in commit messages." + :group 'magit-faces) + +(defface magit-signature-good + '((t :foreground "green")) + "Face for good signatures." + :group 'magit-faces) + +(defface magit-signature-bad + '((t :foreground "red" :weight bold)) + "Face for bad signatures." + :group 'magit-faces) + +(defface magit-signature-untrusted + '((t :foreground "medium aquamarine")) + "Face for good untrusted signatures." + :group 'magit-faces) + +(defface magit-signature-expired + '((t :foreground "orange")) + "Face for signatures that have expired." + :group 'magit-faces) + +(defface magit-signature-expired-key + '((t :inherit magit-signature-expired)) + "Face for signatures made by an expired key." + :group 'magit-faces) + +(defface magit-signature-revoked + '((t :foreground "violet red")) + "Face for signatures made by a revoked key." + :group 'magit-faces) + +(defface magit-signature-error + '((t :foreground "light blue")) + "Face for signatures that cannot be checked (e.g. missing key)." + :group 'magit-faces) + +(defface magit-cherry-unmatched + '((t :foreground "cyan")) + "Face for unmatched cherry commits." + :group 'magit-faces) + +(defface magit-cherry-equivalent + '((t :foreground "magenta")) + "Face for equivalent cherry commits." + :group 'magit-faces) + +(defface magit-filename + '((t :weight normal)) + "Face for filenames." + :group 'magit-faces) + +;;; Global Bindings + +;;;###autoload +(define-obsolete-variable-alias 'global-magit-file-mode + 'magit-define-global-key-bindings "Magit 3.0.0") + +;;;###autoload +(defcustom magit-define-global-key-bindings t + "Whether to bind some Magit commands in the global keymap. + +If this variable is non-nil, then the following bindings may +be added to the global keymap. The default is t. + +key binding +--- ------- +C-x g magit-status +C-x M-g magit-dispatch +C-c M-g magit-file-dispatch + +These bindings may be added when `after-init-hook' is run. +Each binding is added if and only if at that time no other key +is bound to the same command and no other command is bound to +the same key. In other words we try to avoid adding bindings +that are unnecessary, as well as bindings that conflict with +other bindings. + +Adding the above bindings is delayed until `after-init-hook' +is called to allow users to set the variable anywhere in their +init file (without having to make sure to do so before `magit' +is loaded or autoloaded) and to increase the likelihood that +all the potentially conflicting user bindings have already +been added. + +To set this variable use either `setq' or the Custom interface. +Do not use the function `customize-set-variable' because doing +that would cause Magit to be loaded immediately when that form +is evaluated (this differs from `custom-set-variables', which +doesn't load the libraries that define the customized variables). + +Setting this variable to nil has no effect if that is done after +the key bindings have already been added. + +We recommend that you bind \"C-c g\" instead of \"C-c M-g\" to +`magit-file-dispatch'. The former is a much better binding +but the \"C-c \" namespace is strictly reserved for +users; preventing Magit from using it by default. + +Also see info node `(magit)Commands for Buffers Visiting Files'." + :package-version '(magit . "3.0.0") + :group 'magit-essentials + :type 'boolean) + +;;;###autoload +(progn + (defun magit-maybe-define-global-key-bindings (&optional force) + (when magit-define-global-key-bindings + (let ((map (current-global-map))) + (dolist (elt '(("C-x g" . magit-status) + ("C-x M-g" . magit-dispatch) + ("C-c M-g" . magit-file-dispatch))) + (let ((key (kbd (car elt))) + (def (cdr elt))) + (when (or force + (not (or (lookup-key map key) + (where-is-internal def (make-sparse-keymap) t)))) + (define-key map key def))))))) + (if after-init-time + (magit-maybe-define-global-key-bindings) + (add-hook 'after-init-hook #'magit-maybe-define-global-key-bindings t))) + +;;; Dispatch Popup + +;;;###autoload (autoload 'magit-dispatch "magit" nil t) +(transient-define-prefix magit-dispatch () + "Invoke a Magit command from a list of available commands." + :info-manual "(magit)Top" + ["Transient and dwim commands" + ;; → bound in magit-mode-map or magit-section-mode-map + ;; ↓ bound below + [("A" "Apply" magit-cherry-pick) + ;; a ↓ + ("b" "Branch" magit-branch) + ("B" "Bisect" magit-bisect) + ("c" "Commit" magit-commit) + ("C" "Clone" magit-clone) + ("d" "Diff" magit-diff) + ("D" "Diff (change)" magit-diff-refresh) + ("e" "Ediff (dwim)" magit-ediff-dwim) + ("E" "Ediff" magit-ediff) + ("f" "Fetch" magit-fetch) + ("F" "Pull" magit-pull) + ;; g ↓ + ;; G → magit-refresh-all + ("h" "Help" magit-info) + ("H" "Section info" magit-describe-section :if-derived magit-mode)] + [("i" "Ignore" magit-gitignore) + ("I" "Init" magit-init) + ("j" "Jump to section"magit-status-jump :if-mode magit-status-mode) + ("j" "Display status" magit-status-quick :if-not-mode magit-status-mode) + ("J" "Display buffer" magit-display-repository-buffer) + ;; k ↓ + ;; K → magit-file-untrack + ("l" "Log" magit-log) + ("L" "Log (change)" magit-log-refresh) + ("m" "Merge" magit-merge) + ("M" "Remote" magit-remote) + ;; n → magit-section-forward + ;; N reserved → forge-dispatch + ("o" "Submodule" magit-submodule) + ("O" "Subtree" magit-subtree) + ;; p → magit-section-backward + ("P" "Push" magit-push) + ;; q → magit-mode-bury-buffer + ("Q" "Command" magit-git-command)] + [("r" "Rebase" magit-rebase) + ;; R → magit-file-rename + ;; s ↓ + ;; S ↓ + ("t" "Tag" magit-tag) + ("T" "Note" magit-notes) + ;; u ↓ + ;; U ↓ + ;; v ↓ + ("V" "Revert" magit-revert) + ("w" "Apply patches" magit-am) + ("W" "Format patches" magit-patch) + ;; x → magit-reset-quickly + ("X" "Reset" magit-reset) + ("y" "Show Refs" magit-show-refs) + ("Y" "Cherries" magit-cherry) + ("z" "Stash" magit-stash) + ("Z" "Worktree" magit-worktree) + ("!" "Run" magit-run)]] + ["Applying changes" + :if-derived magit-mode + [("a" "Apply" magit-apply) + ("v" "Reverse" magit-reverse) + ("k" "Discard" magit-discard)] + [("s" "Stage" magit-stage) + ("u" "Unstage" magit-unstage)] + [("S" "Stage all" magit-stage-modified) + ("U" "Unstage all" magit-unstage-all)]] + ["Essential commands" + :if-derived magit-mode + [("g" " refresh current buffer" magit-refresh) + ("q" " bury current buffer" magit-mode-bury-buffer) + ("" " toggle section at point" magit-section-toggle) + ("" "visit thing at point" magit-visit-thing)] + [("C-x m" "show all key bindings" describe-mode) + ("C-x i" "show Info manual" magit-info)]]) + +;;; Git Popup + +(defcustom magit-shell-command-verbose-prompt t + "Whether to show the working directory when reading a command. +This affects `magit-git-command', `magit-git-command-topdir', +`magit-shell-command', and `magit-shell-command-topdir'." + :package-version '(magit . "2.11.0") + :group 'magit-commands + :type 'boolean) + +(defvar magit-git-command-history nil) + +;;;###autoload (autoload 'magit-run "magit" nil t) +(transient-define-prefix magit-run () + "Run git or another command, or launch a graphical utility." + [["Run git subcommand" + ("!" "in repository root" magit-git-command-topdir) + ("p" "in working directory" magit-git-command)] + ["Run shell command" + ("s" "in repository root" magit-shell-command-topdir) + ("S" "in working directory" magit-shell-command)] + ["Launch" + ("k" "gitk" magit-run-gitk) + ("a" "gitk --all" magit-run-gitk-all) + ("b" "gitk --branches" magit-run-gitk-branches) + ("g" "git gui" magit-run-git-gui) + ("m" "git mergetool --gui" magit-git-mergetool)]]) + +;;;###autoload +(defun magit-git-command (command) + "Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. \"git \" is +used as initial input, but can be deleted to run another command. + +With a prefix argument COMMAND is run in the top-level directory +of the current working tree, otherwise in `default-directory'." + (interactive (list (magit-read-shell-command nil "git "))) + (magit--shell-command command)) + +;;;###autoload +(defun magit-git-command-topdir (command) + "Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. \"git \" is +used as initial input, but can be deleted to run another command. + +COMMAND is run in the top-level directory of the current +working tree." + (interactive (list (magit-read-shell-command t "git "))) + (magit--shell-command command (magit-toplevel))) + +;;;###autoload +(defun magit-shell-command (command) + "Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. With a +prefix argument COMMAND is run in the top-level directory of +the current working tree, otherwise in `default-directory'." + (interactive (list (magit-read-shell-command))) + (magit--shell-command command)) + +;;;###autoload +(defun magit-shell-command-topdir (command) + "Execute COMMAND asynchronously; display output. + +Interactively, prompt for COMMAND in the minibuffer. COMMAND +is run in the top-level directory of the current working tree." + (interactive (list (magit-read-shell-command t))) + (magit--shell-command command (magit-toplevel))) + +(defun magit--shell-command (command &optional directory) + (let ((default-directory (or directory default-directory))) + (with-environment-variables (("GIT_PAGER" "cat")) + (magit--with-connection-local-variables + (magit-start-process shell-file-name nil + shell-command-switch command)))) + (magit-process-buffer)) + +(defun magit-read-shell-command (&optional toplevel initial-input) + (let ((default-directory + (if (or toplevel current-prefix-arg) + (or (magit-toplevel) + (magit--not-inside-repository-error)) + default-directory))) + (read-shell-command (if magit-shell-command-verbose-prompt + (format "Async shell command in %s: " + (abbreviate-file-name default-directory)) + "Async shell command: ") + initial-input 'magit-git-command-history))) + +;;; Shared Infix Arguments + +(transient-define-argument magit:--gpg-sign () + :description "Sign using gpg" + :class 'transient-option + :shortarg "-S" + :argument "--gpg-sign=" + :allow-empty t + :reader #'magit-read-gpg-signing-key) + +(defvar magit-gpg-secret-key-hist nil) + +(defun magit-read-gpg-secret-key + (prompt &optional initial-input history predicate default) + (require 'epa) + (let* ((keys (cl-mapcan + (lambda (cert) + (and (or (not predicate) + (funcall predicate cert)) + (let* ((key (car (epg-key-sub-key-list cert))) + (fpr (epg-sub-key-fingerprint key)) + (id (epg-sub-key-id key)) + (author + (and-let* ((id-obj + (car (epg-key-user-id-list cert)))) + (let ((id-str (epg-user-id-string id-obj))) + (if (stringp id-str) + id-str + (epg-decode-dn id-obj)))))) + (list + (propertize fpr 'display + (concat (substring fpr 0 (- (length id))) + (propertize id 'face 'highlight) + " " author)))))) + (epg-list-keys (epg-make-context epa-protocol) nil t))) + (choice (or (and (not current-prefix-arg) + (or (and (length= keys 1) (car keys)) + (and default (car (member default keys))))) + (completing-read prompt keys nil nil nil + history nil initial-input)))) + (set-text-properties 0 (length choice) nil choice) + choice)) + +(defun magit-read-gpg-signing-key (prompt &optional initial-input history) + (magit-read-gpg-secret-key + prompt initial-input history + (lambda (cert) + (cl-some (lambda (key) + (memq 'sign (epg-sub-key-capability key))) + (epg-key-sub-key-list cert))) + magit-openpgp-default-signing-key)) + +;;; Font-Lock Keywords + +(defconst magit-font-lock-keywords + (eval-when-compile + `((,(concat "(\\(magit-define-section-jumper\\)\\_>" + "[ \t'\(]*" + "\\(\\(?:\\sw\\|\\s_\\)+\\)?") + (1 'font-lock-keyword-face) + (2 'font-lock-function-name-face nil t)) + (,(concat "(" (regexp-opt '("magit-insert-section" + "magit-section-case" + "magit-bind-match-strings" + "magit-with-temp-index" + "magit-with-blob" + "magit-with-toplevel") t) + "\\_>") + . 1)))) + +(font-lock-add-keywords 'emacs-lisp-mode magit-font-lock-keywords) + +;;; Version + +(defvar magit-version #'undefined + "The version of Magit that you're using. +Use the function by the same name instead of this variable.") + +;;;###autoload +(defun magit-version (&optional print-dest) + "Return the version of Magit currently in use. +If optional argument PRINT-DEST is non-nil, output +stream (interactively, the echo area, or the current buffer with +a prefix argument), also print the used versions of Magit, Git, +and Emacs to it." + (interactive (list (if current-prefix-arg (current-buffer) t))) + (let ((magit-git-global-arguments nil) + (toplib (or load-file-name buffer-file-name)) + debug) + (unless (and toplib + (member (file-name-nondirectory toplib) + '("magit.el" "magit.el.gz"))) + (let ((load-suffixes (reverse load-suffixes))) ; prefer .el than .elc + (setq toplib (locate-library "magit")))) + (setq toplib (and toplib (magit--straight-chase-links toplib))) + (push toplib debug) + (when toplib + (let* ((topdir (file-name-directory toplib)) + (gitdir (expand-file-name + ".git" (file-name-directory + (directory-file-name topdir)))) + (static (locate-library "magit-version.el" nil (list topdir))) + (static (and static (magit--straight-chase-links static)))) + (or (progn + (push 'repo debug) + (when (and (file-exists-p gitdir) + ;; It is a repo, but is it the Magit repo? + (file-exists-p + (expand-file-name "../lisp/magit.el" gitdir))) + (push t debug) + ;; Inside the repo the version file should only exist + ;; while running make. + (when (and static (not noninteractive)) + (ignore-errors (delete-file static))) + (setq magit-version + (let ((default-directory topdir)) + (magit-git-string "describe" + "--tags" "--dirty" "--always"))))) + (progn + (push 'static debug) + (when (and static (file-exists-p static)) + (push t debug) + (load-file static) + magit-version)) + (when (featurep 'package) + (push 'elpa debug) + (ignore-errors + (--when-let (assq 'magit package-alist) + (push t debug) + (setq magit-version + (and (fboundp 'package-desc-version) + (package-version-join + (package-desc-version (cadr it)))))))) + (progn + (push 'dirname debug) + (let ((dirname (file-name-nondirectory + (directory-file-name topdir)))) + (when (string-match "\\`magit-\\([0-9].*\\)" dirname) + (setq magit-version (match-string 1 dirname))))) + ;; If all else fails, just report the commit hash. It's + ;; better than nothing and we cannot do better in the case + ;; of e.g. a shallow clone. + (progn + (push 'hash debug) + ;; Same check as above to see if it's really the Magit repo. + (when (and (file-exists-p gitdir) + (file-exists-p + (expand-file-name "../lisp/magit.el" gitdir))) + (setq magit-version + (let ((default-directory topdir)) + (magit-git-string "rev-parse" "HEAD")))))))) + (if (stringp magit-version) + (when print-dest + (princ (format "Magit %s%s, Git %s, Emacs %s, %s" + (or magit-version "(unknown)") + (or (and (ignore-errors + (magit--version>= magit-version "2008")) + (ignore-errors + (require 'lisp-mnt) + (and (fboundp 'lm-header) + (format + " [>= %s]" + (with-temp-buffer + (insert-file-contents + (locate-library "magit.el" t)) + (lm-header "Package-Version")))))) + "") + (magit--safe-git-version) + emacs-version + system-type) + print-dest)) + (setq debug (reverse debug)) + (setq magit-version 'error) + (when magit-version + (push magit-version debug)) + (unless (equal (getenv "CI") "true") + ;; The repository is a sparse clone. + (message "Cannot determine Magit's version %S" debug))) + magit-version)) + +;;; Startup Asserts + +(defun magit-startup-asserts () + (when-let ((val (getenv "GIT_DIR"))) + (setenv "GIT_DIR") + (message + "Magit unset $GIT_DIR (was %S). See %s" val + ;; Note: Pass URL as argument rather than embedding in the format + ;; string to prevent the single quote from being rendered + ;; according to `text-quoting-style'. + "https://github.com/magit/magit/wiki/Don't-set-$GIT_DIR-and-alike")) + (when-let ((val (getenv "GIT_WORK_TREE"))) + (setenv "GIT_WORK_TREE") + (message + "Magit unset $GIT_WORK_TREE (was %S). See %s" val + ;; See comment above. + "https://github.com/magit/magit/wiki/Don't-set-$GIT_DIR-and-alike")) + ;; Git isn't required while building Magit. + (unless (bound-and-true-p byte-compile-current-file) + (magit-git-version-assert)) + (when (version< emacs-version magit--minimal-emacs) + (display-warning 'magit (format "\ +Magit requires Emacs >= %s, you are using %s. + +If this comes as a surprise to you, because you do actually have +a newer version installed, then that probably means that the +older version happens to appear earlier on the `$PATH'. If you +always start Emacs from a shell, then that can be fixed in the +shell's init file. If you start Emacs by clicking on an icon, +or using some sort of application launcher, then you probably +have to adjust the environment as seen by graphical interface. +For X11 something like ~/.xinitrc should work.\n" + magit--minimal-emacs emacs-version) + :error))) + +;;; Loading Libraries + +(provide 'magit) + +(cl-eval-when (load eval) + (require 'magit-status) + (require 'magit-refs) + (require 'magit-files) + (require 'magit-reset) + (require 'magit-branch) + (require 'magit-merge) + (require 'magit-tag) + (require 'magit-worktree) + (require 'magit-notes) + (require 'magit-sequence) + (require 'magit-commit) + (require 'magit-remote) + (require 'magit-clone) + (require 'magit-fetch) + (require 'magit-pull) + (require 'magit-push) + (require 'magit-bisect) + (require 'magit-stash) + (require 'magit-blame) + (require 'magit-obsolete) + (require 'magit-submodule) + (unless (load "magit-autoloads" t t) + (require 'magit-patch) + (require 'magit-subtree) + (require 'magit-ediff) + (require 'magit-gitignore) + (require 'magit-sparse-checkout) + (require 'magit-extras) + (require 'git-rebase) + (require 'magit-bookmark))) + +(with-eval-after-load 'bookmark + (require 'magit-bookmark)) + +(unless (bound-and-true-p byte-compile-current-file) + (if after-init-time + (progn (magit-startup-asserts) + (magit-version)) + (add-hook 'after-init-hook #'magit-startup-asserts t) + (add-hook 'after-init-hook #'magit-version t))) + +;;; magit.el ends here diff --git a/code/elpa/magit-20220821.1819/magit.info b/code/elpa/magit-20220821.1819/magit.info new file mode 100644 index 0000000..66d65ae --- /dev/null +++ b/code/elpa/magit-20220821.1819/magit.info @@ -0,0 +1,11242 @@ +This is magit.info, produced by makeinfo version 6.7 from magit.texi. + + Copyright (C) 2015-2022 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Magit: (magit). Using Git from Emacs with Magit. +END-INFO-DIR-ENTRY + + +File: magit.info, Node: Top, Next: Introduction, Up: (dir) + +Magit User Manual +***************** + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + +This manual is for Magit version 3.3.0-git. + + Copyright (C) 2015-2022 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +* Menu: + +* Introduction:: +* Installation:: +* Getting Started:: +* Interface Concepts:: +* Inspecting:: +* Manipulating:: +* Transferring:: +* Miscellaneous:: +* Customizing:: +* Plumbing:: +* FAQ:: +* Debugging Tools:: +* Keystroke Index:: +* Function and Command Index:: +* Variable Index:: + +— The Detailed Node Listing — + +Installation + +* Installing from Melpa:: +* Installing from the Git Repository:: +* Post-Installation Tasks:: + +Interface Concepts + +* Modes and Buffers:: +* Sections:: +* Transient Commands:: +* Transient Arguments and Buffer Variables:: +* Completion, Confirmation and the Selection: Completion Confirmation and the Selection. +* Mouse Support:: +* Running Git:: + +Modes and Buffers + +* Switching Buffers:: +* Naming Buffers:: +* Quitting Windows:: +* Automatic Refreshing of Magit Buffers:: +* Automatic Saving of File-Visiting Buffers:: +* Automatic Reverting of File-Visiting Buffers:: + + +Sections + +* Section Movement:: +* Section Visibility:: +* Section Hooks:: +* Section Types and Values:: +* Section Options:: + + +Completion, Confirmation and the Selection + +* Action Confirmation:: +* Completion and Confirmation:: +* The Selection:: +* The hunk-internal region:: +* Support for Completion Frameworks:: +* Additional Completion Options:: + + +Running Git + +* Viewing Git Output:: +* Git Process Status:: +* Running Git Manually:: +* Git Executable:: +* Global Git Arguments:: + + +Inspecting + +* Status Buffer:: +* Repository List:: +* Logging:: +* Diffing:: +* Ediffing:: +* References Buffer:: +* Bisecting:: +* Visiting Files and Blobs:: +* Blaming:: + +Status Buffer + +* Status Sections:: +* Status Header Sections:: +* Status Module Sections:: +* Status Options:: + + +Logging + +* Refreshing Logs:: +* Log Buffer:: +* Log Margin:: +* Select from Log:: +* Reflog:: +* Cherries:: + + +Diffing + +* Refreshing Diffs:: +* Commands Available in Diffs:: +* Diff Options:: +* Revision Buffer:: + + +References Buffer + +* References Sections:: + + +Visiting Files and Blobs + +* General-Purpose Visit Commands:: +* Visiting Files and Blobs from a Diff:: + + +Manipulating + +* Creating Repository:: +* Cloning Repository:: +* Staging and Unstaging:: +* Applying:: +* Committing:: +* Branching:: +* Merging:: +* Resolving Conflicts:: +* Rebasing:: +* Cherry Picking:: +* Resetting:: +* Stashing:: + +Staging and Unstaging + +* Staging from File-Visiting Buffers:: + + +Committing + +* Initiating a Commit:: +* Editing Commit Messages:: + + +Branching + +* The Two Remotes:: +* Branch Commands:: +* Branch Git Variables:: +* Auxiliary Branch Commands:: + + +Rebasing + +* Editing Rebase Sequences:: +* Information About In-Progress Rebase:: + + +Cherry Picking + +* Reverting:: + + +Transferring + +* Remotes:: +* Fetching:: +* Pulling:: +* Pushing:: +* Plain Patches:: +* Maildir Patches:: + +Remotes + +* Remote Commands:: +* Remote Git Variables:: + + +Miscellaneous + +* Tagging:: +* Notes:: +* Submodules:: +* Subtree:: +* Worktree:: +* Sparse checkouts:: +* Bundle:: +* Common Commands:: +* Wip Modes:: +* Commands for Buffers Visiting Files:: +* Minor Mode for Buffers Visiting Blobs:: + +Submodules + +* Listing Submodules:: +* Submodule Transient:: + + +Wip Modes + +* Wip Graph:: +* Legacy Wip Modes:: + + +Customizing + +* Per-Repository Configuration:: +* Essential Settings:: + +Essential Settings + +* Safety:: +* Performance:: +* Default Bindings:: + + +Plumbing + +* Calling Git:: +* Section Plumbing:: +* Refreshing Buffers:: +* Conventions:: + +Calling Git + +* Getting a Value from Git:: +* Calling Git for Effect:: + + +Section Plumbing + +* Creating Sections:: +* Section Selection:: +* Matching Sections:: + + +Conventions + +* Theming Faces:: + + +FAQ + +* FAQ - How to ...?:: +* FAQ - Issues and Errors:: + +FAQ - How to ...? + +* How to pronounce Magit?:: +* How to show git's output?:: +* How to install the gitman info manual?:: +* How to show diffs for gpg-encrypted files?:: +* How does branching and pushing work?:: +* Should I disable VC?:: + + +FAQ - Issues and Errors + +* Magit is slow:: +* I changed several thousand files at once and now Magit is unusable:: +* I am having problems committing:: +* I am using MS Windows and cannot push with Magit:: +* I am using macOS and SOMETHING works in shell, but not in Magit: I am using macOS and SOMETHING works in shell but not in Magit. +* Expanding a file to show the diff causes it to disappear:: +* Point is wrong in the COMMIT_EDITMSG buffer:: +* The mode-line information isn't always up-to-date:: +* A branch and tag sharing the same name breaks SOMETHING:: +* My Git hooks work on the command-line but not inside Magit:: +* git-commit-mode isn't used when committing from the command-line:: +* Point ends up inside invisible text when jumping to a file-visiting buffer:: +* I am unable to stage when using Tramp from MS Windows:: +* I am no longer able to save popup defaults:: + + + + +File: magit.info, Node: Introduction, Next: Installation, Prev: Top, Up: Top + +1 Introduction +************** + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + + Staging and otherwise applying changes is one of the most important +features in a Git porcelain and here Magit outshines anything else, +including Git itself. Git’s own staging interface (‘git add --patch’) +is so cumbersome that many users only use it in exceptional cases. In +Magit staging a hunk or even just part of a hunk is as trivial as +staging all changes made to a file. + + The most visible part of Magit’s interface is the status buffer, +which displays information about the current repository. Its content is +created by running several Git commands and making their output +actionable. Among other things, it displays information about the +current branch, lists unpulled and unpushed changes and contains +sections displaying the staged and unstaged changes. That might sound +noisy, but, since sections are collapsible, it’s not. + + To stage or unstage a change one places the cursor on the change and +then types ‘s’ or ‘u’. The change can be a file or a hunk, or when the +region is active (i.e. when there is a selection) several files or +hunks, or even just part of a hunk. The change or changes that these +commands - and many others - would act on are highlighted. + + Magit also implements several other "apply variants" in addition to +staging and unstaging. One can discard or reverse a change, or apply it +to the working tree. Git’s own porcelain only supports this for staging +and unstaging and you would have to do something like ‘git diff ... | +??? | git apply ...’ to discard, revert, or apply a single hunk on the +command line. In fact that’s exactly what Magit does internally (which +is what lead to the term "apply variants"). + + Magit isn’t just for Git experts, but it does assume some prior +experience with Git as well as Emacs. That being said, many users have +reported that using Magit was what finally taught them what Git is +capable of and how to use it to its fullest. Other users wished they +had switched to Emacs sooner so that they would have gotten their hands +on Magit earlier. + + While one has to know the basic features of Emacs to be able to make +full use of Magit, acquiring just enough Emacs skills doesn’t take long +and is worth it, even for users who prefer other editors. Vim users are +advised to give Evil (https://github.com/emacs-evil/evil), the +"Extensible VI Layer for Emacs", and Spacemacs +(https://github.com/syl20bnr/spacemacs), an "Emacs starter-kit focused +on Evil" a try. + + Magit provides a consistent and efficient Git porcelain. After a +short learning period, you will be able to perform most of your daily +version control tasks faster than you would on the command line. You +will likely also start using features that seemed too daunting in the +past. + + Magit fully embraces Git. It exposes many advanced features using a +simple but flexible interface instead of only wrapping the trivial ones +like many GUI clients do. Of course Magit supports logging, cloning, +pushing, and other commands that usually don’t fail in spectacular ways; +but it also supports tasks that often cannot be completed in a single +step. Magit fully supports tasks such as merging, rebasing, +cherry-picking, reverting, and blaming by not only providing a command +to initiate these tasks but also by displaying context sensitive +information along the way and providing commands that are useful for +resolving conflicts and resuming the sequence after doing so. + + Magit wraps and in many cases improves upon at least the following +Git porcelain commands: ‘add’, ‘am’, ‘bisect’, ‘blame’, ‘branch’, +‘checkout’, ‘cherry’, ‘cherry-pick’, ‘clean’, ‘clone’, ‘commit’, +‘config’, ‘describe’, ‘diff’, ‘fetch’, ‘format-patch’, ‘init’, ‘log’, +‘merge’, ‘merge-tree’, ‘mv’, ‘notes’, ‘pull’, ‘rebase’, ‘reflog’, +‘remote’, ‘request-pull’, ‘reset’, ‘revert’, ‘rm’, ‘show’, ‘stash’, +‘submodule’, ‘subtree’, ‘tag’, and ‘worktree.’ Many more Magit porcelain +commands are implemented on top of Git plumbing commands. + + +File: magit.info, Node: Installation, Next: Getting Started, Prev: Introduction, Up: Top + +2 Installation +************** + +Magit can be installed using Emacs’ package manager or manually from its +development repository. + +* Menu: + +* Installing from Melpa:: +* Installing from the Git Repository:: +* Post-Installation Tasks:: + + +File: magit.info, Node: Installing from Melpa, Next: Installing from the Git Repository, Up: Installation + +2.1 Installing from Melpa +========================= + +Magit is available from Melpa and Melpa-Stable. If you haven’t used +Emacs’ package manager before, then it is high time you familiarize +yourself with it by reading the documentation in the Emacs manual, see +*note (emacs)Packages::. Then add one of the archives to +‘package-archives’: + + • To use Melpa: + + (require 'package) + (add-to-list 'package-archives + '("melpa" . "http://melpa.org/packages/") t) + + • To use Melpa-Stable: + + (require 'package) + (add-to-list 'package-archives + '("melpa-stable" . "http://stable.melpa.org/packages/") t) + + Once you have added your preferred archive, you need to update the +local package list using: + + M-x package-refresh-contents RET + + Once you have done that, you can install Magit and its dependencies +using: + + M-x package-install RET magit RET + + Now see *note Post-Installation Tasks::. + + +File: magit.info, Node: Installing from the Git Repository, Next: Post-Installation Tasks, Prev: Installing from Melpa, Up: Installation + +2.2 Installing from the Git Repository +====================================== + +Magit depends on the ‘dash’, ‘transient’ and ‘with-editor’ libraries +which are available from Melpa and Melpa-Stable. Install them using +‘M-x package-install RET RET’. Of course you may also install +them manually from their repository. + + Then clone the Magit repository: + + $ git clone https://github.com/magit/magit.git ~/.emacs.d/site-lisp/magit + $ cd ~/.emacs.d/site-lisp/magit + + Then compile the libraries and generate the info manuals: + + $ make + + If you haven’t installed ‘dash’, ‘transient’ and ‘with-editor’ from +Melpa or at ‘/path/to/magit/../’, then you have to tell ‘make’ +where to find them. To do so create the file ‘/path/to/magit/config.mk’ +with the following content before running ‘make’: + + LOAD_PATH = -L ~/.emacs.d/site-lisp/magit/lisp + LOAD_PATH += -L ~/.emacs.d/site-lisp/dash + LOAD_PATH += -L ~/.emacs.d/site-lisp/transient/lisp + LOAD_PATH += -L ~/.emacs.d/site-lisp/with-editor + + Finally add this to your init file: + + (add-to-list 'load-path "~/.emacs.d/site-lisp/magit/lisp") + (require 'magit) + + (with-eval-after-load 'info + (info-initialize) + (add-to-list 'Info-directory-list + "~/.emacs.d/site-lisp/magit/Documentation/")) + + Of course if you installed the dependencies manually as well, then +you have to tell Emacs about them too, by prefixing the above with: + + (add-to-list 'load-path "~/.emacs.d/site-lisp/dash") + (add-to-list 'load-path "~/.emacs.d/site-lisp/transient/lisp") + (add-to-list 'load-path "~/.emacs.d/site-lisp/with-editor") + + Note that you have to add the ‘lisp’ subdirectory to the ‘load-path’, +not the top-level of the repository, and that elements of ‘load-path’ +should not end with a slash, while those of ‘Info-directory-list’ +should. + + Instead of requiring the feature ‘magit’, you could load just the +autoload definitions, by loading the file ‘magit-autoloads.el’. + + (load "/path/to/magit/lisp/magit-autoloads") + + Instead of running Magit directly from the repository by adding that +to the ‘load-path’, you might want to instead install it in some other +directory using ‘sudo make install’ and setting ‘load-path’ accordingly. + + To update Magit use: + + $ git pull + $ make + + At times it might be necessary to run ‘make clean all’ instead. + + To view all available targets use ‘make help’. + + Now see *note Post-Installation Tasks::. + + +File: magit.info, Node: Post-Installation Tasks, Prev: Installing from the Git Repository, Up: Installation + +2.3 Post-Installation Tasks +=========================== + +After installing Magit you should verify that you are indeed using the +Magit, Git, and Emacs releases you think you are using. It’s best to +restart Emacs before doing so, to make sure you are not using an +outdated value for ‘load-path’. + + M-x magit-version RET + + should display something like + + Magit 2.8.0, Git 2.10.2, Emacs 25.1.1, gnu/linux + + Then you might also want to read about options that many users likely +want to customize. See *note Essential Settings::. + + To be able to follow cross references to Git manpages found in this +manual, you might also have to manually install the ‘gitman’ info +manual, or advice ‘Info-follow-nearest-node’ to instead open the actual +manpage. See *note How to install the gitman info manual?::. + + If you are completely new to Magit then see *note Getting Started::. + + If you run into problems, then please see the *note FAQ::. Also see +the *note Debugging Tools::. + + And last but not least please consider making a donation, to ensure +that I can keep working on Magit. See . +for various donation options. + + +File: magit.info, Node: Getting Started, Next: Interface Concepts, Prev: Installation, Up: Top + +3 Getting Started +***************** + +This short tutorial describes the most essential features that many +Magitians use on a daily basis. It only scratches the surface but +should be enough to get you started. + + IMPORTANT: It is safest if you clone some repository just for this +tutorial. Alternatively you can use an existing local repository, but +if you do that, then you should commit all uncommitted changes before +proceeding. + + Type ‘C-x g’ to display information about the current Git repository +in a dedicated buffer, called the status buffer. + + Most Magit commands are commonly invoked from the status buffer. It +can be considered the primary interface for interacting with Git using +Magit. Many other Magit buffers may exist at a given time, but they are +often created from this buffer. + + Depending on what state your repository is in, this buffer may +contain sections titled "Staged changes", "Unstaged changes", "Unmerged +into origin/master", "Unpushed to origin/master", and many others. + + Since we are starting from a safe state, which you can easily return +to (by doing a ‘git reset --hard PRE-MAGIT-STATE’), there currently are +no staged or unstaged changes. Edit some files and save the changes. +Then go back to the status buffer, while at the same time refreshing it, +by typing ‘C-x g’. (When the status buffer, or any Magit buffer for +that matter, is the current buffer, then you can also use just ‘g’ to +refresh it). + + Move between sections using ‘p’ and ‘n’. Note that the bodies of +some sections are hidden. Type ‘TAB’ to expand or collapse the section +at point. You can also use ‘C-tab’ to cycle the visibility of the +current section and its children. Move to a file section inside the +section named "Unstaged changes" and type ‘s’ to stage the changes you +have made to that file. That file now appears under "Staged changes". + + Magit can stage and unstage individual hunks, not just complete +files. Move to the file you have just staged, expand it using ‘TAB’, +move to one of the hunks using ‘n’, and unstage just that by typing ‘u’. +Note how the staging (‘s’) and unstaging (‘u’) commands operate on the +change at point. Many other commands behave the same way. + + You can also un-/stage just part of a hunk. Inside the body of a +hunk section (move there using ‘C-n’), set the mark using ‘C-SPC’ and +move down until some added and/or removed lines fall inside the region +but not all of them. Again type ‘s’ to stage. + + It is also possible to un-/stage multiple files at once. Move to a +file section, type ‘C-SPC’, move to the next file using ‘n’, and then +‘s’ to stage both files. Note that both the mark and point have to be +on the headings of sibling sections for this to work. If the region +looks like it does in other buffers, then it doesn’t select Magit +sections that can be acted on as a unit. + + And then of course you want to commit your changes. Type ‘c’. This +shows the available commit commands and arguments in a buffer at the +bottom of the frame. Each command and argument is prefixed with the key +that invokes/sets it. Do not worry about this for now. We want to +create a "normal" commit, which is done by typing ‘c’ again. + + Now two new buffers appear. One is for writing the commit message, +the other shows a diff with the changes that you are about to commit. +Write a message and then type ‘C-c C-c’ to actually create the commit. + + You probably don’t want to push the commit you just created because +you just committed some random changes, but if that is not the case you +could push it by typing ‘P’ to show all the available push commands and +arguments and then ‘p’ to push to a branch with the same name as the +local branch onto the remote configured as the push-remote. (If the +push-remote is not configured yet, then you would first be prompted for +the remote to push to.) + + So far we have mentioned the commit, push, and log menu commands. +These are probably among the menus you will be using the most, but many +others exist. To show a menu that lists all other menus (as well as the +various apply commands and some other essential commands), type ‘h’. +Try a few. (Such menus are also called "transient prefix commands" or +just "transients".) + + The key bindings in that menu correspond to the bindings in Magit +buffers, including but not limited to the status buffer. So you could +type ‘h d’ to bring up the diff menu, but once you remember that "d" +stands for "diff", you would usually do so by just typing ‘d’. But this +"prefix of prefixes" is useful even once you have memorized all the +bindings, as it can provide easy access to Magit commands from non-Magit +buffers. The global binding is ‘C-x M-g’. + + In file visiting buffers ‘C-c M-g’ brings up a similar menu featuring +commands that act on just the visited file, see *note Commands for +Buffers Visiting Files::. + + Magit also provides a context menu and other mouse commands, see +*note Mouse Support::. + + It is not necessary that you do so now, but if you stick with Magit, +then it is highly recommended that you read the next section too. + + +File: magit.info, Node: Interface Concepts, Next: Inspecting, Prev: Getting Started, Up: Top + +4 Interface Concepts +******************** + +* Menu: + +* Modes and Buffers:: +* Sections:: +* Transient Commands:: +* Transient Arguments and Buffer Variables:: +* Completion, Confirmation and the Selection: Completion Confirmation and the Selection. +* Mouse Support:: +* Running Git:: + + +File: magit.info, Node: Modes and Buffers, Next: Sections, Up: Interface Concepts + +4.1 Modes and Buffers +===================== + +Magit provides several major-modes. For each of these modes there +usually exists only one buffer per repository. Separate modes and thus +buffers exist for commits, diffs, logs, and some other things. + + Besides these special purpose buffers, there also exists an overview +buffer, called the *status buffer*. It’s usually from this buffer that +the user invokes Git commands, or creates or visits other buffers. + + In this manual we often speak about "Magit buffers". By that we mean +buffers whose major-modes derive from ‘magit-mode’. + +‘M-x magit-toggle-buffer-lock’ + This command locks the current buffer to its value or if the buffer + is already locked, then it unlocks it. + + Locking a buffer to its value prevents it from being reused to + display another value. The name of a locked buffer contains its + value, which allows telling it apart from other locked buffers and + the unlocked buffer. + + Not all Magit buffers can be locked to their values; for example, + it wouldn’t make sense to lock a status buffer. + + There can only be a single unlocked buffer using a certain + major-mode per repository. So when a buffer is being unlocked and + another unlocked buffer already exists for that mode and + repository, then the former buffer is instead deleted and the + latter is displayed in its place. + +* Menu: + +* Switching Buffers:: +* Naming Buffers:: +* Quitting Windows:: +* Automatic Refreshing of Magit Buffers:: +* Automatic Saving of File-Visiting Buffers:: +* Automatic Reverting of File-Visiting Buffers:: + + +File: magit.info, Node: Switching Buffers, Next: Naming Buffers, Up: Modes and Buffers + +4.1.1 Switching Buffers +----------------------- + + -- Function: magit-display-buffer buffer &optional display-function + This function is a wrapper around ‘display-buffer’ and is used to + display any Magit buffer. It displays BUFFER in some window and, + unlike ‘display-buffer’, also selects that window, provided + ‘magit-display-buffer-noselect’ is ‘nil’. It also runs the hooks + mentioned below. + + If optional DISPLAY-FUNCTION is non-nil, then that is used to + display the buffer. Usually that is ‘nil’ and the function + specified by ‘magit-display-buffer-function’ is used. + + -- Variable: magit-display-buffer-noselect + When this is non-nil, then ‘magit-display-buffer’ only displays the + buffer but forgoes also selecting the window. This variable should + not be set globally, it is only intended to be let-bound, by code + that automatically updates "the other window". This is used for + example when the revision buffer is updated when you move inside + the log buffer. + + -- User Option: magit-display-buffer-function + The function specified here is called by ‘magit-display-buffer’ + with one argument, a buffer, to actually display that buffer. This + function should call ‘display-buffer’ with that buffer as first and + a list of display actions as second argument. + + Magit provides several functions, listed below, that are suitable + values for this option. If you want to use different rules, then a + good way of doing that is to start with a copy of one of these + functions and then adjust it to your needs. + + Instead of using a wrapper around ‘display-buffer’, that function + itself can be used here, in which case the display actions have to + be specified by adding them to ‘display-buffer-alist’ instead. + + To learn about display actions, see *note (elisp)Choosing Window::. + + -- Function: magit-display-buffer-traditional buffer + This function is the current default value of the option + ‘magit-display-buffer-function’. Before that option and this + function were added, the behavior was hard-coded in many places all + over the code base but now all the rules are contained in this one + function (except for the "noselect" special case mentioned above). + + -- Function: magit-display-buffer-same-window-except-diff-v1 + This function displays most buffers in the currently selected + window. If a buffer’s mode derives from ‘magit-diff-mode’ or + ‘magit-process-mode’, it is displayed in another window. + + -- Function: magit-display-buffer-fullframe-status-v1 + This function fills the entire frame when displaying a status + buffer. Otherwise, it behaves like + ‘magit-display-buffer-traditional’. + + -- Function: magit-display-buffer-fullframe-status-topleft-v1 + This function fills the entire frame when displaying a status + buffer. It behaves like ‘magit-display-buffer-fullframe-status-v1’ + except that it displays buffers that derive from ‘magit-diff-mode’ + or ‘magit-process-mode’ to the top or left of the current buffer + rather than to the bottom or right. As a result, Magit buffers + tend to pop up on the same side as they would if + ‘magit-display-buffer-traditional’ were in use. + + -- Function: magit-display-buffer-fullcolumn-most-v1 + This function displays most buffers so that they fill the entire + height of the frame. However, the buffer is displayed in another + window if (1) the buffer’s mode derives from ‘magit-process-mode’, + or (2) the buffer’s mode derives from ‘magit-diff-mode’, provided + that the mode of the current buffer derives from ‘magit-log-mode’ + or ‘magit-cherry-mode’. + + -- User Option: magit-pre-display-buffer-hook + This hook is run by ‘magit-display-buffer’ before displaying the + buffer. + + -- Function: magit-save-window-configuration + This function saves the current window configuration. Later when + the buffer is buried, it may be restored by + ‘magit-restore-window-configuration’. + + -- User Option: magit-post-display-buffer-hook + This hook is run by ‘magit-display-buffer’ after displaying the + buffer. + + -- Function: magit-maybe-set-dedicated + This function remembers if a new window had to be created to + display the buffer, or whether an existing window was reused. This + information is later used by ‘magit-mode-quit-window’, to determine + whether the window should be deleted when its last Magit buffer is + buried. + + +File: magit.info, Node: Naming Buffers, Next: Quitting Windows, Prev: Switching Buffers, Up: Modes and Buffers + +4.1.2 Naming Buffers +-------------------- + + -- User Option: magit-generate-buffer-name-function + The function used to generate the names of Magit buffers. + + Such a function should take the options + ‘magit-uniquify-buffer-names’ as well as ‘magit-buffer-name-format’ + into account. If it doesn’t, then should be clearly stated in the + doc-string. And if it supports %-sequences beyond those mentioned + in the doc-string of the option ‘magit-buffer-name-format’, then + its own doc-string should describe the additions. + + -- Function: magit-generate-buffer-name-default-function mode + This function returns a buffer name suitable for a buffer whose + major-mode is MODE and which shows information about the repository + in which ‘default-directory’ is located. + + This function uses ‘magit-buffer-name-format’ and supporting all of + the %-sequences mentioned the documentation of that option. It + also respects the option ‘magit-uniquify-buffer-names’. + + -- User Option: magit-buffer-name-format + The format string used to name Magit buffers. + + At least the following %-sequences are supported: + + • ‘%m’ + + The name of the major-mode, but with the ‘-mode’ suffix + removed. + + • ‘%M’ + + Like ‘%m’ but abbreviate ‘magit-status-mode’ as ‘magit’. + + • ‘%v’ + + The value the buffer is locked to, in parentheses, or an empty + string if the buffer is not locked to a value. + + • ‘%V’ + + Like ‘%v’, but the string is prefixed with a space, unless it + is an empty string. + + • ‘%t’ + + The top-level directory of the working tree of the repository, + or if ‘magit-uniquify-buffer-names’ is non-nil an abbreviation + of that. + + • ‘%x’ + + If ‘magit-uniquify-buffer-names’ is nil "*", otherwise the + empty string. Due to limitations of the ‘uniquify’ package, + buffer names must end with the path. + + • ‘%T’ + + Obsolete, use "%t%x" instead. Like ‘%t’, but append an + asterisk if and only if ‘magit-uniquify-buffer-names’ is nil. + + The value should always contain ‘%m’ or ‘%M’, ‘%v’ or ‘%V’, and + ‘%t’ (or the obsolete ‘%T’). If ‘magit-uniquify-buffer-names’ is + non-nil, then the value must end with ‘%t’ or ‘%t%x’ (or the + obsolete ‘%T’). See issue #2841. + + -- User Option: magit-uniquify-buffer-names + This option controls whether the names of Magit buffers are + uniquified. If the names are not being uniquified, then they + contain the full path of the top-level of the working tree of the + corresponding repository. If they are being uniquified, then they + end with the basename of the top-level, or if that would conflict + with the name used for other buffers, then the names of all these + buffers are adjusted until they no longer conflict. + + This is done using the ‘uniquify’ package; customize its options to + control how buffer names are uniquified. + + +File: magit.info, Node: Quitting Windows, Next: Automatic Refreshing of Magit Buffers, Prev: Naming Buffers, Up: Modes and Buffers + +4.1.3 Quitting Windows +---------------------- + +‘q’ (‘magit-mode-bury-buffer’) + This command buries the current Magit buffer. + + With a prefix argument, it instead kills the buffer. With a double + prefix argument, also kills all other Magit buffers associated with + the current project. + + -- User Option: magit-bury-buffer-function + The function used to actually bury or kill the current buffer. + + ‘magit-mode-bury-buffer’ calls this function with one argument. If + the argument is non-nil, then the function has to kill the current + buffer. Otherwise it has to bury it alive. The default value + currently is ‘magit-restore-window-configuration’. + + -- Function: magit-restore-window-configuration kill-buffer + Bury or kill the current buffer using ‘quit-window’, which is + called with KILL-BUFFER as first and the selected window as second + argument. + + Then restore the window configuration that existed right before the + current buffer was displayed in the selected frame. Unfortunately + that also means that point gets adjusted in all the buffers, which + are being displayed in the selected frame. + + -- Function: magit-mode-quit-window kill-buffer + Bury or kill the current buffer using ‘quit-window’, which is + called with KILL-BUFFER as first and the selected window as second + argument. + + Then, if the window was originally created to display a Magit + buffer and the buried buffer was the last remaining Magit buffer + that was ever displayed in the window, then that is deleted. + + +File: magit.info, Node: Automatic Refreshing of Magit Buffers, Next: Automatic Saving of File-Visiting Buffers, Prev: Quitting Windows, Up: Modes and Buffers + +4.1.4 Automatic Refreshing of Magit Buffers +------------------------------------------- + +After running a command which may change the state of the current +repository, the current Magit buffer and the corresponding status buffer +are refreshed. The status buffer can be automatically refreshed +whenever a buffer is saved to a file inside the respective repository by +adding a hook, like so: + + (with-eval-after-load 'magit-mode + (add-hook 'after-save-hook 'magit-after-save-refresh-status t)) + + Automatically refreshing Magit buffers ensures that the displayed +information is up-to-date most of the time but can lead to a noticeable +delay in big repositories. Other Magit buffers are not refreshed to +keep the delay to a minimum and also because doing so can sometimes be +undesirable. + + Buffers can also be refreshed explicitly, which is useful in buffers +that weren’t current during the last refresh and after changes were made +to the repository outside of Magit. + +‘g’ (‘magit-refresh’) + This command refreshes the current buffer if its major mode derives + from ‘magit-mode’ as well as the corresponding status buffer. + + If the option ‘magit-revert-buffers’ calls for it, then it also + reverts all unmodified buffers that visit files being tracked in + the current repository. + +‘G’ (‘magit-refresh-all’) + This command refreshes all Magit buffers belonging to the current + repository and also reverts all unmodified buffers that visit files + being tracked in the current repository. + + The file-visiting buffers are always reverted, even if + ‘magit-revert-buffers’ is nil. + + -- User Option: magit-refresh-buffer-hook + This hook is run in each Magit buffer that was refreshed during the + current refresh - normally the current buffer and the status + buffer. + + -- User Option: magit-refresh-status-buffer + When this option is non-nil, then the status buffer is + automatically refreshed after running git for side-effects, in + addition to the current Magit buffer, which is always refreshed + automatically. + + Only set this to nil after exhausting all other options to improve + performance. + + -- Function: magit-after-save-refresh-status + This function is intended to be added to ‘after-save-hook’. After + doing that the corresponding status buffer is refreshed whenever a + buffer is saved to a file inside a repository. + + Note that refreshing a Magit buffer is done by re-creating its + contents from scratch, which can be slow in large repositories. If + you are not satisfied with Magit’s performance, then you should + obviously not add this function to that hook. + + +File: magit.info, Node: Automatic Saving of File-Visiting Buffers, Next: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Refreshing of Magit Buffers, Up: Modes and Buffers + +4.1.5 Automatic Saving of File-Visiting Buffers +----------------------------------------------- + +File-visiting buffers are by default saved at certain points in time. +This doesn’t guarantee that Magit buffers are always up-to-date, but, +provided one only edits files by editing them in Emacs and uses only +Magit to interact with Git, one can be fairly confident. When in doubt +or after outside changes, type ‘g’ (‘magit-refresh’) to save and refresh +explicitly. + + -- User Option: magit-save-repository-buffers + This option controls whether file-visiting buffers are saved before + certain events. + + If this is non-nil then all modified file-visiting buffers + belonging to the current repository may be saved before running + commands, before creating new Magit buffers, and before explicitly + refreshing such buffers. If this is ‘dontask’ then this is done + without user intervention. If it is ‘t’ then the user has to + confirm each save. + + +File: magit.info, Node: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Saving of File-Visiting Buffers, Up: Modes and Buffers + +4.1.6 Automatic Reverting of File-Visiting Buffers +-------------------------------------------------- + +By default Magit automatically reverts buffers that are visiting files +that are being tracked in a Git repository, after they have changed on +disk. When using Magit one often changes files on disk by running Git, +i.e. "outside Emacs", making this a rather important feature. + + For example, if you discard a change in the status buffer, then that +is done by running ‘git apply --reverse ...’, and Emacs considers the +file to have "changed on disk". If Magit did not automatically revert +the buffer, then you would have to type ‘M-x revert-buffer RET RET’ in +the visiting buffer before you could continue making changes. + + -- User Option: magit-auto-revert-mode + When this mode is enabled, then buffers that visit tracked files + are automatically reverted after the visited files change on disk. + + -- User Option: global-auto-revert-mode + When this mode is enabled, then any file-visiting buffer is + automatically reverted after the visited file changes on disk. + + If you like buffers that visit tracked files to be automatically + reverted, then you might also like any buffer to be reverted, not + just those visiting tracked files. If that is the case, then + enable this mode _instead of_ ‘magit-auto-revert-mode’. + + -- User Option: magit-auto-revert-immediately + This option controls whether Magit reverts buffers immediately. + + If this is non-nil and either ‘global-auto-revert-mode’ or + ‘magit-auto-revert-mode’ is enabled, then Magit immediately reverts + buffers by explicitly calling ‘auto-revert-buffers’ after running + Git for side-effects. + + If ‘auto-revert-use-notify’ is non-nil (and file notifications are + actually supported), then ‘magit-auto-revert-immediately’ does not + have to be non-nil, because the reverts happen immediately anyway. + + If ‘magit-auto-revert-immediately’ and ‘auto-revert-use-notify’ are + both ‘nil’, then reverts happen after ‘auto-revert-interval’ + seconds of user inactivity. That is not desirable. + + -- User Option: auto-revert-use-notify + This option controls whether file notification functions should be + used. Note that this variable unfortunately defaults to ‘t’ even + on systems on which file notifications cannot be used. + + -- User Option: magit-auto-revert-tracked-only + This option controls whether ‘magit-auto-revert-mode’ only reverts + tracked files or all files that are located inside Git + repositories, including untracked files and files located inside + Git’s control directory. + + -- User Option: auto-revert-mode + The global mode ‘magit-auto-revert-mode’ works by turning on this + local mode in the appropriate buffers (but + ‘global-auto-revert-mode’ is implemented differently). You can + also turn it on or off manually, which might be necessary if Magit + does not notice that a previously untracked file now is being + tracked or vice-versa. + + -- User Option: auto-revert-stop-on-user-input + This option controls whether the arrival of user input suspends the + automatic reverts for ‘auto-revert-interval’ seconds. + + -- User Option: auto-revert-interval + This option controls how many seconds Emacs waits for before + resuming suspended reverts. + + -- User Option: auto-revert-buffer-list-filter + This option specifies an additional filter used by + ‘auto-revert-buffers’ to determine whether a buffer should be + reverted or not. + + This option is provided by Magit, which also advises + ‘auto-revert-buffers’ to respect it. Magit users who do not turn + on the local mode ‘auto-revert-mode’ themselves, are best served by + setting the value to ‘magit-auto-revert-repository-buffer-p’. + + However the default is nil, so as not to disturb users who do use + the local mode directly. If you experience delays when running + Magit commands, then you should consider using one of the + predicates provided by Magit - especially if you also use Tramp. + + Users who do turn on ‘auto-revert-mode’ in buffers in which Magit + doesn’t do that for them, should likely not use any filter. Users + who turn on ‘global-auto-revert-mode’, do not have to worry about + this option, because it is disregarded if the global mode is + enabled. + + -- User Option: auto-revert-verbose + This option controls whether Emacs reports when a buffer has been + reverted. + + The options with the ‘auto-revert-’ prefix are located in the Custom +group named ‘auto-revert’. The other, Magit-specific, options are +located in the ‘magit’ group. + +* Menu: + +* Risk of Reverting Automatically:: + + +File: magit.info, Node: Risk of Reverting Automatically, Up: Automatic Reverting of File-Visiting Buffers + +Risk of Reverting Automatically +............................... + +For the vast majority of users, automatically reverting file-visiting +buffers after they have changed on disk is harmless. + + If a buffer is modified (i.e. it contains changes that haven’t been +saved yet), then Emacs will refuse to automatically revert it. If you +save a previously modified buffer, then that results in what is seen by +Git as an uncommitted change. Git will then refuse to carry out any +commands that would cause these changes to be lost. In other words, if +there is anything that could be lost, then either Git or Emacs will +refuse to discard the changes. + + However, if you use file-visiting buffers as a sort of ad hoc +"staging area", then the automatic reverts could potentially cause data +loss. So far I have heard from only one user who uses such a workflow. + + An example: You visit some file in a buffer, edit it, and save the +changes. Then, outside of Emacs (or at least not using Magit or by +saving the buffer) you change the file on disk again. At this point the +buffer is the only place where the intermediate version still exists. +You have saved the changes to disk, but that has since been overwritten. +Meanwhile Emacs considers the buffer to be unmodified (because you have +not made any changes to it since you last saved it to the visited file) +and therefore would not object to it being automatically reverted. At +this point an Auto-Revert mode would kick in. It would check whether +the buffer is modified and since that is not the case it would revert +it. The intermediate version would be lost. (Actually you could still +get it back using the ‘undo’ command.) + + If your workflow depends on Emacs preserving the intermediate version +in the buffer, then you have to disable all Auto-Revert modes. But +please consider that such a workflow would be dangerous even without +using an Auto-Revert mode, and should therefore be avoided. If Emacs +crashes or if you quit Emacs by mistake, then you would also lose the +buffer content. There would be no autosave file still containing the +intermediate version (because that was deleted when you saved the +buffer) and you would not be asked whether you want to save the buffer +(because it isn’t modified). + + +File: magit.info, Node: Sections, Next: Transient Commands, Prev: Modes and Buffers, Up: Interface Concepts + +4.2 Sections +============ + +Magit buffers are organized into nested sections, which can be collapsed +and expanded, similar to how sections are handled in Org mode. Each +section also has a type, and some sections also have a value. For each +section type there can also be a local keymap, shared by all sections of +that type. + + Taking advantage of the section value and type, many commands operate +on the current section, or when the region is active and selects +sections of the same type, all of the selected sections. Commands that +only make sense for a particular section type (as opposed to just +behaving differently depending on the type) are usually bound in section +type keymaps. + +* Menu: + +* Section Movement:: +* Section Visibility:: +* Section Hooks:: +* Section Types and Values:: +* Section Options:: + + +File: magit.info, Node: Section Movement, Next: Section Visibility, Up: Sections + +4.2.1 Section Movement +---------------------- + +To move within a section use the usual keys (‘C-p’, ‘C-n’, ‘C-b’, ‘C-f’ +etc), whose global bindings are not shadowed. To move to another +section use the following commands. + +‘p’ (‘magit-section-backward’) + When not at the beginning of a section, then move to the beginning + of the current section. At the beginning of a section, instead + move to the beginning of the previous visible section. + +‘n’ (‘magit-section-forward’) + Move to the beginning of the next visible section. + +‘M-p’ (‘magit-section-backward-siblings’) + Move to the beginning of the previous sibling section. If there is + no previous sibling section, then move to the parent section + instead. + +‘M-n’ (‘magit-section-forward-siblings’) + Move to the beginning of the next sibling section. If there is no + next sibling section, then move to the parent section instead. + +‘^’ (‘magit-section-up’) + Move to the beginning of the parent of the current section. + + The above commands all call the hook ‘magit-section-movement-hook’. +Any of the functions listed below can be used as members of this hook. + + You might want to remove some of the functions that Magit adds using +‘add-hook’. In doing so you have to make sure you do not attempt to +remove function that haven’t even been added yet, for example: + + (with-eval-after-load 'magit-diff + (remove-hook 'magit-section-movement-hook + 'magit-hunk-set-window-start)) + + -- Variable: magit-section-movement-hook + This hook is run by all of the above movement commands, after + arriving at the destination. + + -- Function: magit-hunk-set-window-start + This hook function ensures that the beginning of the current + section is visible, provided it is a ‘hunk’ section. Otherwise, it + does nothing. + + Loading ‘magit-diff’ adds this function to the hook. + + -- Function: magit-section-set-window-start + This hook function ensures that the beginning of the current + section is visible, regardless of the section’s type. If you add + this to ‘magit-section-movement-hook’, then you must remove the + hunk-only variant in turn. + + -- Function: magit-log-maybe-show-more-commits + This hook function only has an effect in log buffers, and ‘point’ + is on the "show more" section. If that is the case, then it + doubles the number of commits that are being shown. + + Loading ‘magit-log’ adds this function to the hook. + + -- Function: magit-log-maybe-update-revision-buffer + When moving inside a log buffer, then this function updates the + revision buffer, provided it is already being displayed in another + window of the same frame. + + Loading ‘magit-log’ adds this function to the hook. + + -- Function: magit-log-maybe-update-blob-buffer + When moving inside a log buffer and another window of the same + frame displays a blob buffer, then this function instead displays + the blob buffer for the commit at point in that window. + + -- Function: magit-status-maybe-update-revision-buffer + When moving inside a status buffer, then this function updates the + revision buffer, provided it is already being displayed in another + window of the same frame. + + -- Function: magit-status-maybe-update-stash-buffer + When moving inside a status buffer, then this function updates the + stash buffer, provided it is already being displayed in another + window of the same frame. + + -- Function: magit-status-maybe-update-blob-buffer + When moving inside a status buffer and another window of the same + frame displays a blob buffer, then this function instead displays + the blob buffer for the commit at point in that window. + + -- Function: magit-stashes-maybe-update-stash-buffer + When moving inside a buffer listing stashes, then this function + updates the stash buffer, provided it is already being displayed in + another window of the same frame. + + -- User Option: magit-update-other-window-delay + Delay before automatically updating the other window. + + When moving around in certain buffers, then certain other buffers, + which are being displayed in another window, may optionally be + updated to display information about the section at point. + + When holding down a key to move by more than just one section, then + that would update that buffer for each section on the way. To + prevent that, updating the revision buffer is delayed, and this + option controls for how long. For optimal experience you might + have to adjust this delay and/or the keyboard repeat rate and delay + of your graphical environment or operating system. + + +File: magit.info, Node: Section Visibility, Next: Section Hooks, Prev: Section Movement, Up: Sections + +4.2.2 Section Visibility +------------------------ + +Magit provides many commands for changing the visibility of sections, +but all you need to get started are the next two. + +‘’ (‘magit-section-toggle’) + Toggle the visibility of the body of the current section. + +‘C-’ (‘magit-section-cycle’) + Cycle the visibility of current section and its children. + +‘M-’ (‘magit-section-cycle-diffs’) + Cycle the visibility of diff-related sections in the current + buffer. + +‘S-’ (‘magit-section-cycle-global’) + Cycle the visibility of all sections in the current buffer. + +‘1’ (‘magit-section-show-level-1’) +‘2’ (‘magit-section-show-level-2’) +‘3’ (‘magit-section-show-level-3’) +‘4’ (‘magit-section-show-level-4’) + Show sections surrounding the current section up to level N. + +‘M-1’ (‘magit-section-show-level-1-all’) +‘M-2’ (‘magit-section-show-level-2-all’) +‘M-3’ (‘magit-section-show-level-3-all’) +‘M-4’ (‘magit-section-show-level-4-all’) + Show all sections up to level N. + + Some functions, which are used to implement the above commands, are +also exposed as commands themselves. By default no keys are bound to +these commands, as they are generally perceived to be much less useful. +But your mileage may vary. + + -- Command: magit-section-show + Show the body of the current section. + + -- Command: magit-section-hide + Hide the body of the current section. + + -- Command: magit-section-show-headings + Recursively show headings of children of the current section. Only + show the headings. Previously shown text-only bodies are hidden. + + -- Command: magit-section-show-children + Recursively show the bodies of children of the current section. + With a prefix argument show children down to the level of the + current section, and hide deeper children. + + -- Command: magit-section-hide-children + Recursively hide the bodies of children of the current section. + + -- Command: magit-section-toggle-children + Toggle visibility of bodies of children of the current section. + + When a buffer is first created then some sections are shown expanded +while others are not. This is hard coded. When a buffer is refreshed +then the previous visibility is preserved. The initial visibility of +certain sections can also be overwritten using the hook +‘magit-section-set-visibility-hook’. + + -- User Option: magit-section-initial-visibility-alist + This options can be used to override the initial visibility of + sections. In the future it will also be used to define the + defaults, but currently a section’s default is still hardcoded. + + The value is an alist. Each element maps a section type or lineage + to the initial visibility state for such sections. The state has + to be one of ‘show’ or ‘hide’, or a function that returns one of + these symbols. A function is called with the section as the only + argument. + + Use the command ‘magit-describe-section-briefly’ to determine a + section’s lineage or type. The vector in the output is the section + lineage and the type is the first element of that vector. + Wildcards can be used, see ‘magit-section-match’. + + -- User Option: magit-section-cache-visibility + This option controls for which sections the previous visibility + state should be restored if a section disappears and later appears + again. The value is a boolean or a list of section types. If t, + then the visibility of all sections is cached. Otherwise this is + only done for sections whose type matches one of the listed types. + + This requires that the function ‘magit-section-cached-visibility’ + is a member of ‘magit-section-set-visibility-hook’. + + -- Variable: magit-section-set-visibility-hook + This hook is run when first creating a buffer and also when + refreshing an existing buffer, and is used to determine the + visibility of the section currently being inserted. + + Each function is called with one argument, the section being + inserted. It should return ‘hide’ or ‘show’, or to leave the + visibility undefined ‘nil’. If no function decides on the + visibility and the buffer is being refreshed, then the visibility + is preserved; or if the buffer is being created, then the hard + coded default is used. + + Usually this should only be used to set the initial visibility but + not during refreshes. If ‘magit-insert-section--oldroot’ is + non-nil, then the buffer is being refreshed and these functions + should immediately return ‘nil’. + + -- User Option: magit-section-visibility-indicator + This option controls whether and how to indicate that a section can + be expanded/collapsed. + + If nil, then no visibility indicators are shown. Otherwise the + value has to have one of these two forms: + + • ‘(EXPANDABLE-BITMAP . COLLAPSIBLE-BITMAP)’ + + Both values have to be variables whose values are fringe + bitmaps. In this case every section that can be expanded or + collapsed gets an indicator in the left fringe. + + To provide extra padding around the indicator, set + ‘left-fringe-width’ in ‘magit-mode-hook’, e.g.: + + (add-hook 'magit-mode-hook (lambda () + (setq left-fringe-width 20))) + + • ‘(STRING . BOOLEAN)’ + + In this case STRING (usually an ellipsis) is shown at the end + of the heading of every collapsed section. Expanded sections + get no indicator. The cdr controls whether the appearance of + these ellipsis take section highlighting into account. Doing + so might potentially have an impact on performance, while not + doing so is kinda ugly. + + +File: magit.info, Node: Section Hooks, Next: Section Types and Values, Prev: Section Visibility, Up: Sections + +4.2.3 Section Hooks +------------------- + +Which sections are inserted into certain buffers is controlled with +hooks. This includes the status and the refs buffers. For other +buffers, e.g. log and diff buffers, this is not possible. The command +‘magit-describe-section’ can be used to see which hook (if any) was +responsible for inserting the section at point. + + For buffers whose sections can be customized by the user, a hook +variable called ‘magit-TYPE-sections-hook’ exists. This hook should be +changed using ‘magit-add-section-hook’. Avoid using ‘add-hooks’ or the +Custom interface. + + The various available section hook variables are described later in +this manual along with the appropriate "section inserter functions". + + -- Function: magit-add-section-hook hook function &optional at append + local + Add the function FUNCTION to the value of section hook HOOK. + + Add FUNCTION at the beginning of the hook list unless optional + APPEND is non-nil, in which case FUNCTION is added at the end. If + FUNCTION already is a member then move it to the new location. + + If optional AT is non-nil and a member of the hook list, then add + FUNCTION next to that instead. Add before or after AT, or replace + AT with FUNCTION depending on APPEND. If APPEND is the symbol + ‘replace’, then replace AT with FUNCTION. For any other non-nil + value place FUNCTION right after AT. If nil, then place FUNCTION + right before AT. If FUNCTION already is a member of the list but + AT is not, then leave FUNCTION where ever it already is. + + If optional LOCAL is non-nil, then modify the hook’s buffer-local + value rather than its global value. This makes the hook local by + copying the default value. That copy is then modified. + + HOOK should be a symbol. If HOOK is void, it is first set to nil. + HOOK’s value must not be a single hook function. FUNCTION should + be a function that takes no arguments and inserts one or multiple + sections at point, moving point forward. FUNCTION may choose not + to insert its section(s), when doing so would not make sense. It + should not be abused for other side-effects. + + To remove a function from a section hook, use ‘remove-hook’. + + +File: magit.info, Node: Section Types and Values, Next: Section Options, Prev: Section Hooks, Up: Sections + +4.2.4 Section Types and Values +------------------------------ + +Each section has a type, for example ‘hunk’, ‘file’, and ‘commit’. +Instances of certain section types also have a value. The value of a +section of type ‘file’, for example, is a file name. + + Users usually do not have to worry about a section’s type and value, +but knowing them can be handy at times. + +‘H’ (‘magit-describe-section’) + This command shows information about the section at point in a + separate buffer. + + -- Command: magit-describe-section-briefly + This command shows information about the section at point in the + echo area, as ‘#’. + + Many commands behave differently depending on the type of the section +at point and/or somehow consume the value of that section. But that is +only one of the reasons why the same key may do something different, +depending on what section is current. + + Additionally for each section type a keymap *might* be defined, named +‘magit-TYPE-section-map’. That keymap is used as text property keymap +of all text belonging to any section of the respective type. If such a +map does not exist for a certain type, then you can define it yourself, +and it will automatically be used. + + +File: magit.info, Node: Section Options, Prev: Section Types and Values, Up: Sections + +4.2.5 Section Options +--------------------- + +This section describes options that have an effect on more than just a +certain type of sections. As you can see there are not many of those. + + -- User Option: magit-section-show-child-count + Whether to append the number of children to section headings. This + only affects sections that could benefit from this information. + + +File: magit.info, Node: Transient Commands, Next: Transient Arguments and Buffer Variables, Prev: Sections, Up: Interface Concepts + +4.3 Transient Commands +====================== + +Many Magit commands are implemented as *transient* commands. First the +user invokes a *prefix* command, which causes its *infix* arguments and +*suffix* commands to be displayed in the echo area. The user then +optionally sets some infix arguments and finally invokes one of the +suffix commands. + + This is implemented in the library ‘transient’. Earlier Magit +releases used the package ‘magit-popup’ and even earlier versions +library ‘magit-key-mode’. + + Transient is documented in *note (transient)Top::. + +‘C-c C-c’ (‘magit-dispatch’) + This transient prefix command binds most of Magit’s other prefix + commands as suffix commands and displays them in a temporary buffer + until one of them is invoked. Invoking such a sub-prefix causes + the suffixes of that command to be bound and displayed instead of + those of ‘magit-dispatch’. + + This command is also, or especially, useful outside Magit buffers, so +you should setup a global binding: + + (global-set-key (kbd "C-x M-g") 'magit-dispatch) + + +File: magit.info, Node: Transient Arguments and Buffer Variables, Next: Completion Confirmation and the Selection, Prev: Transient Commands, Up: Interface Concepts + +4.4 Transient Arguments and Buffer Variables +============================================ + +The infix arguments of many of Magit’s transient prefix commands cease +to have an effect once the ‘git’ command that is called with those +arguments has returned. Commands that create a commit are a good +example for this. If the user changes the arguments, then that only +affects the next invocation of a suffix command. If the same transient +prefix command is later invoked again, then the arguments are initially +reset to the default value. This default value can be set for the +current Emacs session or saved permanently, see *note (transient)Saving +Values::. It is also possible to cycle through previously used sets of +arguments using ‘C-M-p’ and ‘C-M-n’, see *note (transient)Using +History::. + + However the infix arguments of many other transient commands continue +to have an effect even after the ‘git’ command that was called with +those arguments has returned. The most important commands like this are +those that display a diff or log in a dedicated buffer. Their arguments +obviously continue to have an effect for as long as the respective diff +or log is being displayed. Furthermore the used arguments are stored in +buffer-local variables for future reference. + + For commands in the second group it isn’t always desirable to reset +their arguments to the global value when the transient prefix command is +invoked again. + + As mentioned above, it is possible to cycle through previously used +sets of arguments while a transient popup is visible. That means that +we could always reset the infix arguments to the default because the set +of arguments that is active in the existing buffer is only a few ‘C-M-p’ +away. Magit can be configured to behave like that, but because I expect +that most users would not find that very convenient, it is not the +default. + + Also note that it is possible to change the diff and log arguments +used in the current buffer (including the status buffer, which contains +both diff and log sections) using the respective "refresh" transient +prefix commands on ‘D’ and ‘L’. (‘d’ and ‘l’ on the other hand are +intended to change *what* diff or log is being displayed. It is +possible to also change *how* the diff or log is being displayed at the +same time, but if you only want to do the latter, then you should use +the refresh variants.) Because these secondary diff and log transient +prefixes are about *changing* the arguments used in the current buffer, +they *always* start out with the set of arguments that are currently in +effect in that buffer. + + Some commands are usually invoked directly even though they can also +be invoked as the suffix of a transient prefix command. Most +prominently ‘magit-show-commit’ is usually invoked by typing ‘RET’ while +point is on a commit in a log, but it can also be invoked from the +‘magit-diff’ transient prefix. + + When such a command is invoked directly, then it is important to +reuse the arguments as specified by the respective buffer-local values, +instead of using the default arguments. Imagine you press ‘RET’ in a +log to display the commit at point in a different buffer and then use +‘D’ to change how the diff is displayed in that buffer. And then you +press ‘RET’ on another commit to show that instead and the diff +arguments are reset to the default. Not cool; so Magit does not do that +by default. + + -- User Option: magit-prefix-use-buffer-arguments + This option controls whether the infix arguments initially shown in + certain transient prefix commands are based on the arguments that + are currently in effect in the buffer that their suffixes update. + + The ‘magit-diff’ and ‘magit-log’ transient prefix commands are + affected by this option. + + -- User Option: magit-direct-use-buffer-arguments + This option controls whether certain commands, when invoked + directly (i.e. not as the suffix of a transient prefix command), + use the arguments that are currently active in the buffer that they + are about to update. The alternative is to use the default value + for these arguments, which might change the arguments that are used + in the buffer. + +Valid values for both of the above options are: + + • ‘always’: Always use the set of arguments that is currently active + in the respective buffer, provided that buffer exists of course. + • ‘selected’ or ‘t’: Use the set of arguments from the respective + buffer, but only if it is displayed in a window of the current + frame. This is the default for both variables. + • ‘current’: Use the set of arguments from the respective buffer, but + only if it is the current buffer. + • ‘never’: Never use the set of arguments from the respective buffer. + +I am afraid it gets more complicated still: + + • The global diff and log arguments are set for each supported mode + individually. The diff arguments for example have different values + in ‘magit-diff-mode’, ‘magit-revision-mode’, + ‘magit-merge-preview-mode’ and ‘magit-status-mode’ buffers. + Setting or saving the value for one mode does not change the value + for other modes. The history however is shared. + + • When ‘magit-show-commit’ is invoked directly from a log buffer, + then the file filter is picked up from that buffer, not from the + revision buffer or the mode’s global diff arguments. + + • Even though they are suffixes of the diff prefix + ‘magit-show-commit’ and ‘magit-stash-show’ do not use the diff + buffer used by the diff commands, instead they use the dedicated + revision and stash buffers. + + At the time you invoke the diff prefix it is unknown to Magit which + of the suffix commands you are going to invoke. While not certain, + more often than not users invoke one of the commands that use the + diff buffer, so the initial infix arguments are those used in that + buffer. However if you invoke one of these commands directly, then + Magit knows that it should use the arguments from the revision + resp. stash buffer. + + • The log prefix also features reflog commands, but these commands do + not use the log arguments. + + • If ‘magit-show-refs’ is invoked from a ‘magit-refs-mode’ buffer, + then it acts as a refresh prefix and therefore unconditionally uses + the buffer’s arguments as initial arguments. If it is invoked + elsewhere with a prefix argument, then it acts as regular prefix + and therefore respects ‘magit-prefix-use-buffer-arguments’. If it + is invoked elsewhere without a prefix argument, then it acts as a + direct command and therefore respects + ‘magit-direct-use-buffer-arguments’. + + +File: magit.info, Node: Completion Confirmation and the Selection, Next: Mouse Support, Prev: Transient Arguments and Buffer Variables, Up: Interface Concepts + +4.5 Completion, Confirmation and the Selection +============================================== + +* Menu: + +* Action Confirmation:: +* Completion and Confirmation:: +* The Selection:: +* The hunk-internal region:: +* Support for Completion Frameworks:: +* Additional Completion Options:: + + +File: magit.info, Node: Action Confirmation, Next: Completion and Confirmation, Up: Completion Confirmation and the Selection + +4.5.1 Action Confirmation +------------------------- + +By default many actions that could potentially lead to data loss have to +be confirmed. This includes many very common actions, so this can +quickly become annoying. Many of these actions can be undone and if you +have thought about how to undo certain mistakes, then it should be safe +to disable confirmation for the respective actions. + + The option ‘magit-no-confirm’ can be used to tell Magit to perform +certain actions without the user having to confirm them. Note that +while this option can only be used to disable confirmation for a +specific set of actions, the next section explains another way of +telling Magit to ask fewer questions. + + -- User Option: magit-no-confirm + The value of this option is a list of symbols, representing actions + that do not have to be confirmed by the user before being carried + out. + + By default many potentially dangerous commands ask the user for + confirmation. Each of the below symbols stands for an action + which, when invoked unintentionally or without being fully aware of + the consequences, could lead to tears. In many cases there are + several commands that perform variations of a certain action, so we + don’t use the command names but more generic symbols. + + • Applying changes: + + • ‘discard’ Discarding one or more changes (i.e. hunks or + the complete diff for a file) loses that change, + obviously. + + • ‘reverse’ Reverting one or more changes can usually be + undone by reverting the reversion. + + • ‘stage-all-changes’, ‘unstage-all-changes’ When there are + both staged and unstaged changes, then un-/staging + everything would destroy that distinction. Of course + that also applies when un-/staging a single change, but + then less is lost and one does that so often that having + to confirm every time would be unacceptable. + + • Files: + + • ‘delete’ When a file that isn’t yet tracked by Git is + deleted, then it is completely lost, not just the last + changes. Very dangerous. + + • ‘trash’ Instead of deleting a file it can also be move to + the system trash. Obviously much less dangerous than + deleting it. + + Also see option ‘magit-delete-by-moving-to-trash’. + + • ‘resurrect’ A deleted file can easily be resurrected by + "deleting" the deletion, which is done using the same + command that was used to delete the same file in the + first place. + + • ‘untrack’ Untracking a file can be undone by tracking it + again. + + • ‘rename’ Renaming a file can easily be undone. + + • Sequences: + + • ‘reset-bisect’ Aborting (known to Git as "resetting") a + bisect operation loses all information collected so far. + + • ‘abort-rebase’ Aborting a rebase throws away all already + modified commits, but it’s possible to restore those from + the reflog. + + • ‘abort-merge’ Aborting a merge throws away all conflict + resolutions which have already been carried out by the + user. + + • ‘merge-dirty’ Merging with a dirty worktree can make it + hard to go back to the state before the merge was + initiated. + + • References: + + • ‘delete-unmerged-branch’ Once a branch has been deleted, + it can only be restored using low-level recovery tools + provided by Git. And even then the reflog is gone. The + user always has to confirm the deletion of a branch by + accepting the default choice (or selecting another + branch), but when a branch has not been merged yet, also + make sure the user is aware of that. + + • ‘delete-pr-remote’ When deleting a branch that was + created from a pull-request and if no other branches + still exist on that remote, then ‘magit-branch-delete’ + offers to delete the remote as well. This should be safe + because it only happens if no other refs exist in the + remotes namespace, and you can recreate the remote if + necessary. + + • ‘drop-stashes’ Dropping a stash is dangerous because Git + stores stashes in the reflog. Once a stash is removed, + there is no going back without using low-level recovery + tools provided by Git. When a single stash is dropped, + then the user always has to confirm by accepting the + default (or selecting another). This action only + concerns the deletion of multiple stashes at once. + + • Publishing: + + • ‘set-and-push’ When pushing to the upstream or the + push-remote and that isn’t actually configured yet, then + the user can first set the target. If s/he confirms the + default too quickly, then s/he might end up pushing to + the wrong branch and if the remote repository is + configured to disallow fixing such mistakes, then that + can be quite embarrassing and annoying. + + • Edit published history: + + Without adding these symbols here, you will be warned before + editing commits that have already been pushed to one of the + branches listed in ‘magit-published-branches’. + + • ‘amend-published’ Affects most commands that amend to + "HEAD". + + • ‘rebase-published’ Affects commands that perform + interactive rebases. This includes commands from the + commit transient that modify a commit other than "HEAD", + namely the various fixup and squash variants. + + • ‘edit-published’ Affects the commands + ‘magit-edit-line-commit’ and + ‘magit-diff-edit-hunk-commit’. These two commands make + it quite easy to accidentally edit a published commit, so + you should think twice before configuring them not to ask + for confirmation. + + To disable confirmation completely, add all three symbols here + or set ‘magit-published-branches’ to ‘nil’. + + • Various: + + • ‘kill-process’ There seldom is a reason to kill a + process. + + • Global settings: + + Instead of adding all of the above symbols to the value of + this option, you can also set it to the atom ‘t’, which has + the same effect as adding all of the above symbols. Doing + that most certainly is a bad idea, especially because other + symbols might be added in the future. So even if you don’t + want to be asked for confirmation for any of these actions, + you are still better of adding all of the respective symbols + individually. + + When ‘magit-wip-before-change-mode’ is enabled, then the + following actions can be undone fairly easily: ‘discard’, + ‘reverse’, ‘stage-all-changes’, and ‘unstage-all-changes’. If + and only if this mode is enabled, then ‘safe-with-wip’ has the + same effect as adding all of these symbols individually. + + +File: magit.info, Node: Completion and Confirmation, Next: The Selection, Prev: Action Confirmation, Up: Completion Confirmation and the Selection + +4.5.2 Completion and Confirmation +--------------------------------- + +Many Magit commands ask the user to select from a list of possible +things to act on, while offering the most likely choice as the default. +For many of these commands the default is the thing at point, provided +that it actually is a valid thing to act on. For many commands that act +on a branch, the current branch serves as the default if there is no +branch at point. + + These commands combine asking for confirmation and asking for a +target to act on into a single action. The user can confirm the default +target using ‘RET’ or abort using ‘C-g’. This is similar to a +‘y-or-n-p’ prompt, but the keys to confirm or abort differ. + + At the same time the user is also given the opportunity to select +another target, which is useful because for some commands and/or in some +situations you might want to select the action before selecting the +target by moving to it. + + However you might find that for some commands you always want to use +the default target, if any, or even that you want the command to act on +the default without requiring any confirmation at all. The option +‘magit-dwim-selection’ can be used to configure certain commands to that +effect. + + Note that when the region is active then many commands act on the +things that are selected using a mechanism based on the region, in many +cases after asking for confirmation. This region-based mechanism is +called the "selection" and is described in detail in the next section. +When a selection exists that is valid for the invoked command, then that +command never offers to act on something else, and whether it asks for +confirmation is not controlled by this option. + + Also note that Magit asks for confirmation of certain actions that +are not coupled with completion (or the selection). Such dialogs are +also not affected by this option and are described in the previous +section. + + -- User Option: magit-dwim-selection + This option can be used to tell certain commands to use the thing at +point instead of asking the user to select a candidate to act on, with +or without confirmation. + + The value has the form ‘((COMMAND nil|PROMPT DEFAULT)...)’. + + • COMMAND is the command that should not prompt for a choice. To + have an effect, the command has to use the function + ‘magit-completing-read’ or a utility function which in turn uses + that function. + + • If the command uses ‘magit-completing-read’ multiple times, then + PROMPT can be used to only affect one of these uses. PROMPT, if + non-nil, is a regular expression that is used to match against the + PROMPT argument passed to ‘magit-completing-read’. + + • DEFAULT specifies how to use the default. If it is ‘t’, then the + DEFAULT argument passed to ‘magit-completing-read’ is used without + confirmation. If it is ‘ask’, then the user is given a chance to + abort. DEFAULT can also be ‘nil’, in which case the entry has no + effect. + + +File: magit.info, Node: The Selection, Next: The hunk-internal region, Prev: Completion and Confirmation, Up: Completion Confirmation and the Selection + +4.5.3 The Selection +------------------- + +If the region is active, then many Magit commands act on the things that +are selected using a mechanism based on the region instead of one single +thing. When the region is not active, then these commands act on the +thing at point or read a single thing to act on. This is described in +the previous section — this section only covers how multiple things are +selected, how that is visualized, and how certain commands behave when +that is the case. + + Magit’s mechanism for selecting multiple things, or rather sections +that represent these things, is based on the Emacs region, but the area +that Magit considers to be selected is typically larger than the region +and additional restrictions apply. + + Magit makes a distinction between a region that qualifies as forming +a valid Magit selection and a region that does not. If the region does +not qualify, then it is displayed as it is in other Emacs buffers. If +the region does qualify as a Magit selection, then the selection is +always visualized, while the region itself is only visualized if it +begins and ends on the same line. + + For a region to qualify as a Magit selection, it must begin in the +heading of one section and end in the heading of a sibling section. +Note that if the end of the region is at the very beginning of section +heading (i.e. at the very beginning of a line) then that section is +considered to be *inside* the selection. + + This is not consistent with how the region is normally treated in +Emacs — if the region ends at the beginning of a line, then that line is +outside the region. Due to how Magit visualizes the selection, it +should be obvious that this difference exists. + + Not every command acts on every valid selection. Some commands do +not even consider the location of point, others may act on the section +at point but not support acting on the selection, and even commands that +do support the selection of course only do so if it selects things that +they can act on. + + This is the main reason why the selection must include the section at +point. Even if a selection exists, the invoked command may disregard +it, in which case it may act on the current section only. It is much +safer to only act on the current section but not the other selected +sections than it is to act on the current section *instead* of the +selected sections. The latter would be much more surprising and if the +current section always is part of the selection, then that cannot +happen. + + -- Variable: magit-keep-region-overlay + This variable controls whether the region is visualized as usual + even when a valid Magit selection or a hunk-internal region exists. + See the doc-string for more information. + + +File: magit.info, Node: The hunk-internal region, Next: Support for Completion Frameworks, Prev: The Selection, Up: Completion Confirmation and the Selection + +4.5.4 The hunk-internal region +------------------------------ + +Somewhat related to the Magit selection described in the previous +section is the hunk-internal region. + + Like the selection, the hunk-internal region is based on the Emacs +region but causes that region to not be visualized as it would in other +Emacs buffers, and includes the line on which the region ends even if it +ends at the very beginning of that line. + + Unlike the selection, which is based on a region that must begin in +the heading of one section and ends in the section of a sibling section, +the hunk-internal region must begin inside the *body* of a hunk section +and end in the body of the *same* section. + + The hunk-internal region is honored by "apply" commands, which can, +among other targets, act on a hunk. If the hunk-internal region is +active, then such commands act only on the marked part of the hunk +instead of on the complete hunk. + + +File: magit.info, Node: Support for Completion Frameworks, Next: Additional Completion Options, Prev: The hunk-internal region, Up: Completion Confirmation and the Selection + +4.5.5 Support for Completion Frameworks +--------------------------------------- + +The built-in option ‘completing-read-function’ specifies the low-level +function used by ‘completing-read’ to ask a user to select from a list +of choices. Its default value is ‘completing-read-default’. +Alternative completion frameworks typically activate themselves by +substituting their own implementation. + + Mostly for historic reasons Magit provides a similar option named +‘magit-completing-read-function’, which only controls the low-level +function used by ‘magit-completing-read’. This option also makes it +possible to use a different completing mechanism for Magit than for the +rest of Emacs, but doing that is not recommend. + + You most likely don’t have to customize the magit-specific option to +use an alternative completion framework. For example, if you enable +‘ivy-mode’, then Magit will respect that, and if you enable ‘helm-mode’, +then you are done too. + + However if you want to use Ido, then ‘ido-mode’ won’t do the trick. +You will also have to install the ‘ido-completing-read+’ package and use +‘magit-ido-completing-read’ as ‘magit-completing-read-function’. + + -- User Option: magit-completing-read-function + The value of this variable is the low-level function used to + perform completion by code that uses ‘magit-completing-read’ (as + opposed to the built-in ‘completing-read’). + + The default value, ‘magit-builtin-completing-read’, is suitable for + the standard completion mechanism, ‘ivy-mode’, and ‘helm-mode’ at + least. + + The built-in ‘completing-read’ and ‘completing-read-default’ are + *not* suitable to be used here. ‘magit-builtin-completing-read’ + performs some additional work, and any function used in its place + has to do the same. + + -- Function: magit-builtin-completing-read prompt choices &optional + predicate require-match initial-input hist def + This function performs completion using the built-in + ‘completing-read’ and does some additional magit-specific work. + + -- Function: magit-ido-completing-read prompt choices &optional + predicate require-match initial-input hist def + This function performs completion using ‘ido-completing-read+’ from + the package by the same name (which you have to explicitly install) + and does some additional magit-specific work. + + We have to use ‘ido-completing-read+’ instead of the + ‘ido-completing-read’ that comes with Ido itself, because the + latter, while intended as a drop-in replacement, cannot serve that + purpose because it violates too many of the implicit conventions. + + -- Function: magit-completing-read prompt choices &optional predicate + require-match initial-input hist def fallback + This is the function that Magit commands use when they need the + user to select a single thing to act on. The arguments have the + same meaning as for ‘completing-read’, except for FALLBACK, which + is unique to this function and is described below. + + Instead of asking the user to choose from a list of possible + candidates, this function may just return the default specified by + DEF, with or without requiring user confirmation. Whether that is + the case depends on PROMPT, ‘this-command’ and + ‘magit-dwim-selection’. See the documentation of the latter for + more information. + + If it does read a value in the minibuffer, then this function acts + similar to ‘completing-read’, except for the following: + + • COLLECTION must be a list of choices. A function is not + supported. + + • If REQUIRE-MATCH is ‘nil’ and the user exits without a choice, + then ‘nil’ is returned instead of an empty string. + + • If REQUIRE-MATCH is non-nil and the users exits without a + choice, an user-error is raised. + + • FALLBACK specifies a secondary default that is only used if + the primary default DEF is ‘nil’. The secondary default is + not subject to ‘magit-dwim-selection’ — if DEF is ‘nil’ but + FALLBACK is not, then this function always asks the user to + choose a candidate, just as if both defaults were ‘nil’. + + • ": " is appended to PROMPT. + + • PROMPT is modified to end with \" (default DEF|FALLBACK): \" + provided that DEF or FALLBACK is non-nil, that neither + ‘ivy-mode’ nor ‘helm-mode’ is enabled, and that + ‘magit-completing-read-function’ is set to its default value + of ‘magit-builtin-completing-read’. + + +File: magit.info, Node: Additional Completion Options, Prev: Support for Completion Frameworks, Up: Completion Confirmation and the Selection + +4.5.6 Additional Completion Options +----------------------------------- + + -- User Option: magit-list-refs-sortby + For many commands that read a ref or refs from the user, the value + of this option can be used to control the order of the refs. Valid + values include any key accepted by the ‘--sort’ flag of ‘git + for-each-ref’. By default, refs are sorted alphabetically by their + full name (e.g., "refs/heads/master"). + + +File: magit.info, Node: Mouse Support, Next: Running Git, Prev: Completion Confirmation and the Selection, Up: Interface Concepts + +4.6 Mouse Support +================= + +Double clicking on a section heading toggles the visibility of its body, +if any. Likewise clicking in the left fringe toggles the visibility of +the appropriate section. + + A context menu is provided but has to be enabled explicitly. In +Emacs 28 and greater, enable the global mode ‘context-menu-mode’. If +you use an older Emacs release, set +‘magit-section-show-context-menu-for-emacs<28’. + + +File: magit.info, Node: Running Git, Prev: Mouse Support, Up: Interface Concepts + +4.7 Running Git +=============== + +* Menu: + +* Viewing Git Output:: +* Git Process Status:: +* Running Git Manually:: +* Git Executable:: +* Global Git Arguments:: + + +File: magit.info, Node: Viewing Git Output, Next: Git Process Status, Up: Running Git + +4.7.1 Viewing Git Output +------------------------ + +Magit runs Git either for side-effects (e.g. when pushing) or to get +some value (e.g. the name of the current branch). + + When Git is run for side-effects, the process output is logged in a +per-repository log buffer, which can be consulted using the +‘magit-process’ command when things don’t go as expected. + + The output/errors for up to ‘magit-process-log-max’ Git commands are +retained. + +‘$’ (‘magit-process’) + This commands displays the process buffer for the current + repository. + + Inside that buffer, the usual key bindings for navigating and showing +sections are available. There is one additional command. + +‘k’ (‘magit-process-kill’) + This command kills the process represented by the section at point. + + -- Variable: magit-git-debug + This option controls whether additional reporting of git errors is + enabled. + + Magit basically calls git for one of these two reasons: for + side-effects or to do something with its standard output. + + When git is run for side-effects then its output, including error + messages, go into the process buffer which is shown when using ‘$’. + + When git’s output is consumed in some way, then it would be too + expensive to also insert it into this buffer, but when this option + is non-nil and git returns with a non-zero exit status, then at + least its standard error is inserted into this buffer. + + This is only intended for debugging purposes. Do not enable this + permanently, that would negatively affect performance. + + This is only intended for debugging purposes. Do not enable this + permanently, that would negatively affect performance. Also note + that just because git exits with a non-zero exit status and prints + an error message that usually doesn’t mean that it is an error as + far as Magit is concerned, which is another reason we usually hide + these error messages. Whether some error message is relevant in + the context of some unexpected behavior has to be judged on a case + by case basis. + + The command ‘magit-toggle-git-debug’ changes the value of this + variable. + + -- Variable: magit-process-extreme-logging + This option controls whether ‘magit-process-file’ logs to the + ‘*Messages*’ buffer. + + Only intended for temporary use when you try to figure out how + Magit uses Git behind the scene. Output that normally goes to the + magit-process buffer continues to go there. Not all output goes to + either of these two buffers. + + +File: magit.info, Node: Git Process Status, Next: Running Git Manually, Prev: Viewing Git Output, Up: Running Git + +4.7.2 Git Process Status +------------------------ + +When a Git process is running for side-effects, Magit displays an +indicator in the mode line, using the ‘magit-mode-line-process’ face. + + If the Git process exits successfully, the process indicator is +removed from the mode line immediately. + + In the case of a Git error, the process indicator is not removed, but +is instead highlighted with the ‘magit-mode-line-process-error’ face, +and the error details from the process buffer are provided as a tooltip +for mouse users. This error indicator persists in the mode line until +the next magit buffer refresh. + + If you do not wish process errors to be indicated in the mode line, +customize the ‘magit-process-display-mode-line-error’ user option. + + Process errors are additionally indicated at the top of the status +buffer. + + +File: magit.info, Node: Running Git Manually, Next: Git Executable, Prev: Git Process Status, Up: Running Git + +4.7.3 Running Git Manually +-------------------------- + +While Magit provides many Emacs commands to interact with Git, it does +not cover everything. In those cases your existing Git knowledge will +come in handy. Magit provides some commands for running arbitrary Git +commands by typing them into the minibuffer, instead of having to switch +to a shell. + +‘!’ (‘magit-run’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘! !’ (‘magit-git-command-topdir’) + This command reads a command from the user and executes it in the + top-level directory of the current working tree. + + The string "git " is used as initial input when prompting the user + for the command. It can be removed to run another command. + +‘:’ (‘magit-git-command’) +‘! p’ + This command reads a command from the user and executes it in + ‘default-directory’. With a prefix argument the command is + executed in the top-level directory of the current working tree + instead. + + The string "git " is used as initial input when prompting the user + for the command. It can be removed to run another command. + +‘! s’ (‘magit-shell-command-topdir’) + This command reads a command from the user and executes it in the + top-level directory of the current working tree. + +‘! S’ (‘magit-shell-command’) + This command reads a command from the user and executes it in + ‘default-directory’. With a prefix argument the command is + executed in the top-level directory of the current working tree + instead. + + -- User Option: magit-shell-command-verbose-prompt + Whether the prompt, used by the above commands when reading a shell + command, shows the directory in which it will be run. + + These suffix commands start external gui tools. + +‘! k’ (‘magit-run-gitk’) + This command runs ‘gitk’ in the current repository. + +‘! a’ (‘magit-run-gitk-all’) + This command runs ‘gitk --all’ in the current repository. + +‘! b’ (‘magit-run-gitk-branches’) + This command runs ‘gitk --branches’ in the current repository. + +‘! g’ (‘magit-run-git-gui’) + This command runs ‘git gui’ in the current repository. + +‘! m’ (‘magit-git-mergetool’) + This command runs ‘git mergetool --gui’ in the current repository. + + With a prefix argument this acts as a transient prefix command, + allowing the user to select the mergetool and change some settings. + + +File: magit.info, Node: Git Executable, Next: Global Git Arguments, Prev: Running Git Manually, Up: Running Git + +4.7.4 Git Executable +-------------------- + +When Magit calls Git, then it may do so using the absolute path to the +‘git’ executable, or using just its name. + + When running ‘git’ locally and the ‘system-type’ is ‘windows-nt’ (any +Windows version) or ‘darwin’ (macOS) then ‘magit-git-executable’ is set +to an absolute path when Magit is loaded. + + On Windows it is necessary to use an absolute path because Git comes +with several wrapper scripts for the actual ‘git’ binary, which are also +placed on ‘$PATH’, and using one of these wrappers instead of the binary +would degrade performance horribly. For some macOS users using just the +name of the executable also performs horribly, so we avoid doing that on +that platform as well. On other platforms, using just the name seems to +work just fine. + + Using an absolute path when running ‘git’ on a remote machine over +Tramp, would be problematic to use an absolute path that is suitable on +the local machine, so a separate option is used to control the name or +path that is used on remote machines. + + -- User Option: magit-git-executable + The ‘git’ executable used by Magit on the local host. This should + be either the absolute path to the executable, or the string "git" + to let Emacs find the executable itself, using the standard + mechanism for doing such things. + + -- User Option: magit-remote-git-executable + The ‘git’ executable used by Magit on remote machines over Tramp. + Normally this should be just the string "git". Consider + customizing ‘tramp-remote-path’ instead of this option. + + If Emacs is unable to find the correct executable, then you can work +around that by explicitly setting the value of one of these two options. +Doing that should be considered a kludge; it is better to make sure that +the order in ‘exec-path’ or ‘tramp-remote-path’ is correct. + + Note that ‘exec-path’ is set based on the value of the ‘PATH’ +environment variable that is in effect when Emacs is started. If you +set ‘PATH’ in your shell’s init files, then that only has an effect on +Emacs if you start it from that shell (because the environment of a +process is only passed to its child processes, not to arbitrary other +processes). If that is not how you start Emacs, then the +‘exec-path-from-shell’ package can help; though honestly I consider that +a kludge too. + + The command ‘magit-debug-git-executable’ can be useful to find out +where Emacs is searching for ‘git’. + +‘M-x magit-debug-git-executable’ + This command displays a buffer with information about + ‘magit-git-executable’ and ‘magit-remote-git-executable’. + +‘M-x magit-version’ + This command shows the currently used versions of Magit, Git, and + Emacs in the echo area. Non-interactively this just returns the + Magit version. + + +File: magit.info, Node: Global Git Arguments, Prev: Git Executable, Up: Running Git + +4.7.5 Global Git Arguments +-------------------------- + + -- User Option: magit-git-global-arguments + The arguments set here are used every time the git executable is + run as a subprocess. They are placed right after the executable + itself and before the git command - as in ‘git HERE... COMMAND + REST’. For valid arguments see *note (gitman)git::. + + Be careful what you add here, especially if you are using Tramp to + connect to servers with ancient Git versions. Never remove + anything that is part of the default value, unless you really know + what you are doing. And think very hard before adding something; + it will be used every time Magit runs Git for any purpose. + + +File: magit.info, Node: Inspecting, Next: Manipulating, Prev: Interface Concepts, Up: Top + +5 Inspecting +************ + +The functionality provided by Magit can be roughly divided into three +groups: inspecting existing data, manipulating existing data or adding +new data, and transferring data. Of course that is a rather crude +distinction that often falls short, but it’s more useful than no +distinction at all. This section is concerned with inspecting data, the +next two with manipulating and transferring it. Then follows a section +about miscellaneous functionality, which cannot easily be fit into this +distinction. + + Of course other distinctions make sense too, e.g. Git’s distinction +between porcelain and plumbing commands, which for the most part is +equivalent to Emacs’ distinction between interactive commands and +non-interactive functions. All of the sections mentioned before are +mainly concerned with the porcelain – Magit’s plumbing layer is +described later. + +* Menu: + +* Status Buffer:: +* Repository List:: +* Logging:: +* Diffing:: +* Ediffing:: +* References Buffer:: +* Bisecting:: +* Visiting Files and Blobs:: +* Blaming:: + + +File: magit.info, Node: Status Buffer, Next: Repository List, Up: Inspecting + +5.1 Status Buffer +================= + +While other Magit buffers contain e.g. one particular diff or one +particular log, the status buffer contains the diffs for staged and +unstaged changes, logs for unpushed and unpulled commits, lists of +stashes and untracked files, and information related to the current +branch. + + During certain incomplete operations – for example when a merge +resulted in a conflict – additional information is displayed that helps +proceeding with or aborting the operation. + + The command ‘magit-status’ displays the status buffer belonging to +the current repository in another window. This command is used so often +that it should be bound globally. We recommend using ‘C-x g’: + + (global-set-key (kbd "C-x g") 'magit-status) + +‘C-x g’ (‘magit-status’) + When invoked from within an existing Git repository, then this + command shows the status of that repository in a buffer. + + If the current directory isn’t located within a Git repository, + then this command prompts for an existing repository or an + arbitrary directory, depending on the option + ‘magit-repository-directories’, and the status for the selected + repository is shown instead. + + • If that option specifies any existing repositories, then the + user is asked to select one of them. + + • Otherwise the user is asked to select an arbitrary directory + using regular file-name completion. If the selected directory + is the top-level directory of an existing working tree, then + the status buffer for that is shown. + + • Otherwise the user is offered to initialize the selected + directory as a new repository. After creating the repository + its status buffer is shown. + + These fallback behaviors can also be forced using one or more + prefix arguments: + + • With two prefix arguments (or more precisely a numeric prefix + value of 16 or greater) an arbitrary directory is read, which + is then acted on as described above. The same could be + accomplished using the command ‘magit-init’. + + • With a single prefix argument an existing repository is read + from the user, or if no repository can be found based on the + value of ‘magit-repository-directories’, then the behavior is + the same as with two prefix arguments. + + -- User Option: magit-repository-directories + List of directories that are Git repositories or contain Git + repositories. + + Each element has the form ‘(DIRECTORY . DEPTH)’. DIRECTORY has to + be a directory or a directory file-name, a string. DEPTH, an + integer, specifies the maximum depth to look for Git repositories. + If it is 0, then only add DIRECTORY itself. + + This option controls which repositories are being listed by + ‘magit-list-repositories’. It also affects ‘magit-status’ (which + see) in potentially surprising ways (see above). + + -- Command: magit-status-quick + This command is an alternative to ‘magit-status’ that usually + avoids refreshing the status buffer. + + If the status buffer of the current Git repository exists but isn’t + being displayed in the selected frame, then it is displayed without + being refreshed. + + If the status buffer is being displayed in the selected frame, then + this command refreshes it. + + Prefix arguments have the same meaning as for ‘magit-status’, and + additionally cause the buffer to be refresh. + + To use this command add this to your init file: + + (global-set-key (kbd "C-x g") 'magit-status-quick). + + If you do that and then for once want to redisplay the buffer and + also immediately refresh it, then type ‘C-x g’ followed by ‘g’. + + A possible alternative command is + ‘magit-display-repository-buffer’. It supports displaying any + existing Magit buffer that belongs to the current repository; not + just the status buffer. + + -- Command: ido-enter-magit-status + From an Ido prompt used to open a file, instead drop into + ‘magit-status’. This is similar to ‘ido-magic-delete-char’, which, + despite its name, usually causes a Dired buffer to be created. + + To make this command available, use something like: + + (add-hook 'ido-setup-hook + (lambda () + (define-key ido-completion-map + (kbd \"C-x g\") 'ido-enter-magit-status))) + + Starting with Emacs 25.1 the Ido keymaps are defined just once + instead of every time Ido is invoked, so now you can modify it like + pretty much every other keymap: + + (define-key ido-common-completion-map + (kbd \"C-x g\") 'ido-enter-magit-status) + +* Menu: + +* Status Sections:: +* Status Header Sections:: +* Status Module Sections:: +* Status Options:: + + +File: magit.info, Node: Status Sections, Next: Status Header Sections, Up: Status Buffer + +5.1.1 Status Sections +--------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’. See *note Section Hooks:: to learn about +such hooks and how to customize them. + + -- User Option: magit-status-sections-hook + Hook run to insert sections into a status buffer. + + The first function on that hook by default is +‘magit-insert-status-headers’; it is described in the next section. By +default the following functions are also members of that hook: + + -- Function: magit-insert-merge-log + Insert section for the on-going merge. Display the heads that are + being merged. If no merge is in progress, do nothing. + + -- Function: magit-insert-rebase-sequence + Insert section for the on-going rebase sequence. If no such + sequence is in progress, do nothing. + + -- Function: magit-insert-am-sequence + Insert section for the on-going patch applying sequence. If no + such sequence is in progress, do nothing. + + -- Function: magit-insert-sequencer-sequence + Insert section for the on-going cherry-pick or revert sequence. If + no such sequence is in progress, do nothing. + + -- Function: magit-insert-bisect-output + While bisecting, insert section with output from ‘git bisect’. + + -- Function: magit-insert-bisect-rest + While bisecting, insert section visualizing the bisect state. + + -- Function: magit-insert-bisect-log + While bisecting, insert section logging bisect progress. + + -- Function: magit-insert-untracked-files + Maybe insert a list or tree of untracked files. + + Do so depending on the value of ‘status.showUntrackedFiles’. Note + that even if the value is ‘all’, Magit still initially only shows + directories. But the directory sections can then be expanded using + ‘TAB’. + + -- Function: magit-insert-unstaged-changes + Insert section showing unstaged changes. + + -- Function: magit-insert-staged-changes + Insert section showing staged changes. + + -- Function: magit-insert-stashes &optional ref heading + Insert the ‘stashes’ section showing reflog for "refs/stash". If + optional REF is non-nil show reflog for that instead. If optional + HEADING is non-nil use that as section heading instead of + "Stashes:". + + -- Function: magit-insert-unpulled-from-upstream + Insert section showing commits that haven’t been pulled from the + upstream branch yet. + + -- Function: magit-insert-unpulled-from-pushremote + Insert section showing commits that haven’t been pulled from the + push-remote branch yet. + + -- Function: magit-insert-unpushed-to-upstream + Insert section showing commits that haven’t been pushed to the + upstream yet. + + -- Function: magit-insert-unpushed-to-pushremote + Insert section showing commits that haven’t been pushed to the + push-remote yet. + + The following functions can also be added to the above hook: + + -- Function: magit-insert-tracked-files + Insert a tree of tracked files. + + -- Function: magit-insert-ignored-files + Insert a tree of ignored files. Its possible to limit the logs in + the current buffer to a certain directory using ‘D = f + RET g’. If you do that, then that that also affects this command. + + The log filter can be used to limit to multiple files. In that + case this function only respects the first of the files and only if + it is a directory. + + -- Function: magit-insert-skip-worktree-files + Insert a tree of skip-worktree files. If the first element of + ‘magit-buffer-diff-files’ is a directory, then limit the list to + files below that. The value of that variable can be set using ‘D + -- DIRECTORY RET g’. + + -- Function: magit-insert-assumed-unchanged-files + Insert a tree of files that are assumed to be unchanged. If the + first element of ‘magit-buffer-diff-files’ is a directory, then + limit the list to files below that. The value of that variable can + be set using ‘D -- DIRECTORY RET g’. + + -- Function: magit-insert-unpulled-or-recent-commits + Insert section showing unpulled or recent commits. If an upstream + is configured for the current branch and it is ahead of the current + branch, then show the missing commits. Otherwise, show the last + ‘magit-log-section-commit-count’ commits. + + -- Function: magit-insert-recent-commits + Insert section showing the last ‘magit-log-section-commit-count’ + commits. + + -- User Option: magit-log-section-commit-count + How many recent commits ‘magit-insert-recent-commits’ and + ‘magit-insert-unpulled-or-recent-commits’ (provided there are no + unpulled commits) show. + + -- Function: magit-insert-unpulled-cherries + Insert section showing unpulled commits. Like + ‘magit-insert-unpulled-commits’ but prefix each commit that has not + been applied yet (i.e. a commit with a patch-id not shared with + any local commit) with "+", and all others with "-". + + -- Function: magit-insert-unpushed-cherries + Insert section showing unpushed commits. Like + ‘magit-insert-unpushed-commits’ but prefix each commit which has + not been applied to upstream yet (i.e. a commit with a patch-id + not shared with any upstream commit) with "+" and all others with + "-". + + See *note References Buffer:: for some more section inserters, which +could be used here. + + +File: magit.info, Node: Status Header Sections, Next: Status Module Sections, Prev: Status Sections, Up: Status Buffer + +5.1.2 Status Header Sections +---------------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’ (see *note Status Sections::). + + By default ‘magit-insert-status-headers’ is the first member of that +hook variable. + + -- Function: magit-insert-status-headers + Insert headers sections appropriate for ‘magit-status-mode’ + buffers. The sections are inserted by running the functions on the + hook ‘magit-status-headers-hook’. + + -- User Option: magit-status-headers-hook + Hook run to insert headers sections into the status buffer. + + This hook is run by ‘magit-insert-status-headers’, which in turn + has to be a member of ‘magit-status-sections-hook’ to be used at + all. + + By default the following functions are members of the above hook: + + -- Function: magit-insert-error-header + Insert a header line showing the message about the Git error that + just occurred. + + This function is only aware of the last error that occur when Git + was run for side-effects. If, for example, an error occurs while + generating a diff, then that error won’t be inserted. Refreshing + the status buffer causes this section to disappear again. + + -- Function: magit-insert-diff-filter-header + Insert a header line showing the effective diff filters. + + -- Function: magit-insert-head-branch-header + Insert a header line about the current branch or detached ‘HEAD’. + + -- Function: magit-insert-upstream-branch-header + Insert a header line about the branch that is usually pulled into + the current branch. + + -- Function: magit-insert-push-branch-header + Insert a header line about the branch that the current branch is + usually pushed to. + + -- Function: magit-insert-tags-header + Insert a header line about the current and/or next tag, along with + the number of commits between the tag and ‘HEAD’. + + The following functions can also be added to the above hook: + + -- Function: magit-insert-repo-header + Insert a header line showing the path to the repository top-level. + + -- Function: magit-insert-remote-header + Insert a header line about the remote of the current branch. + + If no remote is configured for the current branch, then fall back + showing the "origin" remote, or if that does not exist the first + remote in alphabetic order. + + -- Function: magit-insert-user-header + Insert a header line about the current user. + + +File: magit.info, Node: Status Module Sections, Next: Status Options, Prev: Status Header Sections, Up: Status Buffer + +5.1.3 Status Module Sections +---------------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’ (see *note Status Sections::). + + By default ‘magit-insert-modules’ is _not_ a member of that hook +variable. + + -- Function: magit-insert-modules + Insert submodule sections. + + Hook ‘magit-module-sections-hook’ controls which module sections + are inserted, and option ‘magit-module-sections-nested’ controls + whether they are wrapped in an additional section. + + -- User Option: magit-module-sections-hook + Hook run by ‘magit-insert-modules’. + + -- User Option: magit-module-sections-nested + This option controls whether ‘magit-insert-modules’ wraps inserted + sections in an additional section. + + If this is non-nil, then only a single top-level section is + inserted. If it is nil, then all sections listed in + ‘magit-module-sections-hook’ become top-level sections. + + -- Function: magit-insert-modules-overview + Insert sections for all submodules. For each section insert the + path, the branch, and the output of ‘git describe --tags’, or, + failing that, the abbreviated HEAD commit hash. + + Press ‘RET’ on such a submodule section to show its own status + buffer. Press ‘RET’ on the "Modules" section to display a list of + submodules in a separate buffer. This shows additional information + not displayed in the super-repository’s status buffer. + + -- Function: magit-insert-modules-unpulled-from-upstream + Insert sections for modules that haven’t been pulled from the + upstream yet. These sections can be expanded to show the + respective commits. + + -- Function: magit-insert-modules-unpulled-from-pushremote + Insert sections for modules that haven’t been pulled from the + push-remote yet. These sections can be expanded to show the + respective commits. + + -- Function: magit-insert-modules-unpushed-to-upstream + Insert sections for modules that haven’t been pushed to the + upstream yet. These sections can be expanded to show the + respective commits. + + -- Function: magit-insert-modules-unpushed-to-pushremote + Insert sections for modules that haven’t been pushed to the + push-remote yet. These sections can be expanded to show the + respective commits. + + +File: magit.info, Node: Status Options, Prev: Status Module Sections, Up: Status Buffer + +5.1.4 Status Options +-------------------- + + -- User Option: magit-status-refresh-hook + Hook run after a status buffer has been refreshed. + + -- User Option: magit-status-margin + This option specifies whether the margin is initially shown in + Magit-Status mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + Also see the proceeding section for more options concerning status +buffers. + + +File: magit.info, Node: Repository List, Next: Logging, Prev: Status Buffer, Up: Inspecting + +5.2 Repository List +=================== + + -- Command: magit-list-repositories + This command displays a list of repositories in a separate buffer. + + The options ‘magit-repository-directories’ and + ‘magit-repository-directories-depth’ control which repositories are + displayed. + + -- User Option: magit-repolist-columns + This option controls what columns are displayed by the command + ‘magit-list-repositories’ and how they are displayed. + + Each element has the form ‘(HEADER WIDTH FORMAT PROPS)’. + + HEADER is the string displayed in the header. WIDTH is the width + of the column. FORMAT is a function that is called with one + argument, the repository identification (usually its basename), and + with ‘default-directory’ bound to the toplevel of its working tree. + It has to return a string to be inserted or nil. PROPS is an alist + that supports the keys ‘:right-align’, ‘:pad-right’ and ‘:sort’. + + The ‘:sort’ function has a weird interface described in the + docstring of ‘tabulated-list--get-sort’. Alternatively ‘<’ and + ‘magit-repolist-version<’ can be used as those functions are + automatically replaced with functions that satisfy the interface. + Set ‘:sort’ to ‘nil’ to inhibit sorting; if unspecifed, then the + column is sortable using the default sorter. + + You may wish to display a range of numeric columns using just one + character per column and without any padding between columns, in + which case you should use an appropriat HEADER, set WIDTH to 1, and + set ‘:pad-right’ to 9. ‘+’ is substituted for numbers higher than + 9. + +The following functions can be added to the above option: + + -- Function: magit-repolist-column-ident + This function inserts the identification of the repository. + Usually this is just its basename. + + -- Function: magit-repolist-column-path + This function inserts the absolute path of the repository. + + -- Function: magit-repolist-column-version + This function inserts a description of the repository’s ‘HEAD’ + revision. + + -- Function: magit-repolist-column-branch + This function inserts the name of the current branch. + + -- Function: magit-repolist-column-upstream + This function inserts the name of the upstream branch of the + current branch. + + -- Function: magit-repolist-column-branches + This function inserts the number of branches. + + -- Function: magit-repolist-column-stashes + This function inserts the number of stashes. + + -- Function: magit-repolist-column-flag + This function inserts a flag as specified by + ‘magit-repolist-column-flag-alist’. + + By default this indicates whether there are uncommitted changes. + + • ‘N’ if there is at least one untracked file. + • ‘U’ if there is at least one unstaged file. + • ‘S’ if there is at least one staged file. + + Only the first one of these that applies is shown. + + -- Function: magit-repolist-column-unpulled-from-upstream + This function inserts the number of upstream commits not in the + current branch. + + -- Function: magit-repolist-column-unpulled-from-pushremote + This function inserts the number of commits in the push branch but + not the current branch. + + -- Function: magit-repolist-column-unpushed-to-upstream + This function inserts the number of commits in the current branch + but not its upstream. + + -- Function: magit-repolist-column-unpushed-to-pushremote + This function inserts the number of commits in the current branch + but not its push branch. + +The following commands are available in repolist buffers: + +‘’ (‘magit-repolist-status’) + This command shows the status for the repository at point. + +‘m’ (‘magit-repolist-mark’) + This command marks the repository at point. + +‘u’ (‘magit-repolist-unmark’) + This command unmarks the repository at point. + +‘f’ (‘magit-repolist-fetch’) + This command fetches all marked repositories. If no repositories + are marked, then it offers to fetch all displayed repositories. + +‘5’ (‘magit-repolist-find-file-other-frame’) + This command reads a relative file-name (without completion) and + opens the respective file in each marked repository in a new frame. + If no repositories are marked, then it offers to do this for all + displayed repositories. + + +File: magit.info, Node: Logging, Next: Diffing, Prev: Repository List, Up: Inspecting + +5.3 Logging +=========== + +The status buffer contains logs for the unpushed and unpulled commits, +but that obviously isn’t enough. The transient prefix command +‘magit-log’, on ‘l’, features several suffix commands, which show a +specific log in a separate log buffer. + + Like other transient prefix commands, ‘magit-log’ also features +several infix arguments that can be changed before invoking one of the +suffix commands. However, in the case of the log transient, these +arguments may be taken from those currently in use in the current +repository’s log buffer, depending on the value of +‘magit-prefix-use-buffer-arguments’ (see *note Transient Arguments and +Buffer Variables::). + + For information about the various arguments, see *note +(gitman)git-log::. + + The switch ‘++order=VALUE’ is converted to one of +‘--author-date-order’, ‘--date-order’, or ‘--topo-order’ before being +passed to ‘git log’. + + The log transient also features several reflog commands. See *note +Reflog::. + +‘l’ (‘magit-log’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘l l’ (‘magit-log-current’) + Show log for the current branch. When ‘HEAD’ is detached or with a + prefix argument, show log for one or more revs read from the + minibuffer. + +‘l h’ (‘magit-log-head’) + Show log for ‘HEAD’. + +‘l u’ (‘magit-log-related’) + Show log for the current branch, its upstream and its push target. + When the upstream is a local branch, then also show its own + upstream. When ‘HEAD’ is detached, then show log for that, the + previously checked out branch and its upstream and push-target. + +‘l o’ (‘magit-log-other’) + Show log for one or more revs read from the minibuffer. The user + can input any revision or revisions separated by a space, or even + ranges, but only branches, tags, and a representation of the commit + at point are available as completion candidates. + +‘l L’ (‘magit-log-branches’) + Show log for all local branches and ‘HEAD’. + +‘l b’ (‘magit-log-all-branches’) + Show log for all local and remote branches and ‘HEAD’. + +‘l a’ (‘magit-log-all’) + Show log for all references and ‘HEAD’. + + Two additional commands that show the log for the file or blob that +is being visited in the current buffer exists, see *note Commands for +Buffers Visiting Files::. The command ‘magit-cherry’ also shows a log, +see *note Cherries::. + +* Menu: + +* Refreshing Logs:: +* Log Buffer:: +* Log Margin:: +* Select from Log:: +* Reflog:: +* Cherries:: + + +File: magit.info, Node: Refreshing Logs, Next: Log Buffer, Up: Logging + +5.3.1 Refreshing Logs +--------------------- + +The transient prefix command ‘magit-log-refresh’, on ‘L’, can be used to +change the log arguments used in the current buffer, without changing +which log is shown. This works in dedicated log buffers, but also in +the status buffer. + +‘L’ (‘magit-log-refresh’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘L g’ (‘magit-log-refresh’) + This suffix command sets the local log arguments for the current + buffer. + +‘L s’ (‘magit-log-set-default-arguments’) + This suffix command sets the default log arguments for buffers of + the same type as that of the current buffer. Other existing + buffers of the same type are not affected because their local + values have already been initialized. + +‘L w’ (‘magit-log-save-default-arguments’) + This suffix command sets the default log arguments for buffers of + the same type as that of the current buffer, and saves the value + for future sessions. Other existing buffers of the same type are + not affected because their local values have already been + initialized. + +‘L t’ (‘magit-toggle-margin’) + Show or hide the margin. + + +File: magit.info, Node: Log Buffer, Next: Log Margin, Prev: Refreshing Logs, Up: Logging + +5.3.2 Log Buffer +---------------- + +‘L’ (‘magit-log-refresh’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + See *note Refreshing Logs::. + +‘q’ (‘magit-log-bury-buffer’) + Bury the current buffer or the revision buffer in the same frame. + Like ‘magit-mode-bury-buffer’ (which see) but with a negative + prefix argument instead bury the revision buffer, provided it is + displayed in the current frame. + +‘C-c C-b’ (‘magit-go-backward’) + Move backward in current buffer’s history. + +‘C-c C-f’ (‘magit-go-forward’) + Move forward in current buffer’s history. + +‘C-c C-n’ (‘magit-log-move-to-parent’) + Move to a parent of the current commit. By default, this is the + first parent, but a numeric prefix can be used to specify another + parent. + +‘j’ (‘magit-log-move-to-revision’) + Read a revision and move to it in current log buffer. + + If the chosen reference or revision isn’t being displayed in the + current log buffer, then inform the user about that and do nothing + else. + + If invoked outside any log buffer, then display the log buffer of + the current repository first; creating it if necessary. + +‘’ (‘magit-diff-show-or-scroll-up’) + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer up. If there is no commit or stash at point, + then prompt for a commit. + +‘’ (‘magit-diff-show-or-scroll-down’) + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer down. If there is no commit or stash at point, + then prompt for a commit. + +‘=’ (‘magit-log-toggle-commit-limit’) + Toggle the number of commits the current log buffer is limited to. + If the number of commits is currently limited, then remove that + limit. Otherwise set it to 256. + +‘+’ (‘magit-log-double-commit-limit’) + Double the number of commits the current log buffer is limited to. + +‘-’ (‘magit-log-half-commit-limit’) + Half the number of commits the current log buffer is limited to. + + -- User Option: magit-log-auto-more + Insert more log entries automatically when moving past the last + entry. Only considered when moving past the last entry with + ‘magit-goto-*-section’ commands. + + -- User Option: magit-log-show-refname-after-summary + Whether to show the refnames after the commit summaries. This is + useful if you use really long branch names. + + Magit displays references in logs a bit differently from how Git does +it. + + Local branches are blue and remote branches are green. Of course +that depends on the used theme, as do the colors used for other types of +references. The current branch has a box around it, as do remote +branches that are their respective remote’s ‘HEAD’ branch. + + If a local branch and its push-target point at the same commit, then +their names are combined to preserve space and to make that relationship +visible. For example: + + origin/feature + [green][blue-] + + instead of + + feature origin/feature + [blue-] [green-------] + + Also note that while the transient features the ‘--show-signature’ +argument, that won’t actually be used when enabled, because Magit +defaults to use just one line per commit. Instead the commit colorized +to indicate the validity of the signed commit object, using the faces +named ‘magit-signature-*’ (which see). + + For a description of ‘magit-log-margin’ see *note Log Margin::. + + +File: magit.info, Node: Log Margin, Next: Select from Log, Prev: Log Buffer, Up: Logging + +5.3.3 Log Margin +---------------- + +In buffers which show one or more logs, it is possible to show +additional information about each commit in the margin. The options +used to configure the margin are named ‘magit-INFIX-margin’, where INFIX +is the same as in the respective major-mode ‘magit-INFIX-mode’. In +regular log buffers that would be ‘magit-log-margin’. + + -- User Option: magit-log-margin + This option specifies whether the margin is initially shown in + Magit-Log mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + You can change the STYLE and AUTHOR-WIDTH of all ‘magit-INFIX-margin’ +options to the same values by customizing ‘magit-log-margin’ *before* +‘magit’ is loaded. If you do that, then the respective values for the +other options will default to what you have set for that variable. +Likewise if you set INIT in ‘magit-log-margin’ to ‘nil’, then that is +used in the default of all other options. But setting it to ‘t’, i.e. +re-enforcing the default for that option, does not carry to other +options. + + -- User Option: magit-log-margin-show-committer-date + This option specifies whether to show the committer date in the + margin. This option only controls whether the committer date is + displayed instead of the author date. Whether some date is + displayed in the margin and whether the margin is displayed at all + is controlled by other options. + +‘L’ (‘magit-margin-settings’) + This transient prefix command binds the following suffix commands, + each of which changes the appearance of the margin in some way. + + In some buffers that support the margin, ‘L’ is instead bound to +‘magit-log-refresh’, but that transient features the same commands, and +then some other unrelated commands. + +‘L L’ (‘magit-toggle-margin’) + This command shows or hides the margin. + +‘L l’ (‘magit-cycle-margin-style’) + This command cycles the style used for the margin. + +‘L d’ (‘magit-toggle-margin-details’) + This command shows or hides details in the margin. + + +File: magit.info, Node: Select from Log, Next: Reflog, Prev: Log Margin, Up: Logging + +5.3.4 Select from Log +--------------------- + +When the user has to select a recent commit that is reachable from +‘HEAD’, using regular completion would be inconvenient (because most +humans cannot remember hashes or "HEAD~5", at least not without double +checking). Instead a log buffer is used to select the commit, which has +the advantage that commits are presented in order and with the commit +message. + + Such selection logs are used when selecting the beginning of a rebase +and when selecting the commit to be squashed into. + + In addition to the key bindings available in all log buffers, the +following additional key bindings are available in selection log +buffers: + +‘C-c C-c’ (‘magit-log-select-pick’) + Select the commit at point and act on it. Call + ‘magit-log-select-pick-function’ with the selected commit as + argument. + +‘C-c C-k’ (‘magit-log-select-quit’) + Abort selecting a commit, don’t act on any commit. + + -- User Option: magit-log-select-margin + This option specifies whether the margin is initially shown in + Magit-Log-Select mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Reflog, Next: Cherries, Prev: Select from Log, Up: Logging + +5.3.5 Reflog +------------ + +Also see *note (gitman)git-reflog::. + + These reflog commands are available from the log transient. See +*note Logging::. + +‘l r’ (‘magit-reflog-current’) + Display the reflog of the current branch. + +‘l O’ (‘magit-reflog-other’) + Display the reflog of a branch or another ref. + +‘l H’ (‘magit-reflog-head’) + Display the ‘HEAD’ reflog. + + -- User Option: magit-reflog-margin + This option specifies whether the margin is initially shown in + Magit-Reflog mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Cherries, Prev: Reflog, Up: Logging + +5.3.6 Cherries +-------------- + +Cherries are commits that haven’t been applied upstream (yet), and are +usually visualized using a log. Each commit is prefixed with ‘-’ if it +has an equivalent in the upstream and ‘+’ if it does not, i.e. if it is +a cherry. + + The command ‘magit-cherry’ shows cherries for a single branch, but +the references buffer (see *note References Buffer::) can show cherries +for multiple "upstreams" at once. + + Also see *note (gitman)git-reflog::. + +‘Y’ (‘magit-cherry’) + Show commits that are in a certain branch but that have not been + merged in the upstream branch. + + -- User Option: magit-cherry-margin + This option specifies whether the margin is initially shown in + Magit-Cherry mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Diffing, Next: Ediffing, Prev: Logging, Up: Inspecting + +5.4 Diffing +=========== + +The status buffer contains diffs for the staged and unstaged commits, +but that obviously isn’t enough. The transient prefix command +‘magit-diff’, on ‘d’, features several suffix commands, which show a +specific diff in a separate diff buffer. + + Like other transient prefix commands, ‘magit-diff’ also features +several infix arguments that can be changed before invoking one of the +suffix commands. However, in the case of the diff transient, these +arguments may be taken from those currently in use in the current +repository’s diff buffer, depending on the value of +‘magit-prefix-use-buffer-arguments’ (see *note Transient Arguments and +Buffer Variables::). + + Also see *note (gitman)git-diff::. + +‘d’ (‘magit-diff’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘d d’ (‘magit-diff-dwim’) + Show changes for the thing at point. + +‘d r’ (‘magit-diff-range’) + Show differences between two commits. + + RANGE should be a range (A..B or A...B) but can also be a single + commit. If one side of the range is omitted, then it defaults to + ‘HEAD’. If just a commit is given, then changes in the working + tree relative to that commit are shown. + + If the region is active, use the revisions on the first and last + line of the region. With a prefix argument, instead of diffing the + revisions, choose a revision to view changes along, starting at the + common ancestor of both revisions (i.e., use a "..." range). + +‘d w’ (‘magit-diff-working-tree’) + Show changes between the current working tree and the ‘HEAD’ + commit. With a prefix argument show changes between the working + tree and a commit read from the minibuffer. + +‘d s’ (‘magit-diff-staged’) + Show changes between the index and the ‘HEAD’ commit. With a + prefix argument show changes between the index and a commit read + from the minibuffer. + +‘d u’ (‘magit-diff-unstaged’) + Show changes between the working tree and the index. + +‘d p’ (‘magit-diff-paths’) + Show changes between any two files on disk. + + All of the above suffix commands update the repository’s diff buffer. +The diff transient also features two commands which show differences in +another buffer: + +‘d c’ (‘magit-show-commit’) + Show the commit at point. If there is no commit at point or with a + prefix argument, prompt for a commit. + +‘d t’ (‘magit-stash-show’) + Show all diffs of a stash in a buffer. + + Two additional commands that show the diff for the file or blob that +is being visited in the current buffer exists, see *note Commands for +Buffers Visiting Files::. + +* Menu: + +* Refreshing Diffs:: +* Commands Available in Diffs:: +* Diff Options:: +* Revision Buffer:: + + +File: magit.info, Node: Refreshing Diffs, Next: Commands Available in Diffs, Up: Diffing + +5.4.1 Refreshing Diffs +---------------------- + +The transient prefix command ‘magit-diff-refresh’, on ‘D’, can be used +to change the diff arguments used in the current buffer, without +changing which diff is shown. This works in dedicated diff buffers, but +also in the status buffer. + +‘D’ (‘magit-diff-refresh’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘D g’ (‘magit-diff-refresh’) + This suffix command sets the local diff arguments for the current + buffer. + +‘D s’ (‘magit-diff-set-default-arguments’) + This suffix command sets the default diff arguments for buffers of + the same type as that of the current buffer. Other existing + buffers of the same type are not affected because their local + values have already been initialized. + +‘D w’ (‘magit-diff-save-default-arguments’) + This suffix command sets the default diff arguments for buffers of + the same type as that of the current buffer, and saves the value + for future sessions. Other existing buffers of the same type are + not affected because their local values have already been + initialized. + +‘D t’ (‘magit-diff-toggle-refine-hunk’) + This command toggles hunk refinement on or off. + +‘D r’ (‘magit-diff-switch-range-type’) + This command converts the diff range type from "revA..revB" to + "revB...revA", or vice versa. + +‘D f’ (‘magit-diff-flip-revs’) + This command swaps revisions in the diff range from "revA..revB" to + "revB..revA", or vice versa. + +‘D F’ (‘magit-diff-toggle-file-filter’) + This command toggles the file restriction of the diffs in the + current buffer, allowing you to quickly switch between viewing all + the changes in the commit and the restricted subset. As a special + case, when this command is called from a log buffer, it toggles the + file restriction in the repository’s revision buffer, which is + useful when you display a revision from a log buffer that is + restricted to a file or files. + + In addition to the above transient, which allows changing any of the +supported arguments, there also exist some commands that change only a +particular argument. + +‘-’ (‘magit-diff-less-context’) + This command decreases the context for diff hunks by COUNT lines. + +‘+’ (‘magit-diff-more-context’) + This command increases the context for diff hunks by COUNT lines. + +‘0’ (‘magit-diff-default-context’) + This command resets the context for diff hunks to the default + height. + + The following commands quickly change what diff is being displayed +without having to using one of the diff transient. + +‘C-c C-d’ (‘magit-diff-while-committing’) + While committing, this command shows the changes that are about to + be committed. While amending, invoking the command again toggles + between showing just the new changes or all the changes that will + be committed. + + This binding is available in the diff buffer as well as the commit + message buffer. + +‘C-c C-b’ (‘magit-go-backward’) + This command moves backward in current buffer’s history. + +‘C-c C-f’ (‘magit-go-forward’) + This command moves forward in current buffer’s history. + + +File: magit.info, Node: Commands Available in Diffs, Next: Diff Options, Prev: Refreshing Diffs, Up: Diffing + +5.4.2 Commands Available in Diffs +--------------------------------- + +Some commands are only available if point is inside a diff. + + ‘magit-diff-visit-file’ and related commands visit the appropriate +version of the file that the diff at point is about. Likewise +‘magit-diff-visit-worktree-file’ and related commands visit the worktree +version of the file that the diff at point is about. See *note Visiting +Files and Blobs from a Diff:: for more information and the key bindings. + +‘C-c C-t’ (‘magit-diff-trace-definition’) + This command shows a log for the definition at point. + + -- User Option: magit-log-trace-definition-function + The function specified by this option is used by + ‘magit-log-trace-definition’ to determine the function at point. + For major-modes that have special needs, you could set the local + value using the mode’s hook. + +‘C-c C-e’ (‘magit-diff-edit-hunk-commit’) + From a hunk, this command edits the respective commit and visits + the file. + + First it visits the file being modified by the hunk at the correct + location using ‘magit-diff-visit-file’. This actually visits a + blob. When point is on a diff header, not within an individual + hunk, then this visits the blob the first hunk is about. + + Then it invokes ‘magit-edit-line-commit’, which uses an interactive + rebase to make the commit editable, or if that is not possible + because the commit is not reachable from ‘HEAD’ by checking out + that commit directly. This also causes the actual worktree file to + be visited. + + Neither the blob nor the file buffer are killed when finishing the + rebase. If that is undesirable, then it might be better to use + ‘magit-rebase-edit-command’ instead of this command. + +‘j’ (‘magit-jump-to-diffstat-or-diff’) + This command jumps to the diffstat or diff. When point is on a + file inside the diffstat section, then jump to the respective diff + section. Otherwise, jump to the diffstat section or a child + thereof. + + The next two commands are not specific to Magit-Diff mode (or and +Magit buffer for that matter), but it might be worth pointing out that +they are available here too. + +‘’ (‘scroll-up’) + This command scrolls text upward. + +‘’ (‘scroll-down’) + This command scrolls text downward. + + +File: magit.info, Node: Diff Options, Next: Revision Buffer, Prev: Commands Available in Diffs, Up: Diffing + +5.4.3 Diff Options +------------------ + + -- User Option: magit-diff-refine-hunk + Whether to show word-granularity differences within diff hunks. + + • ‘nil’ Never show fine differences. + • ‘t’ Show fine differences for the current diff hunk only. + • ‘all’ Show fine differences for all displayed diff hunks. + + -- User Option: magit-diff-refine-ignore-whitespace + Whether to ignore whitespace changes in word-granularity + differences. + + -- User Option: magit-diff-adjust-tab-width + Whether to adjust the width of tabs in diffs. + + Determining the correct width can be expensive if it requires + opening large and/or many files, so the widths are cached in the + variable ‘magit-diff--tab-width-cache’. Set that to nil to + invalidate the cache. + + • ‘nil’ Never adjust tab width. Use ‘tab-width’s value from the + Magit buffer itself instead. + + • ‘t’ If the corresponding file-visiting buffer exits, then use + ‘tab-width’’s value from that buffer. Doing this is cheap, so + this value is used even if a corresponding cache entry exists. + + • ‘always’ If there is no such buffer, then temporarily visit + the file to determine the value. + + • NUMBER Like ‘always’, but don’t visit files larger than NUMBER + bytes. + + -- User Option: magit-diff-paint-whitespace + Specify where to highlight whitespace errors. + + See ‘magit-diff-highlight-trailing’, + ‘magit-diff-highlight-indentation’. The symbol ‘t’ means in all + diffs, ‘status’ means only in the status buffer, and nil means + nowhere. + + • ‘nil’ Never highlight whitespace errors. + • ‘t’ Highlight whitespace errors everywhere. + • ‘uncommitted’ Only highlight whitespace errors in diffs + showing uncommitted changes. For backward compatibility + ‘status’ is treated as a synonym. + + -- User Option: magit-diff-paint-whitespace-lines + Specify in what kind of lines to highlight whitespace errors. + + • ‘t’ Highlight only in added lines. + • ‘both’ Highlight in added and removed lines. + • ‘all’ Highlight in added, removed and context lines. + + -- User Option: magit-diff-highlight-trailing + Whether to highlight whitespace at the end of a line in diffs. + Used only when ‘magit-diff-paint-whitespace’ is non-nil. + + -- User Option: magit-diff-highlight-indentation + This option controls whether to highlight the indentation in case + it used the "wrong" indentation style. Indentation is only + highlighted if ‘magit-diff-paint-whitespace’ is also non-nil. + + The value is an alist of the form ‘((REGEXP . INDENT)...)’. The + path to the current repository is matched against each element in + reverse order. Therefore if a REGEXP matches, then earlier + elements are not tried. + + If the used INDENT is ‘tabs’, highlight indentation with tabs. If + INDENT is an integer, highlight indentation with at least that many + spaces. Otherwise, highlight neither. + + -- User Option: magit-diff-hide-trailing-cr-characters + Whether to hide ^M characters at the end of a line in diffs. + + -- User Option: magit-diff-highlight-hunk-region-functions + This option specifies the functions used to highlight the + hunk-internal region. + + ‘magit-diff-highlight-hunk-region-dim-outside’ overlays the outside + of the hunk internal selection with a face that causes the added + and removed lines to have the same background color as context + lines. This function should not be removed from the value of this + option. + + ‘magit-diff-highlight-hunk-region-using-overlays’ and + ‘magit-diff-highlight-hunk-region-using-underline’ emphasize the + region by placing delimiting horizontal lines before and after it. + Both of these functions have glitches which cannot be fixed due to + limitations of Emacs’ display engine. For more information see + ff. + + Instead of, or in addition to, using delimiting horizontal lines, + to emphasize the boundaries, you may wish to emphasize the text + itself, using ‘magit-diff-highlight-hunk-region-using-face’. + + In terminal frames it’s not possible to draw lines as the overlay + and underline variants normally do, so there they fall back to + calling the face function instead. + + -- User Option: magit-diff-unmarked-lines-keep-foreground + This option controls whether added and removed lines outside the + hunk-internal region only lose their distinct background color or + also the foreground color. Whether the outside of the region is + dimmed at all depends on + ‘magit-diff-highlight-hunk-region-functions’. + + -- User Option: magit-diff-extra-stat-arguments + This option specifies additional arguments to be used alongside + ‘--stat’. + + The value is a list of zero or more arguments or a function that + takes no argument and returns such a list. These arguments are + allowed here: ‘--stat-width’, ‘--stat-name-width’, + ‘--stat-graph-width’ and ‘--compact-summary’. Also see *note + (gitman)git-diff::. + + +File: magit.info, Node: Revision Buffer, Prev: Diff Options, Up: Diffing + +5.4.4 Revision Buffer +--------------------- + + -- User Option: magit-revision-insert-related-refs + Whether to show related branches in revision buffers. + + • ‘nil’ Don’t show any related branches. + • ‘t’ Show related local branches. + • ‘all’ Show related local and remote branches. + • ‘mixed’ Show all containing branches and local merged + branches. + + -- User Option: magit-revision-show-gravatars + Whether to show gravatar images in revision buffers. + + If ‘nil’, then don’t insert any gravatar images. If ‘t’, then + insert both images. If ‘author’ or ‘committer’, then insert only + the respective image. + + If you have customized the option ‘magit-revision-headers-format’ + and want to insert the images then you might also have to specify + where to do so. In that case the value has to be a cons-cell of + two regular expressions. The car specifies where to insert the + author’s image. The top half of the image is inserted right after + the matched text, the bottom half on the next line in the same + column. The cdr specifies where to insert the committer’s image, + accordingly. Either the car or the cdr may be nil." + + -- User Option: magit-revision-use-hash-sections + Whether to turn hashes inside the commit message into sections. + + If non-nil, then hashes inside the commit message are turned into + ‘commit’ sections. There is a trade off to be made between + performance and reliability: + + • ‘slow’ calls git for every word to be absolutely sure. + • ‘quick’ skips words less than seven characters long. + • ‘quicker’ additionally skips words that don’t contain a + number. + • ‘quickest’ uses all words that are at least seven characters + long and which contain at least one number as well as at least + one letter. + + If nil, then no hashes are turned into sections, but you can still + visit the commit at point using "RET". + + The diffs shown in the revision buffer may be automatically +restricted to a subset of the changed files. If the revision buffer is +displayed from a log buffer, the revision buffer will share the same +file restriction as that log buffer (also see the command +‘magit-diff-toggle-file-filter’). + + -- User Option: magit-revision-filter-files-on-follow + Whether showing a commit from a log buffer honors the log’s file + filter when the log arguments include ‘--follow’. + + When this option is nil, displaying a commit from a log ignores the + log’s file filter if the log arguments include ‘--follow’. Doing + so avoids showing an empty diff in revision buffers for commits + before a rename event. In such cases, the ‘--patch’ argument of + the log transient can be used to show the file-restricted diffs + inline. + + Set this option to non-nil to keep the log’s file restriction even + if ‘--follow’ is present in the log arguments. + + If the revision buffer is not displayed from a log buffer, the file +restriction is determined as usual (see *note Transient Arguments and +Buffer Variables::). + + +File: magit.info, Node: Ediffing, Next: References Buffer, Prev: Diffing, Up: Inspecting + +5.5 Ediffing +============ + +This section describes how to enter Ediff from Magit buffers. For +information on how to use Ediff itself, see *note (ediff)Top::. + +‘e’ (‘magit-ediff-dwim’) + Compare, stage, or resolve using Ediff. + + This command tries to guess what file, and what commit or range the + user wants to compare, stage, or resolve using Ediff. It might + only be able to guess either the file, or range/commit, in which + case the user is asked about the other. It might not always guess + right, in which case the appropriate ‘magit-ediff-*’ command has to + be used explicitly. If it cannot read the user’s mind at all, then + it asks the user for a command to run. + +‘E’ (‘magit-ediff’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘E r’ (‘magit-ediff-compare’) + Compare two revisions of a file using Ediff. + + If the region is active, use the revisions on the first and last + line of the region. With a prefix argument, instead of diffing the + revisions, choose a revision to view changes along, starting at the + common ancestor of both revisions (i.e., use a "..." range). + +‘E m’ (‘magit-ediff-resolve-rest’) + This command allows you to resolve outstanding conflicts in the + file at point using Ediff. If there is no file at point or if it + doesn’t have any unmerged changes, then this command prompts for a + file. + + Provided that the value of ‘merge.conflictstyle’ is ‘diff3’, you + can view the file’s merge-base revision using ‘/’ in the Ediff + control buffer. + + The A, B and Ancestor buffers are constructed from the conflict + markers in the worktree file. Because you and/or Git may have + already resolved some conflicts, that means that these buffers may + not contain the actual versions from the respective blobs. + +‘E m’ (‘magit-ediff-resolve-all’) + This command allows you to resolve all conflicts in the file at + point using Ediff. If there is no file at point or if it doesn’t + have any unmerged changes, then this command prompts for a file. + + Provided that the value of ‘merge.conflictstyle’ is ‘diff3’, you + can view the file’s merge-base revision using ‘/’ in the Ediff + control buffer. + + First the file in the worktree is moved aside, appending the suffix + ‘.ORIG’, so that you could later go back to that version. Then it + is reconstructed from the two sides of the conflict and the + merge-base, if available. + + It would be nice if the worktree file were just used as-is, but + Ediff does not support that. This means that all conflicts, that + Git has already resolved, are restored. On the other hand Ediff + also tries to resolve conflicts, and in many cases Ediff and Git + should produce similar results. + + However if you have already resolved some conflicts manually, then + those changes are discarded (though you can recover them from the + backup file). In such cases ‘magit-ediff-resolve-rest’ might be + more suitable. + + The advantage that this command has over ‘magit-ediff-resolve-rest’ + is that the A, B and Ancestor buffers correspond to blobs from the + respective commits, allowing you to inspect a side in context and + to use Magit commands in these buffers to do so. Blame and log + commands are particularly useful here. + +‘E t’ (‘magit-git-mergetool’) + This command does not actually use Ediff. While it serves the same + purpose as ‘magit-ediff-resolve-rest’, it uses ‘git mergetool + --gui’ to resolve conflicts. + + With a prefix argument this acts as a transient prefix command, + allowing the user to select the mergetool and change some settings. + +‘E s’ (‘magit-ediff-stage’) + Stage and unstage changes to a file using Ediff, defaulting to the + file at point. + +‘E u’ (‘magit-ediff-show-unstaged’) + Show unstaged changes to a file using Ediff. + +‘E i’ (‘magit-ediff-show-staged’) + Show staged changes to a file using Ediff. + +‘E w’ (‘magit-ediff-show-working-tree’) + Show changes in a file between ‘HEAD’ and working tree using Ediff. + +‘E c’ (‘magit-ediff-show-commit’) + Show changes to a file introduced by a commit using Ediff. + +‘E z’ (‘magit-ediff-show-stash’) + Show changes to a file introduced by a stash using Ediff. + + -- User Option: magit-ediff-dwim-resolve-function + This option controls which function ‘magit-ediff-dwim’ uses to + resolve conflicts. One of ‘magit-ediff-resolve-rest’, + ‘magit-ediff-resolve-all’ or ‘magit-git-mergetool’; which are all + discussed above. + + -- User Option: magit-ediff-dwim-show-on-hunks + This option controls what command ‘magit-ediff-dwim’ calls when + point is on uncommitted hunks. When nil, always run + ‘magit-ediff-stage’. Otherwise, use ‘magit-ediff-show-staged’ and + ‘magit-ediff-show-unstaged’ to show staged and unstaged changes, + respectively. + + -- User Option: magit-ediff-show-stash-with-index + This option controls whether ‘magit-ediff-show-stash’ includes a + buffer containing the file’s state in the index at the time the + stash was created. This makes it possible to tell which changes in + the stash were staged. + + -- User Option: magit-ediff-quit-hook + This hook is run after quitting an Ediff session that was created + using a Magit command. The hook functions are run inside the Ediff + control buffer, and should not change the current buffer. + + This is similar to ‘ediff-quit-hook’ but takes the needs of Magit + into account. The regular ‘ediff-quit-hook’ is ignored by Ediff + sessions that were created using a Magit command. + + +File: magit.info, Node: References Buffer, Next: Bisecting, Prev: Ediffing, Up: Inspecting + +5.6 References Buffer +===================== + +‘y’ (‘magit-show-refs’) + This command lists branches and tags in a dedicated buffer. + + However if this command is invoked again from this buffer or if it + is invoked with a prefix argument, then it acts as a transient + prefix command, which binds the following suffix commands and some + infix arguments. + + All of the following suffix commands list exactly the same branches +and tags. The only difference the optional feature that can be enabled +by changing the value of ‘magit-refs-show-commit-count’ (see below). +These commands specify a different branch or commit against which all +the other references are compared. + +‘y y’ (‘magit-show-refs-head’) + This command lists branches and tags in a dedicated buffer. Each + reference is being compared with ‘HEAD’. + +‘y c’ (‘magit-show-refs-current’) + This command lists branches and tags in a dedicated buffer. Each + reference is being compared with the current branch or ‘HEAD’ if it + is detached. + +‘y o’ (‘magit-show-refs-other’) + This command lists branches and tags in a dedicated buffer. Each + reference is being compared with a branch read from the user. + +‘y r’ (‘magit-refs-set-show-commit-count’) + This command changes for which refs the commit count is shown. + + -- User Option: magit-refs-show-commit-count + Whether to show commit counts in Magit-Refs mode buffers. + + • ‘all’ Show counts for branches and tags. + • ‘branch’ Show counts for branches only. + • ‘nil’ Never show counts. + + The default is ‘nil’ because anything else can be very expensive. + + -- User Option: magit-refs-pad-commit-counts + Whether to pad all commit counts on all sides in Magit-Refs mode + buffers. + + If this is nil, then some commit counts are displayed right next to + one of the branches that appear next to the count, without any + space in between. This might look bad if the branch name faces + look too similar to ‘magit-dimmed’. + + If this is non-nil, then spaces are placed on both sides of all + commit counts. + + -- User Option: magit-refs-show-remote-prefix + Whether to show the remote prefix in lists of remote branches. + + Showing the prefix is redundant because the name of the remote is + already shown in the heading preceding the list of its branches. + + -- User Option: magit-refs-primary-column-width + Width of the primary column in ‘magit-refs-mode’ buffers. The + primary column is the column that contains the name of the branch + that the current row is about. + + If this is an integer, then the column is that many columns wide. + Otherwise it has to be a cons-cell of two integers. The first + specifies the minimal width, the second the maximal width. In that + case the actual width is determined using the length of the names + of the shown local branches. (Remote branches and tags are not + taken into account when calculating to optimal width.) + + -- User Option: magit-refs-focus-column-width + Width of the focus column in ‘magit-refs-mode’ buffers. + + The focus column is the first column, which marks one branch + (usually the current branch) as the focused branch using ‘*’ or + ‘@’. For each other reference, this column optionally shows how + many commits it is ahead of the focused branch and ‘<’, or if it + isn’t ahead then the commits it is behind and ‘>’, or if it isn’t + behind either, then a ‘=’. + + This column may also display only ‘*’ or ‘@’ for the focused + branch, in which case this option is ignored. Use ‘L v’ to change + the verbosity of this column. + + -- User Option: magit-refs-margin + This option specifies whether the margin is initially shown in + Magit-Refs mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + -- User Option: magit-refs-margin-for-tags + This option specifies whether to show information about tags in the + margin. This is disabled by default because it is slow if there + are many tags. + + The following variables control how individual refs are displayed. +If you change one of these variables (especially the "%c" part), then +you should also change the others to keep things aligned. The following +%-sequences are supported: + + • ‘%a’ Number of commits this ref has over the one we compare to. + • ‘%b’ Number of commits the ref we compare to has over this one. + • ‘%c’ Number of commits this ref has over the one we compare to. + For the ref which all other refs are compared this is instead "@", + if it is the current branch, or "#" otherwise. + • ‘%C’ For the ref which all other refs are compared this is "@", if + it is the current branch, or "#" otherwise. For all other refs " + ". + • ‘%h’ Hash of this ref’s tip. + • ‘%m’ Commit summary of the tip of this ref. + • ‘%n’ Name of this ref. + • ‘%u’ Upstream of this local branch. + • ‘%U’ Upstream of this local branch and additional local vs. + upstream information. + + -- User Option: magit-refs-filter-alist + The purpose of this option is to forgo displaying certain refs + based on their name. If you want to not display any refs of a + certain type, then you should remove the appropriate function from + ‘magit-refs-sections-hook’ instead. + + This alist controls which tags and branches are omitted from being + displayed in ‘magit-refs-mode’ buffers. If it is ‘nil’, then all + refs are displayed (subject to ‘magit-refs-sections-hook’). + + All keys are tried in order until one matches. Then its value is + used and subsequent elements are ignored. If the value is non-nil, + then the reference is displayed, otherwise it is not. If no + element matches, then the reference is displayed. + + A key can either be a regular expression that the refname has to + match, or a function that takes the refname as only argument and + returns a boolean. A remote branch such as "origin/master" is + displayed as just "master", however for this comparison the former + is used. + +‘’ (‘magit-visit-ref’) + This command visits the reference or revision at point in another + buffer. If there is no revision at point or with a prefix argument + then it prompts for a revision. + + This command behaves just like ‘magit-show-commit’ as described + above, except if point is on a reference in a ‘magit-refs-mode’ + buffer, in which case the behavior may be different, but only if + you have customized the option ‘magit-visit-ref-behavior’. + + -- User Option: magit-visit-ref-behavior + This option controls how ‘magit-visit-ref’ behaves in + ‘magit-refs-mode’ buffers. + + By default ‘magit-visit-ref’ behaves like ‘magit-show-commit’, in + all buffers, including ‘magit-refs-mode’ buffers. When the type of + the section at point is ‘commit’ then "RET" is bound to + ‘magit-show-commit’, and when the type is either ‘branch’ or ‘tag’ + then it is bound to ‘magit-visit-ref’. + + "RET" is one of Magit’s most essential keys and at least by default + it should behave consistently across all of Magit, especially + because users quickly learn that it does something very harmless; + it shows more information about the thing at point in another + buffer. + + However "RET" used to behave differently in ‘magit-refs-mode’ + buffers, doing surprising things, some of which cannot really be + described as "visit this thing". If you’ve grown accustomed this + behavior, you can restore it by adding one or more of the below + symbols to the value of this option. But keep in mind that by + doing so you don’t only introduce inconsistencies, you also lose + some functionality and might have to resort to ‘M-x + magit-show-commit’ to get it back. + + ‘magit-visit-ref’ looks for these symbols in the order in which + they are described here. If the presence of a symbol applies to + the current situation, then the symbols that follow do not affect + the outcome. + + • ‘focus-on-ref’ + + With a prefix argument update the buffer to show commit counts + and lists of cherry commits relative to the reference at point + instead of relative to the current buffer or ‘HEAD’. + + Instead of adding this symbol, consider pressing "C-u y o + RET". + + • ‘create-branch’ + + If point is on a remote branch, then create a new local branch + with the same name, use the remote branch as its upstream, and + then check out the local branch. + + Instead of adding this symbol, consider pressing "b c RET + RET", like you would do in other buffers. + + • ‘checkout-any’ + + Check out the reference at point. If that reference is a tag + or a remote branch, then this results in a detached ‘HEAD’. + + Instead of adding this symbol, consider pressing "b b RET", + like you would do in other buffers. + + • ‘checkout-branch’ + + Check out the local branch at point. + + Instead of adding this symbol, consider pressing "b b RET", + like you would do in other buffers. + +* Menu: + +* References Sections:: + + +File: magit.info, Node: References Sections, Up: References Buffer + +5.6.1 References Sections +------------------------- + +The contents of references buffers is controlled using the hook +‘magit-refs-sections-hook’. See *note Section Hooks:: to learn about +such hooks and how to customize them. All of the below functions are +members of the default value. Note that it makes much less sense to +customize this hook than it does for the respective hook used for the +status buffer. + + -- User Option: magit-refs-sections-hook + Hook run to insert sections into a references buffer. + + -- Function: magit-insert-local-branches + Insert sections showing all local branches. + + -- Function: magit-insert-remote-branches + Insert sections showing all remote-tracking branches. + + -- Function: magit-insert-tags + Insert sections showing all tags. + + +File: magit.info, Node: Bisecting, Next: Visiting Files and Blobs, Prev: References Buffer, Up: Inspecting + +5.7 Bisecting +============= + +Also see *note (gitman)git-bisect::. + +‘B’ (‘magit-bisect’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + + When bisecting is not in progress, then the transient features the +following suffix commands. + +‘B B’ (‘magit-bisect-start’) + Start a bisect session. + + Bisecting a bug means to find the commit that introduced it. This + command starts such a bisect session by asking for a known good + commit and a known bad commit. If you’re bisecting a change that + isn’t a regression, you can select alternate terms that are + conceptually more fitting than "bad" and "good", but the infix + arguments to do so are disabled by default. + +‘B s’ (‘magit-bisect-run’) + Bisect automatically by running commands after each step. + + When bisecting in progress, then the transient instead features the +following suffix commands. + +‘B b’ (‘magit-bisect-bad’) + Mark the current commit as bad. Use this after you have asserted + that the commit does contain the bug in question. + +‘B g’ (‘magit-bisect-good’) + Mark the current commit as good. Use this after you have asserted + that the commit does not contain the bug in question. + +‘B m’ (‘magit-bisect-mark’) + Mark the current commit with one of the bisect terms. This command + provides an alternative to ‘magit-bisect-bad’ and + ‘magit-bisect-good’ and is useful when using terms other than "bad" + and "good". This suffix is disabled by default. + +‘B k’ (‘magit-bisect-skip’) + Skip the current commit. Use this if for some reason the current + commit is not a good one to test. This command lets Git choose a + different one. + +‘B r’ (‘magit-bisect-reset’) + After bisecting, cleanup bisection state and return to original + ‘HEAD’. + + By default the status buffer shows information about the ongoing +bisect session. + + -- User Option: magit-bisect-show-graph + This option controls whether a graph is displayed for the log of + commits that still have to be bisected. + + +File: magit.info, Node: Visiting Files and Blobs, Next: Blaming, Prev: Bisecting, Up: Inspecting + +5.8 Visiting Files and Blobs +============================ + +Magit provides several commands that visit a file or blob (the version +of a file that is stored in a certain commit). Actually it provides +several *groups* of such commands and the several *variants* within each +group. + +* Menu: + +* General-Purpose Visit Commands:: +* Visiting Files and Blobs from a Diff:: + + +File: magit.info, Node: General-Purpose Visit Commands, Next: Visiting Files and Blobs from a Diff, Up: Visiting Files and Blobs + +5.8.1 General-Purpose Visit Commands +------------------------------------ + +These commands can be used anywhere to open any blob. Currently no keys +are bound to these commands by default, but that is likely to change. + + -- Command: magit-find-file + This command reads a filename and revision from the user and visits + the respective blob in a buffer. The buffer is displayed in the + selected window. + + -- Command: magit-find-file-other-window + This command reads a filename and revision from the user and visits + the respective blob in a buffer. The buffer is displayed in + another window. + + -- Command: magit-find-file-other-frame + This command reads a filename and revision from the user and visits + the respective blob in a buffer. The buffer is displayed in + another frame. + + +File: magit.info, Node: Visiting Files and Blobs from a Diff, Prev: General-Purpose Visit Commands, Up: Visiting Files and Blobs + +5.8.2 Visiting Files and Blobs from a Diff +------------------------------------------ + +These commands can only be used when point is inside a diff. + +‘’ (‘magit-diff-visit-file’) + This command visits the appropriate version of the file that the + diff at point is about. + + This commands visits the worktree version of the appropriate file. + The location of point inside the diff determines which file is + being visited. The visited version depends on what changes the + diff is about. + + 1. If the diff shows uncommitted changes (i.e. staged or + unstaged changes), then visit the file in the working tree + (i.e. the same "real" file that ‘find-file’ would visit. In + all other cases visit a "blob" (i.e. the version of a file as + stored in some commit). + + 2. If point is on a removed line, then visit the blob for the + first parent of the commit that removed that line, i.e. the + last commit where that line still exists. + + 3. If point is on an added or context line, then visit the blob + that adds that line, or if the diff shows from more than a + single commit, then visit the blob from the last of these + commits. + + In the file-visiting buffer this command goes to the line that + corresponds to the line that point is on in the diff. + + The buffer is displayed in the selected window. With a prefix + argument the buffer is displayed in another window instead. + + -- User Option: magit-diff-visit-previous-blob + This option controls whether ‘magit-diff-visit-file’ may visit the + previous blob. When this is ‘t’ (the default) and point is on a + removed line in a diff for a committed change, then + ‘magit-diff-visit-file’ visits the blob from the last revision + which still had that line. + + Currently this is only supported for committed changes, for staged + and unstaged changes ‘magit-diff-visit-file’ always visits the file + in the working tree. + +‘C-’ (‘magit-diff-visit-file-worktree’) + This command visits the worktree version of the appropriate file. + The location of point inside the diff determines which file is + being visited. Unlike ‘magit-diff-visit-file’ it always visits the + "real" file in the working tree, i.e the "current version" of the + file. + + In the file-visiting buffer this command goes to the line that + corresponds to the line that point is on in the diff. Lines that + were added or removed in the working tree, the index and other + commits in between are automatically accounted for. + + The buffer is displayed in the selected window. With a prefix + argument the buffer is displayed in another window instead. + + Variants of the above two commands exist that instead visit the file +in another window or in another frame. If you prefer such behavior, +then you may want to change the above key bindings, but note that the +above commands also use another window when invoked with a prefix +argument. + + -- Command: magit-diff-visit-file-other-window + -- Command: magit-diff-visit-file-other-frame + -- Command: magit-diff-visit-worktree-file-other-window + -- Command: magit-diff-visit-worktree-file-other-frame + + +File: magit.info, Node: Blaming, Prev: Visiting Files and Blobs, Up: Inspecting + +5.9 Blaming +=========== + +Also see *note (gitman)git-blame::. + + To start blaming invoke the ‘magit-file-dispatch’ transient prefix +command by pressing ‘C-c M-g’. + + The blaming suffix commands can be invoked from the dispatch +transient. However if you want to set an infix argument, then you have +to enter the blaming sub-transient first. + + The key bindings shown below assume that you enter the dispatch +transient using the default binding. + +‘C-c M-g B’ (‘magit-blame’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + Note that not all of the following suffixes are available at all +times. For example if ‘magit-blame-mode’ is not enabled, then the +command whose purpose is to turn off that mode would not be of any use +and therefore isn’t available. + +‘C-c M-g b’ (‘magit-blame-addition’) +‘C-c M-g B b’ + This command augments each line or chunk of lines in the current + file-visiting or blob-visiting buffer with information about what + commits last touched these lines. + + If the buffer visits a revision of that file, then history up to + that revision is considered. Otherwise, the file’s full history is + considered, including uncommitted changes. + + If Magit-Blame mode is already turned on in the current buffer then + blaming is done recursively, by visiting REVISION:FILE (using + ‘magit-find-file’), where REVISION is a parent of the revision that + added the current line or chunk of lines. + +‘C-c M-g r’ (‘magit-blame-removal’) +‘C-c M-g B r’ + This command augments each line or chunk of lines in the current + blob-visiting buffer with information about the revision that + removes it. It cannot be used in file-visiting buffers. + + Like ‘magit-blame-addition’, this command can be used recursively. + +‘C-c M-g f’ (‘magit-blame-reverse’) +‘C-c M-g B f’ + This command augments each line or chunk of lines in the current + file-visiting or blob-visiting buffer with information about the + last revision in which a line still existed. + + Like ‘magit-blame-addition’, this command can be used recursively. + +‘C-c M-g e’ (‘magit-blame-echo’) +‘C-c M-g B e’ + This command is like ‘magit-blame-addition’ except that it doesn’t + turn on ‘read-only-mode’ and that it initially uses the + visualization style specified by option ‘magit-blame-echo-style’. + + The following key bindings are available when Magit-Blame mode is +enabled and Read-Only mode is not enabled. These commands are also +available in other buffers; here only the behavior is described that is +relevant in file-visiting buffers that are being blamed. + +‘’ (‘magit-show-commit’) + This command shows the commit that last touched the line at point. + +‘’ (‘magit-diff-show-or-scroll-up’) + This command updates the commit buffer. + + This either shows the commit that last touched the line at point in + the appropriate buffer, or if that buffer is already being + displayed in the current frame and if that buffer contains + information about that commit, then the buffer is scrolled up + instead. + +‘’ (‘magit-diff-show-or-scroll-down’) + This command updates the commit buffer. + + This either shows the commit that last touched the line at point in + the appropriate buffer, or if that buffer is already being + displayed in the current frame and if that buffer contains + information about that commit, then the buffer is scrolled down + instead. + + The following key bindings are available when both Magit-Blame mode +and Read-Only mode are enabled. + +‘b’ (‘magit-blame’) + See above. + +‘n’ (‘magit-blame-next-chunk’) + This command moves to the next chunk. + +‘N’ (‘magit-blame-next-chunk-same-commit’) + This command moves to the next chunk from the same commit. + +‘p’ (‘magit-blame-previous-chunk’) + This command moves to the previous chunk. + +‘P’ (‘magit-blame-previous-chunk-same-commit’) + This command moves to the previous chunk from the same commit. + +‘q’ (‘magit-blame-quit’) + This command turns off Magit-Blame mode. If the buffer was created + during a recursive blame, then it also kills the buffer. + +‘M-w’ (‘magit-blame-copy-hash’) + This command saves the hash of the current chunk’s commit to the + kill ring. + + When the region is active, the command saves the region’s content + instead of the hash, like ‘kill-ring-save’ would. + +‘c’ (‘magit-blame-cycle-style’) + This command changes how blame information is visualized in the + current buffer by cycling through the styles specified using the + option ‘magit-blame-styles’. + + Blaming is also controlled using the following options. + + -- User Option: magit-blame-styles + This option defines a list of styles used to visualize blame + information. For now see its doc-string to learn more. + + -- User Option: magit-blame-echo-style + This option specifies the blame visualization style used by the + command ‘magit-blame-echo’. This must be a symbol that is used as + the identifier for one of the styles defined in + ‘magit-blame-styles’. + + -- User Option: magit-blame-time-format + This option specifies the format string used to display times when + showing blame information. + + -- User Option: magit-blame-read-only + This option controls whether blaming a buffer also makes + temporarily read-only. + + -- User Option: magit-blame-disable-modes + This option lists incompatible minor-modes that should be disabled + temporarily when a buffer contains blame information. They are + enabled again when the buffer no longer shows blame information. + + -- User Option: magit-blame-goto-chunk-hook + This hook is run when moving between chunks. + + +File: magit.info, Node: Manipulating, Next: Transferring, Prev: Inspecting, Up: Top + +6 Manipulating +************** + +* Menu: + +* Creating Repository:: +* Cloning Repository:: +* Staging and Unstaging:: +* Applying:: +* Committing:: +* Branching:: +* Merging:: +* Resolving Conflicts:: +* Rebasing:: +* Cherry Picking:: +* Resetting:: +* Stashing:: + + +File: magit.info, Node: Creating Repository, Next: Cloning Repository, Up: Manipulating + +6.1 Creating Repository +======================= + +‘I’ (‘magit-init’) + This command initializes a repository and then shows the status + buffer for the new repository. + + If the directory is below an existing repository, then the user has + to confirm that a new one should be created inside. If the + directory is the root of the existing repository, then the user has + to confirm that it should be reinitialized. + + +File: magit.info, Node: Cloning Repository, Next: Staging and Unstaging, Prev: Creating Repository, Up: Manipulating + +6.2 Cloning Repository +====================== + +To clone a remote or local repository use ‘C’, which is bound to the +command ‘magit-clone’. This command either act as a transient prefix +command, which binds several infix arguments and suffix commands, or it +can invoke ‘git clone’ directly, depending on whether a prefix argument +is used and on the value of ‘magit-clone-always-transient’. + + -- User Option: magit-clone-always-transient + This option controls whether the command ‘magit-clone’ always acts + as a transient prefix command, regardless of whether a prefix + argument is used or not. If ‘t’, then that command always acts as + a transient prefix. If ‘nil’, then a prefix argument has to be + used for it to act as a transient. + +‘C’ (‘magit-clone’) + This command either acts as a transient prefix command as described + above or does the same thing as ‘transient-clone-regular’ as + described below. + + If it acts as a transient prefix, then it binds the following + suffix commands and several infix arguments. + +‘C C’ (‘magit-clone-regular’) + This command creates a regular clone of an existing repository. + The repository and the target directory are read from the user. + +‘C s’ (‘magit-clone-shallow’) + This command creates a shallow clone of an existing repository. + The repository and the target directory are read from the user. By + default the depth of the cloned history is a single commit, but + with a prefix argument the depth is read from the user. + +‘C >’ (‘magit-clone-sparse’) + This command creates a clone of an existing repository and + initializes a sparse checkout, avoiding a checkout of the full + working tree. To add more directories, use the + ‘magit-sparse-checkout’ transient (see *note Sparse checkouts::). + +‘C b’ (‘magit-clone-bare’) + This command creates a bare clone of an existing repository. The + repository and the target directory are read from the user. + +‘C m’ (‘magit-clone-mirror’) + This command creates a mirror of an existing repository. The + repository and the target directory are read from the user. + + The following suffixes are disabled by default. See *note +(transient)Enabling and Disabling Suffixes:: for how to enable them. + +‘C d’ (‘magit-clone-shallow-since’) + This command creates a shallow clone of an existing repository. + Only commits that were committed after a date are cloned, which is + read from the user. The repository and the target directory are + also read from the user. + +‘C e’ (‘magit-clone-shallow-exclude’) + This command creates a shallow clone of an existing repository. + This reads a branch or tag from the user. Commits that are + reachable from that are not cloned. The repository and the target + directory are also read from the user. + + -- User Option: magit-clone-set-remote-head + This option controls whether cloning causes the reference + ‘refs/remotes//HEAD’ to be created in the clone. The + default is to delete the reference after running ‘git clone’, which + insists on creating it. This is because the reference has not been + found to be particularly useful as it is not automatically updated + when the ‘HEAD’ of the remote changes. Setting this option to ‘t’ + preserves Git’s default behavior of creating the reference. + + -- User Option: magit-clone-set-remote.pushDefault + This option controls whether the value of the Git variable + ‘remote.pushDefault’ is set after cloning. + + • If ‘t’, then it is always set without asking. + • If ‘ask’, then the users are asked every time they clone a + repository. + • If ‘nil’, then it is never set. + + -- User Option: magit-clone-default-directory + This option control the default directory name used when reading + the destination for a cloning operation. + + • If ‘nil’ (the default), then the value of ‘default-directory’ + is used. + • If a directory, then that is used. + • If a function, then that is called with the remote url as the + only argument and the returned value is used. + + -- User Option: magit-clone-name-alist + This option maps regular expressions, which match repository names, + to repository urls, making it possible for users to enter short + names instead of urls when cloning repositories. + + Each element has the form ‘(REGEXP HOSTNAME USER)’. When the user + enters a name when a cloning command asks for a name or url, then + that is looked up in this list. The first element whose REGEXP + matches is used. + + The format specified by option ‘magit-clone-url-format’ is used to + turn the name into an url, using HOSTNAME and the repository name. + If the provided name contains a slash, then that is used. + Otherwise if the name omits the owner of the repository, then the + default user specified in the matched entry is used. + + If USER contains a dot, then it is treated as a Git variable and + the value of that is used as the username. Otherwise it is used as + the username itself. + + -- User Option: magit-clone-url-format + The format specified by this option is used when turning repository + names into urls. ‘%h’ is the hostname and ‘%n’ is the repository + name, including the name of the owner. The value can be a string + (representing a single static format) or an alist with elements + ‘(HOSTNAME . FORMAT)’ mapping hostnames to formats. When an alist + is used, the ‘nil’ key represents the default format. + + Example of a single format string: + + (setq magit-clone-url-format + "git@%h:%n.git") + + Example of by-hostname format strings: + + (setq magit-clone-url-format + '(("git.example.com" . "git@%h:~%n") + (nil . "git@%h:%n.git"))) + + +File: magit.info, Node: Staging and Unstaging, Next: Applying, Prev: Cloning Repository, Up: Manipulating + +6.3 Staging and Unstaging +========================= + +Like Git, Magit can of course stage and unstage complete files. Unlike +Git, it also allows users to gracefully un-/stage individual hunks and +even just part of a hunk. To stage individual hunks and parts of hunks +using Git directly, one has to use the very modal and rather clumsy +interface of a ‘git add --interactive’ session. + + With Magit, on the other hand, one can un-/stage individual hunks by +just moving point into the respective section inside a diff displayed in +the status buffer or a separate diff buffer and typing ‘s’ or ‘u’. To +operate on just parts of a hunk, mark the changes that should be +un-/staged using the region and then press the same key that would be +used to un-/stage. To stage multiple files or hunks at once use a +region that starts inside the heading of such a section and ends inside +the heading of a sibling section of the same type. + + Besides staging and unstaging, Magit also provides several other +"apply variants" that can also operate on a file, multiple files at +once, a hunk, multiple hunks at once, and on parts of a hunk. These +apply variants are described in the next section. + + You can also use Ediff to stage and unstage. See *note Ediffing::. + +‘s’ (‘magit-stage’) + Add the change at point to the staging area. + + With a prefix argument and an untracked file (or files) at point, + stage the file but not its content. This makes it possible to + stage only a subset of the new file’s changes. + +‘S’ (‘magit-stage-modified’) + Stage all changes to files modified in the worktree. Stage all new + content of tracked files and remove tracked files that no longer + exist in the working tree from the index also. With a prefix + argument also stage previously untracked (but not ignored) files. + +‘u’ (‘magit-unstage’) + Remove the change at point from the staging area. + + Only staged changes can be unstaged. But by default this command + performs an action that is somewhat similar to unstaging, when it + is called on a committed change: it reverses the change in the + index but not in the working tree. + +‘U’ (‘magit-unstage-all’) + Remove all changes from the staging area. + + -- User Option: magit-unstage-committed + This option controls whether ‘magit-unstage’ "unstages" committed + changes by reversing them in the index but not the working tree. + The alternative is to raise an error. + +‘M-x magit-reverse-in-index’ + This command reverses the committed change at point in the index + but not the working tree. By default no key is bound directly to + this command, but it is indirectly called when ‘u’ + (‘magit-unstage’) is pressed on a committed change. + + This allows extracting a change from ‘HEAD’, while leaving it in + the working tree, so that it can later be committed using a + separate commit. A typical workflow would be: + + 1. Optionally make sure that there are no uncommitted changes. + 2. Visit the ‘HEAD’ commit and navigate to the change that should + not have been included in that commit. + 3. Type ‘u’ (‘magit-unstage’) to reverse it in the index. This + assumes that ‘magit-unstage-committed-changes’ is non-nil. + 4. Type ‘c e’ to extend ‘HEAD’ with the staged changes, including + those that were already staged before. + 5. Optionally stage the remaining changes using ‘s’ or ‘S’ and + then type ‘c c’ to create a new commit. + +‘M-x magit-reset-index’ + Reset the index to some commit. The commit is read from the user + and defaults to the commit at point. If there is no commit at + point, then it defaults to ‘HEAD’. + +* Menu: + +* Staging from File-Visiting Buffers:: + + +File: magit.info, Node: Staging from File-Visiting Buffers, Up: Staging and Unstaging + +6.3.1 Staging from File-Visiting Buffers +---------------------------------------- + +Fine-grained un-/staging has to be done from the status or a diff +buffer, but it’s also possible to un-/stage all changes made to the file +visited in the current buffer right from inside that buffer. + +‘M-x magit-stage-file’ + When invoked inside a file-visiting buffer, then stage all changes + to that file. In a Magit buffer, stage the file at point if any. + Otherwise prompt for a file to be staged. With a prefix argument + always prompt the user for a file, even in a file-visiting buffer + or when there is a file section at point. + +‘M-x magit-unstage-file’ + When invoked inside a file-visiting buffer, then unstage all + changes to that file. In a Magit buffer, unstage the file at point + if any. Otherwise prompt for a file to be unstaged. With a prefix + argument always prompt the user for a file, even in a file-visiting + buffer or when there is a file section at point. + + +File: magit.info, Node: Applying, Next: Committing, Prev: Staging and Unstaging, Up: Manipulating + +6.4 Applying +============ + +Magit provides several "apply variants": stage, unstage, discard, +reverse, and "regular apply". At least when operating on a hunk they +are all implemented using ‘git apply’, which is why they are called +"apply variants". + + • Stage. Apply a change from the working tree to the index. The + change also remains in the working tree. + + • Unstage. Remove a change from the index. The change remains in + the working tree. + + • Discard. On a staged change, remove it from the working tree and + the index. On an unstaged change, remove it from the working tree + only. + + • Reverse. Reverse a change in the working tree. Both committed and + staged changes can be reversed. Unstaged changes cannot be + reversed. Discard them instead. + + • Apply. Apply a change to the working tree. Both committed and + staged changes can be applied. Unstaged changes cannot be applied + - as they already have been applied. + + The previous section described the staging and unstaging commands. +What follows are the commands which implement the remaining apply +variants. + +‘a’ (‘magit-apply’) + Apply the change at point to the working tree. + + With a prefix argument fallback to a 3-way merge. Doing so causes + the change to be applied to the index as well. + +‘k’ (‘magit-discard’) + Remove the change at point from the working tree. + + On a hunk or file with unresolved conflicts prompt which side to + keep (while discarding the other). If point is within the text of + a side, then keep that side without prompting. + +‘v’ (‘magit-reverse’) + Reverse the change at point in the working tree. + + With a prefix argument fallback to a 3-way merge. Doing so causes + the change to be applied to the index as well. + + With a prefix argument all apply variants attempt a 3-way merge when +appropriate (i.e. when ‘git apply’ is used internally). + + +File: magit.info, Node: Committing, Next: Branching, Prev: Applying, Up: Manipulating + +6.5 Committing +============== + +When the user initiates a commit, Magit calls ‘git commit’ without any +arguments, so Git has to get it from the user. It creates the file +‘.git/COMMIT_EDITMSG’ and then opens that file in an editor. Magit +arranges for that editor to be the Emacsclient. Once the user finishes +the editing session, the Emacsclient exits and Git creates the commit +using the file’s content as message. + +* Menu: + +* Initiating a Commit:: +* Editing Commit Messages:: + + +File: magit.info, Node: Initiating a Commit, Next: Editing Commit Messages, Up: Committing + +6.5.1 Initiating a Commit +------------------------- + +Also see *note (gitman)git-commit::. + +‘c’ (‘magit-commit’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘c c’ (‘magit-commit-create’) + Create a new commit on ‘HEAD’. With a prefix argument amend to the + commit at ‘HEAD’ instead. + +‘c a’ (‘magit-commit-amend’) + Amend the last commit. + +‘c e’ (‘magit-commit-extend’) + Amend the last commit, without editing the message. With a prefix + argument keep the committer date, otherwise change it. The option + ‘magit-commit-extend-override-date’ can be used to inverse the + meaning of the prefix argument. + + Non-interactively respect the optional OVERRIDE-DATE argument and + ignore the option. + +‘c w’ (‘magit-commit-reword’) + Reword the last commit, ignoring staged changes. With a prefix + argument keep the committer date, otherwise change it. The option + ‘magit-commit-reword-override-date’ can be used to inverse the + meaning of the prefix argument. + + Non-interactively respect the optional OVERRIDE-DATE argument and + ignore the option. + +‘c f’ (‘magit-commit-fixup’) + Create a fixup commit. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + +‘c F’ (‘magit-commit-instant-fixup’) + Create a fixup commit and instantly rebase. + +‘c s’ (‘magit-commit-squash’) + Create a squash commit, without editing the squash message. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + +‘c S’ (‘magit-commit-instant-squash’) + Create a squash commit and instantly rebase. + +‘c A’ (‘magit-commit-augment’) + Create a squash commit, editing the squash message. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + + -- User Option: magit-commit-ask-to-stage + Whether to ask to stage all unstaged changes when committing and + nothing is staged. + + -- User Option: magit-commit-show-diff + Whether the relevant diff is automatically shown when committing. + + -- User Option: magit-commit-extend-override-date + Whether using ‘magit-commit-extend’ changes the committer date. + + -- User Option: magit-commit-reword-override-date + Whether using ‘magit-commit-reword’ changes the committer date. + + -- User Option: magit-commit-squash-confirm + Whether the commit targeted by squash and fixup has to be + confirmed. When non-nil then the commit at point (if any) is used + as default choice. Otherwise it has to be confirmed. This option + only affects ‘magit-commit-squash’ and ‘magit-commit-fixup’. The + "instant" variants always require confirmation because making an + error while using those is harder to recover from. + + -- User Option: magit-post-commit-hook + Hook run after creating a commit without the user editing a + message. + + This hook is run by ‘magit-refresh’ if ‘this-command’ is a member + of ‘magit-post-stage-hook-commands’. This only includes commands + named ‘magit-commit-*’ that do *not* require that the user edits + the commit message in a buffer. + + Also see ‘git-commit-post-finish-hook’. + + -- User Option: magit-commit-diff-inhibit-same-window + Whether to inhibit use of same window when showing diff while + committing. + + When writing a commit, then a diff of the changes to be committed + is automatically shown. The idea is that the diff is shown in a + different window of the same frame and for most users that just + works. In other words most users can completely ignore this option + because its value doesn’t make a difference for them. + + However for users who configured Emacs to never create a new window + even when the package explicitly tries to do so, then displaying + two new buffers necessarily means that the first is immediately + replaced by the second. In our case the message buffer is + immediately replaced by the diff buffer, which is of course highly + undesirable. + + A workaround is to suppress this user configuration in this + particular case. Users have to explicitly opt-in by toggling this + option. We cannot enable the workaround unconditionally because + that again causes issues for other users: if the frame is too tiny + or the relevant settings too aggressive, then the diff buffer would + end up being displayed in a new frame. + + Also see . + + +File: magit.info, Node: Editing Commit Messages, Prev: Initiating a Commit, Up: Committing + +6.5.2 Editing Commit Messages +----------------------------- + +After initiating a commit as described in the previous section, two new +buffers appear. One shows the changes that are about to be committed, +while the other is used to write the message. + + Commit messages are edited in an edit session - in the background +‘git’ is waiting for the editor, in our case ‘emacsclient’, to save the +commit message in a file (in most cases ‘.git/COMMIT_EDITMSG’) and then +return. If the editor returns with a non-zero exit status then ‘git’ +does not create the commit. So the most important commands are those +for finishing and aborting the commit. + +‘C-c C-c’ (‘with-editor-finish’) + Finish the current editing session by returning with exit code 0. + Git then creates the commit using the message it finds in the file. + +‘C-c C-k’ (‘with-editor-cancel’) + Cancel the current editing session by returning with exit code 1. + Git then cancels the commit, but leaves the file untouched. + + In addition to being used by ‘git commit’, messages may also be +stored in a ring that persists until Emacs is closed. By default the +message is stored at the beginning and the end of an edit session +(regardless of whether the session is finished successfully or was +canceled). It is sometimes useful to bring back messages from that +ring. + +‘C-c M-s’ (‘git-commit-save-message’) + Save the current buffer content to the commit message ring. + +‘M-p’ (‘git-commit-prev-message’) + Cycle backward through the commit message ring, after saving the + current message to the ring. With a numeric prefix ARG, go back + ARG comments. + +‘M-n’ (‘git-commit-next-message’) + Cycle forward through the commit message ring, after saving the + current message to the ring. With a numeric prefix ARG, go back + ARG comments. + + By default the diff for the changes that are about to be committed +are automatically shown when invoking the commit. To prevent that, +remove ‘magit-commit-diff’ from ‘server-switch-hook’. + + When amending to an existing commit it may be useful to show either +the changes that are about to be added to that commit or to show those +changes alongside those that have already been committed. + +‘C-c C-d’ (‘magit-diff-while-committing’) + While committing, show the changes that are about to be committed. + While amending, invoking the command again toggles between showing + just the new changes or all the changes that will be committed. + +* Menu: + +* Using the Revision Stack:: +* Commit Pseudo Headers:: +* Commit Mode and Hooks:: +* Commit Message Conventions:: + + +File: magit.info, Node: Using the Revision Stack, Next: Commit Pseudo Headers, Up: Editing Commit Messages + +Using the Revision Stack +........................ + +‘C-c C-w’ (‘magit-pop-revision-stack’) + This command inserts a representation of a revision into the + current buffer. It can be used inside buffers used to write commit + messages but also in other buffers such as buffers used to edit + emails or ChangeLog files. + + By default this command pops the revision which was last added to + the ‘magit-revision-stack’ and inserts it into the current buffer + according to ‘magit-pop-revision-stack-format’. Revisions can be + put on the stack using ‘magit-copy-section-value’ and + ‘magit-copy-buffer-revision’. + + If the stack is empty or with a prefix argument it instead reads a + revision in the minibuffer. By using the minibuffer history this + allows selecting an item which was popped earlier or to insert an + arbitrary reference or revision without first pushing it onto the + stack. + + When reading the revision from the minibuffer, then it might not be + possible to guess the correct repository. When this command is + called inside a repository (e.g. while composing a commit + message), then that repository is used. Otherwise (e.g. while + composing an email) then the repository recorded for the top + element of the stack is used (even though we insert another + revision). If not called inside a repository and with an empty + stack, or with two prefix arguments, then read the repository in + the minibuffer too. + + -- User Option: magit-pop-revision-stack-format + This option controls how the command ‘magit-pop-revision-stack’ + inserts a revision into the current buffer. + + The entries on the stack have the format ‘(HASH TOPLEVEL)’ and this + option has the format ‘(POINT-FORMAT EOB-FORMAT INDEX-REGEXP)’, all + of which may be nil or a string (though either one of EOB-FORMAT or + POINT-FORMAT should be a string, and if INDEX-REGEXP is non-nil, + then the two formats should be too). + + First INDEX-REGEXP is used to find the previously inserted entry, + by searching backward from point. The first submatch must match + the index number. That number is incremented by one, and becomes + the index number of the entry to be inserted. If you don’t want to + number the inserted revisions, then use nil for INDEX-REGEXP. + + If INDEX-REGEXP is non-nil then both POINT-FORMAT and EOB-FORMAT + should contain \"%N\", which is replaced with the number that was + determined in the previous step. + + Both formats, if non-nil and after removing %N, are then expanded + using ‘git show --format=FORMAT ...’ inside TOPLEVEL. + + The expansion of POINT-FORMAT is inserted at point, and the + expansion of EOB-FORMAT is inserted at the end of the buffer (if + the buffer ends with a comment, then it is inserted right before + that). + + +File: magit.info, Node: Commit Pseudo Headers, Next: Commit Mode and Hooks, Prev: Using the Revision Stack, Up: Editing Commit Messages + +Commit Pseudo Headers +..................... + +Some projects use pseudo headers in commit messages. Magit colorizes +such headers and provides some commands to insert such headers. + + -- User Option: git-commit-known-pseudo-headers + A list of Git pseudo headers to be highlighted. + +‘C-c C-i’ (‘git-commit-insert-pseudo-header’) + Insert a commit message pseudo header. + +‘C-c C-a’ (‘git-commit-ack’) + Insert a header acknowledging that you have looked at the commit. + +‘C-c C-r’ (‘git-commit-review’) + Insert a header acknowledging that you have reviewed the commit. + +‘C-c C-s’ (‘git-commit-signoff’) + Insert a header to sign off the commit. + +‘C-c C-t’ (‘git-commit-test’) + Insert a header acknowledging that you have tested the commit. + +‘C-c C-o’ (‘git-commit-cc’) + Insert a header mentioning someone who might be interested. + +‘C-c C-p’ (‘git-commit-reported’) + Insert a header mentioning the person who reported the issue being + fixed by the commit. + +‘C-c M-i’ (‘git-commit-suggested’) + Insert a header mentioning the person who suggested the change. + + +File: magit.info, Node: Commit Mode and Hooks, Next: Commit Message Conventions, Prev: Commit Pseudo Headers, Up: Editing Commit Messages + +Commit Mode and Hooks +..................... + +‘git-commit-mode’ is a minor mode that is only used to establish certain +key bindings. This makes it possible to use an arbitrary major mode in +buffers used to edit commit messages. It is even possible to use +different major modes in different repositories, which is useful when +different projects impose different commit message conventions. + + -- User Option: git-commit-major-mode + The value of this option is the major mode used to edit Git commit + messages. + + Because ‘git-commit-mode’ is a minor mode, we don’t use its mode hook +to setup the buffer, except for the key bindings. All other setup +happens in the function ‘git-commit-setup’, which among other things +runs the hook ‘git-commit-setup-hook’. + + -- User Option: git-commit-setup-hook + Hook run at the end of ‘git-commit-setup’. + +The following functions are suitable for this hook: + + -- Function: git-commit-save-message + Save the current buffer content to the commit message ring. + + -- Function: git-commit-setup-changelog-support + After this function is called, ChangeLog entries are treated as + paragraphs. + + -- Function: git-commit-turn-on-auto-fill + Turn on ‘auto-fill-mode’ and set ‘fill-column’ to the value of + ‘git-commit-fill-column’. + + -- Function: git-commit-turn-on-flyspell + Turn on Flyspell mode. Also prevent comments from being checked + and finally check current non-comment text. + + -- Function: git-commit-propertize-diff + Propertize the diff shown inside the commit message buffer. Git + inserts such diffs into the commit message template when the + ‘--verbose’ argument is used. ‘magit-commit’ by default does not + offer that argument because the diff that is shown in a separate + buffer is more useful. But some users disagree, which is why this + function exists. + + -- Function: bug-reference-mode + Hyperlink bug references in the buffer. + + -- Function: with-editor-usage-message + Show usage information in the echo area. + + -- User Option: git-commit-post-finish-hook + Hook run after the user finished writing a commit message. + + This hook is only run after pressing ‘C-c C-c’ in a buffer used to + edit a commit message. If a commit is created without the user + typing a message into a buffer, then this hook is not run. + + This hook is not run until the new commit has been created. If + doing so takes Git longer than one second, then this hook isn’t run + at all. For certain commands such as ‘magit-rebase-continue’ this + hook is never run because doing so would lead to a race condition. + + This hook is only run if ‘magit’ is available. + + Also see ‘magit-post-commit-hook’. + + +File: magit.info, Node: Commit Message Conventions, Prev: Commit Mode and Hooks, Up: Editing Commit Messages + +Commit Message Conventions +.......................... + +Git-Commit highlights certain violations of commonly accepted commit +message conventions. Certain violations even cause Git-Commit to ask +you to confirm that you really want to do that. This nagging can of +course be turned off, but the result of doing that usually is that +instead of some code it’s now the human who is reviewing your commits +who has to waste some time telling you to fix your commits. + + -- User Option: git-commit-summary-max-length + The intended maximal length of the summary line of commit messages. + Characters beyond this column are colorized to indicate that this + preference has been violated. + + -- User Option: git-commit-fill-column + Column beyond which automatic line-wrapping should happen in commit + message buffers. + + -- User Option: git-commit-finish-query-functions + List of functions called to query before performing commit. + + The commit message buffer is current while the functions are + called. If any of them returns nil, then the commit is not + performed and the buffer is not killed. The user should then fix + the issue and try again. + + The functions are called with one argument. If it is non-nil then + that indicates that the user used a prefix argument to force + finishing the session despite issues. Functions should usually + honor this wish and return non-nil. + + By default the only member is ‘git-commit-check-style-conventions’. + + -- Function: git-commit-check-style-conventions + This function checks for violations of certain basic style + conventions. For each violation it asks users if they want to + proceed anyway. + + -- User Option: git-commit-style-convention-checks + This option controls what conventions the function by the same name + tries to enforce. The value is a list of self-explanatory symbols + identifying certain conventions; ‘non-empty-second-line’ and + ‘overlong-summary-line’. + + +File: magit.info, Node: Branching, Next: Merging, Prev: Committing, Up: Manipulating + +6.6 Branching +============= + +* Menu: + +* The Two Remotes:: +* Branch Commands:: +* Branch Git Variables:: +* Auxiliary Branch Commands:: + + +File: magit.info, Node: The Two Remotes, Next: Branch Commands, Up: Branching + +6.6.1 The Two Remotes +--------------------- + +The upstream branch of some local branch is the branch into which the +commits on that local branch should eventually be merged, usually +something like ‘origin/master’. For the ‘master’ branch itself the +upstream branch and the branch it is being pushed to, are usually the +same remote branch. But for a feature branch the upstream branch and +the branch it is being pushed to should differ. + + The commits on feature branches too should _eventually_ end up in a +remote branch such as ‘origin/master’ or ‘origin/maint’. Such a branch +should therefore be used as the upstream. But feature branches +shouldn’t be pushed directly to such branches. Instead a feature branch +‘my-feature’ is usually pushed to ‘my-fork/my-feature’ or if you are a +contributor ‘origin/my-feature’. After the new feature has been +reviewed, the maintainer merges the feature into ‘master’. And finally +‘master’ (not ‘my-feature’ itself) is pushed to ‘origin/master’. + + But new features seldom are perfect on the first try, and so feature +branches usually have to be reviewed, improved, and re-pushed several +times. Pushing should therefore be easy to do, and for that reason many +Git users have concluded that it is best to use the remote branch to +which the local feature branch is being pushed as its upstream. + + But luckily Git has long ago gained support for a push-remote which +can be configured separately from the upstream branch, using the +variables ‘branch..pushRemote’ and ‘remote.pushDefault’. So we no +longer have to choose which of the two remotes should be used as "the +remote". + + Each of the fetching, pulling, and pushing transient commands +features three suffix commands that act on the current branch and some +other branch. Of these, ‘p’ is bound to a command which acts on the +push-remote, ‘u’ is bound to a command which acts on the upstream, and +‘e’ is bound to a command which acts on any other branch. The status +buffer shows unpushed and unpulled commits for both the push-remote and +the upstream. + + It’s fairly simple to configure these two remotes. The values of all +the variables that are related to fetching, pulling, and pushing (as +well as some other branch-related variables) can be inspected and +changed using the command ‘magit-branch-configure’, which is available +from many transient prefix commands that deal with branches. It is also +possible to set the push-remote or upstream while pushing (see *note +Pushing::). + + +File: magit.info, Node: Branch Commands, Next: Branch Git Variables, Prev: The Two Remotes, Up: Branching + +6.6.2 Branch Commands +--------------------- + +The transient prefix command ‘magit-branch’ is used to create and +checkout branches, and to make changes to existing branches. It is not +used to fetch, pull, merge, rebase, or push branches, i.e. this command +deals with branches themselves, not with the commits reachable from +them. Those features are available from separate transient command. + +‘b’ (‘magit-branch’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + + By default it also binds and displays the values of some + branch-related Git variables and allows changing their values. + + -- User Option: magit-branch-direct-configure + This option controls whether the transient command ‘magit-branch’ + can be used to directly change the values of Git variables. This + defaults to ‘t’ (to avoid changing key bindings). When set to + ‘nil’, then no variables are displayed by that transient command, + and its suffix command ‘magit-branch-configure’ has to be used + instead to view and change branch related variables. + +‘b C’ (‘magit-branch-configure’) +‘f C’ +‘F C’ +‘P C’ + This transient prefix command binds commands that set the value of + branch-related variables and displays them in a temporary buffer + until the transient is exited. + + With a prefix argument, this command always prompts for a branch. + + Without a prefix argument this depends on whether it was invoked as + a suffix of ‘magit-branch’ and on the + ‘magit-branch-direct-configure’ option. If ‘magit-branch’ already + displays the variables for the current branch, then it isn’t useful + to invoke another transient that displays them for the same branch. + In that case this command prompts for a branch. + + The variables are described in *note Branch Git Variables::. + +‘b b’ (‘magit-checkout’) + Checkout a revision read in the minibuffer and defaulting to the + branch or arbitrary revision at point. If the revision is a local + branch then that becomes the current branch. If it is something + else then ‘HEAD’ becomes detached. Checkout fails if the working + tree or the staging area contain changes. + +‘b n’ (‘magit-branch-create’) + Create a new branch. The user is asked for a branch or arbitrary + revision to use as the starting point of the new branch. When a + branch name is provided, then that becomes the upstream branch of + the new branch. The name of the new branch is also read in the + minibuffer. + + Also see option ‘magit-branch-prefer-remote-upstream’. + +‘b c’ (‘magit-branch-and-checkout’) + This command creates a new branch like ‘magit-branch-create’, but + then also checks it out. + + Also see option ‘magit-branch-prefer-remote-upstream’. + +‘b l’ (‘magit-branch-checkout’) + This command checks out an existing or new local branch. It reads + a branch name from the user offering all local branches and a + subset of remote branches as candidates. Remote branches for which + a local branch by the same name exists are omitted from the list of + candidates. The user can also enter a completely new branch name. + + • If the user selects an existing local branch, then that is + checked out. + + • If the user selects a remote branch, then it creates and + checks out a new local branch with the same name, and + configures the selected remote branch as the push target. + + • If the user enters a new branch name, then it creates and + checks that out, after also reading the starting-point from + the user. + + In the latter two cases the upstream is also set. Whether it is + set to the chosen starting point or something else depends on the + value of ‘magit-branch-adjust-remote-upstream-alist’. + +‘b s’ (‘magit-branch-spinoff’) + This command creates and checks out a new branch starting at and + tracking the current branch. That branch in turn is reset to the + last commit it shares with its upstream. If the current branch has + no upstream or no unpushed commits, then the new branch is created + anyway and the previously current branch is not touched. + + This is useful to create a feature branch after work has already + began on the old branch (likely but not necessarily "master"). + + If the current branch is a member of the value of option + ‘magit-branch-prefer-remote-upstream’ (which see), then the current + branch will be used as the starting point as usual, but the + upstream of the starting-point may be used as the upstream of the + new branch, instead of the starting-point itself. + + If optional FROM is non-nil, then the source branch is reset to + ‘FROM~’, instead of to the last commit it shares with its upstream. + Interactively, FROM is only ever non-nil, if the region selects + some commits, and among those commits, FROM is the commit that is + the fewest commits ahead of the source branch. + + The commit at the other end of the selection actually does not + matter, all commits between FROM and ‘HEAD’ are moved to the new + branch. If FROM is not reachable from ‘HEAD’ or is reachable from + the source branch’s upstream, then an error is raised. + +‘b S’ (‘magit-branch-spinout’) + This command behaves like ‘magit-branch-spinoff’, except that it + does not change the current branch. If there are any uncommitted + changes, then it behaves exactly like ‘magit-branch-spinoff’. + +‘b x’ (‘magit-branch-reset’) + This command resets a branch, defaulting to the branch at point, to + the tip of another branch or any other commit. + + When the branch being reset is the current branch, then a hard + reset is performed. If there are any uncommitted changes, then the + user has to confirm the reset because those changes would be lost. + + This is useful when you have started work on a feature branch but + realize it’s all crap and want to start over. + + When resetting to another branch and a prefix argument is used, + then the target branch is set as the upstream of the branch that is + being reset. + +‘b k’ (‘magit-branch-delete’) + Delete one or multiple branches. If the region marks multiple + branches, then offer to delete those. Otherwise, prompt for a + single branch to be deleted, defaulting to the branch at point. + +‘b m’ (‘magit-branch-rename’) + Rename a branch. The branch and the new name are read in the + minibuffer. With prefix argument the branch is renamed even if + that name conflicts with an existing branch. + + -- User Option: magit-branch-read-upstream-first + When creating a branch, whether to read the upstream branch before + the name of the branch that is to be created. The default is ‘t’, + and I recommend you leave it at that. + + -- User Option: magit-branch-prefer-remote-upstream + This option specifies whether remote upstreams are favored over + local upstreams when creating new branches. + + When a new branch is created, then the branch, commit, or stash at + point is suggested as the starting point of the new branch, or if + there is no such revision at point the current branch. In either + case the user may choose another starting point. + + If the chosen starting point is a branch, then it may also be set + as the upstream of the new branch, depending on the value of the + Git variable ‘branch.autoSetupMerge’. By default this is done for + remote branches, but not for local branches. + + You might prefer to always use some remote branch as upstream. If + the chosen starting point is (1) a local branch, (2) whose name + matches a member of the value of this option, (3) the upstream of + that local branch is a remote branch with the same name, and (4) + that remote branch can be fast-forwarded to the local branch, then + the chosen branch is used as starting point, but its own upstream + is used as the upstream of the new branch. + + Members of this option’s value are treated as branch names that + have to match exactly unless they contain a character that makes + them invalid as a branch name. Recommended characters to use to + trigger interpretation as a regexp are "*" and "^". Some other + characters which you might expect to be invalid, actually are not, + e.g. ".+$" are all perfectly valid. More precisely, if ‘git + check-ref-format --branch STRING’ exits with a non-zero status, + then treat STRING as a regexp. + + Assuming the chosen branch matches these conditions you would end + up with with e.g.: + + feature --upstream--> origin/master + + instead of + + feature --upstream--> master --upstream--> origin/master + + Which you prefer is a matter of personal preference. If you do + prefer the former, then you should add branches such as ‘master’, + ‘next’, and ‘maint’ to the value of this options. + + -- User Option: magit-branch-adjust-remote-upstream-alist + The value of this option is an alist of branches to be used as the + upstream when branching a remote branch. + + When creating a local branch from an ephemeral branch located on a + remote, e.g. a feature or hotfix branch, then that remote branch + should usually not be used as the upstream branch, since the + push-remote already allows accessing it and having both the + upstream and the push-remote reference the same related branch + would be wasteful. Instead a branch like "maint" or "master" + should be used as the upstream. + + This option allows specifying the branch that should be used as the + upstream when branching certain remote branches. The value is an + alist of the form ‘((UPSTREAM . RULE)...)’. The first matching + element is used, the following elements are ignored. + + UPSTREAM is the branch to be used as the upstream for branches + specified by RULE. It can be a local or a remote branch. + + RULE can either be a regular expression, matching branches whose + upstream should be the one specified by UPSTREAM. Or it can be a + list of the only branches that should *not* use UPSTREAM; all other + branches will. Matching is done after stripping the remote part of + the name of the branch that is being branched from. + + If you use a finite set of non-ephemeral branches across all your + repositories, then you might use something like: + + (("origin/master" . ("master" "next" "maint"))) + + Or if the names of all your ephemeral branches contain a slash, at + least in some repositories, then a good value could be: + + (("origin/master" . "/")) + + Of course you can also fine-tune: + + (("origin/maint" . "\\`hotfix/") + ("origin/master" . "\\`feature/")) + + UPSTREAM can be a local branch: + + (("master" . ("master" "next" "maint"))) + + Because the main branch is no longer almost always named "master" you +should also account for other common names: + + (("main" . ("main" "master" "next" "maint")) + ("master" . ("main" "master" "next" "maint"))) + + -- Command: magit-branch-orphan + This command creates and checks out a new orphan branch with + contents from a given revision. + + -- Command: magit-branch-or-checkout + This command is a hybrid between ‘magit-checkout’ and + ‘magit-branch-and-checkout’ and is intended as a replacement for + the former in ‘magit-branch’. + + It first asks the user for an existing branch or revision. If the + user input actually can be resolved as a branch or revision, then + it checks that out, just like ‘magit-checkout’ would. + + Otherwise it creates and checks out a new branch using the input as + its name. Before doing so it reads the starting-point for the new + branch. This is similar to what ‘magit-branch-and-checkout’ does. + + To use this command instead of ‘magit-checkout’ add this to your + init file: + + (transient-replace-suffix 'magit-branch 'magit-checkout + '("b" "dwim" magit-branch-or-checkout)) + + +File: magit.info, Node: Branch Git Variables, Next: Auxiliary Branch Commands, Prev: Branch Commands, Up: Branching + +6.6.3 Branch Git Variables +-------------------------- + +These variables can be set from the transient prefix command +‘magit-branch-configure’. By default they can also be set from +‘magit-branch’. See *note Branch Commands::. + + -- Variable: branch.NAME.merge + Together with ‘branch.NAME.remote’ this variable defines the + upstream branch of the local branch named NAME. The value of this + variable is the full reference of the upstream _branch_. + + -- Variable: branch.NAME.remote + Together with ‘branch.NAME.merge’ this variable defines the + upstream branch of the local branch named NAME. The value of this + variable is the name of the upstream _remote_. + + -- Variable: branch.NAME.rebase + This variable controls whether pulling into the branch named NAME + is done by rebasing or by merging the fetched branch. + + • When ‘true’ then pulling is done by rebasing. + • When ‘false’ then pulling is done by merging. + • When undefined then the value of ‘pull.rebase’ is used. The + default of that variable is ‘false’. + + -- Variable: branch.NAME.pushRemote + This variable specifies the remote that the branch named NAME is + usually pushed to. The value has to be the name of an existing + remote. + + It is not possible to specify the name of _branch_ to push the + local branch to. The name of the remote branch is always the same + as the name of the local branch. + + If this variable is undefined but ‘remote.pushDefault’ is defined, + then the value of the latter is used. By default + ‘remote.pushDefault’ is undefined. + + -- Variable: branch.NAME.description + This variable can be used to describe the branch named NAME. That + description is used e.g. when turning the branch into a series of + patches. + + The following variables specify defaults which are used if the above +branch-specific variables are not set. + + -- Variable: pull.rebase + This variable specifies whether pulling is done by rebasing or by + merging. It can be overwritten using ‘branch.NAME.rebase’. + + • When ‘true’ then pulling is done by rebasing. + • When ‘false’ (the default) then pulling is done by merging. + + Since it is never a good idea to merge the upstream branch into a + feature or hotfix branch and most branches are such branches, you + should consider setting this to ‘true’, and ‘branch.master.rebase’ + to ‘false’. + + -- Variable: remote.pushDefault + This variable specifies what remote the local branches are usually + pushed to. This can be overwritten per branch using + ‘branch.NAME.pushRemote’. + + The following variables are used during the creation of a branch and +control whether the various branch-specific variables are automatically +set at this time. + + -- Variable: branch.autoSetupMerge + This variable specifies under what circumstances creating a branch + NAME should result in the variables ‘branch.NAME.merge’ and + ‘branch.NAME.remote’ being set according to the starting point used + to create the branch. If the starting point isn’t a branch, then + these variables are never set. + + • When ‘always’ then the variables are set regardless of whether + the starting point is a local or a remote branch. + • When ‘true’ (the default) then the variables are set when the + starting point is a remote branch, but not when it is a local + branch. + • When ‘false’ then the variables are never set. + + -- Variable: branch.autoSetupRebase + This variable specifies whether creating a branch NAME should + result in the variable ‘branch.NAME.rebase’ being set to ‘true’. + + • When ‘always’ then the variable is set regardless of whether + the starting point is a local or a remote branch. + • When ‘local’ then the variable are set when the starting point + is a local branch, but not when it is a remote branch. + • When ‘remote’ then the variable are set when the starting + point is a remote branch, but not when it is a local branch. + • When ‘never’ (the default) then the variable is never set. + + Note that the respective commands always change the repository-local +values. If you want to change the global value, which is used when the +local value is undefined, then you have to do so on the command line, +e.g.: + + git config --global remote.autoSetupMerge always + + For more information about these variables you should also see + + *note (gitman)git-config::. Also see *note (gitman)git-branch::. , +*note (gitman)git-checkout::. and *note Pushing::. + + -- User Option: magit-prefer-remote-upstream + This option controls whether commands that read a branch from the + user and then set it as the upstream branch, offer a local or a + remote branch as default completion candidate, when they have the + choice. + + This affects all commands that use ‘magit-read-upstream-branch’ or + ‘magit-read-starting-point’, which includes all commands that + change the upstream and many which create new branches. + + +File: magit.info, Node: Auxiliary Branch Commands, Prev: Branch Git Variables, Up: Branching + +6.6.4 Auxiliary Branch Commands +------------------------------- + +These commands are not available from the transient ‘magit-branch’ by +default. + + -- Command: magit-branch-shelve + This command shelves a branch. This is done by deleting the + branch, and creating a new reference "refs/shelved/BRANCH-NAME" + pointing at the same commit as the branch pointed at. If the + deleted branch had a reflog, then that is preserved as the reflog + of the new reference. + + This is useful if you want to move a branch out of sight, but are + not ready to completely discard it yet. + + -- Command: magit-branch-unshelve + This command unshelves a branch that was previously shelved using + ‘magit-branch-shelve’. This is done by deleting the reference + "refs/shelved/BRANCH-NAME" and creating a branch "BRANCH-NAME" + pointing at the same commit as the deleted reference pointed at. + If the deleted reference had a reflog, then that is restored as the + reflog of the branch. + + +File: magit.info, Node: Merging, Next: Resolving Conflicts, Prev: Branching, Up: Manipulating + +6.7 Merging +=========== + +Also see *note (gitman)git-merge::. For information on how to resolve +merge conflicts see the next section. + +‘m’ (‘magit-merge’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no merge is in progress, then the transient features the +following suffix commands. + +‘m m’ (‘magit-merge-plain’) + This command merges another branch or an arbitrary revision into + the current branch. The branch or revision to be merged is read in + the minibuffer and defaults to the branch at point. + + Unless there are conflicts or a prefix argument is used, then the + resulting merge commit uses a generic commit message, and the user + does not get a chance to inspect or change it before the commit is + created. With a prefix argument this does not actually create the + merge commit, which makes it possible to inspect how conflicts were + resolved and to adjust the commit message. + +‘m e’ (‘magit-merge-editmsg’) + This command merges another branch or an arbitrary revision into + the current branch and opens a commit message buffer, so that the + user can make adjustments. The commit is not actually created + until the user finishes with ‘C-c C-c’. + +‘m n’ (‘magit-merge-nocommit’) + This command merges another branch or an arbitrary revision into + the current branch, but does not actually create the merge commit. + The user can then further adjust the merge, even when automatic + conflict resolution succeeded and/or adjust the commit message. + +‘m a’ (‘magit-merge-absorb’) + This command merges another local branch into the current branch + and then removes the former. + + Before the source branch is merged, it is first force pushed to its + push-remote, provided the respective remote branch already exists. + This ensures that the respective pull-request (if any) won’t get + stuck on some obsolete version of the commits that are being + merged. Finally, if ‘magit-branch-pull-request’ was used to create + the merged branch, then the respective remote branch is also + removed. + +‘m i’ (‘magit-merge-into’) + This command merges the current branch into another local branch + and then removes the former. The latter becomes the new current + branch. + + Before the source branch is merged, it is first force pushed to its + push-remote, provided the respective remote branch already exists. + This ensures that the respective pull-request (if any) won’t get + stuck on some obsolete version of the commits that are being + merged. Finally, if ‘magit-branch-pull-request’ was used to create + the merged branch, then the respective remote branch is also + removed. + +‘m s’ (‘magit-merge-squash’) + This command squashes the changes introduced by another branch or + an arbitrary revision into the current branch. This only applies + the changes made by the squashed commits. No information is + preserved that would allow creating an actual merge commit. + Instead of this command you should probably use a command from the + apply transient. + +‘m p’ (‘magit-merge-preview’) + This command shows a preview of merging another branch or an + arbitrary revision into the current branch. + + When a merge is in progress, then the transient instead features the +following suffix commands. + +‘m m’ (‘magit-merge’) + After the user resolved conflicts, this command proceeds with the + merge. If some conflicts weren’t resolved, then this command + fails. + +‘m a’ (‘magit-merge-abort’) + This command aborts the current merge operation. + + +File: magit.info, Node: Resolving Conflicts, Next: Rebasing, Prev: Merging, Up: Manipulating + +6.8 Resolving Conflicts +======================= + +When merging branches (or otherwise combining or changing history) +conflicts can occur. If you edited two completely different parts of +the same file in two branches and then merge one of these branches into +the other, then Git can resolve that on its own, but if you edit the +same area of a file, then a human is required to decide how the two +versions, or "sides of the conflict", are to be combined into one. + + Here we can only provide a brief introduction to the subject and +point you toward some tools that can help. If you are new to this, then +please also consult Git’s own documentation as well as other resources. + + If a file has conflicts and Git cannot resolve them by itself, then +it puts both versions into the affected file along with special markers +whose purpose is to denote the boundaries of the unresolved part of the +file and between the different versions. These boundary lines begin +with the strings consisting of six times the same character, one of ‘<’, +‘|’, ‘=’ and ‘>’ and are followed by information about the source of the +respective versions, e.g.: + + <<<<<<< HEAD + Take the blue pill. + ======= + Take the red pill. + >>>>>>> feature + + In this case you have chosen to take the red pill on one branch and +on another you picked the blue pill. Now that you are merging these two +diverging branches, Git cannot possibly know which pill you want to +take. + + To resolve that conflict you have to create a version of the affected +area of the file by keeping only one of the sides, possibly by editing +it in order to bring in the changes from the other side, remove the +other versions as well as the markers, and then stage the result. A +possible resolution might be: + + Take both pills. + + Often it is useful to see not only the two sides of the conflict but +also the "original" version from before the same area of the file was +modified twice on different branches. Instruct Git to insert that +version as well by running this command once: + + git config --global merge.conflictStyle diff3 + + The above conflict might then have looked like this: + + <<<<<<< HEAD + Take the blue pill. + ||||||| merged common ancestors + Take either the blue or the red pill, but not both. + ======= + Take the red pill. + >>>>>>> feature + + If that were the case, then the above conflict resolution would not +have been correct, which demonstrates why seeing the original version +alongside the conflicting versions can be useful. + + You can perform the conflict resolution completely by hand, but Emacs +also provides some packages that help in the process: Smerge, Ediff +(*note (ediff)Top::), and Emerge (*note (emacs)Emerge::). Magit does +not provide its own tools for conflict resolution, but it does make +using Smerge and Ediff more convenient. (Ediff supersedes Emerge, so +you probably don’t want to use the latter anyway.) + + In the Magit status buffer, files with unresolved conflicts are +listed in the "Unstaged changes" and/or "Staged changes" sections. They +are prefixed with the word "unmerged", which in this context essentially +is a synonym for "unresolved". + + Pressing ‘RET’ while point is on such a file section shows a buffer +visiting that file, turns on ‘smerge-mode’ in that buffer, and places +point inside the first area with conflicts. You should then resolve +that conflict using regular edit commands and/or Smerge commands. + + Unfortunately Smerge does not have a manual, but you can get a list +of commands and binding ‘C-c ^ C-h’ and press ‘RET’ while point is on a +command name to read its documentation. + + Normally you would edit one version and then tell Smerge to keep only +that version. Use ‘C-c ^ m’ (‘smerge-keep-mine’) to keep the ‘HEAD’ +version or ‘C-c ^ o’ (‘smerge-keep-other’) to keep the version that +follows "|||||||". Then use ‘C-c ^ n’ to move to the next conflicting +area in the same file. Once you are done resolving conflicts, return to +the Magit status buffer. The file should now be shown as "modified", no +longer as "unmerged", because Smerge automatically stages the file when +you save the buffer after resolving the last conflict. + + Magit now wraps the mentioned Smerge commands, allowing you to use +these key bindings without having to go to the file-visiting buffer. +Additionally ‘k’ (‘magit-discard’) on a hunk with unresolved conflicts +asks which side to keep or, if point is on a side, then it keeps it +without prompting. Similarly ‘k’ on a unresolved file ask which side to +keep. + + Alternatively you could use Ediff, which uses separate buffers for +the different versions of the file. To resolve conflicts in a file +using Ediff press ‘e’ while point is on such a file in the status +buffer. + + Ediff can be used for other purposes as well. For more information +on how to enter Ediff from Magit, see *note Ediffing::. Explaining how +to use Ediff is beyond the scope of this manual, instead see *note +(ediff)Top::. + + If you are unsure whether you should Smerge or Ediff, then use the +former. It is much easier to understand and use, and except for truly +complex conflicts, the latter is usually overkill. + + +File: magit.info, Node: Rebasing, Next: Cherry Picking, Prev: Resolving Conflicts, Up: Manipulating + +6.9 Rebasing +============ + +Also see *note (gitman)git-rebase::. For information on how to resolve +conflicts that occur during rebases see the preceding section. + +‘r’ (‘magit-rebase’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no rebase is in progress, then the transient features the +following suffix commands. + + Using one of these commands _starts_ a rebase sequence. Git might +then stop somewhere along the way, either because you told it to do so, +or because applying a commit failed due to a conflict. When that +happens, then the status buffer shows information about the rebase +sequence which is in progress in a section similar to a log section. +See *note Information About In-Progress Rebase::. + + For information about the upstream and the push-remote, see *note The +Two Remotes::. + +‘r p’ (‘magit-rebase-onto-pushremote’) + This command rebases the current branch onto its push-remote. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘r u’ (‘magit-rebase-onto-upstream’) + This command rebases the current branch onto its upstream branch. + + With a prefix argument or when the upstream is either not + configured or unusable, then let the user first configure the + upstream. + +‘r e’ (‘magit-rebase-branch’) + This command rebases the current branch onto a branch read in the + minibuffer. All commits that are reachable from head but not from + the selected branch TARGET are being rebased. + +‘r s’ (‘magit-rebase-subset’) + This command starts a non-interactive rebase sequence to transfer + commits from START to ‘HEAD’ onto NEWBASE. START has to be + selected from a list of recent commits. + + By default Magit uses the ‘--autostash’ argument, which causes +uncommitted changes to be stored in a stash before the rebase begins. +These changes are restored after the rebase completes and if possible +the stash is removed. If the stash does not apply cleanly, then the +stash is not removed. In case something goes wrong when resolving the +conflicts, this allows you to start over. + + Even though one of the actions is dedicated to interactive rebases, +the transient also features the infix argument ‘--interactive’. This +can be used to turn one of the other, non-interactive rebase variants +into an interactive rebase. + + For example if you want to clean up a feature branch and at the same +time rebase it onto ‘master’, then you could use ‘r-iu’. But we +recommend that you instead do that in two steps. First use ‘ri’ to +cleanup the feature branch, and then in a second step ‘ru’ to rebase it +onto ‘master’. That way if things turn out to be more complicated than +you thought and/or you make a mistake and have to start over, then you +only have to redo half the work. + + Explicitly enabling ‘--interactive’ won’t have an effect on the +following commands as they always use that argument anyway, even if it +is not enabled in the transient. + +‘r i’ (‘magit-rebase-interactive’) + This command starts an interactive rebase sequence. + +‘r f’ (‘magit-rebase-autosquash’) + This command combines squash and fixup commits with their intended + targets. + +‘r m’ (‘magit-rebase-edit-commit’) + This command starts an interactive rebase sequence that lets the + user edit a single older commit. + +‘r w’ (‘magit-rebase-reword-commit’) + This command starts an interactive rebase sequence that lets the + user reword a single older commit. + +‘r k’ (‘magit-rebase-remove-commit’) + This command removes a single older commit using rebase. + + When a rebase is in progress, then the transient instead features the +following suffix commands. + +‘r r’ (‘magit-rebase-continue’) + This command restart the current rebasing operation. + + In some cases this pops up a commit message buffer for you do edit. + With a prefix argument the old message is reused as-is. + +‘r s’ (‘magit-rebase-skip’) + This command skips the current commit and restarts the current + rebase operation. + +‘r e’ (‘magit-rebase-edit’) + This command lets the user edit the todo list of the current rebase + operation. + +‘r a’ (‘magit-rebase-abort’) + This command aborts the current rebase operation, restoring the + original branch. + +* Menu: + +* Editing Rebase Sequences:: +* Information About In-Progress Rebase:: + + +File: magit.info, Node: Editing Rebase Sequences, Next: Information About In-Progress Rebase, Up: Rebasing + +6.9.1 Editing Rebase Sequences +------------------------------ + +‘C-c C-c’ (‘with-editor-finish’) + Finish the current editing session by returning with exit code 0. + Git then uses the rebase instructions it finds in the file. + +‘C-c C-k’ (‘with-editor-cancel’) + Cancel the current editing session by returning with exit code 1. + Git then forgoes starting the rebase sequence. + +‘’ (‘git-rebase-show-commit’) + Show the commit on the current line in another buffer and select + that buffer. + +‘’ (‘git-rebase-show-or-scroll-up’) + Show the commit on the current line in another buffer without + selecting that buffer. If the revision buffer is already visible + in another window of the current frame, then instead scroll that + window up. + +‘’ (‘git-rebase-show-or-scroll-down’) + Show the commit on the current line in another buffer without + selecting that buffer. If the revision buffer is already visible + in another window of the current frame, then instead scroll that + window down. + +‘p’ (‘git-rebase-backward-line’) + Move to previous line. + +‘n’ (‘forward-line’) + Move to next line. + +‘M-p’ (‘git-rebase-move-line-up’) + Move the current commit (or command) up. + +‘M-n’ (‘git-rebase-move-line-down’) + Move the current commit (or command) down. + +‘r’ (‘git-rebase-reword’) + Edit message of commit on current line. + +‘e’ (‘git-rebase-edit’) + Stop at the commit on the current line. + +‘s’ (‘git-rebase-squash’) + Meld commit on current line into previous commit, and edit message. + +‘f’ (‘git-rebase-fixup’) + Meld commit on current line into previous commit, discarding the + current commit’s message. + +‘k’ (‘git-rebase-kill-line’) + Kill the current action line. + +‘c’ (‘git-rebase-pick’) + Use commit on current line. + +‘x’ (‘git-rebase-exec’) + Insert a shell command to be run after the proceeding commit. + + If there already is such a command on the current line, then edit + that instead. With a prefix argument insert a new command even + when there already is one on the current line. With empty input + remove the command on the current line, if any. + +‘b’ (‘git-rebase-break’) + Insert a break action before the current line, instructing Git to + return control to the user. + +‘y’ (‘git-rebase-insert’) + Read an arbitrary commit and insert it below current line. + +‘C-x u’ (‘git-rebase-undo’) + Undo some previous changes. Like ‘undo’ but works in read-only + buffers. + + -- User Option: git-rebase-auto-advance + Whether to move to next line after changing a line. + + -- User Option: git-rebase-show-instructions + Whether to show usage instructions inside the rebase buffer. + + -- User Option: git-rebase-confirm-cancel + Whether confirmation is required to cancel. + + When a rebase is performed with the ‘--rebase-merges’ option, the +sequence will include a few other types of actions and the following +commands become relevant. + +‘l’ (‘git-rebase-label’) + This commands inserts a label action or edits the one at point. + +‘t’ (‘git-rebase-reset’) + This command inserts a reset action or edits the one at point. The + prompt will offer the labels that are currently present in the + buffer. + +‘MM’ (‘git-rebase-merge’) + The command inserts a merge action or edits the one at point. The + prompt will offer the labels that are currently present in the + buffer. Specifying a message to reuse via ‘-c’ or ‘-C’ is not + supported; an editor will always be invoked for the merge. + +‘Mt’ (‘git-rebase-merge-toggle-editmsg’) + This command toggles between the ‘-C’ and ‘-c’ options of the merge + action at point. These options both specify a commit whose message + should be reused. The lower-case variant instructs Git to invoke + the editor when creating the merge, allowing the user to edit the + message. + + +File: magit.info, Node: Information About In-Progress Rebase, Prev: Editing Rebase Sequences, Up: Rebasing + +6.9.2 Information About In-Progress Rebase +------------------------------------------ + +While a rebase sequence is in progress, the status buffer features a +section that lists the commits that have already been applied as well as +the commits that still have to be applied. + + The commits are split in two halves. When rebase stops at a commit, +either because the user has to deal with a conflict or because s/he +explicitly requested that rebase stops at that commit, then point is +placed on the commit that separates the two groups, i.e. on ‘HEAD’. +The commits above it have not been applied yet, while the ‘HEAD’ and the +commits below it have already been applied. In between these two groups +of applied and yet-to-be applied commits, there sometimes is a commit +which has been dropped. + + Each commit is prefixed with a word and these words are additionally +shown in different colors to indicate the status of the commits. + + The following colors are used: + + • Commits that use the same foreground color as the ‘default’ face + have not been applied yet. + + • Yellow commits have some special relationship to the commit rebase + stopped at. This is used for the words "join", "goal", "same" and + "work" (see below). + + • Gray commits have already been applied. + + • The blue commit is the ‘HEAD’ commit. + + • The green commit is the commit the rebase sequence stopped at. If + this is the same commit as ‘HEAD’ (e.g. because you haven’t done + anything yet after rebase stopped at the commit, then this commit + is shown in blue, not green). There can only be a green *and* a + blue commit at the same time, if you create one or more new commits + after rebase stops at a commit. + + • Red commits have been dropped. They are shown for reference only, + e.g. to make it easier to diff. + + Of course these colors are subject to the color-theme in use. + + The following words are used: + + • Commits prefixed with ‘pick’, ‘reword’, ‘edit’, ‘squash’, and + ‘fixup’ have not been applied yet. These words have the same + meaning here as they do in the buffer used to edit the rebase + sequence. See *note Editing Rebase Sequences::. When the + ‘--rebase-merges’ option was specified, ‘reset’, ‘label’, and + ‘merge’ lines may also be present. + + • Commits prefixed with ‘done’ and ‘onto’ have already been applied. + It is possible for such a commit to be the ‘HEAD’, in which case it + is blue. Otherwise it is grey. + + • The commit prefixed with ‘onto’ is the commit on top of which + all the other commits are being re-applied. This commit + itself did not have to be re-applied, it is the commit rebase + did rewind to before starting to re-apply other commits. + + • Commits prefixed with ‘done’ have already been re-applied. + This includes commits that have been re-applied but also new + commits that you have created during the rebase. + + • All other commits, those not prefixed with any of the above words, + are in some way related to the commit at which rebase stopped. + + To determine whether a commit is related to the stopped-at commit + their hashes, trees and patch-ids (1) are being compared. The + commit message is not used for this purpose. + + Generally speaking commits that are related to the stopped-at + commit can have any of the used colors, though not all color/word + combinations are possible. + + Words used for stopped-at commits are: + + • When a commit is prefixed with ‘void’, then that indicates + that Magit knows for sure that all the changes in that commit + have been applied using several new commits. This commit is + no longer reachable from ‘HEAD’, and it also isn’t one of the + commits that will be applied when resuming the session. + + • When a commit is prefixed with ‘join’, then that indicates + that the rebase sequence stopped at that commit due to a + conflict - you now have to join (merge) the changes with what + has already been applied. In a sense this is the commit + rebase stopped at, but while its effect is already in the + index and in the worktree (with conflict markers), the commit + itself has not actually been applied yet (it isn’t the + ‘HEAD’). So it is shown in yellow, like the other commits + that still have to be applied. + + • When a commit is prefixed with ‘stop’ or a _blue_ or _green_ + ‘same’, then that indicates that rebase stopped at this + commit, that it is still applied or has been applied again, + and that at least its patch-id is unchanged. + + • When a commit is prefixed with ‘stop’, then that + indicates that rebase stopped at that commit because you + requested that earlier, and its patch-id is unchanged. + It might even still be the exact same commit. + + • When a commit is prefixed with a _blue_ or _green_ + ‘same’, then that indicates that while its tree or hash + changed, its patch-id did not. If it is blue, then it is + the ‘HEAD’ commit (as always for blue). When it is + green, then it no longer is ‘HEAD’ because other commit + have been created since (but before continuing the + rebase). + + • When a commit is prefixed with ‘goal’, a _yellow_ ‘same,’ or + ‘work’, then that indicates that rebase applied that commit + but that you then reset ‘HEAD’ to an earlier commit (likely to + split it up into multiple commits), and that there are some + uncommitted changes remaining which likely (but not + necessarily) originate from that commit. + + • When a commit is prefixed with ‘goal’, then that + indicates that it is still possible to create a new + commit with the exact same tree (the "goal") without + manually editing any files, by committing the index, or + by staging all changes and then committing that. This is + the case when the original tree still exists in the index + or worktree in untainted form. + + • When a commit is prefixed with a yellow ‘same’, then that + indicates that it is no longer possible to create a + commit with the exact same tree, but that it is still + possible to create a commit with the same patch-id. This + would be the case if you created a new commit with other + changes, but the changes from the original commit still + exist in the index or working tree in untainted form. + + • When a commit is prefixed with ‘work’, then that + indicates that you reset ‘HEAD’ to an earlier commit, and + that there are some staged and/or unstaged changes + (likely, but not necessarily) originating from that + commit. However it is no longer possible to create a new + commit with the same tree or at least the same patch-id + because you have already made other changes. + + • When a commit is prefixed with ‘poof’ or ‘gone’, then that + indicates that rebase applied that commit but that you then + reset ‘HEAD’ to an earlier commit (likely to split it up into + multiple commits), and that there are no uncommitted changes. + + • When a commit is prefixed with ‘poof’, then that + indicates that it is no longer reachable from ‘HEAD’, but + that it has been replaced with one or more commits, which + together have the exact same effect. + + • When a commit is prefixed with ‘gone’, then that + indicates that it is no longer reachable from ‘HEAD’ and + that we also cannot determine whether its changes are + still in effect in one or more new commits. They might + be, but if so, then there must also be other changes + which makes it impossible to know for sure. + + Do not worry if you do not fully understand the above. That’s okay, +you will acquire a good enough understanding through practice. + + For other sequence operations such as cherry-picking, a similar +section is displayed, but they lack some of the features described +above, due to limitations in the git commands used to implement them. +Most importantly these sequences only support "picking" a commit but not +other actions such as "rewording", and they do not keep track of the +commits which have already been applied. + + ---------- Footnotes ---------- + + (1) The patch-id is a hash of the _changes_ introduced by a commit. +It differs from the hash of the commit itself, which is a hash of the +result of applying that change (i.e. the resulting trees and blobs) as +well as author and committer information, the commit message, and the +hashes of the parents of the commit. The patch-id hash on the other +hand is created only from the added and removed lines, even line numbers +and whitespace changes are ignored when calculating this hash. The +patch-ids of two commits can be used to answer the question "Do these +commits make the same change?". + + +File: magit.info, Node: Cherry Picking, Next: Resetting, Prev: Rebasing, Up: Manipulating + +6.10 Cherry Picking +=================== + +Also see *note (gitman)git-cherry-pick::. + +‘A’ (‘magit-cherry-pick’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no cherry-pick or revert is in progress, then the transient +features the following suffix commands. + +‘A A’ (‘magit-cherry-copy’) + This command copies COMMITS from another branch onto the current + branch. If the region selects multiple commits, then those are + copied, without prompting. Otherwise the user is prompted for a + commit or range, defaulting to the commit at point. + +‘A a’ (‘magit-cherry-apply’) + This command applies the changes in COMMITS from another branch + onto the current branch. If the region selects multiple commits, + then those are used, without prompting. Otherwise the user is + prompted for a commit or range, defaulting to the commit at point. + + This command also has a top-level binding, which can be invoked + without using the transient by typing ‘a’ at the top-level. + + The following commands not only apply some commits to some branch, +but also remove them from some other branch. The removal is performed +using either ‘git-update-ref’ or if necessary ‘git-rebase’. Both +applying commits as well as removing them using ‘git-rebase’ can lead to +conflicts. If that happens, then these commands abort and you not only +have to resolve the conflicts but also finish the process the same way +you would have to if these commands didn’t exist at all. + +‘A h’ (‘magit-cherry-harvest’) + This command moves the selected COMMITS that must be located on + another BRANCH onto the current branch instead, removing them from + the former. When this command succeeds, then the same branch is + current as before. + + Applying the commits on the current branch or removing them from + the other branch can lead to conflicts. When that happens, then + this command stops and you have to resolve the conflicts and then + finish the process manually. + +‘A d’ (‘magit-cherry-donate’) + This command moves the selected COMMITS from the current branch + onto another existing BRANCH, removing them from the former. When + this command succeeds, then the same branch is current as before. + ‘HEAD’ is allowed to be detached initially. + + Applying the commits on the other branch or removing them from the + current branch can lead to conflicts. When that happens, then this + command stops and you have to resolve the conflicts and then finish + the process manually. + +‘A n’ (‘magit-cherry-spinout’) + This command moves the selected COMMITS from the current branch + onto a new branch BRANCH, removing them from the former. When this + command succeeds, then the same branch is current as before. + + Applying the commits on the other branch or removing them from the + current branch can lead to conflicts. When that happens, then this + command stops and you have to resolve the conflicts and then finish + the process manually. + +‘A s’ (‘magit-cherry-spinoff’) + This command moves the selected COMMITS from the current branch + onto a new branch BRANCH, removing them from the former. When this + command succeeds, then the new branch is checked out. + + Applying the commits on the other branch or removing them from the + current branch can lead to conflicts. When that happens, then this + command stops and you have to resolve the conflicts and then finish + the process manually. + + When a cherry-pick or revert is in progress, then the transient +instead features the following suffix commands. + +‘A A’ (‘magit-sequence-continue’) + Resume the current cherry-pick or revert sequence. + +‘A s’ (‘magit-sequence-skip’) + Skip the stopped at commit during a cherry-pick or revert sequence. + +‘A a’ (‘magit-sequence-abort’) + Abort the current cherry-pick or revert sequence. This discards + all changes made since the sequence started. + +* Menu: + +* Reverting:: + + +File: magit.info, Node: Reverting, Up: Cherry Picking + +6.10.1 Reverting +---------------- + +‘V’ (‘magit-revert’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no cherry-pick or revert is in progress, then the transient +features the following suffix commands. + +‘V V’ (‘magit-revert-and-commit’) + Revert a commit by creating a new commit. Prompt for a commit, + defaulting to the commit at point. If the region selects multiple + commits, then revert all of them, without prompting. + +‘V v’ (‘magit-revert-no-commit’) + Revert a commit by applying it in reverse to the working tree. + Prompt for a commit, defaulting to the commit at point. If the + region selects multiple commits, then revert all of them, without + prompting. + + When a cherry-pick or revert is in progress, then the transient +instead features the following suffix commands. + +‘V A’ (‘magit-sequence-continue’) + Resume the current cherry-pick or revert sequence. + +‘V s’ (‘magit-sequence-skip’) + Skip the stopped at commit during a cherry-pick or revert sequence. + +‘V a’ (‘magit-sequence-abort’) + Abort the current cherry-pick or revert sequence. This discards + all changes made since the sequence started. + + +File: magit.info, Node: Resetting, Next: Stashing, Prev: Cherry Picking, Up: Manipulating + +6.11 Resetting +============== + +Also see *note (gitman)git-reset::. + +‘x’ (‘magit-reset-quickly’) + Reset the ‘HEAD’ and index to some commit read from the user and + defaulting to the commit at point, and possibly also reset the + working tree. With a prefix argument reset the working tree + otherwise don’t. + +‘X m’ (‘magit-reset-mixed’) + Reset the ‘HEAD’ and index to some commit read from the user and + defaulting to the commit at point. The working tree is kept as-is. + +‘X s’ (‘magit-reset-soft’) + Reset the ‘HEAD’ to some commit read from the user and defaulting + to the commit at point. The index and the working tree are kept + as-is. + +‘X h’ (‘magit-reset-hard’) + Reset the ‘HEAD’, index, and working tree to some commit read from + the user and defaulting to the commit at point. + +‘X k’ (‘magit-reset-keep’) + Reset the ‘HEAD’, index, and working tree to some commit read from + the user and defaulting to the commit at point. Uncommitted + changes are kept as-is. + +‘X i’ (‘magit-reset-index’) + Reset the index to some commit read from the user and defaulting to + the commit at point. Keep the ‘HEAD’ and working tree as-is, so if + the commit refers to the ‘HEAD’, then this effectively unstages all + changes. + +‘X w’ (‘magit-reset-worktree’) + Reset the working tree to some commit read from the user and + defaulting to the commit at point. Keep the ‘HEAD’ and index + as-is. + +‘X f’ (‘magit-file-checkout’) + Update file in the working tree and index to the contents from a + revision. Both the revision and file are read from the user. + + +File: magit.info, Node: Stashing, Prev: Resetting, Up: Manipulating + +6.12 Stashing +============= + +Also see *note (gitman)git-stash::. + +‘z’ (‘magit-stash’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘z z’ (‘magit-stash-both’) + Create a stash of the index and working tree. Untracked files are + included according to infix arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’. + +‘z i’ (‘magit-stash-index’) + Create a stash of the index only. Unstaged and untracked changes + are not stashed. + +‘z w’ (‘magit-stash-worktree’) + Create a stash of unstaged changes in the working tree. Untracked + files are included according to infix arguments. One prefix + argument is equivalent to ‘--include-untracked’ while two prefix + arguments are equivalent to ‘--all’. + +‘z x’ (‘magit-stash-keep-index’) + Create a stash of the index and working tree, keeping index intact. + Untracked files are included according to infix arguments. One + prefix argument is equivalent to ‘--include-untracked’ while two + prefix arguments are equivalent to ‘--all’. + +‘z Z’ (‘magit-snapshot-both’) + Create a snapshot of the index and working tree. Untracked files + are included according to infix arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’. + +‘z I’ (‘magit-snapshot-index’) + Create a snapshot of the index only. Unstaged and untracked + changes are not stashed. + +‘z W’ (‘magit-snapshot-worktree’) + Create a snapshot of unstaged changes in the working tree. + Untracked files are included according to infix arguments. One + prefix argument is equivalent to ‘--include-untracked’ while two + prefix arguments are equivalent to ‘--all’-. + +‘z a’ (‘magit-stash-apply’) + Apply a stash to the working tree. Try to preserve the stash + index. If that fails because there are staged changes, apply + without preserving the stash index. + +‘z p’ (‘magit-stash-pop’) + Apply a stash to the working tree and remove it from stash list. + Try to preserve the stash index. If that fails because there are + staged changes, apply without preserving the stash index and forgo + removing the stash. + +‘z k’ (‘magit-stash-drop’) + Remove a stash from the stash list. When the region is active, + offer to drop all contained stashes. + +‘z v’ (‘magit-stash-show’) + Show all diffs of a stash in a buffer. + +‘z b’ (‘magit-stash-branch’) + Create and checkout a new BRANCH from STASH. The branch starts at + the commit that was current when the stash was created. + +‘z B’ (‘magit-stash-branch-here’) + Create and checkout a new BRANCH using ‘magit-branch’ with the + current branch or ‘HEAD’ as the starting-point. Then apply STASH, + dropping it if it applies cleanly. + +‘z f’ (‘magit-stash-format-patch’) + Create a patch from STASH. + +‘k’ (‘magit-stash-clear’) + Remove all stashes saved in REF’s reflog by deleting REF. + +‘z l’ (‘magit-stash-list’) + List all stashes in a buffer. + + -- User Option: magit-stashes-margin + This option specifies whether the margin is initially shown in + stashes buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Transferring, Next: Miscellaneous, Prev: Manipulating, Up: Top + +7 Transferring +************** + +* Menu: + +* Remotes:: +* Fetching:: +* Pulling:: +* Pushing:: +* Plain Patches:: +* Maildir Patches:: + + +File: magit.info, Node: Remotes, Next: Fetching, Up: Transferring + +7.1 Remotes +=========== + +* Menu: + +* Remote Commands:: +* Remote Git Variables:: + + +File: magit.info, Node: Remote Commands, Next: Remote Git Variables, Up: Remotes + +7.1.1 Remote Commands +--------------------- + +The transient prefix command ‘magit-remote’ is used to add remotes and +to make changes to existing remotes. This command only deals with +remotes themselves, not with branches or the transfer of commits. Those +features are available from separate transient commands. + + Also see *note (gitman)git-remote::. + +‘M’ (‘magit-remote’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + + By default it also binds and displays the values of some + remote-related Git variables and allows changing their values. + + -- User Option: magit-remote-direct-configure + This option controls whether remote-related Git variables are + accessible directly from the transient ‘magit-remote’. + + If ‘t’ (the default) and a local branch is checked out, then + ‘magit-remote’ features the variables for the upstream remote of + that branch, or if ‘HEAD’ is detached, for ‘origin’, provided that + exists. + + If ‘nil’, then ‘magit-remote-configure’ has to be used to do so. + +‘M C’ (‘magit-remote-configure’) + This transient prefix command binds commands that set the value of + remote-related variables and displays them in a temporary buffer + until the transient is exited. + + With a prefix argument, this command always prompts for a remote. + + Without a prefix argument this depends on whether it was invoked as + a suffix of ‘magit-remote’ and on the + ‘magit-remote-direct-configure’ option. If ‘magit-remote’ already + displays the variables for the upstream, then it does not make + sense to invoke another transient that displays them for the same + remote. In that case this command prompts for a remote. + + The variables are described in *note Remote Git Variables::. + +‘M a’ (‘magit-remote-add’) + This command add a remote and fetches it. The remote name and url + are read in the minibuffer. + +‘M r’ (‘magit-remote-rename’) + This command renames a remote. Both the old and the new names are + read in the minibuffer. + +‘M u’ (‘magit-remote-set-url’) + This command changes the url of a remote. Both the remote and the + new url are read in the minibuffer. + +‘M k’ (‘magit-remote-remove’) + This command deletes a remote, read in the minibuffer. + +‘M p’ (‘magit-remote-prune’) + This command removes stale remote-tracking branches for a remote + read in the minibuffer. + +‘M P’ (‘magit-remote-prune-refspecs’) + This command removes stale refspecs for a remote read in the + minibuffer. + + A refspec is stale if there no longer exists at least one branch on + the remote that would be fetched due to that refspec. A stale + refspec is problematic because its existence causes Git to refuse + to fetch according to the remaining non-stale refspecs. + + If only stale refspecs remain, then this command offers to either + delete the remote or to replace the stale refspecs with the default + refspec ("+refs/heads/*:refs/remotes/REMOTE/*"). + + This command also removes the remote-tracking branches that were + created due to the now stale refspecs. Other stale branches are + not removed. + + -- User Option: magit-remote-add-set-remote.pushDefault + This option controls whether the user is asked whether they want to + set ‘remote.pushDefault’ after adding a remote. + + If ‘ask’, then users is always ask. If ‘ask-if-unset’, then the + user is only if the variable isn’t set already. If ‘nil’, then the + user isn’t asked and the variable isn’t set. If the value is a + string, then the variable is set without the user being asked, + provided that the name of the added remote is equal to that string + and the variable isn’t already set. + + +File: magit.info, Node: Remote Git Variables, Prev: Remote Commands, Up: Remotes + +7.1.2 Remote Git Variables +-------------------------- + +These variables can be set from the transient prefix command +‘magit-remote-configure’. By default they can also be set from +‘magit-remote’. See *note Remote Commands::. + + -- Variable: remote.NAME.url + This variable specifies the url of the remote named NAME. It can + have multiple values. + + -- Variable: remote.NAME.fetch + The refspec used when fetching from the remote named NAME. It can + have multiple values. + + -- Variable: remote.NAME.pushurl + This variable specifies the url used for pushing to the remote + named NAME. If it is not specified, then ‘remote.NAME.url’ is used + instead. It can have multiple values. + + -- Variable: remote.NAME.push + The refspec used when pushing to the remote named NAME. It can + have multiple values. + + -- Variable: remote.NAME.tagOpts + This variable specifies what tags are fetched by default. If the + value is ‘--no-tags’ then no tags are fetched. If the value is + ‘--tags’, then all tags are fetched. If this variable has no + value, then only tags are fetched that are reachable from fetched + branches. + + +File: magit.info, Node: Fetching, Next: Pulling, Prev: Remotes, Up: Transferring + +7.2 Fetching +============ + +Also see *note (gitman)git-fetch::. For information about the upstream +and the push-remote, see *note The Two Remotes::. + +‘f’ (‘magit-fetch’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘f p’ (‘magit-fetch-from-pushremote’) + This command fetches from the current push-remote. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘f u’ (‘magit-fetch-from-upstream’) + This command fetch from the upstream of the current branch. + + If the upstream is configured for the current branch and names an + existing remote, then use that. Otherwise try to use another + remote: If only a single remote is configured, then use that. + Otherwise if a remote named "origin" exists, then use that. + + If no remote can be determined, then this command is not available + from the ‘magit-fetch’ transient prefix and invoking it directly + results in an error. + +‘f e’ (‘magit-fetch-other’) + This command fetch from a repository read from the minibuffer. + +‘f o’ (‘magit-fetch-branch’) + This command fetches a branch from a remote, both of which are read + from the minibuffer. + +‘f r’ (‘magit-fetch-refspec’) + This command fetches from a remote using an explicit refspec, both + of which are read from the minibuffer. + +‘f a’ (‘magit-fetch-all’) + This command fetches from all remotes. + +‘f m’ (‘magit-submodule-fetch’) + This command fetches all submodules. With a prefix argument it + fetches all remotes of all submodules. + + -- User Option: magit-pull-or-fetch + By default fetch and pull commands are available from separate + transient prefix command. Setting this to ‘t’ adds some (but not + all) of the above suffix commands to the ‘magit-pull’ transient. + + If you do that, then you might also want to change the key binding + for these prefix commands, e.g.: + + (setq magit-pull-or-fetch t) + (define-key magit-mode-map "f" 'magit-pull) ; was magit-fetch + (define-key magit-mode-map "F" nil) ; was magit-pull + + +File: magit.info, Node: Pulling, Next: Pushing, Prev: Fetching, Up: Transferring + +7.3 Pulling +=========== + +Also see *note (gitman)git-pull::. For information about the upstream +and the push-remote, see *note The Two Remotes::. + +‘F’ (‘magit-pull’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘F p’ (‘magit-pull-from-pushremote’) + This command pulls from the push-remote of the current branch. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘F u’ (‘magit-pull-from-upstream’) + This command pulls from the upstream of the current branch. + + With a prefix argument or when the upstream is either not + configured or unusable, then let the user first configure the + upstream. + +‘F e’ (‘magit-pull-branch’) + This command pulls from a branch read in the minibuffer. + + +File: magit.info, Node: Pushing, Next: Plain Patches, Prev: Pulling, Up: Transferring + +7.4 Pushing +=========== + +Also see *note (gitman)git-push::. For information about the upstream +and the push-remote, see *note The Two Remotes::. + +‘P’ (‘magit-push’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘P p’ (‘magit-push-current-to-pushremote’) + This command pushes the current branch to its push-remote. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘P u’ (‘magit-push-current-to-upstream’) + This command pushes the current branch to its upstream branch. + + With a prefix argument or when the upstream is either not + configured or unusable, then let the user first configure the + upstream. + +‘P e’ (‘magit-push-current’) + This command pushes the current branch to a branch read in the + minibuffer. + +‘P o’ (‘magit-push-other’) + This command pushes an arbitrary branch or commit somewhere. Both + the source and the target are read in the minibuffer. + +‘P r’ (‘magit-push-refspecs’) + This command pushes one or multiple refspecs to a remote, both of + which are read in the minibuffer. + + To use multiple refspecs, separate them with commas. Completion is + only available for the part before the colon, or when no colon is + used. + +‘P m’ (‘magit-push-matching’) + This command pushes all matching branches to another repository. + + If only one remote exists, then push to that. Otherwise prompt for + a remote, offering the remote configured for the current branch as + default. + +‘P t’ (‘magit-push-tags’) + This command pushes all tags to another repository. + + If only one remote exists, then push to that. Otherwise prompt for + a remote, offering the remote configured for the current branch as + default. + +‘P T’ (‘magit-push-tag’) + This command pushes a tag to another repository. + + One of the infix arguments, ‘--force-with-lease’, deserves a word of +caution. It is passed without a value, which means "permit a force push +as long as the remote-tracking branches match their counterparts on the +remote end". If you’ve set up a tool to do automatic fetches (Magit +itself does not provide such functionality), using ‘--force-with-lease’ +can be dangerous because you don’t actually control or know the state of +the remote-tracking refs. In that case, you should consider setting +‘push.useForceIfIncludes’ to ‘true’ (available since Git 2.30). + + Two more push commands exist, which by default are not available from +the push transient. See their doc-strings for instructions on how to +add them to the transient. + + -- Command: magit-push-implicitly args + This command pushes somewhere without using an explicit refspec. + + This command simply runs ‘git push -v [ARGS]’. ARGS are the infix + arguments. No explicit refspec arguments are used. Instead the + behavior depends on at least these Git variables: ‘push.default’, + ‘remote.pushDefault’, ‘branch..pushRemote’, + ‘branch..remote’, ‘branch..merge’, and + ‘remote..push’. + + If you add this suffix to a transient prefix without explicitly + specifying the description, then an attempt is made to predict what + this command will do. For example: + + (transient-insert-suffix 'magit-push \"p\" + '(\"i\" magit-push-implicitly))" + + -- Command: magit-push-to-remote remote args + This command pushes to the remote REMOTE without using an explicit + refspec. The remote is read in the minibuffer. + + This command simply runs ‘git push -v [ARGS] REMOTE’. ARGS are the + infix arguments. No refspec arguments are used. Instead the + behavior depends on at least these Git variables: ‘push.default’, + ‘remote.pushDefault’, ‘branch..pushRemote’, + ‘branch..remote’, ‘branch..merge’, and + ‘remote..push’. + + +File: magit.info, Node: Plain Patches, Next: Maildir Patches, Prev: Pushing, Up: Transferring + +7.5 Plain Patches +================= + +‘W’ (‘magit-patch’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘W c’ (‘magit-patch-create’) + This command creates patches for a set commits. If the region + marks several commits, then it creates patches for all of them. + Otherwise it functions as a transient prefix command, which + features several infix arguments and binds itself as a suffix + command. When this command is invoked as a suffix of itself, then + it creates a patch using the specified infix arguments. + +‘w a’ (‘magit-patch-apply’) + This command applies a patch. This is a transient prefix command, + which features several infix arguments and binds itself as a suffix + command. When this command is invoked as a suffix of itself, then + it applies a patch using the specified infix arguments. + +‘W s’ (‘magit-patch-save’) + This command creates a patch from the current diff. + + Inside ‘magit-diff-mode’ or ‘magit-revision-mode’ buffers, ‘C-x + C-w’ is also bound to this command. + + It is also possible to save a plain patch file by using ‘C-x C-w’ +inside a ‘magit-diff-mode’ or ‘magit-revision-mode’ buffer. + + +File: magit.info, Node: Maildir Patches, Prev: Plain Patches, Up: Transferring + +7.6 Maildir Patches +=================== + +Also see *note (gitman)git-am::. and *note (gitman)git-apply::. + +‘w’ (‘magit-am’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘w w’ (‘magit-am-apply-patches’) + This command applies one or more patches. If the region marks + files, then those are applied as patches. Otherwise this command + reads a file-name in the minibuffer, defaulting to the file at + point. + +‘w m’ (‘magit-am-apply-maildir’) + This command applies patches from a maildir. + +‘w a’ (‘magit-patch-apply’) + This command applies a plain patch. For a longer description see + *note Plain Patches::. This command is only available from the + ‘magit-am’ transient for historic reasons. + + When an "am" operation is in progress, then the transient instead +features the following suffix commands. + +‘w w’ (‘magit-am-continue’) + This command resumes the current patch applying sequence. + +‘w s’ (‘magit-am-skip’) + This command skips the stopped at patch during a patch applying + sequence. + +‘w a’ (‘magit-am-abort’) + This command aborts the current patch applying sequence. This + discards all changes made since the sequence started. + + +File: magit.info, Node: Miscellaneous, Next: Customizing, Prev: Transferring, Up: Top + +8 Miscellaneous +*************** + +* Menu: + +* Tagging:: +* Notes:: +* Submodules:: +* Subtree:: +* Worktree:: +* Sparse checkouts:: +* Bundle:: +* Common Commands:: +* Wip Modes:: +* Commands for Buffers Visiting Files:: +* Minor Mode for Buffers Visiting Blobs:: + + +File: magit.info, Node: Tagging, Next: Notes, Up: Miscellaneous + +8.1 Tagging +=========== + +Also see *note (gitman)git-tag::. + +‘t’ (‘magit-tag’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘t t’ (‘magit-tag-create’) + This command creates a new tag with the given NAME at REV. With a + prefix argument it creates an annotated tag. + +‘t r’ (‘magit-tag-release’) + This commands creates a release tag. It assumes that release tags + match ‘magit-release-tag-regexp’. + + First it prompts for the name of the new tag using the highest + existing tag as initial input and leaving it to the user to + increment the desired part of the version string. If you use + unconventional release tags or version numbers (e.g., + ‘v1.2.3-custom.1’), you can set the ‘magit-release-tag-regexp’ and + ‘magit-tag-version-regexp-alist’ variables. + + If ‘--annotate’ is enabled then it prompts for the message of the + new tag. The proposed tag message is based on the message of the + highest tag, provided that that contains the corresponding version + string and substituting the new version string for that. Otherwise + it proposes something like "Foo-Bar 1.2.3", given, for example, a + TAG "v1.2.3" and a repository located at something like + "/path/to/foo-bar". + +‘t k’ (‘magit-tag-delete’) + This command deletes one or more tags. If the region marks + multiple tags (and nothing else), then it offers to delete those. + Otherwise, it prompts for a single tag to be deleted, defaulting to + the tag at point. + +‘t p’ (‘magit-tag-prune’) + This command offers to delete tags missing locally from REMOTE, and + vice versa. + + +File: magit.info, Node: Notes, Next: Submodules, Prev: Tagging, Up: Miscellaneous + +8.2 Notes +========= + +Also see *note (gitman)git-notes::. + +‘T’ (‘magit-notes’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘T T’ (‘magit-notes-edit’) + Edit the note attached to a commit, defaulting to the commit at + point. + + By default use the value of Git variable ‘core.notesRef’ or + "refs/notes/commits" if that is undefined. + +‘T r’ (‘magit-notes-remove’) + Remove the note attached to a commit, defaulting to the commit at + point. + + By default use the value of Git variable ‘core.notesRef’ or + "refs/notes/commits" if that is undefined. + +‘T p’ (‘magit-notes-prune’) + Remove notes about unreachable commits. + + It is possible to merge one note ref into another. That may result +in conflicts which have to resolved in the temporary worktree +".git/NOTES_MERGE_WORKTREE". + +‘T m’ (‘magit-notes-merge’) + Merge the notes of a ref read from the user into the current notes + ref. The current notes ref is the value of Git variable + ‘core.notesRef’ or "refs/notes/commits" if that is undefined. + + When a notes merge is in progress then the transient features the +following suffix commands, instead of those listed above. + +‘T c’ (‘magit-notes-merge-commit’) + Commit the current notes ref merge, after manually resolving + conflicts. + +‘T a’ (‘magit-notes-merge-abort’) + Abort the current notes ref merge. + + The following variables control what notes reference ‘magit-notes-*’, +‘git notes’ and ‘git show’ act on and display. Both the local and +global values are displayed and can be modified. + + -- Variable: core.notesRef + This variable specifies the notes ref that is displayed by default + and which commands act on by default. + + -- Variable: notes.displayRef + This variable specifies additional notes ref to be displayed in + addition to the ref specified by ‘core.notesRef’. It can have + multiple values and may end with ‘*’ to display all refs in the + ‘refs/notes/’ namespace (or ‘**’ if some names contain slashes). + + +File: magit.info, Node: Submodules, Next: Subtree, Prev: Notes, Up: Miscellaneous + +8.3 Submodules +============== + +Also see *note (gitman)git-submodule::. + +* Menu: + +* Listing Submodules:: +* Submodule Transient:: + + +File: magit.info, Node: Listing Submodules, Next: Submodule Transient, Up: Submodules + +8.3.1 Listing Submodules +------------------------ + +The command ‘magit-list-submodules’ displays a list of the current +repository’s submodules in a separate buffer. It’s also possible to +display information about submodules directly in the status buffer of +the super-repository by adding ‘magit-insert-modules’ to the hook +‘magit-status-sections-hook’ as described in *note Status Module +Sections::. + + -- Command: magit-list-submodules + This command displays a list of the current repository’s submodules + in a separate buffer. + + It can be invoked by pressing ‘RET’ on the section titled + "Modules". + + -- User Option: magit-submodule-list-columns + This option controls what columns are displayed by the command + ‘magit-list-submodules’ and how they are displayed. + + Each element has the form ‘(HEADER WIDTH FORMAT PROPS)’. + + HEADER is the string displayed in the header. WIDTH is the width + of the column. FORMAT is a function that is called with one + argument, the repository identification (usually its basename), and + with ‘default-directory’ bound to the toplevel of its working tree. + It has to return a string to be inserted or nil. PROPS is an alist + that supports the keys ‘:right-align’, ‘:pad-right’ and ‘:sort’. + + The ‘:sort’ function has a weird interface described in the + docstring of ‘tabulated-list--get-sort’. Alternatively ‘<’ and + ‘magit-repolist-version<’ can be used as those functions are + automatically replaced with functions that satisfy the interface. + Set ‘:sort’ to ‘nil’ to inhibit sorting; if unspecifed, then the + column is sortable using the default sorter. + + You may wish to display a range of numeric columns using just one + character per column and without any padding between columns, in + which case you should use an appropriat HEADER, set WIDTH to 1, and + set ‘:pad-right’ to 9. ‘+’ is substituted for numbers higher than + 9. + + +File: magit.info, Node: Submodule Transient, Prev: Listing Submodules, Up: Submodules + +8.3.2 Submodule Transient +------------------------- + +‘o’ (‘magit-submodule’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + Some of the below commands default to act on the modules that are +selected using the region. For brevity their description talk about +"the selected modules", but if no modules are selected, then they act on +the current module instead, or if point isn’t on a module, then the read +a single module to act on. With a prefix argument these commands ignore +the selection and the current module and instead act on all suitable +modules. + +‘o a’ (‘magit-submodule-add’) + This commands adds the repository at URL as a module. Optional + PATH is the path to the module relative to the root of the + super-project. If it is nil then the path is determined based on + URL. + +‘o r’ (‘magit-submodule-register’) + This command registers the selected modules by copying their urls + from ".gitmodules" to "$GIT_DIR/config". These values can then be + edited before running ‘magit-submodule-populate’. If you don’t + need to edit any urls, then use the latter directly. + +‘o p’ (‘magit-submodule-populate’) + This command creates the working directory or directories of the + selected modules, checking out the recorded commits. + +‘o u’ (‘magit-submodule-update’) + This command updates the selected modules checking out the recorded + commits. + +‘o s’ (‘magit-submodule-synchronize’) + This command synchronizes the urls of the selected modules, copying + the values from ".gitmodules" to the ".git/config" of the + super-project as well those of the modules. + +‘o d’ (‘magit-submodule-unpopulate’) + This command removes the working directory of the selected modules. + +‘o l’ (‘magit-list-submodules’) + This command displays a list of the current repository’s modules. + +‘o f’ (‘magit-fetch-modules’) + This command fetches all modules. + + Option ‘magit-fetch-modules-jobs’ controls how many submodules are + being fetched in parallel. Also fetch the super-repository, + because ‘git fetch’ does not support not doing that. With a prefix + argument fetch all remotes. + + +File: magit.info, Node: Subtree, Next: Worktree, Prev: Submodules, Up: Miscellaneous + +8.4 Subtree +=========== + +Also see *note (gitman)git-subtree::. + +‘O’ (‘magit-subtree’) + This transient prefix command binds the two sub-transients; one for + importing a subtree and one for exporting a subtree. + +‘O i’ (‘magit-subtree-import’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + The suffixes of this command import subtrees. + + If the ‘--prefix’ argument is set, then the suffix commands use + that prefix without prompting the user. If it is unset, then they + read the prefix in the minibuffer. + +‘O i a’ (‘magit-subtree-add’) + This command adds COMMIT from REPOSITORY as a new subtree at + PREFIX. + +‘O i c’ (‘magit-subtree-add-commit’) + This command add COMMIT as a new subtree at PREFIX. + +‘O i m’ (‘magit-subtree-merge’) + This command merges COMMIT into the PREFIX subtree. + +‘O i f’ (‘magit-subtree-pull’) + This command pulls COMMIT from REPOSITORY into the PREFIX subtree. + +‘O e’ (‘magit-subtree-export’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + The suffixes of this command export subtrees. + + If the ‘--prefix’ argument is set, then the suffix commands use + that prefix without prompting the user. If it is unset, then they + read the prefix in the minibuffer. + +‘O e p’ (‘magit-subtree-push’) + This command extract the history of the subtree PREFIX and pushes + it to REF on REPOSITORY. + +‘O e s’ (‘magit-subtree-split’) + This command extracts the history of the subtree PREFIX. + + +File: magit.info, Node: Worktree, Next: Sparse checkouts, Prev: Subtree, Up: Miscellaneous + +8.5 Worktree +============ + +Also see *note (gitman)git-worktree::. + +‘Z’ (‘magit-worktree’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘Z b’ (‘magit-worktree-checkout’) + Checkout BRANCH in a new worktree at PATH. + +‘Z c’ (‘magit-worktree-branch’) + Create a new BRANCH and check it out in a new worktree at PATH. + +‘Z m’ (‘magit-worktree-move’) + Move an existing worktree to a new PATH. + +‘Z k’ (‘magit-worktree-delete’) + Delete a worktree, defaulting to the worktree at point. The + primary worktree cannot be deleted. + +‘Z g’ (‘magit-worktree-status’) + Show the status for the worktree at point. + + If there is no worktree at point, then read one in the minibuffer. + If the worktree at point is the one whose status is already being + displayed in the current buffer, then show it in Dired instead. + + +File: magit.info, Node: Sparse checkouts, Next: Bundle, Prev: Worktree, Up: Miscellaneous + +8.6 Sparse checkouts +==================== + +Sparse checkouts provide a way to restrict the working tree to a subset +of directories. See *note (gitman)git-sparse-checkout::. + + *Warning*: Git introduced the ‘git sparse-checkout’ command in +version 2.25 and still advertises it as experimental and subject to +change. Magit’s interface should be considered the same. In +particular, if Git introduces a backward incompatible change, Magit’s +sparse checkout functionality may be updated in a way that requires a +more recent Git version. + +‘>’ (‘magit-sparse-checkout’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘> e’ (‘magit-sparse-checkout-enable’) + This command initializes a sparse checkout that includes only the + files in the top-level directory. + + Note that ‘magit-sparse-checkout-set’ and + ‘magit-sparse-checkout-add’ automatically initialize a sparse + checkout if necessary. However, you may want to call + ‘magit-sparse-checkout-enable’ explicitly to re-initialize a sparse + checkout after calling ‘magit-sparse-checkout-disable’, to pass + additional arguments to ‘git sparse-checkout init’, or to execute + the initialization asynchronously. + +‘> s’ (‘magit-sparse-checkout-set’) + This command takes a list of directories and configures the sparse + checkout to include only files in those subdirectories. Any + previously included directories are excluded unless they are in the + provided list of directories. + +‘> a’ (‘magit-sparse-checkout-add’) + This command is like ‘magit-sparse-checkout-set’, but instead adds + the specified list of directories to the set of directories that is + already included in the sparse checkout. + +‘> r’ (‘magit-sparse-checkout-reapply’) + This command applies the currently configured sparse checkout + patterns to the working tree. This is useful to call if excluded + files have been checked out after operations such as merging or + rebasing. + +‘> d’ (‘magit-sparse-checkout-disable’) + This command restores the full checkout. To return to the previous + sparse checkout, call ‘magit-sparse-checkout-enable’. + + A sparse checkout can also be initiated when cloning a repository by +using the ‘magit-clone-sparse’ command in the ‘magit-clone’ transient +(see *note Cloning Repository::). + + If you want the status buffer to indicate when a sparse checkout is +enabled, add the function ‘magit-sparse-checkout-insert-header’ to +‘magit-status-headers-hook’. + + +File: magit.info, Node: Bundle, Next: Common Commands, Prev: Sparse checkouts, Up: Miscellaneous + +8.7 Bundle +========== + +Also see *note (gitman)git-bundle::. + + -- Command: magit-bundle + This transient prefix command binds several suffix commands for + running ‘git bundle’ subcommands and displays them in a temporary + buffer until a suffix is invoked. + + +File: magit.info, Node: Common Commands, Next: Wip Modes, Prev: Bundle, Up: Miscellaneous + +8.8 Common Commands +=================== + + -- Command: magit-switch-to-repository-buffer + -- Command: magit-switch-to-repository-buffer-other-window + -- Command: magit-switch-to-repository-buffer-other-frame + -- Command: magit-display-repository-buffer + These commands read any existing Magit buffer that belongs to the + current repository from the user and then switch to the selected + buffer (without refreshing it). + + The last variant uses ‘magit-display-buffer’ to do so and thus + respects ‘magit-display-buffer-function’. + + These are some of the commands that can be used in all buffers whose +major-modes derive from ‘magit-mode’. There are other common commands +beside the ones below, but these didn’t fit well anywhere else. + +‘C-w’ (‘magit-copy-section-value’) + This command saves the value of the current section to the + ‘kill-ring’, and, provided that the current section is a commit, + branch, or tag section, it also pushes the (referenced) revision to + the ‘magit-revision-stack’. + + When the current section is a branch or a tag, and a prefix + argument is used, then it saves the revision at its tip to the + ‘kill-ring’ instead of the reference name. + + When the region is active, this command saves that to the + ‘kill-ring’, like ‘kill-ring-save’ would, instead of behaving as + described above. If a prefix argument is used and the region is + within a hunk, then it strips the diff marker column and keeps only + either the added or removed lines, depending on the sign of the + prefix argument. + +‘M-w’ (‘magit-copy-buffer-revision’) + This command saves the revision being displayed in the current + buffer to the ‘kill-ring’ and also pushes it to the + ‘magit-revision-stack’. It is mainly intended for use in + ‘magit-revision-mode’ buffers, the only buffers where it is always + unambiguous exactly which revision should be saved. + + Most other Magit buffers usually show more than one revision, in + some way or another, so this command has to select one of them, and + that choice might not always be the one you think would have been + the best pick. + + Outside of Magit ‘M-w’ and ‘C-w’ are usually bound to +‘kill-ring-save’ and ‘kill-region’, and these commands would also be +useful in Magit buffers. Therefore when the region is active, then both +of these commands behave like ‘kill-ring-save’ instead of as described +above. + + +File: magit.info, Node: Wip Modes, Next: Commands for Buffers Visiting Files, Prev: Common Commands, Up: Miscellaneous + +8.9 Wip Modes +============= + +Git keeps *committed* changes around long enough for users to recover +changes they have accidentally deleted. It does so by not garbage +collecting any committed but no longer referenced objects for a certain +period of time, by default 30 days. + + But Git does *not* keep track of *uncommitted* changes in the working +tree and not even the index (the staging area). Because Magit makes it +so convenient to modify uncommitted changes, it also makes it easy to +shoot yourself in the foot in the process. + + For that reason Magit provides a global mode that saves *tracked* +files to work-in-progress references after or before certain actions. +(At present untracked files are never saved and for technical reasons +nothing is saved before the first commit has been created). + + Two separate work-in-progress references are used to track the state +of the index and of the working tree: ‘refs/wip/index/’ and +‘refs/wip/wtree/’, where ‘’ is the full ref of the +current branch, e.g. ‘refs/heads/master’. When the ‘HEAD’ is detached +then ‘HEAD’ is used in place of ‘’. + + Checking out another branch (or detaching ‘HEAD’) causes the use of +different wip refs for subsequent changes. + + -- User Option: magit-wip-mode + When this mode is enabled, then uncommitted changes are committed + to dedicated work-in-progress refs whenever appropriate (i.e. when + dataloss would be a possibility otherwise). + + Setting this variable directly does not take effect; either use the + Custom interface to do so or call the respective mode function. + + For historic reasons this mode is implemented on top of four other + ‘magit-wip-*’ modes, which can also be used individually, if you + want finer control over when the wip refs are updated; but that is + discouraged. See *note Legacy Wip Modes::. + + To view the log for a branch and its wip refs use the commands +‘magit-wip-log’ and ‘magit-wip-log-current’. You should use ‘--graph’ +when using these commands. + + -- Command: magit-wip-log + This command shows the log for a branch and its wip refs. With a + negative prefix argument only the worktree wip ref is shown. + + The absolute numeric value of the prefix argument controls how many + "branches" of each wip ref are shown. This is only relevant if the + value of ‘magit-wip-merge-branch’ is ‘nil’. + + -- Command: magit-wip-log-current + This command shows the log for the current branch and its wip refs. + With a negative prefix argument only the worktree wip ref is shown. + + The absolute numeric value of the prefix argument controls how many + "branches" of each wip ref are shown. This is only relevant if the + value of ‘magit-wip-merge-branch’ is ‘nil’. + +‘X w’ (‘magit-reset-worktree’) + This command resets the working tree to some commit read from the + user and defaulting to the commit at point, while keeping the + ‘HEAD’ and index as-is. + + This can be used to restore files to the state committed to a wip + ref. Note that this will discard any unstaged changes that might + have existed before invoking this command (but of course only after + committing that to the working tree wip ref). + + Note that even if you enable ‘magit-wip-mode’ this won’t give you +perfect protection. The most likely scenario for losing changes despite +the use of ‘magit-wip-mode’ is making a change outside Emacs and then +destroying it also outside Emacs. In some such a scenario, Magit, being +an Emacs package, didn’t get the opportunity to keep you from shooting +yourself in the foot. + + When you are unsure whether Magit did commit a change to the wip +refs, then you can explicitly request that all changes to all tracked +files are being committed. + +‘M-x magit-wip-commit’ + This command commits all changes to all tracked files to the index + and working tree work-in-progress refs. Like the modes described + above, it does not commit untracked files, but it does check all + tracked files for changes. Use this command when you suspect that + the modes might have overlooked a change made outside Emacs/Magit. + + -- User Option: magit-wip-namespace + The namespace used for work-in-progress refs. It has to end with a + slash. The wip refs are named ‘index/’ and + ‘wtree/’. When snapshots are created while + the ‘HEAD’ is detached then ‘HEAD’ is used in place of + ‘’. + + -- User Option: magit-wip-mode-lighter + Mode-line lighter for ‘magit-wip--mode’. + +* Menu: + +* Wip Graph:: +* Legacy Wip Modes:: + + +File: magit.info, Node: Wip Graph, Next: Legacy Wip Modes, Up: Wip Modes + +8.9.1 Wip Graph +--------------- + + -- User Option: magit-wip-merge-branch + This option controls whether the current branch is merged into the + wip refs after a new commit was created on the branch. + + If non-nil and the current branch has new commits, then it is + merged into the wip ref before creating a new wip commit. This + makes it easier to inspect wip history and the wip commits are + never garbage collected. + + If nil and the current branch has new commits, then the wip ref is + reset to the tip of the branch before creating a new wip commit. + With this setting wip commits are eventually garbage collected. + + When ‘magit-wip-merge-branch’ is ‘t’, then the history looks like +this: + + *--*--*--*--*--* refs/wip/index/refs/heads/master + / / / + A-----B-----C refs/heads/master + + When ‘magit-wip-merge-branch’ is ‘nil’, then creating a commit on the +real branch and then making a change causes the wip refs to be recreated +to fork from the new commit. But the old commits on the wip refs are +not lost. They are still available from the reflog. To make it easier +to see when the fork point of a wip ref was changed, an additional +commit with the message "restart autosaving" is created on it (‘xxO’ +commits below are such boundary commits). + + Starting with + + BI0---BI1 refs/wip/index/refs/heads/master + / + A---B refs/heads/master + \ + BW0---BW1 refs/wip/wtree/refs/heads/master + + and committing the staged changes and editing and saving a file would +result in + + BI0---BI1 refs/wip/index/refs/heads/master + / + A---B---C refs/heads/master + \ \ + \ CW0---CW1 refs/wip/wtree/refs/heads/master + \ + BW0---BW1 refs/wip/wtree/refs/heads/master@{2} + + The fork-point of the index wip ref is not changed until some change +is being staged. Likewise just checking out a branch or creating a +commit does not change the fork-point of the working tree wip ref. The +fork-points are not adjusted until there actually is a change that +should be committed to the respective wip ref. + + +File: magit.info, Node: Legacy Wip Modes, Prev: Wip Graph, Up: Wip Modes + +8.9.2 Legacy Wip Modes +---------------------- + +It is recommended that you use the mode ‘magit-wip-mode’ (which see) and +ignore the existence of the following modes, which are preserved for +historic reasons. + + Setting the following variables directly does not take effect; either +use the Custom interface to do so or call the respective mode functions. + + -- User Option: magit-wip-after-save-mode + When this mode is enabled, then saving a buffer that visits a file + tracked in a Git repository causes its current state to be + committed to the working tree wip ref for the current branch. + + -- User Option: magit-wip-after-apply-mode + When this mode is enabled, then applying (i.e. staging, unstaging, + discarding, reversing, and regularly applying) a change to a file + tracked in a Git repository causes its current state to be + committed to the index and/or working tree wip refs for the current + branch. + + If you only ever edit files using Emacs and only ever interact with +Git using Magit, then the above two modes should be enough to protect +each and every change from accidental loss. In practice nobody does +that. Two additional modes exists that do commit to the wip refs before +making changes that could cause the loss of earlier changes. + + -- User Option: magit-wip-before-change-mode + When this mode is enabled, then certain commands commit the + existing changes to the files they are about to make changes to. + + -- User Option: magit-wip-initial-backup-mode + When this mode is enabled, then the current version of a file is + committed to the worktree wip ref before the buffer visiting that + file is saved for the first time since the buffer was created. + + This backs up the same version of the file that ‘backup-buffer’ + would save. While ‘backup-buffer’ uses a backup file, this mode + uses the same worktree wip ref as used by the other Magit Wip + modes. Like ‘backup-buffer’, it only does this once; unless you + kill the buffer and visit the file again only one backup will be + created per Emacs session. + + This mode ignores the variables that affect ‘backup-buffer’ and can + be used along-side that function, which is recommended because it + only backs up files that are tracked in a Git repository. + + -- User Option: magit-wip-after-save-local-mode-lighter + Mode-line lighter for ‘magit-wip-after-save-local-mode’. + + -- User Option: magit-wip-after-apply-mode-lighter + Mode-line lighter for ‘magit-wip-after-apply-mode’. + + -- User Option: magit-wip-before-change-mode-lighter + Mode-line lighter for ‘magit-wip-before-change-mode’. + + -- User Option: magit-wip-initial-backup-mode-lighter + Mode-line lighter for ‘magit-wip-initial-backup-mode’. + + +File: magit.info, Node: Commands for Buffers Visiting Files, Next: Minor Mode for Buffers Visiting Blobs, Prev: Wip Modes, Up: Miscellaneous + +8.10 Commands for Buffers Visiting Files +======================================== + +Magit defines a few global key bindings unless the user sets +‘magit-define-global-key-bindings’ to ‘nil’. This includes binding ‘C-c +M-g’ to ‘magit-file-dispatch’. ‘C-c g’ would be a much better binding +but the ‘C-c ’ namespace is reserved for users, meaning that +packages are not allowed to use it. If you want to use ‘C-c g’, then +you have to add that binding yourself. Also see *note Default +Bindings:: and *note (elisp)Key Binding Conventions::. + + If you want a better binding, you have to add it yourself: + + (global-set-key (kbd "C-c g") 'magit-file-dispatch) + + The key bindings shown below assume that you have not improved the +binding for ‘magit-file-dispatch’. + +‘C-c M-g’ (‘magit-file-dispatch’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + + When invoked in a buffer that does not visit a file, then it falls + back to regular ‘magit-dispatch’. + +‘C-c M-g s’ (‘magit-stage-file’) + Stage all changes to the file being visited in the current buffer. + +‘C-c M-g u’ (‘magit-unstage-file’) + Unstage all changes to the file being visited in the current + buffer. + +‘C-c M-g c’ (‘magit-commit’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. See *note Initiating a + Commit::. + +‘C-c M-g D’ (‘magit-diff’) + This transient prefix command binds several diff suffix commands + and infix arguments and displays them in a temporary buffer until a + suffix is invoked. See *note Diffing::. + + This is the same command that ‘d’ is bound to in Magit buffers. If + this command is invoked from a file-visiting buffer, then the + initial value of the option (‘--’) that limits the diff to certain + file(s) is set to the visited file. + +‘C-c M-g d’ (‘magit-diff-buffer-file’) + This command shows the diff for the file of blob that the current + buffer visits. + + -- User Option: magit-diff-buffer-file-locked + This option controls whether ‘magit-diff-buffer-file’ uses a + dedicated buffer. See *note Modes and Buffers::. + +‘C-c M-g L’ (‘magit-log’) + This transient prefix command binds several log suffix commands and + infix arguments and displays them in a temporary buffer until a + suffix is invoked. See *note Logging::. + + This is the same command that ‘l’ is bound to in Magit buffers. If + this command is invoked from a file-visiting buffer, then the + initial value of the option (‘--’) that limits the log to certain + file(s) is set to the visited file. + +‘C-c M-g l’ (‘magit-log-buffer-file’) + This command shows the log for the file of blob that the current + buffer visits. Renames are followed when a prefix argument is used + or when ‘--follow’ is an active log argument. When the region is + active, the log is restricted to the selected line range. + +‘C-c M-g t’ (‘magit-log-trace-definition’) + This command shows the log for the definition at point. + + -- User Option: magit-log-buffer-file-locked + This option controls whether ‘magit-log-buffer-file’ uses a + dedicated buffer. See *note Modes and Buffers::. + +‘C-c M-g B’ (‘magit-blame’) + This transient prefix command binds all blaming suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + For more information about this and the following commands also see + *note Blaming::. + + In addition to the ‘magit-blame’ sub-transient, the dispatch +transient also binds several blaming suffix commands directly. See +*note Blaming:: for information about those commands and bindings. + +‘C-c M-g e’ (‘magit-edit-line-commit’) + This command makes the commit editable that added the current line. + + With a prefix argument it makes the commit editable that removes + the line, if any. The commit is determined using ‘git blame’ and + made editable using ‘git rebase --interactive’ if it is reachable + from ‘HEAD’, or by checking out the commit (or a branch that points + at it) otherwise. + +‘C-c M-g p’ (‘magit-blob-previous’) + Visit the previous blob which modified the current file. + + There are a few additional commands that operate on a single file but +are not enabled in the file transient command by default: + + -- Command: magit-file-rename + This command renames a file read from the user. + + -- Command: magit-file-delete + This command deletes a file read from the user. + + -- Command: magit-file-untrack + This command untracks a file read from the user. + + -- Command: magit-file-checkout + This command updates a file in the working tree and index to the + contents from a revision. Both the revision and file are read from + the user. + + To enable them invoke the transient (‘C-c M-g’), enter "edit mode" +(‘C-x l’), set the "transient level" (‘C-x l’ again), enter ‘5’, and +leave edit mode (‘C-g’). Also see *note (transient)Enabling and +Disabling Suffixes::. + + +File: magit.info, Node: Minor Mode for Buffers Visiting Blobs, Prev: Commands for Buffers Visiting Files, Up: Miscellaneous + +8.11 Minor Mode for Buffers Visiting Blobs +========================================== + +The ‘magit-blob-mode’ enables certain Magit features in blob-visiting +buffers. Such buffers can be created using ‘magit-find-file’ and some +of the commands mentioned below, which also take care of turning on this +minor mode. Currently this mode only establishes a few key bindings, +but this might be extended. + +‘p’ (‘magit-blob-previous’) + Visit the previous blob which modified the current file. + +‘n’ (‘magit-blob-next’) + Visit the next blob which modified the current file. + +‘q’ (‘magit-kill-this-buffer’) + Kill the current buffer. + + +File: magit.info, Node: Customizing, Next: Plumbing, Prev: Miscellaneous, Up: Top + +9 Customizing +************* + +Both Git and Emacs are highly customizable. Magit is both a Git +porcelain as well as an Emacs package, so it makes sense to customize it +using both Git variables as well as Emacs options. However this +flexibility doesn’t come without problems, including but not limited to +the following. + + • Some Git variables automatically have an effect in Magit without + requiring any explicit support. Sometimes that is desirable - in + other cases, it breaks Magit. + + When a certain Git setting breaks Magit but you want to keep using + that setting on the command line, then that can be accomplished by + overriding the value for Magit only by appending something like + ‘("-c" "some.variable=compatible-value")’ to + ‘magit-git-global-arguments’. + + • Certain settings like ‘fetch.prune=true’ are respected by Magit + commands (because they simply call the respective Git command) but + their value is not reflected in the respective transient buffers. + In this case the ‘--prune’ argument in ‘magit-fetch’ might be + active or inactive, but that doesn’t keep the Git variable from + being honored by the suffix commands anyway. So pruning might + happen despite the ‘--prune’ arguments being displayed in a way + that seems to indicate that no pruning will happen. + + I intend to address these and similar issues in a future release. + +* Menu: + +* Per-Repository Configuration:: +* Essential Settings:: + + +File: magit.info, Node: Per-Repository Configuration, Next: Essential Settings, Up: Customizing + +9.1 Per-Repository Configuration +================================ + +Magit can be configured on a per-repository level using both Git +variables as well as Emacs options. + + To set a Git variable for one repository only, simply set it in +‘/path/to/repo/.git/config’ instead of ‘$HOME/.gitconfig’ or +‘/etc/gitconfig’. See *note (gitman)git-config::. + + Similarly, Emacs options can be set for one repository only by +editing ‘/path/to/repo/.dir-locals.el’. See *note (emacs)Directory +Variables::. For example to disable automatic refreshes of +file-visiting buffers in just one huge repository use this: + + • ‘/path/to/huge/repo/.dir-locals.el’ + + ((nil . ((magit-refresh-buffers . nil)))) + + It might only be costly to insert certain information into Magit +buffers for repositories that are exceptionally large, in which case you +can disable the respective section inserters just for that repository: + + • ‘/path/to/tag/invested/repo/.dir-locals.el’ + + ((magit-status-mode + . ((eval . (magit-disable-section-inserter 'magit-insert-tags-header))))) + + -- Function: magit-disable-section-inserter fn + This function disables the section inserter FN in the current + repository. It is only intended for use in ‘.dir-locals.el’ and + ‘.dir-locals-2.el’. + + If you want to apply the same settings to several, but not all, +repositories then keeping the repository-local config files in sync +would quickly become annoying. To avoid that you can create config +files for certain classes of repositories (e.g. "huge repositories") +and then include those files in the per-repository config files. For +example: + + • ‘/path/to/huge/repo/.git/config’ + + [include] + path = /path/to/huge-gitconfig + + • ‘/path/to/huge-gitconfig’ + + [status] + showUntrackedFiles = no + + • ‘$HOME/.emacs.d/init.el’ + + (dir-locals-set-class-variables 'huge-git-repository + '((nil . ((magit-refresh-buffers . nil))))) + + (dir-locals-set-directory-class + "/path/to/huge/repo/" 'huge-git-repository) + + +File: magit.info, Node: Essential Settings, Prev: Per-Repository Configuration, Up: Customizing + +9.2 Essential Settings +====================== + +The next two sections list and discuss several variables that many users +might want to customize, for safety and/or performance reasons. + +* Menu: + +* Safety:: +* Performance:: +* Default Bindings:: + + +File: magit.info, Node: Safety, Next: Performance, Up: Essential Settings + +9.2.1 Safety +------------ + +This section discusses various variables that you might want to change +(or *not* change) for safety reasons. + + Git keeps *committed* changes around long enough for users to recover +changes they have accidentally been deleted. It does not do the same +for *uncommitted* changes in the working tree and not even the index +(the staging area). Because Magit makes it so easy to modify +uncommitted changes, it also makes it easy to shoot yourself in the foot +in the process. For that reason Magit provides three global modes that +save *tracked* files to work-in-progress references after or before +certain actions. See *note Wip Modes::. + + These modes are not enabled by default because of performance +concerns. Instead a lot of potentially destructive commands require +confirmation every time they are used. In many cases this can be +disabled by adding a symbol to ‘magit-no-confirm’ (see *note Completion +and Confirmation::). If you enable the various wip modes then you +should add ‘safe-with-wip’ to this list. + + Similarly it isn’t necessary to require confirmation before moving a +file to the system trash - if you trashed a file by mistake then you can +recover it from there. Option ‘magit-delete-by-moving-to-trash’ +controls whether the system trash is used, which is the case by default. +Nevertheless, ‘trash’ isn’t a member of ‘magit-no-confirm’ - you might +want to change that. + + By default buffers visiting files are automatically reverted when the +visited file changes on disk. This isn’t as risky as it might seem, but +to make an informed decision you should see *note Risk of Reverting +Automatically::. + + +File: magit.info, Node: Performance, Next: Default Bindings, Prev: Safety, Up: Essential Settings + +9.2.2 Performance +----------------- + +After Magit has run ‘git’ for side-effects, it also refreshes the +current Magit buffer and the respective status buffer. This is +necessary because otherwise outdated information might be displayed +without the user noticing. Magit buffers are updated by recreating +their content from scratch, which makes updating simpler and less +error-prone, but also more costly. Keeping it simple and just +re-creating everything from scratch is an old design decision and +departing from that will require major refactoring. + + I plan to do that in time for the next major release. I also intend +to create logs and diffs asynchronously, which should also help a lot +but also requires major refactoring. + + Meanwhile you can tell Magit to only automatically refresh the +current Magit buffer, but not the status buffer. If you do that, then +the status buffer is only refreshed automatically if it is the current +buffer. + + (setq magit-refresh-status-buffer nil) + + You should also check whether any third-party packages have added +anything to ‘magit-refresh-buffer-hook’, ‘magit-status-refresh-hook’, +‘magit-pre-refresh-hook’, and ‘magit-post-refresh-hook’. If so, then +check whether those additions impact performance significantly. + + Magit can be told to refresh buffers verbosely using ‘M-x +magit-toggle-verbose-refresh’. Enabling this helps figuring out which +sections are bottlenecks. The additional output can be found in the +‘*Messages*’ buffer. + + Magit also reverts buffers for visited files located inside the +current repository when the visited file changes on disk. That is +implemented on top of ‘auto-revert-mode’ from the built-in library +‘autorevert’. To figure out whether that impacts performance, check +whether performance is significantly worse, when many buffers exist +and/or when some buffers visit files using TRAMP. If so, then this +should help. + + (setq auto-revert-buffer-list-filter + 'magit-auto-revert-repository-buffer-p) + + For alternative approaches see *note Automatic Reverting of +File-Visiting Buffers::. + + If you have enabled any features that are disabled by default, then +you should check whether they impact performance significantly. It’s +likely that they were not enabled by default because it is known that +they reduce performance at least in large repositories. + + If performance is only slow inside certain unusually large +repositories, then you might want to disable certain features on a +per-repository or per-repository-class basis only. See *note +Per-Repository Configuration::. For example it takes a long time to +determine the next and current tag in repository with exceptional +numbers of tags. It would therefore be a good idea to disable +‘magit-insert-tags-headers’, as explained at the mentioned node. + +* Menu: + +* Microsoft Windows Performance:: +* MacOS Performance:: + +Log Performance +............... + +When showing logs, Magit limits the number of commits initially shown in +the hope that this avoids unnecessary work. When using ‘--graph’ is +used, then this unfortunately does not have the desired effect for large +histories. Junio, Git’s maintainer, said on the git mailing list +(): "‘--graph’ wants to +compute the whole history and the max-count only affects the output +phase after ‘--graph’ does its computation". + + In other words, it’s not that Git is slow at outputting the +differences, or that Magit is slow at parsing the output - the problem +is that Git first goes outside and has a smoke. + + We actually work around this issue by limiting the number of commits +not only by using ‘-’ but by also using a range. But unfortunately +that’s not always possible. + + When more than a few thousand commits are shown, then the use of +‘--graph’ can slow things down. + + Using ‘--color --graph’ is even slower. Magit uses code that is part +of Emacs to turn control characters into faces. That code is pretty +slow and this is quite noticeable when showing a log with many branches +and merges. For that reason ‘--color’ is not enabled by default +anymore. Consider leaving it at that. + +Diff Performance +................ + +If diffs are slow, then consider turning off some optional diff features +by setting all or some of the following variables to ‘nil’: +‘magit-diff-highlight-indentation’, ‘magit-diff-highlight-trailing’, +‘magit-diff-paint-whitespace’, ‘magit-diff-highlight-hunk-body’, and +‘magit-diff-refine-hunk’. + + When showing a commit instead of some arbitrary diff, then some +additional information is displayed. Calculating this information can +be quite expensive given certain circumstances. If looking at a commit +using ‘magit-revision-mode’ takes considerably more time than looking at +the same commit in ‘magit-diff-mode’, then consider setting +‘magit-revision-insert-related-refs’ to ‘nil’. + + When you are often confronted with diffs that contain deleted files, +then you might want to enable the ‘--irreversible-delete’ argument. If +you do that then diffs still show that a file was deleted but without +also showing the complete deleted content of the file. This argument is +not available by default, see *note (transient)Enabling and Disabling +Suffixes::. Once you have done that you should enable it and save that +setting, see *note (transient)Saving Values::. You should do this in +both the diff (‘d’) and the diff refresh (‘D’) transient popups. + +Refs Buffer Performance +....................... + +When refreshing the "references buffer" is slow, then that’s usually +because several hundred refs are being displayed. The best way to +address that is to display fewer refs, obviously. + + If you are not, or only mildly, interested in seeing the list of +tags, then start by not displaying them: + + (remove-hook 'magit-refs-sections-hook 'magit-insert-tags) + + Then you should also make sure that the listed remote branches +actually all exist. You can do so by pruning branches which no longer +exist using ‘f-pa’. + +Committing Performance +...................... + +When you initiate a commit, then Magit by default automatically shows a +diff of the changes you are about to commit. For large commits this can +take a long time, which is especially distracting when you are +committing large amounts of generated data which you don’t actually +intend to inspect before committing. This behavior can be turned off +using: + + (remove-hook 'server-switch-hook 'magit-commit-diff) + + Then you can type ‘C-c C-d’ to show the diff when you actually want +to see it, but only then. Alternatively you can leave the hook alone +and just type ‘C-g’ in those cases when it takes too long to generate +the diff. If you do that, then you will end up with a broken diff +buffer, but doing it this way has the advantage that you usually get to +see the diff, which is useful because it increases the odds that you +spot potential issues. + + +File: magit.info, Node: Microsoft Windows Performance, Next: MacOS Performance, Up: Performance + +Microsoft Windows Performance +............................. + +In order to update the status buffer, ‘git’ has to be run a few dozen +times. That is problematic on Microsoft Windows, because that operating +system is exceptionally slow at starting processes. Sadly this is an +issue that can only be fixed by Microsoft itself, and they don’t appear +to be particularly interested in doing so. + + Beside the subprocess issue, there are also other Windows-specific +performance issues. Some of these have workarounds. The maintainers of +"Git for Windows" try to improve performance on Windows. Always use the +latest release in order to benefit from the latest performance tweaks. +Magit too tries to work around some Windows-specific issues. + + According to some sources, setting the following Git variables can +also help. + + git config --global core.preloadindex true # default since v2.1 + git config --global core.fscache true # default since v2.8 + git config --global gc.auto 256 + + You should also check whether an anti-virus program is affecting +performance. + + +File: magit.info, Node: MacOS Performance, Prev: Microsoft Windows Performance, Up: Performance + +MacOS Performance +................. + +Before Emacs 26.1 child processes were created using ‘fork’ on macOS. +That needlessly copied GUI resources, which is expensive. The result +was that forking took about 30 times as long on Darwin than on Linux, +and because Magit starts many ‘git’ processes that made quite a +difference. + + So make sure that you are using at least Emacs 26.1, in which case +the faster ‘vfork’ will be used. (The creation of child processes still +takes about twice as long on Darwin compared to Linux.) See (1) for +more information. + + ---------- Footnotes ---------- + + (1) + + + +File: magit.info, Node: Default Bindings, Prev: Performance, Up: Essential Settings + +9.2.3 Default Bindings +---------------------- + + -- User Option: magit-define-global-key-bindings + This option controls whether some Magit commands are automatically + bound in the global keymap even before Magit is used for the first + time in the current session. + + If this variable is non-nil, which it is by default, then the + following bindings may be added to the global keymap. + + ‘C-x g’ ‘magit-status’ + ‘C-x M-g’ ‘magit-dispatch’ + ‘C-c M-g’ ‘magit-file-dispatch’ + + These bindings may be added when ‘after-init-hook’ is run. Each + binding is added if and only if at that time no other key is bound + to the same command and no other command is bound to the same key. + In other words we try to avoid adding bindings that are + unnecessary, as well as bindings that conflict with other bindings. + + Adding the above bindings is delayed until ‘after-init-hook’ is + called to allow users to set the variable anywhere in their init + file (without having to make sure to do so before ‘magit’ is loaded + or autoloaded) and to increase the likelihood that all the + potentially conflicting user bindings have already been added. + + To set this variable use either ‘setq’ or the Custom interface. Do + not use the function ‘customize-set-variable’ because doing that + would cause Magit to be loaded immediately when that form is + evaluated (this differs from ‘custom-set-variables’, which doesn’t + load the libraries that define the customized variables). + + Setting this variable to nil has no effect if that is done after + the key bindings have already been added. + + We recommend that you bind ‘C-c g’ instead of ‘C-c M-g’ to + ‘magit-file-dispatch’. The former is a much better binding but the + ‘C-c ’ namespace is strictly reserved for users; preventing + Magit from using it by default. + + (global-set-key (kbd "C-c g") 'magit-file-dispatch) + + Also see *note Commands for Buffers Visiting Files:: and *note + (elisp)Key Binding Conventions::. + + +File: magit.info, Node: Plumbing, Next: FAQ, Prev: Customizing, Up: Top + +10 Plumbing +*********** + +The following sections describe how to use several of Magit’s core +abstractions to extend Magit itself or implement a separate extension. + + A few of the low-level features used by Magit have been factored out +into separate libraries/packages, so that they can be used by other +packages, without having to depend on Magit. See *note +(with-editor)Top:: for information about ‘with-editor’. ‘transient’ +doesn’t have a manual yet. + + If you are trying to find an unused key that you can bind to a +command provided by your own Magit extension, then checkout +. + +* Menu: + +* Calling Git:: +* Section Plumbing:: +* Refreshing Buffers:: +* Conventions:: + + +File: magit.info, Node: Calling Git, Next: Section Plumbing, Up: Plumbing + +10.1 Calling Git +================ + +Magit provides many specialized functions for calling Git. All of these +functions are defined in either ‘magit-git.el’ or ‘magit-process.el’ and +have one of the prefixes ‘magit-run-’, ‘magit-call-’, ‘magit-start-’, or +‘magit-git-’ (which is also used for other things). + + All of these functions accept an indefinite number of arguments, +which are strings that specify command line arguments for Git (or in +some cases an arbitrary executable). These arguments are flattened +before being passed on to the executable; so instead of strings they can +also be lists of strings and arguments that are ‘nil’ are silently +dropped. Some of these functions also require a single mandatory +argument before these command line arguments. + + Roughly speaking, these functions run Git either to get some value or +for side-effects. The functions that return a value are useful to +collect the information necessary to populate a Magit buffer, while the +others are used to implement Magit commands. + + The functions in the value-only group always run synchronously, and +they never trigger a refresh. The function in the side-effect group can +be further divided into subgroups depending on whether they run Git +synchronously or asynchronously, and depending on whether they trigger a +refresh when the executable has finished. + +* Menu: + +* Getting a Value from Git:: +* Calling Git for Effect:: + + +File: magit.info, Node: Getting a Value from Git, Next: Calling Git for Effect, Up: Calling Git + +10.1.1 Getting a Value from Git +------------------------------- + +These functions run Git in order to get a value, an exit status, or +output. Of course you could also use them to run Git commands that have +side-effects, but that should be avoided. + + -- Function: magit-git-exit-code &rest args + Executes git with ARGS and returns its exit code. + + -- Function: magit-git-success &rest args + Executes git with ARGS and returns ‘t’ if the exit code is ‘0’, + ‘nil’ otherwise. + + -- Function: magit-git-failure &rest args + Executes git with ARGS and returns ‘t’ if the exit code is ‘1’, + ‘nil’ otherwise. + + -- Function: magit-git-true &rest args + Executes git with ARGS and returns ‘t’ if the first line printed by + git is the string "true", ‘nil’ otherwise. + + -- Function: magit-git-false &rest args + Executes git with ARGS and returns ‘t’ if the first line printed by + git is the string "false", ‘nil’ otherwise. + + -- Function: magit-git-insert &rest args + Executes git with ARGS and inserts its output at point. + + -- Function: magit-git-string &rest args + Executes git with ARGS and returns the first line of its output. + If there is no output or if it begins with a newline character, + then this returns ‘nil’. + + -- Function: magit-git-lines &rest args + Executes git with ARGS and returns its output as a list of lines. + Empty lines anywhere in the output are omitted. + + -- Function: magit-git-items &rest args + Executes git with ARGS and returns its null-separated output as a + list. Empty items anywhere in the output are omitted. + + If the value of option ‘magit-git-debug’ is non-nil and git exits + with a non-zero exit status, then warn about that in the echo area + and add a section containing git’s standard error in the current + repository’s process buffer. + + -- Function: magit-process-git destination &rest args + Calls Git synchronously in a separate process, returning its exit + code. DESTINATION specifies how to handle the output, like for + ‘call-process’, except that file handlers are supported. Enables + Cygwin’s "noglob" option during the call and ensures unix eol + conversion. + + -- Function: magit-process-file process &optional infile buffer display + &rest args + Processes files synchronously in a separate process. Identical to + ‘process-file’ but temporarily enables Cygwin’s "noglob" option + during the call and ensures unix eol conversion. + + If an error occurs when using one of the above functions, then that +is usually due to a bug, i.e. using an argument which is not actually +supported. Such errors are usually not reported, but when they occur we +need to be able to debug them. + + -- User Option: magit-git-debug + Whether to report errors that occur when using ‘magit-git-insert’, + ‘magit-git-string’, ‘magit-git-lines’, or ‘magit-git-items’. This + does not actually raise an error. Instead a message is shown in + the echo area, and git’s standard error is insert into a new + section in the current repository’s process buffer. + + -- Function: magit-git-str &rest args + This is a variant of ‘magit-git-string’ that ignores the option + ‘magit-git-debug’. It is mainly intended to be used while handling + errors in functions that do respect that option. Using such a + function while handing an error could cause yet another error and + therefore lead to an infinite recursion. You probably won’t ever + need to use this function. + + +File: magit.info, Node: Calling Git for Effect, Prev: Getting a Value from Git, Up: Calling Git + +10.1.2 Calling Git for Effect +----------------------------- + +These functions are used to run git to produce some effect. Most Magit +commands that actually run git do so by using such a function. + + Because we do not need to consume git’s output when using these +functions, their output is instead logged into a per-repository buffer, +which can be shown using ‘$’ from a Magit buffer or ‘M-x magit-process’ +elsewhere. + + These functions can have an effect in two distinct ways. Firstly, +running git may change something, i.e. create or push a new commit. +Secondly, that change may require that Magit buffers are refreshed to +reflect the changed state of the repository. But refreshing isn’t +always desirable, so only some of these functions do perform such a +refresh after git has returned. + + Sometimes it is useful to run git asynchronously. For example, when +the user has just initiated a push, then there is no reason to make her +wait until that has completed. In other cases it makes sense to wait +for git to complete before letting the user do something else. For +example after staging a change it is useful to wait until after the +refresh because that also automatically moves to the next change. + + -- Function: magit-call-git &rest args + Calls git synchronously with ARGS. + + -- Function: magit-call-process program &rest args + Calls PROGRAM synchronously with ARGS. + + -- Function: magit-run-git &rest args + Calls git synchronously with ARGS and then refreshes. + + -- Function: magit-run-git-with-input &rest args + Calls git synchronously with ARGS and sends it the content of the + current buffer on standard input. + + If the current buffer’s ‘default-directory’ is on a remote + filesystem, this function actually runs git asynchronously. But + then it waits for the process to return, so the function itself is + synchronous. + + -- Function: magit-git &rest args + Calls git synchronously with ARGS for side-effects only. This + function does not refresh the buffer. + + -- Function: magit-git-wash washer &rest args + Execute Git with ARGS, inserting washed output at point. Actually + first insert the raw output at point. If there is no output call + ‘magit-cancel-section’. Otherwise temporarily narrow the buffer to + the inserted text, move to its beginning, and then call function + WASHER with ARGS as its sole argument. + + And now for the asynchronous variants. + + -- Function: magit-run-git-async &rest args + Start Git, prepare for refresh, and return the process object. + ARGS is flattened and then used as arguments to Git. + + Display the command line arguments in the echo area. + + After Git returns some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + Unmodified buffers visiting files that are tracked in the current + repository are reverted if ‘magit-revert-buffers’ is non-nil. + + -- Function: magit-run-git-with-editor &rest args + Export GIT_EDITOR and start Git. Also prepare for refresh and + return the process object. ARGS is flattened and then used as + arguments to Git. + + Display the command line arguments in the echo area. + + After Git returns some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + + -- Function: magit-start-git input &rest args + Start Git, prepare for refresh, and return the process object. + + If INPUT is non-nil, it has to be a buffer or the name of an + existing buffer. The buffer content becomes the processes standard + input. + + Option ‘magit-git-executable’ specifies the Git executable and + option ‘magit-git-global-arguments’ specifies constant arguments. + The remaining arguments ARGS specify arguments to Git. They are + flattened before use. + + After Git returns, some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + Unmodified buffers visiting files that are tracked in the current + repository are reverted if ‘magit-revert-buffers’ is non-nil. + + -- Function: magit-start-process &rest args + Start PROGRAM, prepare for refresh, and return the process object. + + If optional argument INPUT is non-nil, it has to be a buffer or the + name of an existing buffer. The buffer content becomes the + processes standard input. + + The process is started using ‘start-file-process’ and then setup to + use the sentinel ‘magit-process-sentinel’ and the filter + ‘magit-process-filter’. Information required by these functions is + stored in the process object. When this function returns the + process has not started to run yet so it is possible to override + the sentinel and filter. + + After the process returns, ‘magit-process-sentinel’ refreshes the + buffer that was current when ‘magit-start-process’ was called (if + it is a Magit buffer and still alive), as well as the respective + Magit status buffer. Unmodified buffers visiting files that are + tracked in the current repository are reverted if + ‘magit-revert-buffers’ is non-nil. + + -- Variable: magit-this-process + The child process which is about to start. This can be used to + change the filter and sentinel. + + -- Variable: magit-process-raise-error + When this is non-nil, then ‘magit-process-sentinel’ raises an error + if git exits with a non-zero exit status. For debugging purposes. + + +File: magit.info, Node: Section Plumbing, Next: Refreshing Buffers, Prev: Calling Git, Up: Plumbing + +10.2 Section Plumbing +===================== + +* Menu: + +* Creating Sections:: +* Section Selection:: +* Matching Sections:: + + +File: magit.info, Node: Creating Sections, Next: Section Selection, Up: Section Plumbing + +10.2.1 Creating Sections +------------------------ + + -- Macro: magit-insert-section &rest args + Insert a section at point. + + TYPE is the section type, a symbol. Many commands that act on the + current section behave differently depending on that type. Also if + a variable ‘magit-TYPE-section-map’ exists, then use that as the + text-property ‘keymap’ of all text belonging to the section (but + this may be overwritten in subsections). TYPE can also have the + form ‘(eval FORM)’ in which case FORM is evaluated at runtime. + + Optional VALUE is the value of the section, usually a string that + is required when acting on the section. + + When optional HIDE is non-nil collapse the section body by default, + i.e. when first creating the section, but not when refreshing the + buffer. Otherwise, expand it by default. This can be overwritten + using ‘magit-section-set-visibility-hook’. When a section is + recreated during a refresh, then the visibility of predecessor is + inherited and HIDE is ignored (but the hook is still honored). + + BODY is any number of forms that actually insert the section’s + heading and body. Optional NAME, if specified, has to be a symbol, + which is then bound to the struct of the section being inserted. + + Before BODY is evaluated the ‘start’ of the section object is set + to the value of ‘point’ and after BODY was evaluated its ‘end’ is + set to the new value of ‘point’; BODY is responsible for moving + ‘point’ forward. + + If it turns out inside BODY that the section is empty, then + ‘magit-cancel-section’ can be used to abort and remove all traces + of the partially inserted section. This can happen when creating a + section by washing Git’s output and Git didn’t actually output + anything this time around. + + -- Function: magit-insert-heading &rest args + Insert the heading for the section currently being inserted. + + This function should only be used inside ‘magit-insert-section’. + + When called without any arguments, then just set the ‘content’ slot + of the object representing the section being inserted to a marker + at ‘point’. The section should only contain a single line when + this function is used like this. + + When called with arguments ARGS, which have to be strings, then + insert those strings at point. The section should not contain any + text before this happens and afterwards it should again only + contain a single line. If the ‘face’ property is set anywhere + inside any of these strings, then insert all of them unchanged. + Otherwise use the ‘magit-section-heading’ face for all inserted + text. + + The ‘content’ property of the section struct is the end of the + heading (which lasts from ‘start’ to ‘content’) and the beginning + of the body (which lasts from ‘content’ to ‘end’). If the value of + ‘content’ is nil, then the section has no heading and its body + cannot be collapsed. If a section does have a heading then its + height must be exactly one line, including a trailing newline + character. This isn’t enforced; you are responsible for getting it + right. The only exception is that this function does insert a + newline character if necessary. + + -- Function: magit-cancel-section + Cancel the section currently being inserted. This exits the + innermost call to ‘magit-insert-section’ and removes all traces of + what has already happened inside that call. + + -- Function: magit-define-section-jumper sym title &optional value + Define an interactive function to go to section SYM. TITLE is the + displayed title of the section. + + +File: magit.info, Node: Section Selection, Next: Matching Sections, Prev: Creating Sections, Up: Section Plumbing + +10.2.2 Section Selection +------------------------ + + -- Function: magit-current-section + Return the section at point. + + -- Function: magit-region-sections &optional condition multiple + Return a list of the selected sections. + + When the region is active and constitutes a valid section + selection, then return a list of all selected sections. This is + the case when the region begins in the heading of a section and + ends in the heading of the same section or in that of a sibling + section. If optional MULTIPLE is non-nil, then the region cannot + begin and end in the same section. + + When the selection is not valid, then return nil. In this case, + most commands that can act on the selected sections will instead + act on the section at point. + + When the region looks like it would in any other buffer then the + selection is invalid. When the selection is valid then the region + uses the ‘magit-section-highlight’ face. This does not apply to + diffs where things get a bit more complicated, but even here if the + region looks like it usually does, then that’s not a valid + selection as far as this function is concerned. + + If optional CONDITION is non-nil, then the selection not only has + to be valid; all selected sections additionally have to match + CONDITION, or nil is returned. See ‘magit-section-match’ for the + forms CONDITION can take. + + -- Function: magit-region-values &optional condition multiple + Return a list of the values of the selected sections. + + Return the values that themselves would be returned by + ‘magit-region-sections’ (which see). + + +File: magit.info, Node: Matching Sections, Prev: Section Selection, Up: Section Plumbing + +10.2.3 Matching Sections +------------------------ + +‘M-x magit-describe-section-briefly’ + Show information about the section at point. This command is + intended for debugging purposes. + + -- Function: magit-section-ident section + Return an unique identifier for SECTION. The return value has the + form ‘((TYPE . VALUE)...)’. + + -- Function: magit-get-section ident &optional root + Return the section identified by IDENT. IDENT has to be a list as + returned by ‘magit-section-ident’. + + -- Function: magit-section-match condition &optional section + Return ‘t’ if SECTION matches CONDITION. SECTION defaults to the + section at point. If SECTION is not specified and there also is no + section at point, then return ‘nil’. + + CONDITION can take the following forms: + • ‘(CONDITION...)’ + + matches if any of the CONDITIONs matches. + + • ‘[CLASS...]’ + + matches if the section’s class is the same as the first CLASS + or a subclass of that; the section’s parent class matches the + second CLASS; and so on. + + • ‘[* CLASS...]’ + + matches sections that match ‘[CLASS...]’ and also recursively + all their child sections. + + • ‘CLASS’ + + matches if the section’s class is the same as CLASS or a + subclass of that; regardless of the classes of the parent + sections. + + Each CLASS should be a class symbol, identifying a class that + derives from ‘magit-section’. For backward compatibility CLASS can + also be a "type symbol". A section matches such a symbol if the + value of its ‘type’ slot is ‘eq’. If a type symbol has an entry in + ‘magit--section-type-alist’, then a section also matches that type + if its class is a subclass of the class that corresponds to the + type as per that alist. + + Note that it is not necessary to specify the complete section + lineage as printed by ‘magit-describe-section-briefly’, unless of + course you want to be that precise. + + -- Function: magit-section-value-if condition &optional section + If the section at point matches CONDITION, then return its value. + + If optional SECTION is non-nil then test whether that matches + instead. If there is no section at point and SECTION is nil, then + return nil. If the section does not match, then return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + + -- Function: magit-section-case &rest clauses + Choose among clauses on the type of the section at point. + + Each clause looks like (CONDITION BODY...). The type of the + section is compared against each CONDITION; the BODY forms of the + first match are evaluated sequentially and the value of the last + form is returned. Inside BODY the symbol ‘it’ is bound to the + section at point. If no clause succeeds or if there is no section + at point return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + Additionally a CONDITION of t is allowed in the final clause and + matches if no other CONDITION match, even if there is no section at + point. + + -- Variable: magit-root-section + The root section in the current buffer. All other sections are + descendants of this section. The value of this variable is set by + ‘magit-insert-section’ and you should never modify it. + + For diff related sections a few additional tools exist. + + -- Function: magit-diff-type &optional section + Return the diff type of SECTION. + + The returned type is one of the symbols ‘staged’, ‘unstaged’, + ‘committed’, or ‘undefined’. This type serves a similar purpose as + the general type common to all sections (which is stored in the + ‘type’ slot of the corresponding ‘magit-section’ struct) but takes + additional information into account. When the SECTION isn’t + related to diffs and the buffer containing it also isn’t a + diff-only buffer, then return nil. + + Currently the type can also be one of ‘tracked’ and ‘untracked’, + but these values are not handled explicitly in every place they + should be. A possible fix could be to just return nil here. + + The section has to be a ‘diff’ or ‘hunk’ section, or a section + whose children are of type ‘diff’. If optional SECTION is nil, + return the diff type for the current section. In buffers whose + major mode is ‘magit-diff-mode’ SECTION is ignored and the type is + determined using other means. In ‘magit-revision-mode’ buffers the + type is always ‘committed’. + + -- Function: magit-diff-scope &optional section strict + Return the diff scope of SECTION or the selected section(s). + + A diff’s "scope" describes what part of a diff is selected, it is a + symbol, one of ‘region’, ‘hunk’, ‘hunks’, ‘file’, ‘files’, or + ‘list’. Do not confuse this with the diff "type", as returned by + ‘magit-diff-type’. + + If optional SECTION is non-nil, then return the scope of that, + ignoring the sections selected by the region. Otherwise return the + scope of the current section, or if the region is active and + selects a valid group of diff related sections, the type of these + sections, i.e. ‘hunks’ or ‘files’. If SECTION (or if the current + section that is nil) is a ‘hunk’ section and the region starts and + ends inside the body of a that section, then the type is ‘region’. + + If optional STRICT is non-nil then return nil if the diff type of + the section at point is ‘untracked’ or the section at point is not + actually a ‘diff’ but a ‘diffstat’ section. + + +File: magit.info, Node: Refreshing Buffers, Next: Conventions, Prev: Section Plumbing, Up: Plumbing + +10.3 Refreshing Buffers +======================= + +All commands that create a new Magit buffer or change what is being +displayed in an existing buffer do so by calling ‘magit-mode-setup’. +Among other things, that function sets the buffer local values of +‘default-directory’ (to the top-level of the repository), +‘magit-refresh-function’, and ‘magit-refresh-args’. + + Buffers are refreshed by calling the function that is the local value +of ‘magit-refresh-function’ (a function named ‘magit-*-refresh-buffer’, +where ‘*’ may be something like ‘diff’) with the value of +‘magit-refresh-args’ as arguments. + + -- Macro: magit-mode-setup buffer switch-func mode refresh-func + &optional refresh-args + This function displays and selects BUFFER, turns on MODE, and + refreshes a first time. + + This function displays and optionally selects BUFFER by calling + ‘magit-mode-display-buffer’ with BUFFER, MODE and SWITCH-FUNC as + arguments. Then it sets the local value of + ‘magit-refresh-function’ to REFRESH-FUNC and that of + ‘magit-refresh-args’ to REFRESH-ARGS. Finally it creates the + buffer content by calling REFRESH-FUNC with REFRESH-ARGS as + arguments. + + All arguments are evaluated before switching to BUFFER. + + -- Function: magit-mode-display-buffer buffer mode &optional + switch-function + This function display BUFFER in some window and select it. BUFFER + may be a buffer or a string, the name of a buffer. The buffer is + returned. + + Unless BUFFER is already displayed in the selected frame, store the + previous window configuration as a buffer local value, so that it + can later be restored by ‘magit-mode-bury-buffer’. + + The buffer is displayed and selected using SWITCH-FUNCTION. If + that is ‘nil’ then ‘pop-to-buffer’ is used if the current buffer’s + major mode derives from ‘magit-mode’. Otherwise ‘switch-to-buffer’ + is used. + + -- Variable: magit-refresh-function + The value of this buffer-local variable is the function used to + refresh the current buffer. It is called with ‘magit-refresh-args’ + as arguments. + + -- Variable: magit-refresh-args + The list of arguments used by ‘magit-refresh-function’ to refresh + the current buffer. ‘magit-refresh-function’ is called with these + arguments. + + The value is usually set using ‘magit-mode-setup’, but in some + cases it’s also useful to provide commands that can change the + value. For example, the ‘magit-diff-refresh’ transient can be used + to change any of the arguments used to display the diff, without + having to specify again which differences should be shown, but + ‘magit-diff-more-context’, ‘magit-diff-less-context’ and + ‘magit-diff-default-context’ change just the ‘-U’ argument. In + both case this is done by changing the value of this variable and + then calling this ‘magit-refresh-function’. + + +File: magit.info, Node: Conventions, Prev: Refreshing Buffers, Up: Plumbing + +10.4 Conventions +================ + +Also see *note Completion and Confirmation::. + +* Menu: + +* Theming Faces:: + + +File: magit.info, Node: Theming Faces, Up: Conventions + +10.4.1 Theming Faces +-------------------- + +The default theme uses blue for local branches, green for remote +branches, and goldenrod (brownish yellow) for tags. When creating a new +theme, you should probably follow that example. If your theme already +uses other colors, then stick to that. + + In older releases these reference faces used to have a background +color and a box around them. The basic default faces no longer do so, +to make Magit buffers much less noisy, and you should follow that +example at least with regards to boxes. (Boxes were used in the past to +work around a conflict between the highlighting overlay and text +property backgrounds. That’s no longer necessary because highlighting +no longer causes other background colors to disappear.) Alternatively +you can keep the background color and/or box, but then have to take +special care to adjust ‘magit-branch-current’ accordingly. By default +it looks mostly like ‘magit-branch-local’, but with a box (by default +the former is the only face that uses a box, exactly so that it sticks +out). If the former also uses a box, then you have to make sure that it +differs in some other way from the latter. + + The most difficult faces to theme are those related to diffs, +headings, highlighting, and the region. There are faces that fall into +all four groups - expect to spend some time getting this right. + + The ‘region’ face in the default theme, in both the light and dark +variants, as well as in many other themes, distributed with Emacs or by +third-parties, is very ugly. It is common to use a background color +that really sticks out, which is ugly but if that were the only problem +then it would be acceptable. Unfortunately many themes also set the +foreground color, which ensures that all text within the region is +readable. Without doing that there might be cases where some foreground +color is too close to the region background color to still be readable. +But it also means that text within the region loses all syntax +highlighting. + + I consider the work that went into getting the ‘region’ face right to +be a good indicator for the general quality of a theme. My +recommendation for the ‘region’ face is this: use a background color +slightly different from the background color of the ‘default’ face, and +do not set the foreground color at all. So for a light theme you might +use a light (possibly tinted) gray as the background color of ‘default’ +and a somewhat darker gray for the background of ‘region’. That should +usually be enough to not collide with the foreground color of any other +face. But if some other faces also set a light gray as background +color, then you should also make sure it doesn’t collide with those (in +some cases it might be acceptable though). + + Magit only uses the ‘region’ face when the region is "invalid" by its +own definition. In a Magit buffer the region is used to either select +multiple sibling sections, so that commands which support it act on all +of these sections instead of just the current section, or to select +lines within a single hunk section. In all other cases, the section is +considered invalid and Magit won’t act on it. But such invalid sections +happen, either because the user has not moved point enough yet to make +it valid or because she wants to use a non-magit command to act on the +region, e.g. ‘kill-region’. + + So using the regular ‘region’ face for invalid sections is a feature. +It tells the user that Magit won’t be able to act on it. It’s +acceptable if that face looks a bit odd and even (but less so) if it +collides with the background colors of section headings and other things +that have a background color. + + Magit highlights the current section. If a section has subsections, +then all of them are highlighted. This is done using faces that have +"highlight" in their names. For most sections, +‘magit-section-highlight’ is used for both the body and the heading. +Like the ‘region’ face, it should only set the background color to +something similar to that of ‘default’. The highlight background color +must be different from both the ‘region’ background color and the +‘default’ background color. + + For diff related sections Magit uses various faces to highlight +different parts of the selected section(s). Note that hunk headings, +unlike all other section headings, by default have a background color, +because it is useful to have very visible separators between hunks. +That face ‘magit-diff-hunk-heading’, should be different from both +‘magit-diff-hunk-heading-highlight’ and ‘magit-section-highlight’, as +well as from ‘magit-diff-context’ and ‘magit-diff-context-highlight’. +By default we do that by changing the foreground color. Changing the +background color would lead to complications, and there are already +enough we cannot get around. (Also note that it is generally a good +idea for section headings to always be bold, but only for sections that +have subsections). + + When there is a valid region selecting diff-related sibling sections, +i.e. multiple files or hunks, then the bodies of all these sections use +the respective highlight faces, but additionally the headings instead +use one of the faces ‘magit-diff-file-heading-selection’ or +‘magit-diff-hunk-heading-selection’. These faces have to be different +from the regular highlight variants to provide explicit visual +indication that the region is active. + + When theming diff related faces, start by setting the option +‘magit-diff-refine-hunk’ to ‘all’. You might personally prefer to only +refine the current hunk or not use hunk refinement at all, but some of +the users of your theme want all hunks to be refined, so you have to +cater to that. + + (Also turn on ‘magit-diff-highlight-indentation’, +‘magit-diff-highlight-trailing’, and ‘magit-diff-paint-whitespace’; and +insert some whitespace errors into the code you use for testing.) + + For added lines you have to adjust three faces: ‘magit-diff-added’, +‘magit-diff-added-highlight’, and ‘diff-refined-added’. Make sure that +the latter works well with both of the former, as well as ‘smerge-other’ +and ‘diff-added’. Then do the same for the removed lines, context +lines, lines added by us, and lines added by them. Also make sure the +respective added, removed, and context faces use approximately the same +saturation for both the highlighted and unhighlighted variants. Also +make sure the file and diff headings work nicely with context lines +(e.g. make them look different). Line faces should set both the +foreground and the background color. For example, for added lines use +two different greens. + + It’s best if the foreground color of both the highlighted and the +unhighlighted variants are the same, so you will need to have to find a +color that works well on the highlight and unhighlighted background, the +refine background, and the highlight context background. When there is +an hunk internal region, then the added- and removed-lines background +color is used only within that region. Outside the region the +highlighted context background color is used. This makes it easier to +see what is being staged. With an hunk internal region the hunk heading +is shown using ‘magit-diff-hunk-heading-selection’, and so are the thin +lines that are added around the lines that fall within the region. The +background color of that has to be distinct enough from the various +other involved background colors. + + Nobody said this would be easy. If your theme restricts itself to a +certain set of colors, then you should make an exception here. +Otherwise it would be impossible to make the diffs look good in each and +every variation. Actually you might want to just stick to the default +definitions for these faces. You have been warned. Also please note +that if you do not get this right, this will in some cases look to users +like bugs in Magit - so please do it right or not at all. + + +File: magit.info, Node: FAQ, Next: Debugging Tools, Prev: Plumbing, Up: Top + +Appendix A FAQ +************** + +The next two nodes lists frequently asked questions. For a list of +frequently *and recently* asked questions, i.e. questions that haven’t +made it into the manual yet, see +. + + Please also see *note Debugging Tools::. + +* Menu: + +* FAQ - How to ...?:: +* FAQ - Issues and Errors:: + + +File: magit.info, Node: FAQ - How to ...?, Next: FAQ - Issues and Errors, Up: FAQ + +A.1 FAQ - How to ...? +===================== + +* Menu: + +* How to pronounce Magit?:: +* How to show git's output?:: +* How to install the gitman info manual?:: +* How to show diffs for gpg-encrypted files?:: +* How does branching and pushing work?:: +* Should I disable VC?:: + + +File: magit.info, Node: How to pronounce Magit?, Next: How to show git's output?, Up: FAQ - How to ...? + +A.1.1 How to pronounce Magit? +----------------------------- + +Either ‘mu[m's] git’ or ‘magi{c => t}’ is fine. + + The slogan is "It’s Magit! The magical Git client", so it makes +sense to pronounce Magit like magic, while taking into account that C +and T do not sound the same. + + The German "Magie" is not pronounced the same as the English "magic", +so if you speak German then you can use the above rational to justify +using the former pronunciation; ‘Mag{ie => it}’. + + You can also choose to use the former pronunciation just because you +like it better. + + Also see . Also see +. + + +File: magit.info, Node: How to show git's output?, Next: How to install the gitman info manual?, Prev: How to pronounce Magit?, Up: FAQ - How to ...? + +A.1.2 How to show git’s output? +------------------------------- + +To show the output of recently run git commands, press ‘$’ (or, if that +isn’t available, ‘M-x magit-process-buffer’). This will show a buffer +containing a section per git invocation; as always press ‘TAB’ to expand +or collapse them. + + By default, git’s output is only inserted into the process buffer if +it is run for side-effects. When the output is consumed in some way, +also inserting it into the process buffer would be too expensive. For +debugging purposes, it’s possible to do so anyway by setting +‘magit-git-debug’ to ‘t’. + + +File: magit.info, Node: How to install the gitman info manual?, Next: How to show diffs for gpg-encrypted files?, Prev: How to show git's output?, Up: FAQ - How to ...? + +A.1.3 How to install the gitman info manual? +-------------------------------------------- + +Git’s manpages can be exported as an info manual called ‘gitman’. +Magit’s own info manual links to nodes in that manual instead of the +actual manpages because Info doesn’t support linking to manpages. + + Unfortunately some distributions do not install the ‘gitman’ manual +by default and you will have to install a separate documentation package +to get it. + + Magit patches Info adding the ability to visit links to the ‘gitman’ +Info manual by instead viewing the respective manpage. If you prefer +that approach, then set the value of ‘magit-view-git-manual-method’ to +one of the supported packages ‘man’ or ‘woman’, e.g.: + + (setq magit-view-git-manual-method 'man) + + +File: magit.info, Node: How to show diffs for gpg-encrypted files?, Next: How does branching and pushing work?, Prev: How to install the gitman info manual?, Up: FAQ - How to ...? + +A.1.4 How to show diffs for gpg-encrypted files? +------------------------------------------------ + +Git supports showing diffs for encrypted files, but has to be told to do +so. Since Magit just uses Git to get the diffs, configuring Git also +affects the diffs displayed inside Magit. + + git config --global diff.gpg.textconv "gpg --no-tty --decrypt" + echo "*.gpg filter=gpg diff=gpg" > .gitattributes + + +File: magit.info, Node: How does branching and pushing work?, Next: Should I disable VC?, Prev: How to show diffs for gpg-encrypted files?, Up: FAQ - How to ...? + +A.1.5 How does branching and pushing work? +------------------------------------------ + +Please see *note Branching:: and + + + +File: magit.info, Node: Should I disable VC?, Prev: How does branching and pushing work?, Up: FAQ - How to ...? + +A.1.6 Should I disable VC? +-------------------------- + +If you don’t use VC (the built-in version control interface) then you +might be tempted to disable it, not least because we used to recommend +that you do that. + + We no longer recommend that you disable VC. Doing so would break +useful third-party packages (such as ‘diff-hl’), which depend on VC +being enabled. + + If you choose to disable VC anyway, then you can do so by changing +the value of ‘vc-handled-backends’. + + +File: magit.info, Node: FAQ - Issues and Errors, Prev: FAQ - How to ...?, Up: FAQ + +A.2 FAQ - Issues and Errors +=========================== + +* Menu: + +* Magit is slow:: +* I changed several thousand files at once and now Magit is unusable:: +* I am having problems committing:: +* I am using MS Windows and cannot push with Magit:: +* I am using macOS and SOMETHING works in shell, but not in Magit: I am using macOS and SOMETHING works in shell but not in Magit. +* Expanding a file to show the diff causes it to disappear:: +* Point is wrong in the COMMIT_EDITMSG buffer:: +* The mode-line information isn't always up-to-date:: +* A branch and tag sharing the same name breaks SOMETHING:: +* My Git hooks work on the command-line but not inside Magit:: +* git-commit-mode isn't used when committing from the command-line:: +* Point ends up inside invisible text when jumping to a file-visiting buffer:: +* I am unable to stage when using Tramp from MS Windows:: +* I am no longer able to save popup defaults:: + + +File: magit.info, Node: Magit is slow, Next: I changed several thousand files at once and now Magit is unusable, Up: FAQ - Issues and Errors + +A.2.1 Magit is slow +------------------- + +See *note Performance:: and *note I changed several thousand files at +once and now Magit is unusable::. + + +File: magit.info, Node: I changed several thousand files at once and now Magit is unusable, Next: I am having problems committing, Prev: Magit is slow, Up: FAQ - Issues and Errors + +A.2.2 I changed several thousand files at once and now Magit is unusable +------------------------------------------------------------------------ + +Magit is currently not expected to work well under such conditions. It +sure would be nice if it did. Reaching satisfactory performance under +such conditions will require some heavy refactoring. This is no small +task but I hope to eventually find the time to make it happen. + + But for now we recommend you use the command line to complete this +one commit. Also see *note Performance::. + + +File: magit.info, Node: I am having problems committing, Next: I am using MS Windows and cannot push with Magit, Prev: I changed several thousand files at once and now Magit is unusable, Up: FAQ - Issues and Errors + +A.2.3 I am having problems committing +------------------------------------- + +That likely means that Magit is having problems finding an appropriate +emacsclient executable. See *note (with-editor)Configuring +With-Editor:: and *note (with-editor)Debugging::. + + +File: magit.info, Node: I am using MS Windows and cannot push with Magit, Next: I am using macOS and SOMETHING works in shell but not in Magit, Prev: I am having problems committing, Up: FAQ - Issues and Errors + +A.2.4 I am using MS Windows and cannot push with Magit +------------------------------------------------------ + +It’s almost certain that Magit is only incidental to this issue. It is +much more likely that this is a configuration issue, even if you can +push on the command line. + + Detailed setup instructions can be found at +. + + +File: magit.info, Node: I am using macOS and SOMETHING works in shell but not in Magit, Next: Expanding a file to show the diff causes it to disappear, Prev: I am using MS Windows and cannot push with Magit, Up: FAQ - Issues and Errors + +A.2.5 I am using macOS and SOMETHING works in shell, but not in Magit +--------------------------------------------------------------------- + +This usually occurs because Emacs doesn’t have the same environment +variables as your shell. Try installing and configuring +. By default it +synchronizes ‘$PATH’, which helps Magit find the same ‘git’ as the one +you are using on the shell. + + If SOMETHING is "passphrase caching with gpg-agent for commit and/or +tag signing", then you’ll also need to synchronize ‘$GPG_AGENT_INFO’. + + +File: magit.info, Node: Expanding a file to show the diff causes it to disappear, Next: Point is wrong in the COMMIT_EDITMSG buffer, Prev: I am using macOS and SOMETHING works in shell but not in Magit, Up: FAQ - Issues and Errors + +A.2.6 Expanding a file to show the diff causes it to disappear +-------------------------------------------------------------- + +This is probably caused by a change of a ‘diff.*’ Git variable. You +probably set that variable for a reason, and should therefore only undo +that setting in Magit by customizing ‘magit-git-global-arguments’. + + +File: magit.info, Node: Point is wrong in the COMMIT_EDITMSG buffer, Next: The mode-line information isn't always up-to-date, Prev: Expanding a file to show the diff causes it to disappear, Up: FAQ - Issues and Errors + +A.2.7 Point is wrong in the ‘COMMIT_EDITMSG’ buffer +--------------------------------------------------- + +Neither Magit nor ‘git-commit‘ fiddle with point in the buffer used to +write commit messages, so something else must be doing it. + + You have probably globally enabled a mode which does restore point in +file-visiting buffers. It might be a bit surprising, but when you write +a commit message, then you are actually editing a file. + + So you have to figure out which package is doing. ‘saveplace’, +‘pointback’, and ‘session’ are likely candidates. These snippets might +help: + + (setq session-name-disable-regexp "\\(?:\\`'\\.git/[A-Z_]+\\'\\)") + + (with-eval-after-load 'pointback + (lambda () + (when (or git-commit-mode git-rebase-mode) + (pointback-mode -1)))) + + +File: magit.info, Node: The mode-line information isn't always up-to-date, Next: A branch and tag sharing the same name breaks SOMETHING, Prev: Point is wrong in the COMMIT_EDITMSG buffer, Up: FAQ - Issues and Errors + +A.2.8 The mode-line information isn’t always up-to-date +------------------------------------------------------- + +Magit is not responsible for the version control information that is +being displayed in the mode-line and looks something like ‘Git-master’. +The built-in "Version Control" package, also known as "VC", updates that +information, and can be told to do so more often: + + (setq auto-revert-check-vc-info t) + + But doing so isn’t good for performance. For more (overly +optimistic) information see *note (emacs)VC Mode Line::. + + If you don’t really care about seeing this information in the +mode-line, but just don’t want to see _incorrect_ information, then +consider simply not displaying it in the mode-line: + + (setq-default mode-line-format + (delete '(vc-mode vc-mode) mode-line-format)) + + +File: magit.info, Node: A branch and tag sharing the same name breaks SOMETHING, Next: My Git hooks work on the command-line but not inside Magit, Prev: The mode-line information isn't always up-to-date, Up: FAQ - Issues and Errors + +A.2.9 A branch and tag sharing the same name breaks SOMETHING +------------------------------------------------------------- + +Or more generally, ambiguous refnames break SOMETHING. + + Magit assumes that refs are named non-ambiguously across the +"refs/heads/", "refs/tags/", and "refs/remotes/" namespaces (i.e., all +the names remain unique when those prefixes are stripped). We consider +ambiguous refnames unsupported and recommend that you use a +non-ambiguous naming scheme. However, if you do work with a repository +that has ambiguous refnames, please report any issues you encounter so +that we can investigate whether there is a simple fix. + + +File: magit.info, Node: My Git hooks work on the command-line but not inside Magit, Next: git-commit-mode isn't used when committing from the command-line, Prev: A branch and tag sharing the same name breaks SOMETHING, Up: FAQ - Issues and Errors + +A.2.10 My Git hooks work on the command-line but not inside Magit +----------------------------------------------------------------- + +When Magit calls ‘git’ it adds a few global arguments including +‘--literal-pathspecs’ and the ‘git’ process started by Magit then passes +that setting on to other ‘git’ process it starts itself. It does so by +setting the environment variable ‘GIT_LITERAL_PATHSPECS’, not by calling +subprocesses with the ‘--literal-pathspecs’ argument. You can therefore +override this setting in hook scripts using ‘unset +GIT_LITERAL_PATHSPECS’. + + +File: magit.info, Node: git-commit-mode isn't used when committing from the command-line, Next: Point ends up inside invisible text when jumping to a file-visiting buffer, Prev: My Git hooks work on the command-line but not inside Magit, Up: FAQ - Issues and Errors + +A.2.11 ‘git-commit-mode’ isn’t used when committing from the command-line +------------------------------------------------------------------------- + +The reason for this is that ‘git-commit.el’ has not been loaded yet +and/or that the server has not been started yet. These things have +always already been taken care of when you commit from Magit because in +order to do so, Magit has to be loaded and doing that involves loading +‘git-commit’ and starting the server. + + If you want to commit from the command-line, then you have to take +care of these things yourself. Your ‘init.el’ file should contain: + + (require 'git-commit) + (server-mode) + + Instead of ‘(require ’git-commit)‘ you may also use: + + (load "/path/to/magit-autoloads.el") + + You might want to do that because loading ‘git-commit’ causes large +parts of Magit to be loaded. + + There are also some variations of ‘(server-mode)’ that you might want +to try. Personally I use: + + (use-package server + :config (or (server-running-p) (server-mode))) + + Now you can use: + + $ emacs& + $ EDITOR=emacsclient git commit + + However you cannot use: + + $ killall emacs + $ EDITOR="emacsclient --alternate-editor emacs" git commit + + This will actually end up using ‘emacs’, not ‘emacsclient’. If you +do this, then you can still edit the commit message but +‘git-commit-mode’ won’t be used and you have to exit ‘emacs’ to finish +the process. + + Tautology ahead. If you want to be able to use ‘emacsclient’ to +connect to a running ‘emacs’ instance, even though no ‘emacs’ instance +is running, then you cannot use ‘emacsclient’ directly. + + Instead you have to create a script that does something like this: + + Try to use ‘emacsclient’ (without using ‘--alternate-editor’). If +that succeeds, do nothing else. Otherwise start ‘emacs &’ (and +‘init.el’ must call ‘server-start’) and try to use ‘emacsclient’ again. + + +File: magit.info, Node: Point ends up inside invisible text when jumping to a file-visiting buffer, Next: I am unable to stage when using Tramp from MS Windows, Prev: git-commit-mode isn't used when committing from the command-line, Up: FAQ - Issues and Errors + +A.2.12 Point ends up inside invisible text when jumping to a file-visiting buffer +--------------------------------------------------------------------------------- + +This can happen when you type ‘RET’ on a hunk to visit the respective +file at the respective position. One solution to this problem is to use +‘global-reveal-mode’. It makes sure that text around point is always +visible. If that is too drastic for your taste, then you may instead +use ‘magit-diff-visit-file-hook’ to reveal the text, possibly using +‘reveal-post-command’ or for Org buffers ‘org-reveal’. + + +File: magit.info, Node: I am unable to stage when using Tramp from MS Windows, Next: I am no longer able to save popup defaults, Prev: Point ends up inside invisible text when jumping to a file-visiting buffer, Up: FAQ - Issues and Errors + +A.2.13 I am unable to stage when using Tramp from MS Windows +------------------------------------------------------------ + +Magit may be unable to stage (or otherwise apply) individual hunks when +you are connected to remote machine using Tramp and the local machine +uses MS Windows. + + There appears to be a problem with ‘process-send-eof’ in this +scenario, as mentioned at the end of ‘tramp-tests.el’. I have contacted +the Tramp maintainer about this. For now this unfortunately means that +it just doesn’t work and we cannot do anything about it. If you have +more information, then please comment on +. + + +File: magit.info, Node: I am no longer able to save popup defaults, Prev: I am unable to stage when using Tramp from MS Windows, Up: FAQ - Issues and Errors + +A.2.14 I am no longer able to save popup defaults +------------------------------------------------- + +Magit used to use Magit-Popup to implement the transient popup menus. +Now it used Transient instead, which is Magit-Popup’s successor. + + In the older Magit-Popup menus, it was possible to save user settings +(e.g. setting the gpg signing key for commits) by using ‘C-c C-c’ in +the popup buffer. This would dismiss the popup, but save the settings +as the defaults for future popups. + + When switching to Transient menus, this functionality is now +available via ‘C-x C-s’ instead; the ‘C-x’ prefix has other options as +well when using Transient, which will be displayed when it is typed. +See +for more details. + + +File: magit.info, Node: Debugging Tools, Next: Keystroke Index, Prev: FAQ, Up: Top + +B Debugging Tools +***************** + +Magit and its dependencies provide a few debugging tools, and we +appreciate it very much if you use those tools before reporting an +issue. Please include all relevant output when reporting an issue. + +‘M-x magit-version’ + This command shows the currently used versions of Magit, Git, and + Emacs in the echo area. Non-interactively this just returns the + Magit version. + +‘M-x magit-emacs-Q-command’ + This command shows a debugging shell command in the echo area and + adds it to the kill ring. Paste that command into a shell and run + it. + + This shell command starts ‘emacs’ with only ‘magit’ and its + dependencies loaded. Neither your configuration nor other + installed packages are loaded. This makes it easier to determine + whether some issue lays with Magit or something else. + + If you run Magit from its Git repository, then you should be able + to use ‘make emacs-Q’ instead of the output of this command. + +‘M-x magit-toggle-git-debug’ + This command toggles whether additional git errors are reported. + + Magit basically calls git for one of these two reasons: for + side-effects or to do something with its standard output. + + When git is run for side-effects then its output, including error + messages, go into the process buffer which is shown when using ‘$’. + + When git’s output is consumed in some way, then it would be too + expensive to also insert it into this buffer, but when this option + is non-nil and git returns with a non-zero exit status, then at + least its standard error is inserted into this buffer. + + This is only intended for debugging purposes. Do not enable this + permanently, that would negatively affect performance. Also note + that just because git exits with a non-zero exit status and prints + an error message that usually doesn’t mean that it is an error as + far as Magit is concerned, which is another reason we usually hide + these error messages. Whether some error message is relevant in + the context of some unexpected behavior has to be judged on a case + by case basis. + +‘M-x magit-toggle-verbose-refresh’ + This command toggles whether Magit refreshes buffers verbosely. + Enabling this helps figuring out which sections are bottlenecks. + The additional output can be found in the ‘*Messages*’ buffer. + +‘M-x magit-debug-git-executable’ + This command displays a buffer containing information about the + available and used ‘git’ executable(s), and can be useful when + investigating ‘exec-path’ issues. + + Also see *note Git Executable::. + +‘M-x with-editor-debug’ + This command displays a buffer containing information about the + available and used ‘emacsclient’ executable(s), and can be useful + when investigating why Magit (or rather ‘with-editor’) cannot find + an appropriate ‘emacsclient’ executable. + + Also see *note (with-editor)Debugging::. + +Please also see *note FAQ::. + + +File: magit.info, Node: Keystroke Index, Next: Function and Command Index, Prev: Debugging Tools, Up: Top + +Appendix C Keystroke Index +************************** + +[index] +* Menu: + +* !: Running Git Manually. + (line 13) +* ! !: Running Git Manually. + (line 17) +* ! a: Running Git Manually. + (line 53) +* ! b: Running Git Manually. + (line 56) +* ! g: Running Git Manually. + (line 59) +* ! k: Running Git Manually. + (line 50) +* ! m: Running Git Manually. + (line 62) +* ! p: Running Git Manually. + (line 25) +* ! s: Running Git Manually. + (line 34) +* ! S: Running Git Manually. + (line 38) +* $: Viewing Git Output. (line 17) +* +: Log Buffer. (line 64) +* + <1>: Refreshing Diffs. (line 61) +* -: Log Buffer. (line 67) +* - <1>: Refreshing Diffs. (line 58) +* 0: Refreshing Diffs. (line 64) +* 1: Section Visibility. (line 26) +* 2: Section Visibility. (line 26) +* 3: Section Visibility. (line 26) +* 4: Section Visibility. (line 26) +* 5: Repository List. (line 109) +* :: Running Git Manually. + (line 25) +* =: Log Buffer. (line 59) +* >: Sparse checkouts. (line 17) +* > a: Sparse checkouts. (line 39) +* > d: Sparse checkouts. (line 50) +* > e: Sparse checkouts. (line 21) +* > r: Sparse checkouts. (line 44) +* > s: Sparse checkouts. (line 33) +* ^: Section Movement. (line 28) +* a: Applying. (line 34) +* A: Cherry Picking. (line 9) +* A A: Cherry Picking. (line 17) +* A a: Cherry Picking. (line 23) +* A A <1>: Cherry Picking. (line 85) +* A a <1>: Cherry Picking. (line 91) +* A d: Cherry Picking. (line 51) +* A h: Cherry Picking. (line 40) +* A n: Cherry Picking. (line 62) +* A s: Cherry Picking. (line 72) +* A s <1>: Cherry Picking. (line 88) +* B: Bisecting. (line 9) +* b: Blaming. (line 95) +* b <1>: Branch Commands. (line 13) +* b <2>: Editing Rebase Sequences. + (line 70) +* B B: Bisecting. (line 16) +* B b: Bisecting. (line 32) +* b b: Branch Commands. (line 47) +* b C: Branch Commands. (line 31) +* b c: Branch Commands. (line 63) +* B g: Bisecting. (line 36) +* B k: Bisecting. (line 46) +* b k: Branch Commands. (line 138) +* b l: Branch Commands. (line 69) +* B m: Bisecting. (line 40) +* b m: Branch Commands. (line 143) +* b n: Branch Commands. (line 54) +* B r: Bisecting. (line 51) +* B s: Bisecting. (line 26) +* b s: Branch Commands. (line 91) +* b S: Branch Commands. (line 118) +* b x: Branch Commands. (line 123) +* c: Blaming. (line 121) +* C: Cloning Repository. (line 20) +* c <1>: Initiating a Commit. (line 9) +* c <2>: Editing Rebase Sequences. + (line 59) +* C >: Cloning Repository. (line 38) +* c a: Initiating a Commit. (line 18) +* c A: Initiating a Commit. (line 59) +* C b: Cloning Repository. (line 44) +* C C: Cloning Repository. (line 28) +* c c: Initiating a Commit. (line 14) +* C d: Cloning Repository. (line 55) +* C e: Cloning Repository. (line 61) +* c e: Initiating a Commit. (line 21) +* c f: Initiating a Commit. (line 39) +* c F: Initiating a Commit. (line 46) +* C m: Cloning Repository. (line 48) +* C s: Cloning Repository. (line 32) +* c s: Initiating a Commit. (line 49) +* c S: Initiating a Commit. (line 56) +* c w: Initiating a Commit. (line 30) +* C-: Visiting Files and Blobs from a Diff. + (line 50) +* C-: Section Visibility. (line 13) +* C-c C-a: Commit Pseudo Headers. + (line 16) +* C-c C-b: Log Buffer. (line 20) +* C-c C-b <1>: Refreshing Diffs. (line 80) +* C-c C-c: Transient Commands. (line 19) +* C-c C-c <1>: Select from Log. (line 21) +* C-c C-c <2>: Editing Commit Messages. + (line 18) +* C-c C-c <3>: Editing Rebase Sequences. + (line 7) +* C-c C-d: Refreshing Diffs. (line 71) +* C-c C-d <1>: Editing Commit Messages. + (line 54) +* C-c C-e: Commands Available in Diffs. + (line 24) +* C-c C-f: Log Buffer. (line 23) +* C-c C-f <1>: Refreshing Diffs. (line 83) +* C-c C-i: Commit Pseudo Headers. + (line 13) +* C-c C-k: Select from Log. (line 26) +* C-c C-k <1>: Editing Commit Messages. + (line 22) +* C-c C-k <2>: Editing Rebase Sequences. + (line 11) +* C-c C-n: Log Buffer. (line 26) +* C-c C-o: Commit Pseudo Headers. + (line 28) +* C-c C-p: Commit Pseudo Headers. + (line 31) +* C-c C-r: Commit Pseudo Headers. + (line 19) +* C-c C-s: Commit Pseudo Headers. + (line 22) +* C-c C-t: Commands Available in Diffs. + (line 15) +* C-c C-t <1>: Commit Pseudo Headers. + (line 25) +* C-c C-w: Using the Revision Stack. + (line 7) +* C-c M-g: Commands for Buffers Visiting Files. + (line 22) +* C-c M-g B: Blaming. (line 19) +* C-c M-g b: Blaming. (line 30) +* C-c M-g B <1>: Commands for Buffers Visiting Files. + (line 83) +* C-c M-g B b: Blaming. (line 30) +* C-c M-g B e: Blaming. (line 61) +* C-c M-g B f: Blaming. (line 53) +* C-c M-g B r: Blaming. (line 45) +* C-c M-g c: Commands for Buffers Visiting Files. + (line 36) +* C-c M-g D: Commands for Buffers Visiting Files. + (line 42) +* C-c M-g d: Commands for Buffers Visiting Files. + (line 52) +* C-c M-g e: Blaming. (line 61) +* C-c M-g e <1>: Commands for Buffers Visiting Files. + (line 95) +* C-c M-g f: Blaming. (line 53) +* C-c M-g L: Commands for Buffers Visiting Files. + (line 60) +* C-c M-g l: Commands for Buffers Visiting Files. + (line 70) +* C-c M-g p: Commands for Buffers Visiting Files. + (line 104) +* C-c M-g r: Blaming. (line 45) +* C-c M-g s: Commands for Buffers Visiting Files. + (line 29) +* C-c M-g t: Commands for Buffers Visiting Files. + (line 76) +* C-c M-g u: Commands for Buffers Visiting Files. + (line 32) +* C-c M-i: Commit Pseudo Headers. + (line 35) +* C-c M-s: Editing Commit Messages. + (line 33) +* C-w: Common Commands. (line 22) +* C-x g: Status Buffer. (line 23) +* C-x u: Editing Rebase Sequences. + (line 77) +* d: Diffing. (line 22) +* D: Refreshing Diffs. (line 12) +* d c: Diffing. (line 63) +* d d: Diffing. (line 27) +* D f: Refreshing Diffs. (line 41) +* D F: Refreshing Diffs. (line 45) +* D g: Refreshing Diffs. (line 17) +* d p: Diffing. (line 56) +* d r: Diffing. (line 30) +* D r: Refreshing Diffs. (line 37) +* d s: Diffing. (line 48) +* D s: Refreshing Diffs. (line 21) +* d t: Diffing. (line 67) +* D t: Refreshing Diffs. (line 34) +* d u: Diffing. (line 53) +* d w: Diffing. (line 43) +* D w: Refreshing Diffs. (line 27) +* DEL: Log Buffer. (line 50) +* DEL <1>: Commands Available in Diffs. + (line 56) +* DEL <2>: Blaming. (line 83) +* DEL <3>: Editing Rebase Sequences. + (line 25) +* e: Ediffing. (line 10) +* E: Ediffing. (line 21) +* e <1>: Editing Rebase Sequences. + (line 46) +* E c: Ediffing. (line 100) +* E i: Ediffing. (line 94) +* E m: Ediffing. (line 33) +* E m <1>: Ediffing. (line 48) +* E r: Ediffing. (line 25) +* E s: Ediffing. (line 87) +* E t: Ediffing. (line 79) +* E u: Ediffing. (line 91) +* E w: Ediffing. (line 97) +* E z: Ediffing. (line 103) +* f: Repository List. (line 105) +* f <1>: Editing Rebase Sequences. + (line 52) +* f <2>: Fetching. (line 10) +* F: Pulling. (line 10) +* f a: Fetching. (line 45) +* f C: Branch Commands. (line 31) +* F C: Branch Commands. (line 31) +* f e: Fetching. (line 34) +* F e: Pulling. (line 28) +* f m: Fetching. (line 48) +* f o: Fetching. (line 37) +* f p: Fetching. (line 15) +* F p: Pulling. (line 14) +* f r: Fetching. (line 41) +* f u: Fetching. (line 22) +* F u: Pulling. (line 21) +* g: Automatic Refreshing of Magit Buffers. + (line 26) +* G: Automatic Refreshing of Magit Buffers. + (line 34) +* H: Section Types and Values. + (line 14) +* I: Creating Repository. (line 7) +* j: Log Buffer. (line 31) +* j <1>: Commands Available in Diffs. + (line 43) +* k: Viewing Git Output. (line 24) +* k <1>: Applying. (line 40) +* k <2>: Editing Rebase Sequences. + (line 56) +* k <3>: Stashing. (line 82) +* l: Logging. (line 30) +* L: Refreshing Logs. (line 12) +* L <1>: Log Buffer. (line 7) +* L <2>: Log Margin. (line 52) +* l <1>: Editing Rebase Sequences. + (line 94) +* l a: Logging. (line 61) +* l b: Logging. (line 58) +* L d: Log Margin. (line 66) +* L g: Refreshing Logs. (line 17) +* l h: Logging. (line 40) +* l H: Reflog. (line 18) +* l l: Logging. (line 35) +* l L: Logging. (line 55) +* L L: Log Margin. (line 60) +* L l: Log Margin. (line 63) +* l o: Logging. (line 49) +* l O: Reflog. (line 15) +* l r: Reflog. (line 12) +* L s: Refreshing Logs. (line 21) +* L t: Refreshing Logs. (line 34) +* l u: Logging. (line 43) +* L w: Refreshing Logs. (line 27) +* m: Repository List. (line 99) +* m <1>: Merging. (line 10) +* M: Remote Commands. (line 14) +* m a: Merging. (line 42) +* m a <1>: Merging. (line 87) +* M a: Remote Commands. (line 48) +* M C: Remote Commands. (line 32) +* m e: Merging. (line 30) +* m i: Merging. (line 54) +* M k: Remote Commands. (line 60) +* m m: Merging. (line 18) +* m m <1>: Merging. (line 82) +* m n: Merging. (line 36) +* m p: Merging. (line 75) +* M p: Remote Commands. (line 63) +* M P: Remote Commands. (line 67) +* M r: Remote Commands. (line 52) +* m s: Merging. (line 67) +* M u: Remote Commands. (line 56) +* M-1: Section Visibility. (line 32) +* M-2: Section Visibility. (line 32) +* M-3: Section Visibility. (line 32) +* M-4: Section Visibility. (line 32) +* M-: Section Visibility. (line 16) +* M-n: Section Movement. (line 24) +* M-n <1>: Editing Commit Messages. + (line 41) +* M-n <2>: Editing Rebase Sequences. + (line 40) +* M-p: Section Movement. (line 19) +* M-p <1>: Editing Commit Messages. + (line 36) +* M-p <2>: Editing Rebase Sequences. + (line 37) +* M-w: Blaming. (line 114) +* M-w <1>: Common Commands. (line 39) +* MM: Editing Rebase Sequences. + (line 102) +* Mt: Editing Rebase Sequences. + (line 108) +* n: Section Movement. (line 16) +* n <1>: Blaming. (line 98) +* N: Blaming. (line 101) +* n <2>: Editing Rebase Sequences. + (line 34) +* n <3>: Minor Mode for Buffers Visiting Blobs. + (line 16) +* o: Submodule Transient. (line 7) +* O: Subtree. (line 9) +* o a: Submodule Transient. (line 20) +* o d: Submodule Transient. (line 45) +* O e: Subtree. (line 37) +* O e p: Subtree. (line 48) +* O e s: Subtree. (line 52) +* o f: Submodule Transient. (line 51) +* O i: Subtree. (line 13) +* O i a: Subtree. (line 24) +* O i c: Subtree. (line 28) +* O i f: Subtree. (line 34) +* O i m: Subtree. (line 31) +* o l: Submodule Transient. (line 48) +* o p: Submodule Transient. (line 32) +* o r: Submodule Transient. (line 26) +* o s: Submodule Transient. (line 40) +* o u: Submodule Transient. (line 36) +* p: Section Movement. (line 11) +* p <1>: Blaming. (line 104) +* P: Blaming. (line 107) +* p <2>: Editing Rebase Sequences. + (line 31) +* P <1>: Pushing. (line 10) +* p <3>: Minor Mode for Buffers Visiting Blobs. + (line 13) +* P C: Branch Commands. (line 31) +* P e: Pushing. (line 29) +* P m: Pushing. (line 45) +* P o: Pushing. (line 33) +* P p: Pushing. (line 15) +* P r: Pushing. (line 37) +* P t: Pushing. (line 52) +* P T: Pushing. (line 59) +* P u: Pushing. (line 22) +* q: Quitting Windows. (line 7) +* q <1>: Log Buffer. (line 14) +* q <2>: Blaming. (line 110) +* q <3>: Minor Mode for Buffers Visiting Blobs. + (line 19) +* r: Rebasing. (line 10) +* r <1>: Editing Rebase Sequences. + (line 43) +* r a: Rebasing. (line 111) +* r e: Rebasing. (line 42) +* r e <1>: Rebasing. (line 107) +* r f: Rebasing. (line 79) +* r i: Rebasing. (line 76) +* r k: Rebasing. (line 91) +* r m: Rebasing. (line 83) +* r p: Rebasing. (line 28) +* r r: Rebasing. (line 97) +* r s: Rebasing. (line 47) +* r s <1>: Rebasing. (line 103) +* r u: Rebasing. (line 35) +* r w: Rebasing. (line 87) +* RET: Repository List. (line 96) +* RET <1>: References Buffer. (line 159) +* RET <2>: Visiting Files and Blobs from a Diff. + (line 9) +* RET <3>: Blaming. (line 71) +* RET <4>: Editing Rebase Sequences. + (line 15) +* s: Staging and Unstaging. + (line 29) +* S: Staging and Unstaging. + (line 36) +* s <1>: Editing Rebase Sequences. + (line 49) +* S-: Section Visibility. (line 20) +* SPC: Log Buffer. (line 41) +* SPC <1>: Commands Available in Diffs. + (line 53) +* SPC <2>: Blaming. (line 74) +* SPC <3>: Editing Rebase Sequences. + (line 19) +* t: Editing Rebase Sequences. + (line 97) +* t <1>: Tagging. (line 9) +* T: Notes. (line 9) +* T a: Notes. (line 47) +* T c: Notes. (line 43) +* t k: Tagging. (line 37) +* T m: Notes. (line 35) +* t p: Tagging. (line 43) +* T p: Notes. (line 28) +* t r: Tagging. (line 18) +* T r: Notes. (line 21) +* t t: Tagging. (line 14) +* T T: Notes. (line 14) +* TAB: Section Visibility. (line 10) +* u: Repository List. (line 102) +* u <1>: Staging and Unstaging. + (line 42) +* U: Staging and Unstaging. + (line 50) +* v: Applying. (line 47) +* V: Reverting. (line 7) +* V A: Reverting. (line 29) +* V a: Reverting. (line 35) +* V s: Reverting. (line 32) +* V V: Reverting. (line 15) +* V v: Reverting. (line 20) +* W: Plain Patches. (line 7) +* w: Maildir Patches. (line 9) +* w a: Plain Patches. (line 20) +* w a <1>: Maildir Patches. (line 23) +* w a <2>: Maildir Patches. (line 38) +* W c: Plain Patches. (line 12) +* w m: Maildir Patches. (line 20) +* W s: Plain Patches. (line 26) +* w s: Maildir Patches. (line 34) +* w w: Maildir Patches. (line 14) +* w w <1>: Maildir Patches. (line 31) +* x: Editing Rebase Sequences. + (line 62) +* x <1>: Resetting. (line 9) +* X f: Resetting. (line 44) +* X h: Resetting. (line 24) +* X i: Resetting. (line 33) +* X k: Resetting. (line 28) +* X m: Resetting. (line 15) +* X s: Resetting. (line 19) +* X w: Resetting. (line 39) +* X w <1>: Wip Modes. (line 64) +* Y: Cherries. (line 18) +* y: References Buffer. (line 7) +* y <1>: Editing Rebase Sequences. + (line 74) +* y c: References Buffer. (line 25) +* y o: References Buffer. (line 30) +* y r: References Buffer. (line 34) +* y y: References Buffer. (line 21) +* z: Stashing. (line 9) +* Z: Worktree. (line 9) +* z a: Stashing. (line 52) +* z b: Stashing. (line 70) +* z B: Stashing. (line 74) +* Z b: Worktree. (line 13) +* Z c: Worktree. (line 16) +* z f: Stashing. (line 79) +* Z g: Worktree. (line 26) +* z i: Stashing. (line 20) +* z I: Stashing. (line 42) +* z k: Stashing. (line 63) +* Z k: Worktree. (line 22) +* z l: Stashing. (line 85) +* Z m: Worktree. (line 19) +* z p: Stashing. (line 57) +* z v: Stashing. (line 67) +* z w: Stashing. (line 24) +* z W: Stashing. (line 46) +* z x: Stashing. (line 30) +* z z: Stashing. (line 14) +* z Z: Stashing. (line 36) + + +File: magit.info, Node: Function and Command Index, Next: Variable Index, Prev: Keystroke Index, Up: Top + +Appendix D Function and Command Index +************************************* + +[index] +* Menu: + +* bug-reference-mode: Commit Mode and Hooks. + (line 49) +* forward-line: Editing Rebase Sequences. + (line 34) +* git-commit-ack: Commit Pseudo Headers. + (line 16) +* git-commit-cc: Commit Pseudo Headers. + (line 28) +* git-commit-check-style-conventions: Commit Message Conventions. + (line 37) +* git-commit-insert-pseudo-header: Commit Pseudo Headers. + (line 13) +* git-commit-next-message: Editing Commit Messages. + (line 41) +* git-commit-prev-message: Editing Commit Messages. + (line 36) +* git-commit-propertize-diff: Commit Mode and Hooks. + (line 41) +* git-commit-reported: Commit Pseudo Headers. + (line 31) +* git-commit-review: Commit Pseudo Headers. + (line 19) +* git-commit-save-message: Editing Commit Messages. + (line 33) +* git-commit-save-message <1>: Commit Mode and Hooks. + (line 26) +* git-commit-setup-changelog-support: Commit Mode and Hooks. + (line 29) +* git-commit-signoff: Commit Pseudo Headers. + (line 22) +* git-commit-suggested: Commit Pseudo Headers. + (line 35) +* git-commit-test: Commit Pseudo Headers. + (line 25) +* git-commit-turn-on-auto-fill: Commit Mode and Hooks. + (line 33) +* git-commit-turn-on-flyspell: Commit Mode and Hooks. + (line 37) +* git-rebase-backward-line: Editing Rebase Sequences. + (line 31) +* git-rebase-break: Editing Rebase Sequences. + (line 70) +* git-rebase-edit: Editing Rebase Sequences. + (line 46) +* git-rebase-exec: Editing Rebase Sequences. + (line 62) +* git-rebase-fixup: Editing Rebase Sequences. + (line 52) +* git-rebase-insert: Editing Rebase Sequences. + (line 74) +* git-rebase-kill-line: Editing Rebase Sequences. + (line 56) +* git-rebase-label: Editing Rebase Sequences. + (line 94) +* git-rebase-merge: Editing Rebase Sequences. + (line 102) +* git-rebase-merge-toggle-editmsg: Editing Rebase Sequences. + (line 108) +* git-rebase-move-line-down: Editing Rebase Sequences. + (line 40) +* git-rebase-move-line-up: Editing Rebase Sequences. + (line 37) +* git-rebase-pick: Editing Rebase Sequences. + (line 59) +* git-rebase-reset: Editing Rebase Sequences. + (line 97) +* git-rebase-reword: Editing Rebase Sequences. + (line 43) +* git-rebase-show-commit: Editing Rebase Sequences. + (line 15) +* git-rebase-show-or-scroll-down: Editing Rebase Sequences. + (line 25) +* git-rebase-show-or-scroll-up: Editing Rebase Sequences. + (line 19) +* git-rebase-squash: Editing Rebase Sequences. + (line 49) +* git-rebase-undo: Editing Rebase Sequences. + (line 77) +* ido-enter-magit-status: Status Buffer. (line 96) +* magit-add-section-hook: Section Hooks. (line 20) +* magit-after-save-refresh-status: Automatic Refreshing of Magit Buffers. + (line 55) +* magit-am: Maildir Patches. (line 9) +* magit-am-abort: Maildir Patches. (line 38) +* magit-am-apply-maildir: Maildir Patches. (line 20) +* magit-am-apply-patches: Maildir Patches. (line 14) +* magit-am-continue: Maildir Patches. (line 31) +* magit-am-skip: Maildir Patches. (line 34) +* magit-apply: Applying. (line 34) +* magit-bisect: Bisecting. (line 9) +* magit-bisect-bad: Bisecting. (line 32) +* magit-bisect-good: Bisecting. (line 36) +* magit-bisect-mark: Bisecting. (line 40) +* magit-bisect-reset: Bisecting. (line 51) +* magit-bisect-run: Bisecting. (line 26) +* magit-bisect-skip: Bisecting. (line 46) +* magit-bisect-start: Bisecting. (line 16) +* magit-blame: Blaming. (line 19) +* magit-blame <1>: Blaming. (line 95) +* magit-blame <2>: Commands for Buffers Visiting Files. + (line 83) +* magit-blame-addition: Blaming. (line 30) +* magit-blame-copy-hash: Blaming. (line 114) +* magit-blame-cycle-style: Blaming. (line 121) +* magit-blame-echo: Blaming. (line 61) +* magit-blame-next-chunk: Blaming. (line 98) +* magit-blame-next-chunk-same-commit: Blaming. (line 101) +* magit-blame-previous-chunk: Blaming. (line 104) +* magit-blame-previous-chunk-same-commit: Blaming. (line 107) +* magit-blame-quit: Blaming. (line 110) +* magit-blame-removal: Blaming. (line 45) +* magit-blame-reverse: Blaming. (line 53) +* magit-blob-next: Minor Mode for Buffers Visiting Blobs. + (line 16) +* magit-blob-previous: Commands for Buffers Visiting Files. + (line 104) +* magit-blob-previous <1>: Minor Mode for Buffers Visiting Blobs. + (line 13) +* magit-branch: Branch Commands. (line 13) +* magit-branch-and-checkout: Branch Commands. (line 63) +* magit-branch-checkout: Branch Commands. (line 69) +* magit-branch-configure: Branch Commands. (line 31) +* magit-branch-create: Branch Commands. (line 54) +* magit-branch-delete: Branch Commands. (line 138) +* magit-branch-or-checkout: Branch Commands. (line 251) +* magit-branch-orphan: Branch Commands. (line 247) +* magit-branch-rename: Branch Commands. (line 143) +* magit-branch-reset: Branch Commands. (line 123) +* magit-branch-shelve: Auxiliary Branch Commands. + (line 9) +* magit-branch-spinoff: Branch Commands. (line 91) +* magit-branch-spinout: Branch Commands. (line 118) +* magit-branch-unshelve: Auxiliary Branch Commands. + (line 19) +* magit-builtin-completing-read: Support for Completion Frameworks. + (line 41) +* magit-bundle: Bundle. (line 8) +* magit-call-git: Calling Git for Effect. + (line 28) +* magit-call-process: Calling Git for Effect. + (line 31) +* magit-cancel-section: Creating Sections. (line 69) +* magit-checkout: Branch Commands. (line 47) +* magit-cherry: Cherries. (line 18) +* magit-cherry-apply: Cherry Picking. (line 23) +* magit-cherry-copy: Cherry Picking. (line 17) +* magit-cherry-donate: Cherry Picking. (line 51) +* magit-cherry-harvest: Cherry Picking. (line 40) +* magit-cherry-pick: Cherry Picking. (line 9) +* magit-cherry-spinoff: Cherry Picking. (line 72) +* magit-cherry-spinout: Cherry Picking. (line 62) +* magit-clone: Cloning Repository. (line 20) +* magit-clone-bare: Cloning Repository. (line 44) +* magit-clone-mirror: Cloning Repository. (line 48) +* magit-clone-regular: Cloning Repository. (line 28) +* magit-clone-shallow: Cloning Repository. (line 32) +* magit-clone-shallow-exclude: Cloning Repository. (line 61) +* magit-clone-shallow-since: Cloning Repository. (line 55) +* magit-clone-sparse: Cloning Repository. (line 38) +* magit-commit: Initiating a Commit. (line 9) +* magit-commit <1>: Commands for Buffers Visiting Files. + (line 36) +* magit-commit-amend: Initiating a Commit. (line 18) +* magit-commit-augment: Initiating a Commit. (line 59) +* magit-commit-create: Initiating a Commit. (line 14) +* magit-commit-extend: Initiating a Commit. (line 21) +* magit-commit-fixup: Initiating a Commit. (line 39) +* magit-commit-instant-fixup: Initiating a Commit. (line 46) +* magit-commit-instant-squash: Initiating a Commit. (line 56) +* magit-commit-reword: Initiating a Commit. (line 30) +* magit-commit-squash: Initiating a Commit. (line 49) +* magit-completing-read: Support for Completion Frameworks. + (line 57) +* magit-copy-buffer-revision: Common Commands. (line 39) +* magit-copy-section-value: Common Commands. (line 22) +* magit-current-section: Section Selection. (line 6) +* magit-cycle-margin-style: Log Margin. (line 63) +* magit-debug-git-executable: Git Executable. (line 55) +* magit-debug-git-executable <1>: Debugging Tools. (line 57) +* magit-define-section-jumper: Creating Sections. (line 74) +* magit-describe-section: Section Types and Values. + (line 14) +* magit-describe-section-briefly: Section Types and Values. + (line 17) +* magit-describe-section-briefly <1>: Matching Sections. (line 7) +* magit-diff: Diffing. (line 22) +* magit-diff <1>: Commands for Buffers Visiting Files. + (line 42) +* magit-diff-buffer-file: Commands for Buffers Visiting Files. + (line 52) +* magit-diff-default-context: Refreshing Diffs. (line 64) +* magit-diff-dwim: Diffing. (line 27) +* magit-diff-edit-hunk-commit: Commands Available in Diffs. + (line 24) +* magit-diff-flip-revs: Refreshing Diffs. (line 41) +* magit-diff-less-context: Refreshing Diffs. (line 58) +* magit-diff-more-context: Refreshing Diffs. (line 61) +* magit-diff-paths: Diffing. (line 56) +* magit-diff-range: Diffing. (line 30) +* magit-diff-refresh: Refreshing Diffs. (line 12) +* magit-diff-refresh <1>: Refreshing Diffs. (line 17) +* magit-diff-save-default-arguments: Refreshing Diffs. (line 27) +* magit-diff-scope: Matching Sections. (line 110) +* magit-diff-set-default-arguments: Refreshing Diffs. (line 21) +* magit-diff-show-or-scroll-down: Log Buffer. (line 50) +* magit-diff-show-or-scroll-down <1>: Blaming. (line 83) +* magit-diff-show-or-scroll-up: Log Buffer. (line 41) +* magit-diff-show-or-scroll-up <1>: Blaming. (line 74) +* magit-diff-staged: Diffing. (line 48) +* magit-diff-switch-range-type: Refreshing Diffs. (line 37) +* magit-diff-toggle-file-filter: Refreshing Diffs. (line 45) +* magit-diff-toggle-refine-hunk: Refreshing Diffs. (line 34) +* magit-diff-trace-definition: Commands Available in Diffs. + (line 15) +* magit-diff-type: Matching Sections. (line 88) +* magit-diff-unstaged: Diffing. (line 53) +* magit-diff-visit-file: Visiting Files and Blobs from a Diff. + (line 9) +* magit-diff-visit-file-other-frame: Visiting Files and Blobs from a Diff. + (line 71) +* magit-diff-visit-file-other-window: Visiting Files and Blobs from a Diff. + (line 70) +* magit-diff-visit-file-worktree: Visiting Files and Blobs from a Diff. + (line 50) +* magit-diff-visit-worktree-file-other-frame: Visiting Files and Blobs from a Diff. + (line 73) +* magit-diff-visit-worktree-file-other-window: Visiting Files and Blobs from a Diff. + (line 72) +* magit-diff-while-committing: Refreshing Diffs. (line 71) +* magit-diff-while-committing <1>: Editing Commit Messages. + (line 54) +* magit-diff-working-tree: Diffing. (line 43) +* magit-disable-section-inserter: Per-Repository Configuration. + (line 31) +* magit-discard: Applying. (line 40) +* magit-dispatch: Transient Commands. (line 19) +* magit-display-buffer: Switching Buffers. (line 6) +* magit-display-buffer-fullcolumn-most-v1: Switching Buffers. (line 68) +* magit-display-buffer-fullframe-status-topleft-v1: Switching Buffers. + (line 59) +* magit-display-buffer-fullframe-status-v1: Switching Buffers. + (line 54) +* magit-display-buffer-same-window-except-diff-v1: Switching Buffers. + (line 49) +* magit-display-buffer-traditional: Switching Buffers. (line 42) +* magit-display-repository-buffer: Common Commands. (line 9) +* magit-ediff: Ediffing. (line 21) +* magit-ediff-compare: Ediffing. (line 25) +* magit-ediff-dwim: Ediffing. (line 10) +* magit-ediff-resolve-all: Ediffing. (line 48) +* magit-ediff-resolve-rest: Ediffing. (line 33) +* magit-ediff-show-commit: Ediffing. (line 100) +* magit-ediff-show-staged: Ediffing. (line 94) +* magit-ediff-show-stash: Ediffing. (line 103) +* magit-ediff-show-unstaged: Ediffing. (line 91) +* magit-ediff-show-working-tree: Ediffing. (line 97) +* magit-ediff-stage: Ediffing. (line 87) +* magit-edit-line-commit: Commands for Buffers Visiting Files. + (line 95) +* magit-emacs-Q-command: Debugging Tools. (line 16) +* magit-fetch: Fetching. (line 10) +* magit-fetch-all: Fetching. (line 45) +* magit-fetch-branch: Fetching. (line 37) +* magit-fetch-from-pushremote: Fetching. (line 15) +* magit-fetch-from-upstream: Fetching. (line 22) +* magit-fetch-modules: Submodule Transient. (line 51) +* magit-fetch-other: Fetching. (line 34) +* magit-fetch-refspec: Fetching. (line 41) +* magit-file-checkout: Resetting. (line 44) +* magit-file-checkout <1>: Commands for Buffers Visiting Files. + (line 118) +* magit-file-delete: Commands for Buffers Visiting Files. + (line 112) +* magit-file-dispatch: Commands for Buffers Visiting Files. + (line 22) +* magit-file-rename: Commands for Buffers Visiting Files. + (line 109) +* magit-file-untrack: Commands for Buffers Visiting Files. + (line 115) +* magit-find-file: General-Purpose Visit Commands. + (line 9) +* magit-find-file-other-frame: General-Purpose Visit Commands. + (line 19) +* magit-find-file-other-window: General-Purpose Visit Commands. + (line 14) +* magit-generate-buffer-name-default-function: Naming Buffers. + (line 16) +* magit-get-section: Matching Sections. (line 14) +* magit-git: Calling Git for Effect. + (line 46) +* magit-git-command: Running Git Manually. + (line 25) +* magit-git-command-topdir: Running Git Manually. + (line 17) +* magit-git-exit-code: Getting a Value from Git. + (line 10) +* magit-git-failure: Getting a Value from Git. + (line 17) +* magit-git-false: Getting a Value from Git. + (line 25) +* magit-git-insert: Getting a Value from Git. + (line 29) +* magit-git-items: Getting a Value from Git. + (line 41) +* magit-git-lines: Getting a Value from Git. + (line 37) +* magit-git-mergetool: Running Git Manually. + (line 62) +* magit-git-mergetool <1>: Ediffing. (line 79) +* magit-git-str: Getting a Value from Git. + (line 75) +* magit-git-string: Getting a Value from Git. + (line 32) +* magit-git-success: Getting a Value from Git. + (line 13) +* magit-git-true: Getting a Value from Git. + (line 21) +* magit-git-wash: Calling Git for Effect. + (line 50) +* magit-go-backward: Log Buffer. (line 20) +* magit-go-backward <1>: Refreshing Diffs. (line 80) +* magit-go-forward: Log Buffer. (line 23) +* magit-go-forward <1>: Refreshing Diffs. (line 83) +* magit-hunk-set-window-start: Section Movement. (line 45) +* magit-ido-completing-read: Support for Completion Frameworks. + (line 46) +* magit-init: Creating Repository. (line 7) +* magit-insert-am-sequence: Status Sections. (line 25) +* magit-insert-assumed-unchanged-files: Status Sections. (line 98) +* magit-insert-bisect-log: Status Sections. (line 39) +* magit-insert-bisect-output: Status Sections. (line 33) +* magit-insert-bisect-rest: Status Sections. (line 36) +* magit-insert-diff-filter-header: Status Header Sections. + (line 35) +* magit-insert-error-header: Status Header Sections. + (line 26) +* magit-insert-head-branch-header: Status Header Sections. + (line 38) +* magit-insert-heading: Creating Sections. (line 41) +* magit-insert-ignored-files: Status Sections. (line 83) +* magit-insert-local-branches: References Sections. (line 16) +* magit-insert-merge-log: Status Sections. (line 17) +* magit-insert-modules: Status Module Sections. + (line 12) +* magit-insert-modules-overview: Status Module Sections. + (line 30) +* magit-insert-modules-unpulled-from-pushremote: Status Module Sections. + (line 45) +* magit-insert-modules-unpulled-from-upstream: Status Module Sections. + (line 40) +* magit-insert-modules-unpushed-to-pushremote: Status Module Sections. + (line 55) +* magit-insert-modules-unpushed-to-upstream: Status Module Sections. + (line 50) +* magit-insert-push-branch-header: Status Header Sections. + (line 45) +* magit-insert-rebase-sequence: Status Sections. (line 21) +* magit-insert-recent-commits: Status Sections. (line 110) +* magit-insert-remote-branches: References Sections. (line 19) +* magit-insert-remote-header: Status Header Sections. + (line 58) +* magit-insert-repo-header: Status Header Sections. + (line 55) +* magit-insert-section: Creating Sections. (line 6) +* magit-insert-sequencer-sequence: Status Sections. (line 29) +* magit-insert-skip-worktree-files: Status Sections. (line 92) +* magit-insert-staged-changes: Status Sections. (line 53) +* magit-insert-stashes: Status Sections. (line 56) +* magit-insert-status-headers: Status Header Sections. + (line 12) +* magit-insert-tags: References Sections. (line 22) +* magit-insert-tags-header: Status Header Sections. + (line 49) +* magit-insert-tracked-files: Status Sections. (line 80) +* magit-insert-unpulled-cherries: Status Sections. (line 119) +* magit-insert-unpulled-from-pushremote: Status Sections. (line 66) +* magit-insert-unpulled-from-upstream: Status Sections. (line 62) +* magit-insert-unpulled-or-recent-commits: Status Sections. (line 104) +* magit-insert-unpushed-cherries: Status Sections. (line 125) +* magit-insert-unpushed-to-pushremote: Status Sections. (line 74) +* magit-insert-unpushed-to-upstream: Status Sections. (line 70) +* magit-insert-unstaged-changes: Status Sections. (line 50) +* magit-insert-untracked-files: Status Sections. (line 42) +* magit-insert-upstream-branch-header: Status Header Sections. + (line 41) +* magit-insert-user-header: Status Header Sections. + (line 65) +* magit-jump-to-diffstat-or-diff: Commands Available in Diffs. + (line 43) +* magit-kill-this-buffer: Minor Mode for Buffers Visiting Blobs. + (line 19) +* magit-list-repositories: Repository List. (line 6) +* magit-list-submodules: Listing Submodules. (line 13) +* magit-list-submodules <1>: Submodule Transient. (line 48) +* magit-log: Logging. (line 30) +* magit-log <1>: Commands for Buffers Visiting Files. + (line 60) +* magit-log-all: Logging. (line 61) +* magit-log-all-branches: Logging. (line 58) +* magit-log-branches: Logging. (line 55) +* magit-log-buffer-file: Commands for Buffers Visiting Files. + (line 70) +* magit-log-bury-buffer: Log Buffer. (line 14) +* magit-log-current: Logging. (line 35) +* magit-log-double-commit-limit: Log Buffer. (line 64) +* magit-log-half-commit-limit: Log Buffer. (line 67) +* magit-log-head: Logging. (line 40) +* magit-log-maybe-show-more-commits: Section Movement. (line 58) +* magit-log-maybe-update-blob-buffer: Section Movement. (line 72) +* magit-log-maybe-update-revision-buffer: Section Movement. (line 65) +* magit-log-move-to-parent: Log Buffer. (line 26) +* magit-log-move-to-revision: Log Buffer. (line 31) +* magit-log-other: Logging. (line 49) +* magit-log-refresh: Refreshing Logs. (line 12) +* magit-log-refresh <1>: Refreshing Logs. (line 17) +* magit-log-refresh <2>: Log Buffer. (line 7) +* magit-log-related: Logging. (line 43) +* magit-log-save-default-arguments: Refreshing Logs. (line 27) +* magit-log-select-pick: Select from Log. (line 21) +* magit-log-select-quit: Select from Log. (line 26) +* magit-log-set-default-arguments: Refreshing Logs. (line 21) +* magit-log-toggle-commit-limit: Log Buffer. (line 59) +* magit-log-trace-definition: Commands for Buffers Visiting Files. + (line 76) +* magit-margin-settings: Log Margin. (line 52) +* magit-maybe-set-dedicated: Switching Buffers. (line 89) +* magit-merge: Merging. (line 10) +* magit-merge <1>: Merging. (line 82) +* magit-merge-abort: Merging. (line 87) +* magit-merge-absorb: Merging. (line 42) +* magit-merge-editmsg: Merging. (line 30) +* magit-merge-into: Merging. (line 54) +* magit-merge-nocommit: Merging. (line 36) +* magit-merge-plain: Merging. (line 18) +* magit-merge-preview: Merging. (line 75) +* magit-merge-squash: Merging. (line 67) +* magit-mode-bury-buffer: Quitting Windows. (line 7) +* magit-mode-display-buffer: Refreshing Buffers. (line 32) +* magit-mode-quit-window: Quitting Windows. (line 31) +* magit-mode-setup: Refreshing Buffers. (line 17) +* magit-notes: Notes. (line 9) +* magit-notes-edit: Notes. (line 14) +* magit-notes-merge: Notes. (line 35) +* magit-notes-merge-abort: Notes. (line 47) +* magit-notes-merge-commit: Notes. (line 43) +* magit-notes-prune: Notes. (line 28) +* magit-notes-remove: Notes. (line 21) +* magit-patch: Plain Patches. (line 7) +* magit-patch-apply: Plain Patches. (line 20) +* magit-patch-apply <1>: Maildir Patches. (line 23) +* magit-patch-create: Plain Patches. (line 12) +* magit-patch-save: Plain Patches. (line 26) +* magit-pop-revision-stack: Using the Revision Stack. + (line 7) +* magit-process: Viewing Git Output. (line 17) +* magit-process-file: Getting a Value from Git. + (line 57) +* magit-process-git: Getting a Value from Git. + (line 50) +* magit-process-kill: Viewing Git Output. (line 24) +* magit-pull: Pulling. (line 10) +* magit-pull-branch: Pulling. (line 28) +* magit-pull-from-pushremote: Pulling. (line 14) +* magit-pull-from-upstream: Pulling. (line 21) +* magit-push: Pushing. (line 10) +* magit-push-current: Pushing. (line 29) +* magit-push-current-to-pushremote: Pushing. (line 15) +* magit-push-current-to-upstream: Pushing. (line 22) +* magit-push-implicitly: Pushing. (line 74) +* magit-push-matching: Pushing. (line 45) +* magit-push-other: Pushing. (line 33) +* magit-push-refspecs: Pushing. (line 37) +* magit-push-tag: Pushing. (line 59) +* magit-push-tags: Pushing. (line 52) +* magit-push-to-remote: Pushing. (line 91) +* magit-rebase: Rebasing. (line 10) +* magit-rebase-abort: Rebasing. (line 111) +* magit-rebase-autosquash: Rebasing. (line 79) +* magit-rebase-branch: Rebasing. (line 42) +* magit-rebase-continue: Rebasing. (line 97) +* magit-rebase-edit: Rebasing. (line 107) +* magit-rebase-edit-commit: Rebasing. (line 83) +* magit-rebase-interactive: Rebasing. (line 76) +* magit-rebase-onto-pushremote: Rebasing. (line 28) +* magit-rebase-onto-upstream: Rebasing. (line 35) +* magit-rebase-remove-commit: Rebasing. (line 91) +* magit-rebase-reword-commit: Rebasing. (line 87) +* magit-rebase-skip: Rebasing. (line 103) +* magit-rebase-subset: Rebasing. (line 47) +* magit-reflog-current: Reflog. (line 12) +* magit-reflog-head: Reflog. (line 18) +* magit-reflog-other: Reflog. (line 15) +* magit-refresh: Automatic Refreshing of Magit Buffers. + (line 26) +* magit-refresh-all: Automatic Refreshing of Magit Buffers. + (line 34) +* magit-refs-set-show-commit-count: References Buffer. (line 34) +* magit-region-sections: Section Selection. (line 9) +* magit-region-values: Section Selection. (line 35) +* magit-remote: Remote Commands. (line 14) +* magit-remote-add: Remote Commands. (line 48) +* magit-remote-configure: Remote Commands. (line 32) +* magit-remote-prune: Remote Commands. (line 63) +* magit-remote-prune-refspecs: Remote Commands. (line 67) +* magit-remote-remove: Remote Commands. (line 60) +* magit-remote-rename: Remote Commands. (line 52) +* magit-remote-set-url: Remote Commands. (line 56) +* magit-repolist-column-branch: Repository List. (line 52) +* magit-repolist-column-branches: Repository List. (line 59) +* magit-repolist-column-flag: Repository List. (line 65) +* magit-repolist-column-ident: Repository List. (line 41) +* magit-repolist-column-path: Repository List. (line 45) +* magit-repolist-column-stashes: Repository List. (line 62) +* magit-repolist-column-unpulled-from-pushremote: Repository List. + (line 81) +* magit-repolist-column-unpulled-from-upstream: Repository List. + (line 77) +* magit-repolist-column-unpushed-to-pushremote: Repository List. + (line 89) +* magit-repolist-column-unpushed-to-upstream: Repository List. + (line 85) +* magit-repolist-column-upstream: Repository List. (line 55) +* magit-repolist-column-version: Repository List. (line 48) +* magit-repolist-fetch: Repository List. (line 105) +* magit-repolist-find-file-other-frame: Repository List. (line 109) +* magit-repolist-mark: Repository List. (line 99) +* magit-repolist-status: Repository List. (line 96) +* magit-repolist-unmark: Repository List. (line 102) +* magit-reset-hard: Resetting. (line 24) +* magit-reset-index: Staging and Unstaging. + (line 78) +* magit-reset-index <1>: Resetting. (line 33) +* magit-reset-keep: Resetting. (line 28) +* magit-reset-mixed: Resetting. (line 15) +* magit-reset-quickly: Resetting. (line 9) +* magit-reset-soft: Resetting. (line 19) +* magit-reset-worktree: Resetting. (line 39) +* magit-reset-worktree <1>: Wip Modes. (line 64) +* magit-restore-window-configuration: Quitting Windows. (line 21) +* magit-reverse: Applying. (line 47) +* magit-reverse-in-index: Staging and Unstaging. + (line 58) +* magit-revert: Reverting. (line 7) +* magit-revert-and-commit: Reverting. (line 15) +* magit-revert-no-commit: Reverting. (line 20) +* magit-run: Running Git Manually. + (line 13) +* magit-run-git: Calling Git for Effect. + (line 34) +* magit-run-git-async: Calling Git for Effect. + (line 59) +* magit-run-git-gui: Running Git Manually. + (line 59) +* magit-run-git-with-editor: Calling Git for Effect. + (line 71) +* magit-run-git-with-input: Calling Git for Effect. + (line 37) +* magit-run-gitk: Running Git Manually. + (line 50) +* magit-run-gitk-all: Running Git Manually. + (line 53) +* magit-run-gitk-branches: Running Git Manually. + (line 56) +* magit-save-window-configuration: Switching Buffers. (line 80) +* magit-section-backward: Section Movement. (line 11) +* magit-section-backward-siblings: Section Movement. (line 19) +* magit-section-case: Matching Sections. (line 66) +* magit-section-cycle: Section Visibility. (line 13) +* magit-section-cycle-diffs: Section Visibility. (line 16) +* magit-section-cycle-global: Section Visibility. (line 20) +* magit-section-forward: Section Movement. (line 16) +* magit-section-forward-siblings: Section Movement. (line 24) +* magit-section-hide: Section Visibility. (line 42) +* magit-section-hide-children: Section Visibility. (line 54) +* magit-section-ident: Matching Sections. (line 10) +* magit-section-match: Matching Sections. (line 18) +* magit-section-set-window-start: Section Movement. (line 52) +* magit-section-show: Section Visibility. (line 39) +* magit-section-show-children: Section Visibility. (line 49) +* magit-section-show-headings: Section Visibility. (line 45) +* magit-section-show-level-1: Section Visibility. (line 26) +* magit-section-show-level-1-all: Section Visibility. (line 32) +* magit-section-show-level-2: Section Visibility. (line 26) +* magit-section-show-level-2-all: Section Visibility. (line 32) +* magit-section-show-level-3: Section Visibility. (line 26) +* magit-section-show-level-3-all: Section Visibility. (line 32) +* magit-section-show-level-4: Section Visibility. (line 26) +* magit-section-show-level-4-all: Section Visibility. (line 32) +* magit-section-toggle: Section Visibility. (line 10) +* magit-section-toggle-children: Section Visibility. (line 57) +* magit-section-up: Section Movement. (line 28) +* magit-section-value-if: Matching Sections. (line 57) +* magit-sequence-abort: Cherry Picking. (line 91) +* magit-sequence-abort <1>: Reverting. (line 35) +* magit-sequence-continue: Cherry Picking. (line 85) +* magit-sequence-continue <1>: Reverting. (line 29) +* magit-sequence-skip: Cherry Picking. (line 88) +* magit-sequence-skip <1>: Reverting. (line 32) +* magit-shell-command: Running Git Manually. + (line 38) +* magit-shell-command-topdir: Running Git Manually. + (line 34) +* magit-show-commit: Diffing. (line 63) +* magit-show-commit <1>: Blaming. (line 71) +* magit-show-refs: References Buffer. (line 7) +* magit-show-refs-current: References Buffer. (line 25) +* magit-show-refs-head: References Buffer. (line 21) +* magit-show-refs-other: References Buffer. (line 30) +* magit-snapshot-both: Stashing. (line 36) +* magit-snapshot-index: Stashing. (line 42) +* magit-snapshot-worktree: Stashing. (line 46) +* magit-sparse-checkout: Sparse checkouts. (line 17) +* magit-sparse-checkout-add: Sparse checkouts. (line 39) +* magit-sparse-checkout-disable: Sparse checkouts. (line 50) +* magit-sparse-checkout-enable: Sparse checkouts. (line 21) +* magit-sparse-checkout-reapply: Sparse checkouts. (line 44) +* magit-sparse-checkout-set: Sparse checkouts. (line 33) +* magit-stage: Staging and Unstaging. + (line 29) +* magit-stage-file: Staging from File-Visiting Buffers. + (line 11) +* magit-stage-file <1>: Commands for Buffers Visiting Files. + (line 29) +* magit-stage-modified: Staging and Unstaging. + (line 36) +* magit-start-git: Calling Git for Effect. + (line 82) +* magit-start-process: Calling Git for Effect. + (line 100) +* magit-stash: Stashing. (line 9) +* magit-stash-apply: Stashing. (line 52) +* magit-stash-both: Stashing. (line 14) +* magit-stash-branch: Stashing. (line 70) +* magit-stash-branch-here: Stashing. (line 74) +* magit-stash-clear: Stashing. (line 82) +* magit-stash-drop: Stashing. (line 63) +* magit-stash-format-patch: Stashing. (line 79) +* magit-stash-index: Stashing. (line 20) +* magit-stash-keep-index: Stashing. (line 30) +* magit-stash-list: Stashing. (line 85) +* magit-stash-pop: Stashing. (line 57) +* magit-stash-show: Diffing. (line 67) +* magit-stash-show <1>: Stashing. (line 67) +* magit-stash-worktree: Stashing. (line 24) +* magit-stashes-maybe-update-stash-buffer: Section Movement. (line 92) +* magit-status: Status Buffer. (line 23) +* magit-status-maybe-update-blob-buffer: Section Movement. (line 87) +* magit-status-maybe-update-revision-buffer: Section Movement. + (line 77) +* magit-status-maybe-update-stash-buffer: Section Movement. (line 82) +* magit-status-quick: Status Buffer. (line 70) +* magit-submodule: Submodule Transient. (line 7) +* magit-submodule-add: Submodule Transient. (line 20) +* magit-submodule-fetch: Fetching. (line 48) +* magit-submodule-populate: Submodule Transient. (line 32) +* magit-submodule-register: Submodule Transient. (line 26) +* magit-submodule-synchronize: Submodule Transient. (line 40) +* magit-submodule-unpopulate: Submodule Transient. (line 45) +* magit-submodule-update: Submodule Transient. (line 36) +* magit-subtree: Subtree. (line 9) +* magit-subtree-add: Subtree. (line 24) +* magit-subtree-add-commit: Subtree. (line 28) +* magit-subtree-export: Subtree. (line 37) +* magit-subtree-import: Subtree. (line 13) +* magit-subtree-merge: Subtree. (line 31) +* magit-subtree-pull: Subtree. (line 34) +* magit-subtree-push: Subtree. (line 48) +* magit-subtree-split: Subtree. (line 52) +* magit-switch-to-repository-buffer: Common Commands. (line 6) +* magit-switch-to-repository-buffer-other-frame: Common Commands. + (line 8) +* magit-switch-to-repository-buffer-other-window: Common Commands. + (line 7) +* magit-tag: Tagging. (line 9) +* magit-tag-create: Tagging. (line 14) +* magit-tag-delete: Tagging. (line 37) +* magit-tag-prune: Tagging. (line 43) +* magit-tag-release: Tagging. (line 18) +* magit-toggle-buffer-lock: Modes and Buffers. (line 18) +* magit-toggle-git-debug: Debugging Tools. (line 29) +* magit-toggle-margin: Refreshing Logs. (line 34) +* magit-toggle-margin <1>: Log Margin. (line 60) +* magit-toggle-margin-details: Log Margin. (line 66) +* magit-toggle-verbose-refresh: Debugging Tools. (line 52) +* magit-unstage: Staging and Unstaging. + (line 42) +* magit-unstage-all: Staging and Unstaging. + (line 50) +* magit-unstage-file: Staging from File-Visiting Buffers. + (line 18) +* magit-unstage-file <1>: Commands for Buffers Visiting Files. + (line 32) +* magit-version: Git Executable. (line 59) +* magit-version <1>: Debugging Tools. (line 11) +* magit-visit-ref: References Buffer. (line 159) +* magit-wip-commit: Wip Modes. (line 85) +* magit-wip-log: Wip Modes. (line 47) +* magit-wip-log-current: Wip Modes. (line 55) +* magit-worktree: Worktree. (line 9) +* magit-worktree-branch: Worktree. (line 16) +* magit-worktree-checkout: Worktree. (line 13) +* magit-worktree-delete: Worktree. (line 22) +* magit-worktree-move: Worktree. (line 19) +* magit-worktree-status: Worktree. (line 26) +* scroll-down: Commands Available in Diffs. + (line 56) +* scroll-up: Commands Available in Diffs. + (line 53) +* with-editor-cancel: Editing Commit Messages. + (line 22) +* with-editor-cancel <1>: Editing Rebase Sequences. + (line 11) +* with-editor-debug: Debugging Tools. (line 64) +* with-editor-finish: Editing Commit Messages. + (line 18) +* with-editor-finish <1>: Editing Rebase Sequences. + (line 7) +* with-editor-usage-message: Commit Mode and Hooks. + (line 52) + + +File: magit.info, Node: Variable Index, Prev: Function and Command Index, Up: Top + +Appendix E Variable Index +************************* + +[index] +* Menu: + +* auto-revert-buffer-list-filter: Automatic Reverting of File-Visiting Buffers. + (line 73) +* auto-revert-interval: Automatic Reverting of File-Visiting Buffers. + (line 69) +* auto-revert-mode: Automatic Reverting of File-Visiting Buffers. + (line 57) +* auto-revert-stop-on-user-input: Automatic Reverting of File-Visiting Buffers. + (line 65) +* auto-revert-use-notify: Automatic Reverting of File-Visiting Buffers. + (line 46) +* auto-revert-verbose: Automatic Reverting of File-Visiting Buffers. + (line 94) +* branch.autoSetupMerge: Branch Git Variables. + (line 71) +* branch.autoSetupRebase: Branch Git Variables. + (line 85) +* branch.NAME.description: Branch Git Variables. + (line 42) +* branch.NAME.merge: Branch Git Variables. + (line 10) +* branch.NAME.pushRemote: Branch Git Variables. + (line 29) +* branch.NAME.rebase: Branch Git Variables. + (line 20) +* branch.NAME.remote: Branch Git Variables. + (line 15) +* core.notesRef: Notes. (line 53) +* git-commit-fill-column: Commit Message Conventions. + (line 18) +* git-commit-finish-query-functions: Commit Message Conventions. + (line 22) +* git-commit-known-pseudo-headers: Commit Pseudo Headers. + (line 9) +* git-commit-major-mode: Commit Mode and Hooks. + (line 12) +* git-commit-post-finish-hook: Commit Mode and Hooks. + (line 55) +* git-commit-setup-hook: Commit Mode and Hooks. + (line 21) +* git-commit-style-convention-checks: Commit Message Conventions. + (line 42) +* git-commit-summary-max-length: Commit Message Conventions. + (line 13) +* git-rebase-auto-advance: Editing Rebase Sequences. + (line 80) +* git-rebase-confirm-cancel: Editing Rebase Sequences. + (line 86) +* git-rebase-show-instructions: Editing Rebase Sequences. + (line 83) +* global-auto-revert-mode: Automatic Reverting of File-Visiting Buffers. + (line 21) +* magit-auto-revert-immediately: Automatic Reverting of File-Visiting Buffers. + (line 30) +* magit-auto-revert-mode: Automatic Reverting of File-Visiting Buffers. + (line 17) +* magit-auto-revert-tracked-only: Automatic Reverting of File-Visiting Buffers. + (line 51) +* magit-bisect-show-graph: Bisecting. (line 57) +* magit-blame-disable-modes: Blaming. (line 145) +* magit-blame-echo-style: Blaming. (line 131) +* magit-blame-goto-chunk-hook: Blaming. (line 150) +* magit-blame-read-only: Blaming. (line 141) +* magit-blame-styles: Blaming. (line 127) +* magit-blame-time-format: Blaming. (line 137) +* magit-branch-adjust-remote-upstream-alist: Branch Commands. (line 196) +* magit-branch-direct-configure: Branch Commands. (line 19) +* magit-branch-prefer-remote-upstream: Branch Commands. (line 152) +* magit-branch-read-upstream-first: Branch Commands. (line 147) +* magit-buffer-name-format: Naming Buffers. (line 25) +* magit-bury-buffer-function: Quitting Windows. (line 13) +* magit-cherry-margin: Cherries. (line 21) +* magit-clone-always-transient: Cloning Repository. (line 12) +* magit-clone-default-directory: Cloning Repository. (line 84) +* magit-clone-name-alist: Cloning Repository. (line 94) +* magit-clone-set-remote-head: Cloning Repository. (line 66) +* magit-clone-set-remote.pushDefault: Cloning Repository. (line 75) +* magit-clone-url-format: Cloning Repository. (line 114) +* magit-commit-ask-to-stage: Initiating a Commit. (line 65) +* magit-commit-diff-inhibit-same-window: Initiating a Commit. (line 97) +* magit-commit-extend-override-date: Initiating a Commit. (line 72) +* magit-commit-reword-override-date: Initiating a Commit. (line 75) +* magit-commit-show-diff: Initiating a Commit. (line 69) +* magit-commit-squash-confirm: Initiating a Commit. (line 78) +* magit-completing-read-function: Support for Completion Frameworks. + (line 27) +* magit-define-global-key-bindings: Default Bindings. (line 6) +* magit-diff-adjust-tab-width: Diff Options. (line 17) +* magit-diff-buffer-file-locked: Commands for Buffers Visiting Files. + (line 55) +* magit-diff-extra-stat-arguments: Diff Options. (line 112) +* magit-diff-hide-trailing-cr-characters: Diff Options. (line 77) +* magit-diff-highlight-hunk-region-functions: Diff Options. (line 80) +* magit-diff-highlight-indentation: Diff Options. (line 63) +* magit-diff-highlight-trailing: Diff Options. (line 59) +* magit-diff-paint-whitespace: Diff Options. (line 38) +* magit-diff-paint-whitespace-lines: Diff Options. (line 52) +* magit-diff-refine-hunk: Diff Options. (line 6) +* magit-diff-refine-ignore-whitespace: Diff Options. (line 13) +* magit-diff-unmarked-lines-keep-foreground: Diff Options. (line 105) +* magit-diff-visit-previous-blob: Visiting Files and Blobs from a Diff. + (line 38) +* magit-direct-use-buffer-arguments: Transient Arguments and Buffer Variables. + (line 73) +* magit-display-buffer-function: Switching Buffers. (line 25) +* magit-display-buffer-noselect: Switching Buffers. (line 17) +* magit-dwim-selection: Completion and Confirmation. + (line 42) +* magit-ediff-dwim-resolve-function: Ediffing. (line 105) +* magit-ediff-dwim-show-on-hunks: Ediffing. (line 111) +* magit-ediff-quit-hook: Ediffing. (line 124) +* magit-ediff-show-stash-with-index: Ediffing. (line 118) +* magit-generate-buffer-name-function: Naming Buffers. (line 6) +* magit-git-debug: Viewing Git Output. (line 26) +* magit-git-debug <1>: Getting a Value from Git. + (line 68) +* magit-git-executable: Git Executable. (line 26) +* magit-git-global-arguments: Global Git Arguments. + (line 6) +* magit-keep-region-overlay: The Selection. (line 52) +* magit-list-refs-sortby: Additional Completion Options. + (line 6) +* magit-log-auto-more: Log Buffer. (line 69) +* magit-log-buffer-file-locked: Commands for Buffers Visiting Files. + (line 78) +* magit-log-margin: Log Margin. (line 12) +* magit-log-margin-show-committer-date: Log Margin. (line 44) +* magit-log-section-commit-count: Status Sections. (line 114) +* magit-log-select-margin: Select from Log. (line 28) +* magit-log-show-refname-after-summary: Log Buffer. (line 74) +* magit-log-trace-definition-function: Commands Available in Diffs. + (line 17) +* magit-module-sections-hook: Status Module Sections. + (line 19) +* magit-module-sections-nested: Status Module Sections. + (line 22) +* magit-no-confirm: Action Confirmation. (line 18) +* magit-pop-revision-stack-format: Using the Revision Stack. + (line 34) +* magit-post-commit-hook: Initiating a Commit. (line 86) +* magit-post-display-buffer-hook: Switching Buffers. (line 85) +* magit-pre-display-buffer-hook: Switching Buffers. (line 76) +* magit-prefer-remote-upstream: Branch Git Variables. + (line 109) +* magit-prefix-use-buffer-arguments: Transient Arguments and Buffer Variables. + (line 65) +* magit-process-extreme-logging: Viewing Git Output. (line 56) +* magit-process-raise-error: Calling Git for Effect. + (line 125) +* magit-pull-or-fetch: Fetching. (line 51) +* magit-reflog-margin: Reflog. (line 20) +* magit-refresh-args: Refreshing Buffers. (line 52) +* magit-refresh-buffer-hook: Automatic Refreshing of Magit Buffers. + (line 41) +* magit-refresh-function: Refreshing Buffers. (line 47) +* magit-refresh-status-buffer: Automatic Refreshing of Magit Buffers. + (line 46) +* magit-refs-filter-alist: References Buffer. (line 137) +* magit-refs-focus-column-width: References Buffer. (line 75) +* magit-refs-margin: References Buffer. (line 89) +* magit-refs-margin-for-tags: References Buffer. (line 112) +* magit-refs-pad-commit-counts: References Buffer. (line 45) +* magit-refs-primary-column-width: References Buffer. (line 63) +* magit-refs-sections-hook: References Sections. (line 13) +* magit-refs-show-commit-count: References Buffer. (line 36) +* magit-refs-show-remote-prefix: References Buffer. (line 57) +* magit-remote-add-set-remote.pushDefault: Remote Commands. (line 83) +* magit-remote-direct-configure: Remote Commands. (line 20) +* magit-remote-git-executable: Git Executable. (line 32) +* magit-repolist-columns: Repository List. (line 13) +* magit-repository-directories: Status Buffer. (line 57) +* magit-revision-filter-files-on-follow: Revision Buffer. (line 55) +* magit-revision-insert-related-refs: Revision Buffer. (line 6) +* magit-revision-show-gravatars: Revision Buffer. (line 15) +* magit-revision-use-hash-sections: Revision Buffer. (line 31) +* magit-root-section: Matching Sections. (line 81) +* magit-save-repository-buffers: Automatic Saving of File-Visiting Buffers. + (line 13) +* magit-section-cache-visibility: Section Visibility. (line 82) +* magit-section-initial-visibility-alist: Section Visibility. (line 66) +* magit-section-movement-hook: Section Movement. (line 41) +* magit-section-set-visibility-hook: Section Visibility. (line 92) +* magit-section-show-child-count: Section Options. (line 9) +* magit-section-visibility-indicator: Section Visibility. (line 109) +* magit-shell-command-verbose-prompt: Running Git Manually. + (line 43) +* magit-stashes-margin: Stashing. (line 87) +* magit-status-headers-hook: Status Header Sections. + (line 17) +* magit-status-margin: Status Options. (line 9) +* magit-status-refresh-hook: Status Options. (line 6) +* magit-status-sections-hook: Status Sections. (line 10) +* magit-submodule-list-columns: Listing Submodules. (line 20) +* magit-this-process: Calling Git for Effect. + (line 121) +* magit-uniquify-buffer-names: Naming Buffers. (line 71) +* magit-unstage-committed: Staging and Unstaging. + (line 52) +* magit-update-other-window-delay: Section Movement. (line 97) +* magit-visit-ref-behavior: References Buffer. (line 168) +* magit-wip-after-apply-mode: Legacy Wip Modes. (line 18) +* magit-wip-after-apply-mode-lighter: Legacy Wip Modes. (line 54) +* magit-wip-after-save-local-mode-lighter: Legacy Wip Modes. (line 51) +* magit-wip-after-save-mode: Legacy Wip Modes. (line 13) +* magit-wip-before-change-mode: Legacy Wip Modes. (line 31) +* magit-wip-before-change-mode-lighter: Legacy Wip Modes. (line 57) +* magit-wip-initial-backup-mode: Legacy Wip Modes. (line 35) +* magit-wip-initial-backup-mode-lighter: Legacy Wip Modes. (line 60) +* magit-wip-merge-branch: Wip Graph. (line 6) +* magit-wip-mode: Wip Modes. (line 30) +* magit-wip-mode-lighter: Wip Modes. (line 98) +* magit-wip-namespace: Wip Modes. (line 91) +* notes.displayRef: Notes. (line 57) +* pull.rebase: Branch Git Variables. + (line 50) +* remote.NAME.fetch: Remote Git Variables. + (line 14) +* remote.NAME.push: Remote Git Variables. + (line 23) +* remote.NAME.pushurl: Remote Git Variables. + (line 18) +* remote.NAME.tagOpts: Remote Git Variables. + (line 27) +* remote.NAME.url: Remote Git Variables. + (line 10) +* remote.pushDefault: Branch Git Variables. + (line 62) + + + +Tag Table: +Node: Top754 +Node: Introduction6582 +Node: Installation11298 +Node: Installing from Melpa11628 +Node: Installing from the Git Repository12701 +Node: Post-Installation Tasks15433 +Node: Getting Started16718 +Node: Interface Concepts22050 +Node: Modes and Buffers22429 +Node: Switching Buffers24140 +Node: Naming Buffers28879 +Node: Quitting Windows32182 +Node: Automatic Refreshing of Magit Buffers33920 +Node: Automatic Saving of File-Visiting Buffers36801 +Node: Automatic Reverting of File-Visiting Buffers37985 +Node: Risk of Reverting Automatically42970 +Node: Sections45352 +Node: Section Movement46278 +Node: Section Visibility51152 +Node: Section Hooks57167 +Node: Section Types and Values59573 +Node: Section Options60988 +Node: Transient Commands61459 +Node: Transient Arguments and Buffer Variables62691 +Node: Completion Confirmation and the Selection69708 +Node: Action Confirmation70154 +Node: Completion and Confirmation78006 +Node: The Selection81191 +Node: The hunk-internal region84089 +Node: Support for Completion Frameworks85178 +Node: Additional Completion Options90081 +Node: Mouse Support90679 +Node: Running Git91255 +Node: Viewing Git Output91500 +Node: Git Process Status94204 +Node: Running Git Manually95169 +Node: Git Executable97859 +Node: Global Git Arguments100867 +Node: Inspecting101672 +Node: Status Buffer102829 +Node: Status Sections107839 +Node: Status Header Sections113366 +Node: Status Module Sections115985 +Node: Status Options118482 +Node: Repository List119945 +Node: Logging124512 +Node: Refreshing Logs127354 +Node: Log Buffer128775 +Node: Log Margin132975 +Node: Select from Log136128 +Node: Reflog138338 +Node: Cherries139975 +Node: Diffing141813 +Node: Refreshing Diffs144847 +Node: Commands Available in Diffs148356 +Node: Diff Options150870 +Node: Revision Buffer156333 +Node: Ediffing159653 +Node: References Buffer165703 +Node: References Sections176297 +Node: Bisecting177154 +Node: Visiting Files and Blobs179465 +Node: General-Purpose Visit Commands179935 +Node: Visiting Files and Blobs from a Diff180888 +Node: Blaming184332 +Node: Manipulating190467 +Node: Creating Repository190809 +Node: Cloning Repository191346 +Node: Staging and Unstaging197535 +Node: Staging from File-Visiting Buffers201516 +Node: Applying202622 +Node: Committing204695 +Node: Initiating a Commit205278 +Node: Editing Commit Messages210467 +Node: Using the Revision Stack213240 +Node: Commit Pseudo Headers216285 +Node: Commit Mode and Hooks217580 +Node: Commit Message Conventions220514 +Node: Branching222637 +Node: The Two Remotes222863 +Node: Branch Commands225516 +Node: Branch Git Variables238061 +Node: Auxiliary Branch Commands243434 +Node: Merging244550 +Node: Resolving Conflicts248508 +Node: Rebasing253879 +Node: Editing Rebase Sequences258668 +Node: Information About In-Progress Rebase262884 +Ref: Information About In-Progress Rebase-Footnote-1271997 +Node: Cherry Picking272593 +Node: Reverting276927 +Node: Resetting278346 +Node: Stashing280172 +Node: Transferring284783 +Node: Remotes285005 +Node: Remote Commands285157 +Node: Remote Git Variables289196 +Node: Fetching290467 +Node: Pulling292913 +Node: Pushing293939 +Node: Plain Patches298230 +Node: Maildir Patches299701 +Node: Miscellaneous301180 +Node: Tagging301526 +Node: Notes303419 +Node: Submodules305754 +Node: Listing Submodules305972 +Node: Submodule Transient308108 +Node: Subtree310585 +Node: Worktree312516 +Node: Sparse checkouts313592 +Node: Bundle316368 +Node: Common Commands316743 +Node: Wip Modes319371 +Node: Wip Graph324262 +Node: Legacy Wip Modes326575 +Node: Commands for Buffers Visiting Files329462 +Node: Minor Mode for Buffers Visiting Blobs335020 +Node: Customizing335818 +Node: Per-Repository Configuration337414 +Node: Essential Settings339668 +Node: Safety340013 +Node: Performance341774 +Ref: Log Performance344803 +Ref: Diff Performance346113 +Ref: Refs Buffer Performance347454 +Ref: Committing Performance348029 +Node: Microsoft Windows Performance348942 +Node: MacOS Performance350133 +Ref: MacOS Performance-Footnote-1350838 +Node: Default Bindings350920 +Node: Plumbing353161 +Node: Calling Git353990 +Node: Getting a Value from Git355515 +Node: Calling Git for Effect359243 +Node: Section Plumbing365137 +Node: Creating Sections365365 +Node: Section Selection369261 +Node: Matching Sections371057 +Node: Refreshing Buffers376978 +Node: Conventions380122 +Node: Theming Faces380314 +Node: FAQ388419 +Node: FAQ - How to ...?388857 +Node: How to pronounce Magit?389214 +Node: How to show git's output?390016 +Node: How to install the gitman info manual?390802 +Node: How to show diffs for gpg-encrypted files?391772 +Node: How does branching and pushing work?392368 +Node: Should I disable VC?392700 +Node: FAQ - Issues and Errors393303 +Node: Magit is slow394306 +Node: I changed several thousand files at once and now Magit is unusable394599 +Node: I am having problems committing395325 +Node: I am using MS Windows and cannot push with Magit395806 +Node: I am using macOS and SOMETHING works in shell but not in Magit396424 +Node: Expanding a file to show the diff causes it to disappear397258 +Node: Point is wrong in the COMMIT_EDITMSG buffer397840 +Node: The mode-line information isn't always up-to-date398886 +Node: A branch and tag sharing the same name breaks SOMETHING399949 +Node: My Git hooks work on the command-line but not inside Magit400835 +Node: git-commit-mode isn't used when committing from the command-line401681 +Node: Point ends up inside invisible text when jumping to a file-visiting buffer403952 +Node: I am unable to stage when using Tramp from MS Windows404812 +Node: I am no longer able to save popup defaults405719 +Node: Debugging Tools406679 +Node: Keystroke Index409853 +Node: Function and Command Index444181 +Node: Variable Index496211 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/magit-section-20220810.1158/dir b/code/elpa/magit-section-20220810.1158/dir new file mode 100644 index 0000000..6e44681 --- /dev/null +++ b/code/elpa/magit-section-20220810.1158/dir @@ -0,0 +1,19 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* Magit-Section: (magit-section). + Use Magit sections in your own packages. diff --git a/code/elpa/magit-section-20220810.1158/magit-section-autoloads.el b/code/elpa/magit-section-20220810.1158/magit-section-autoloads.el new file mode 100644 index 0000000..e31297d --- /dev/null +++ b/code/elpa/magit-section-20220810.1158/magit-section-autoloads.el @@ -0,0 +1,26 @@ +;;; magit-section-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "magit-section" "magit-section.el" (0 0 0 0)) +;;; Generated autoloads from magit-section.el + +(register-definition-prefixes "magit-section" '("isearch-clean-overlays@magit-mode" "magit-")) + +;;;*** + +;;;### (autoloads nil nil ("magit-section-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; magit-section-autoloads.el ends here diff --git a/code/elpa/magit-section-20220810.1158/magit-section-pkg.el b/code/elpa/magit-section-20220810.1158/magit-section-pkg.el new file mode 100644 index 0000000..9d9aa8e --- /dev/null +++ b/code/elpa/magit-section-20220810.1158/magit-section-pkg.el @@ -0,0 +1,14 @@ +(define-package "magit-section" "20220810.1158" "Sections for read-only buffers." + '((emacs "25.1") + (compat "28.1.1.2") + (dash "20210826")) + :commit "712be4632b0ddc7899ca90db8f9be20d90b4326f" :authors + '(("Jonas Bernoulli" . "jonas@bernoul.li")) + :maintainer + '("Jonas Bernoulli" . "jonas@bernoul.li") + :keywords + '("tools") + :url "https://github.com/magit/magit") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/magit-section-20220810.1158/magit-section.el b/code/elpa/magit-section-20220810.1158/magit-section.el new file mode 100644 index 0000000..0d3e062 --- /dev/null +++ b/code/elpa/magit-section-20220810.1158/magit-section.el @@ -0,0 +1,2216 @@ +;;; magit-section.el --- Sections for read-only buffers -*- lexical-binding:t; coding:utf-8 -*- + +;; Copyright (C) 2008-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Maintainer: Jonas Bernoulli + +;; Homepage: https://github.com/magit/magit +;; Keywords: tools + +;; Package-Version: 3.3.0-git +;; Package-Requires: ((emacs "25.1") (compat "28.1.1.2") (dash "2.19.1")) + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit 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. +;; +;; Magit 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 Magit. If not, see . + +;; You should have received a copy of the AUTHORS.md file, which +;; lists all contributors. If not, see https://magit.vc/authors. + +;;; Commentary: + +;; This package implements the main user interface of Magit — the +;; collapsible sections that make up its buffers. This package used +;; to be distributed as part of Magit but now it can also be used by +;; other packages that have nothing to do with Magit or Git. + +;;; Code: + +(require 'cl-lib) +(require 'compat) +(require 'compat-26) +(require 'compat-27) +(require 'dash) +(require 'eieio) +(require 'format-spec) +(require 'seq) +(require 'subr-x) + +(eval-when-compile (require 'benchmark)) + +;; For `magit-section-get-relative-position' +(declare-function magit-hunk-section-p "magit-diff" (section) t) + +;;; Hooks + +(defvar magit-section-movement-hook nil + "Hook run by `magit-section-goto'. +That function in turn is used by all section movement commands.") + +(defvar magit-section-highlight-hook + '(magit-section-highlight + magit-section-highlight-selection) + "Functions used to highlight the current section. +Each function is run with the current section as only argument +until one of them returns non-nil.") + +(defvar magit-section-unhighlight-hook nil + "Functions used to unhighlight the previously current section. +Each function is run with the current section as only argument +until one of them returns non-nil. Most sections are properly +unhighlighted without requiring a specialized unhighlighter, +diff-related sections being the only exception.") + +(defvar magit-section-set-visibility-hook + '(magit-section-cached-visibility) + "Hook used to set the initial visibility of a section. +Stop at the first function that returns non-nil. The returned +value should be `show', `hide' or nil. If no function returns +non-nil, determine the visibility as usual, i.e. use the +hardcoded section specific default (see `magit-insert-section').") + +;;; Options + +(defgroup magit-section nil + "Expandable sections." + :link '(info-link "(magit)Sections") + :group 'extensions) + +(defcustom magit-section-show-child-count t + "Whether to append the number of children to section headings. +This only applies to sections for which doing so makes sense." + :package-version '(magit-section . "2.1.0") + :group 'magit-section + :type 'boolean) + +(defcustom magit-section-cache-visibility t + "Whether to cache visibility of sections. + +Sections always retain their visibility state when they are being +recreated during a refresh. But if a section disappears and then +later reappears again, then this option controls whether this is +the case. + +If t, then cache the visibility of all sections. If a list of +section types, then only do so for matching sections. If nil, +then don't do so for any sections." + :package-version '(magit-section . "2.12.0") + :group 'magit-section + :type '(choice (const :tag "Don't cache visibility" nil) + (const :tag "Cache visibility of all sections" t) + (repeat :tag "Cache visibility for section types" symbol))) + +(defcustom magit-section-initial-visibility-alist + '((stashes . hide)) + "Alist controlling the initial visibility of sections. + +Each element maps a section type or lineage to the initial +visibility state for such sections. The state has to be one of +`show' or `hide', or a function that returns one of these symbols. +A function is called with the section as the only argument. + +Use the command `magit-describe-section' to determine a section's +lineage or type. The vector in the output is the section lineage +and the type is the first element of that vector. Wildcards can +be used, see `magit-section-match'. + +Currently this option is only used to override hardcoded defaults, +but in the future it will also be used set the defaults. + +An entry whose key is `magit-status-initial-section' specifies +the visibility of the section `magit-status-goto-initial-section' +jumps to. This does not only override defaults, but also other +entries of this alist." + :package-version '(magit-section . "2.12.0") + :group 'magit-section + :type '(alist :key-type (sexp :tag "Section type/lineage") + :value-type (choice (const hide) + (const show) + function))) + +(defcustom magit-section-visibility-indicator + (if (window-system) + '(magit-fringe-bitmap> . magit-fringe-bitmapv) + (cons (if (char-displayable-p ?…) "…" "...") + t)) + "Whether and how to indicate that a section can be expanded/collapsed. + +If nil, then don't show any indicators. +Otherwise the value has to have one of these two forms: + +\(EXPANDABLE-BITMAP . COLLAPSIBLE-BITMAP) + + Both values have to be variables whose values are fringe + bitmaps. In this case every section that can be expanded or + collapsed gets an indicator in the left fringe. + + To provide extra padding around the indicator, set + `left-fringe-width' in `magit-mode-hook'. + +\(STRING . BOOLEAN) + + In this case STRING (usually an ellipsis) is shown at the end + of the heading of every collapsed section. Expanded sections + get no indicator. The cdr controls whether the appearance of + these ellipsis take section highlighting into account. Doing + so might potentially have an impact on performance, while not + doing so is kinda ugly." + :package-version '(magit-section . "3.0.0") + :group 'magit-section + :type '(choice (const :tag "No indicators" nil) + (cons :tag "Use +- fringe indicators" + (const magit-fringe-bitmap+) + (const magit-fringe-bitmap-)) + (cons :tag "Use >v fringe indicators" + (const magit-fringe-bitmap>) + (const magit-fringe-bitmapv)) + (cons :tag "Use bold >v fringe indicators)" + (const magit-fringe-bitmap-bold>) + (const magit-fringe-bitmap-boldv)) + (cons :tag "Use custom fringe indicators" + (variable :tag "Expandable bitmap variable") + (variable :tag "Collapsible bitmap variable")) + (cons :tag "Use ellipses at end of headings" + (string :tag "Ellipsis" "…") + (choice :tag "Use face kludge" + (const :tag "Yes (potentially slow)" t) + (const :tag "No (kinda ugly)" nil))))) + +(define-obsolete-variable-alias 'magit-keep-region-overlay + 'magit-section-keep-region-overlay "Magit-Section 3.4.0") +(defcustom magit-section-keep-region-overlay nil + "Whether to keep the region overlay when there is a valid selection. + +By default Magit removes the regular region overlay if, and only +if, that region constitutes a valid selection as understood by +Magit commands. Otherwise it does not remove that overlay, and +the region looks like it would in other buffers. + +There are two types of such valid selections: hunk-internal +regions and regions that select two or more sibling sections. +In such cases Magit removes the region overlay and instead +highlights a slightly larger range. All text (for hunk-internal +regions) or the headings of all sections (for sibling selections) +that are inside that range (not just inside the region) are acted +on by commands such as the staging command. This buffer range +begins at the beginning of the line on which the region begins +and ends at the end of the line on which the region ends. + +Because Magit acts on this larger range and not the region, it is +actually quite important to visualize that larger range. If we +don't do that, then one might think that these commands act on +the region instead. If you want to *also* visualize the region, +then set this option to t. But please note that when the region +does *not* constitute a valid selection, then the region is +*always* visualized as usual, and that it is usually under such +circumstances that you want to use a non-magit command to act on +the region. + +Besides keeping the region overlay, setting this option to t also +causes all face properties, except for `:foreground', to be +ignored for the faces used to highlight headings of selected +sections. This avoids the worst conflicts that result from +displaying the region and the selection overlays at the same +time. We are not interested in dealing with other conflicts. +In fact we *already* provide a way to avoid all of these +conflicts: *not* changing the value of this option. + +It should be clear by now that we consider it a mistake to set +this to display the region when the Magit selection is also +visualized, but since it has been requested a few times and +because it doesn't cost much to offer this option we do so. +However that might change. If the existence of this option +starts complicating other things, then it will be removed." + :package-version '(magit-section . "2.3.0") + :group 'magit-section + :type 'boolean) + +(defcustom magit-section-disable-line-numbers t + "In Magit buffers, whether to disable modes that display line numbers. + +Some users who turn on `global-display-line-numbers-mode' (or +`global-nlinum-mode' or `global-linum-mode') expect line numbers +to be displayed everywhere except in Magit buffers. Other users +do not expect Magit buffers to be treated differently. At least +in theory users in the first group should not use the global mode, +but that ship has sailed, thus this option." + :package-version '(magit-section . "3.0.0") + :group 'magit-section + :type 'boolean) + +(defcustom magit-section-show-context-menu-for-emacs<28 nil + "Whether `mouse-3' shows a context menu for Emacs < 28. + +This has to be set before loading `magit-section' or it has +no effect. This also has no effect for Emacs >= 28, where +`context-menu-mode' should be enabled instead." + :package-version '(magit-section . "3.4.0") + :group 'magit-section + :type 'boolean) + +;;; Variables + +(defvar-local magit-section-pre-command-region-p nil) +(defvar-local magit-section-pre-command-section nil) +(defvar-local magit-section-highlight-force-update nil) +(defvar-local magit-section-highlight-overlays nil) +(defvar-local magit-section-highlighted-sections nil) +(defvar-local magit-section-unhighlight-sections nil) + +;;; Faces + +(defgroup magit-section-faces nil + "Faces used by Magit-Section." + :group 'magit-section + :group 'faces) + +(defface magit-section-highlight + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey95") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey20")) + "Face for highlighting the current section." + :group 'magit-section-faces) + +(defface magit-section-heading + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "DarkGoldenrod4" + :weight bold) + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "LightGoldenrod2" + :weight bold)) + "Face for section headings." + :group 'magit-section-faces) + +(defface magit-section-secondary-heading + `((t ,@(and (>= emacs-major-version 27) '(:extend t)) + :weight bold)) + "Face for section headings of some secondary headings." + :group 'magit-section-faces) + +(defface magit-section-heading-selection + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "salmon4") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :foreground "LightSalmon3")) + "Face for selected section headings." + :group 'magit-section-faces) + +(defface magit-section-child-count '((t nil)) + "Face used for child counts at the end of some section headings." + :group 'magit-section-faces) + +;;; Classes + +(defvar magit--current-section-hook nil + "Internal variable used for `magit-describe-section'.") + +(defvar magit--section-type-alist nil) + +(defclass magit-section () + ((keymap :initform nil) + (type :initform nil :initarg :type) + (value :initform nil :initarg :value) + (start :initform nil :initarg :start) + (content :initform nil) + (end :initform nil) + (hidden :initform nil) + (washer :initform nil) + (process :initform nil) + (heading-highlight-face :initform nil) + (inserter :initform (symbol-value 'magit--current-section-hook)) + (parent :initform nil :initarg :parent) + (children :initform nil))) + +;;; Mode + +(defvar symbol-overlay-inhibit-map) + +(defvar magit-section-heading-map + (let ((map (make-sparse-keymap))) + (define-key map [double-down-mouse-1] #'ignore) + (define-key map [double-mouse-1] #'magit-mouse-toggle-section) + (define-key map [double-mouse-2] #'magit-mouse-toggle-section) + map) + "Keymap used in the heading line of all expandable sections. +This keymap is used in addition to the section-specifi keymap, +if any.") + +(defvar magit-section-mode-map + (let ((map (make-keymap))) + (suppress-keymap map t) + (when (and magit-section-show-context-menu-for-emacs<28 + (< emacs-major-version 28)) + (define-key map [mouse-3] nil) + (define-key + map [down-mouse-3] + `( menu-item "" ,(make-sparse-keymap) + :filter ,(lambda (_) + (let ((menu (make-sparse-keymap))) + (if (fboundp 'context-menu-local) + (context-menu-local menu last-input-event) + (magit--context-menu-local menu last-input-event)) + (magit-section-context-menu menu last-input-event) + menu))))) + (define-key map [left-fringe mouse-1] #'magit-mouse-toggle-section) + (define-key map [left-fringe mouse-2] #'magit-mouse-toggle-section) + (define-key map (kbd "TAB") #'magit-section-toggle) + (define-key map [C-tab] #'magit-section-cycle) + (define-key map [M-tab] #'magit-section-cycle) + ;; [backtab] is the most portable binding for Shift+Tab. + (define-key map [backtab] #'magit-section-cycle-global) + (define-key map (kbd "^") #'magit-section-up) + (define-key map (kbd "p") #'magit-section-backward) + (define-key map (kbd "n") #'magit-section-forward) + (define-key map (kbd "M-p") #'magit-section-backward-sibling) + (define-key map (kbd "M-n") #'magit-section-forward-sibling) + (define-key map (kbd "1") #'magit-section-show-level-1) + (define-key map (kbd "2") #'magit-section-show-level-2) + (define-key map (kbd "3") #'magit-section-show-level-3) + (define-key map (kbd "4") #'magit-section-show-level-4) + (define-key map (kbd "M-1") #'magit-section-show-level-1-all) + (define-key map (kbd "M-2") #'magit-section-show-level-2-all) + (define-key map (kbd "M-3") #'magit-section-show-level-3-all) + (define-key map (kbd "M-4") #'magit-section-show-level-4-all) + map) + "Parent keymap for all keymaps of modes derived from `magit-section-mode'.") + +(define-derived-mode magit-section-mode special-mode "Magit-Sections" + "Parent major mode from which major modes with Magit-like sections inherit. + +Magit-Section is documented in info node `(magit-section)'." + :group 'magit-section + (buffer-disable-undo) + (setq truncate-lines t) + (setq buffer-read-only t) + (setq-local line-move-visual t) ; see #1771 + ;; Turn off syntactic font locking, but not by setting + ;; `font-lock-defaults' because that would enable font locking, and + ;; not all magit plugins may be ready for that (see #3950). + (setq-local font-lock-syntactic-face-function #'ignore) + (setq show-trailing-whitespace nil) + (setq-local symbol-overlay-inhibit-map t) + (setq list-buffers-directory (abbreviate-file-name default-directory)) + ;; (hack-dir-local-variables-non-file-buffer) + (make-local-variable 'text-property-default-nonsticky) + (push (cons 'keymap t) text-property-default-nonsticky) + (add-hook 'pre-command-hook #'magit-section-pre-command-hook nil t) + (add-hook 'post-command-hook #'magit-section-post-command-hook t t) + (add-hook 'deactivate-mark-hook #'magit-section-deactivate-mark t t) + (setq-local redisplay-highlight-region-function + #'magit-section--highlight-region) + (setq-local redisplay-unhighlight-region-function + #'magit-section--unhighlight-region) + (when (fboundp 'magit-section-context-menu) + (add-hook 'context-menu-functions #'magit-section-context-menu 10 t)) + (when magit-section-disable-line-numbers + (when (bound-and-true-p global-linum-mode) + (linum-mode -1)) + (when (and (fboundp 'nlinum-mode) + (bound-and-true-p global-nlinum-mode)) + (nlinum-mode -1)) + (when (and (fboundp 'display-line-numbers-mode) + (bound-and-true-p global-display-line-numbers-mode)) + (display-line-numbers-mode -1))) + (when (fboundp 'magit-preserve-section-visibility-cache) + (add-hook 'kill-buffer-hook #'magit-preserve-section-visibility-cache))) + +;;; Core + +(defvar-local magit-root-section nil + "The root section in the current buffer. +All other sections are descendants of this section. The value +of this variable is set by `magit-insert-section' and you should +never modify it.") +(put 'magit-root-section 'permanent-local t) + +(defvar-local magit--context-menu-section nil "For internal use only.") + +(defvar magit--context-menu-buffer nil "For internal use only.") + +(defun magit-point () + "Return point or the position where the context menu was invoked. +When using the context menu, return the position the user clicked +on, provided the current buffer is the buffer in which the click +occurred. Otherwise return the same value as `point'." + (if magit--context-menu-section + (magit-menu-position) + (point))) + +(defun magit-thing-at-point (thing &optional no-properties) + "Return the THING at point or where the context menu was invoked. +When using the context menu, return the thing the user clicked +on, provided the current buffer is the buffer in which the click +occurred. Otherwise return the same value as `thing-at-point'. +For the meaning of THING and NO-PROPERTIES see that function." + (if-let ((pos (magit-menu-position))) + (save-excursion + (goto-char pos) + (thing-at-point thing no-properties)) + (thing-at-point thing no-properties))) + +(defun magit-current-section () + "Return the section at point or where the context menu was invoked. +When using the context menu, return the section that the user +clicked on, provided the current buffer is the buffer in which +the click occurred. Otherwise return the section at point." + (or magit--context-menu-section + (magit-section-at) + magit-root-section)) + +(defun magit-section-at (&optional position) + "Return the section at POSITION, defaulting to point." + (get-text-property (or position (point)) 'magit-section)) + +(defun magit-section-ident (section) + "Return an unique identifier for SECTION. +The return value has the form ((TYPE . VALUE)...)." + (cons (cons (oref section type) + (magit-section-ident-value section)) + (and-let* ((parent (oref section parent))) + (magit-section-ident parent)))) + +(cl-defgeneric magit-section-ident-value (object) + "Return OBJECT's value, making it constant and unique if necessary. + +This is used to correlate different incarnations of the same +section, see `magit-section-ident' and `magit-get-section'. + +Sections whose values that are not constant and/or unique should +implement a method that return a value that can be used for this +purpose.") + +(cl-defmethod magit-section-ident-value ((section magit-section)) + "Return the value unless it is an object. + +Different object incarnations representing the same value then to +not be equal, so call this generic function on the object itself +to determine a constant value." + (let ((value (oref section value))) + (if (eieio-object-p value) + (magit-section-ident-value value) + value))) + +(cl-defmethod magit-section-ident-value ((object eieio-default-superclass)) + "Simply return the object itself. That likely isn't +good enough, so you need to implement your own method." + object) + +(defun magit-get-section (ident &optional root) + "Return the section identified by IDENT. +IDENT has to be a list as returned by `magit-section-ident'. +If optional ROOT is non-nil, then search in that section tree +instead of in the one whose root `magit-root-section' is." + (setq ident (reverse ident)) + (let ((section (or root magit-root-section))) + (when (eq (car (pop ident)) + (oref section type)) + (while (and ident + (pcase-let ((`(,type . ,value) (car ident))) + (setq section + (cl-find-if + (lambda (section) + (and (eq (oref section type) type) + (equal (magit-section-ident-value section) + value))) + (oref section children))))) + (pop ident)) + section))) + +(defun magit-section-lineage (section) + "Return the lineage of SECTION. +The return value has the form (TYPE...)." + (cons (oref section type) + (and-let* ((parent (oref section parent))) + (magit-section-lineage parent)))) + +(defvar magit-insert-section--current nil "For internal use only.") +(defvar magit-insert-section--parent nil "For internal use only.") +(defvar magit-insert-section--oldroot nil "For internal use only.") + +;;; Menu + +(defvar magit-menu-common-value nil "See function `magit-menu-common-value'.") +(defvar magit-menu--desc-values nil "For internal use only.") + +(defun magit-section-context-menu (menu click) + "Populate MENU with Magit-Section commands at CLICK." + (when-let ((section (save-excursion + (unless (region-active-p) + (mouse-set-point click)) + (magit-section-at)))) + (unless (region-active-p) + (setq magit--context-menu-buffer (current-buffer)) + (if-let ((alt (save-excursion + (mouse-set-point click) + (run-hook-with-args-until-success + 'magit-menu-alternative-section-hook section)))) + (setq magit--context-menu-section (setq section alt)) + (setq magit--context-menu-section section) + (magit-section-update-highlight t))) + (when (magit-section-content-p section) + (define-key-after menu [magit-section-toggle] + `(menu-item + ,(if (oref section hidden) "Expand section" "Collapse section") + magit-section-toggle)) + (unless (oref section hidden) + (when-let ((children (oref section children))) + (when (seq-some #'magit-section-content-p children) + (when (seq-some (lambda (c) (oref c hidden)) children) + (define-key-after menu [magit-section-show-children] + `(menu-item "Expand children" + magit-section-show-children))) + (when (seq-some (lambda (c) (not (oref c hidden))) children) + (define-key-after menu [magit-section-hide-children] + `(menu-item "Collapse children" + magit-section-hide-children)))))) + (define-key-after menu [separator-magit-1] menu-bar-separator)) + (define-key-after menu [magit-describe-section] + `(menu-item "Describe section" magit-describe-section)) + (when-let ((map (oref section keymap))) + (define-key-after menu [separator-magit-2] menu-bar-separator) + (when (symbolp map) + (setq map (symbol-value map))) + (setq magit-menu-common-value (magit-menu-common-value section)) + (setq magit-menu--desc-values (magit-menu--desc-values section)) + (map-keymap (lambda (key binding) + (when (consp binding) + (define-key-after menu (vector key) + (copy-sequence binding)))) + (if (fboundp 'menu-bar-keymap) + (menu-bar-keymap map) + (magit--menu-bar-keymap map))))) + menu) + +(defun magit-menu-set (keymap key def desc &optional props after) + "In KEYMAP, define KEY and a menu entry for DEF. + +Add the menu item (menu-item DESC DEF . PROPS) at the end of +KEYMAP, or if optional AFTER is non-nil, then after that. + +Because it is so common, and would otherwise result in overlong +lines or else unsightly line wrapping, a definition [remap CMD] +can be written as just [CMD]. As a result KEY might have to be +a string when otherwise a vector would have worked. + +If DESC is a string that contains a support %-spec, substitute +the expression (magit-menu-format-desc DESC) for that. See +`magit-menu-format-desc'." + (declare (indent defun)) + (when (vectorp key) + ;; Expand the short-hand. + (unless (eq (aref key 0) 'remap) + (setq key (vconcat [remap] key))) + ;; The default binding is RET, but in my configuration it + ;; is . In that case the displayed binding would + ;; be instead of , for unknown reasons. The + ;; same does not happen for similar events, such as . + (when (and (equal key [remap magit-visit-thing]) + (boundp 'magit-mode-map) + (ignore-errors (eq (lookup-key magit-mode-map [return]) + 'magit-visit-thing))) + (setq key [return])) + ;; `define-key-after' cannot deal with [remap CMD], + ;; so we have to add the key binding separately. + (define-key keymap key def) + (unless (symbolp def) + (error "When KEY is a remapping, then DEF must be a symbol: %s" def)) + (setq key (vector def))) + (when (and (stringp desc) (string-match-p "%[tTvsmMx]" desc)) + (setq desc (list 'magit-menu-format-desc desc))) + (define-key-after keymap key + `( menu-item ,desc ,def ,@props + ;; Without this, the keys for point would be shown instead + ;; of the relevant ones from where the click occurred. + ,@(and (not (region-active-p)) + (list :keys + (lambda () + (or (ignore-errors + (save-excursion + (goto-char (magit-menu-position)) + (key-description (where-is-internal def nil t)))) + ""))))) + after)) + +(defun magit-menu-position () + "Return the position where the context-menu was invoked. +If the current command wasn't invoked using the context-menu, +then return nil." + (and magit--context-menu-section + (ignore-errors + (posn-point (event-start (aref (this-command-keys-vector) 0)))))) + +(defun magit-menu-highlight-point-section () + (setq magit-section-highlight-force-update t) + (if (eq (current-buffer) magit--context-menu-buffer) + (setq magit--context-menu-section nil) + (if-let ((window (get-buffer-window magit--context-menu-buffer))) + (with-selected-window window + (setq magit--context-menu-section nil) + (magit-section-update-highlight)) + (with-current-buffer magit--context-menu-buffer + (setq magit--context-menu-section nil)))) + (setq magit--context-menu-buffer nil)) + +(defvar magit--plural-append-es '(branch)) + +(cl-defgeneric magit-menu-common-value (_section) + "Return some value to be used by multiple menu items. +This function is called by `magit-section-context-menu', which +stores the value in `magit-menu-common-value'. Individual menu +items can use it, e.g., in the expression used to set their +description." + nil) + +(defun magit-menu--desc-values (section) + (let ((type (oref section type)) + (value (oref section value)) + (multiple (magit-region-sections nil t))) + (list type + value + (format "%s %s" type value) + (and multiple (length multiple)) + (if (memq type magit--plural-append-es) "es" "s")))) + +(defun magit-menu-format-desc (format) + "Format a string based on FORMAT and menu section or selection. +The following %-specs are allowed: +%t means \"TYPE\". +%T means \"TYPE\", or \"TYPEs\" if multiple sections are selected. +%v means \"VALUE\". +%s means \"TYPE VALUE\". +%m means \"TYPE VALUE\", or \"COUNT TYPEs\" if multiple sections + are selected. +%M means \"VALUE\", or \"COUNT TYPEs\" if multiple sections are + selected. +%x means the value of `magit-menu-common-value'." + (pcase-let* ((`(,type ,value ,single ,count ,suffix) magit-menu--desc-values) + (multiple (and count (format "%s %s%s" count type suffix)))) + (format-spec format + `((?t . ,type) + (?T . ,(format "%s%s" type (if count suffix ""))) + (?v . ,value) + (?s . ,single) + (?m . ,(or multiple single)) + (?M . ,(or multiple value)) + (?x . ,(format "%s" magit-menu-common-value)))))) + +(defun magit--menu-bar-keymap (keymap) + "Backport of `menu-bar-keymap' for Emacs < 28. +Slight trimmed down." + (let ((menu-bar nil)) + (map-keymap (lambda (key binding) + (push (cons key binding) menu-bar)) + keymap) + (cons 'keymap (nreverse menu-bar)))) + +(defun magit--context-menu-local (menu _click) + "Backport of `context-menu-local' for Emacs < 28." + (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) + (define-key-after menu [separator-local] menu-bar-separator) + (let ((keymap (local-key-binding [menu-bar]))) + (when keymap + (map-keymap (lambda (key binding) + (when (consp binding) + (define-key-after menu (vector key) + (copy-sequence binding)))) + (magit--menu-bar-keymap keymap)))) + menu) + +(advice-add 'context-menu-region :around + (lambda (fn menu click) + "Disable in `magit-section-mode' buffers." + (if (derived-mode-p 'magit-section-mode) + menu + (funcall fn menu click)))) + +;;; Commands +;;;; Movement + +(defun magit-section-forward () + "Move to the beginning of the next visible section." + (interactive) + (if (eobp) + (user-error "No next section") + (let ((section (magit-current-section))) + (if (oref section parent) + (let ((next (and (not (oref section hidden)) + (not (= (oref section end) + (1+ (point)))) + (car (oref section children))))) + (while (and section (not next)) + (unless (setq next (car (magit-section-siblings section 'next))) + (setq section (oref section parent)))) + (if next + (magit-section-goto next) + (user-error "No next section"))) + (magit-section-goto 1))))) + +(defun magit-section-backward () + "Move to the beginning of the current or the previous visible section. +When point is at the beginning of a section then move to the +beginning of the previous visible section. Otherwise move to +the beginning of the current section." + (interactive) + (if (bobp) + (user-error "No previous section") + (let ((section (magit-current-section)) children) + (cond + ((and (= (point) + (1- (oref section end))) + (setq children (oref section children))) + (magit-section-goto (car (last children)))) + ((and (oref section parent) + (not (= (point) + (oref section start)))) + (magit-section-goto section)) + (t + (let ((prev (car (magit-section-siblings section 'prev)))) + (if prev + (while (and (not (oref prev hidden)) + (setq children (oref prev children))) + (setq prev (car (last children)))) + (setq prev (oref section parent))) + (cond (prev + (magit-section-goto prev)) + ((oref section parent) + (user-error "No previous section")) + ;; Eob special cases. + ((not (get-text-property (1- (point)) 'invisible)) + (magit-section-goto -1)) + (t + (goto-char (previous-single-property-change + (1- (point)) 'invisible)) + (forward-line -1) + (magit-section-goto (magit-current-section)))))))))) + +(defun magit-section-up () + "Move to the beginning of the parent section." + (interactive) + (--if-let (oref (magit-current-section) parent) + (magit-section-goto it) + (user-error "No parent section"))) + +(defun magit-section-forward-sibling () + "Move to the beginning of the next sibling section. +If there is no next sibling section, then move to the parent." + (interactive) + (let ((current (magit-current-section))) + (if (oref current parent) + (--if-let (car (magit-section-siblings current 'next)) + (magit-section-goto it) + (magit-section-forward)) + (magit-section-goto 1)))) + +(defun magit-section-backward-sibling () + "Move to the beginning of the previous sibling section. +If there is no previous sibling section, then move to the parent." + (interactive) + (let ((current (magit-current-section))) + (if (oref current parent) + (--if-let (car (magit-section-siblings current 'prev)) + (magit-section-goto it) + (magit-section-backward)) + (magit-section-goto -1)))) + +(defun magit-section-goto (arg) + (if (integerp arg) + (progn (forward-line arg) + (setq arg (magit-current-section))) + (goto-char (oref arg start))) + (run-hook-with-args 'magit-section-movement-hook arg)) + +(defun magit-section-set-window-start (section) + "Ensure the beginning of SECTION is visible." + (unless (pos-visible-in-window-p (oref section end)) + (set-window-start (selected-window) (oref section start)))) + +(defmacro magit-define-section-jumper (name heading type &optional value) + "Define an interactive function to go some section. +Together TYPE and VALUE identify the section. +HEADING is the displayed heading of the section." + (declare (indent defun)) + `(defun ,name (&optional expand) ,(format "\ +Jump to the section \"%s\". +With a prefix argument also expand it." heading) + (interactive "P") + (--if-let (magit-get-section + (cons (cons ',type ,value) + (magit-section-ident magit-root-section))) + (progn (goto-char (oref it start)) + (when expand + (with-local-quit (magit-section-show it)) + (recenter 0))) + (message ,(format "Section \"%s\" wasn't found" heading))))) + +;;;; Visibility + +(defun magit-section-show (section) + "Show the body of the current section." + (interactive (list (magit-current-section))) + (oset section hidden nil) + (magit-section--maybe-wash section) + (when-let ((beg (oref section content))) + (remove-overlays beg (oref section end) 'invisible t)) + (magit-section-maybe-update-visibility-indicator section) + (magit-section-maybe-cache-visibility section) + (dolist (child (oref section children)) + (if (oref child hidden) + (magit-section-hide child) + (magit-section-show child)))) + +(defun magit-section--maybe-wash (section) + (when-let ((washer (oref section washer))) + (oset section washer nil) + (let ((inhibit-read-only t) + (magit-insert-section--parent section) + (content (oref section content))) + (save-excursion + (if (and content (< content (oref section end))) + (funcall washer section) ; already partially washed (hunk) + (goto-char (oref section end)) + (oset section content (point-marker)) + (funcall washer) + (oset section end (point-marker))))) + (setq magit-section-highlight-force-update t))) + +(defun magit-section-hide (section) + "Hide the body of the current section." + (interactive (list (magit-current-section))) + (if (eq section magit-root-section) + (user-error "Cannot hide root section") + (oset section hidden t) + (when-let ((beg (oref section content))) + (let ((end (oref section end))) + (when (< beg (point) end) + (goto-char (oref section start))) + (remove-overlays beg end 'invisible t) + (let ((o (make-overlay beg end))) + (overlay-put o 'evaporate t) + (overlay-put o 'invisible t)))) + (magit-section-maybe-update-visibility-indicator section) + (magit-section-maybe-cache-visibility section))) + +(defun magit-section-toggle (section) + "Toggle visibility of the body of the current section." + (interactive (list (magit-current-section))) + (cond ((eq section magit-root-section) + (user-error "Cannot hide root section")) + ((oref section hidden) + (magit-section-show section)) + (t (magit-section-hide section)))) + +(defun magit-section-toggle-children (section) + "Toggle visibility of bodies of children of the current section." + (interactive (list (magit-current-section))) + (let* ((children (oref section children)) + (show (--any-p (oref it hidden) children))) + (dolist (c children) + (oset c hidden show))) + (magit-section-show section)) + +(defun magit-section-show-children (section &optional depth) + "Recursively show the bodies of children of the current section. +With a prefix argument show children that deep and hide deeper +children." + (interactive (list (magit-current-section))) + (magit-section-show-children-1 section depth) + (magit-section-show section)) + +(defun magit-section-show-children-1 (section &optional depth) + (dolist (child (oref section children)) + (oset child hidden nil) + (if depth + (if (> depth 0) + (magit-section-show-children-1 child (1- depth)) + (magit-section-hide child)) + (magit-section-show-children-1 child)))) + +(defun magit-section-hide-children (section) + "Recursively hide the bodies of children of the current section." + (interactive (list (magit-current-section))) + (mapc #'magit-section-hide (oref section children))) + +(defun magit-section-show-headings (section) + "Recursively show headings of children of the current section. +Only show the headings, previously shown text-only bodies are +hidden." + (interactive (list (magit-current-section))) + (magit-section-show-headings-1 section) + (magit-section-show section)) + +(defun magit-section-show-headings-1 (section) + (dolist (child (oref section children)) + (oset child hidden nil) + (when (or (oref child children) + (not (oref child content))) + (magit-section-show-headings-1 child)))) + +(defun magit-section-cycle (section) + "Cycle visibility of current section and its children." + (interactive (list (magit-current-section))) + (if (oref section hidden) + (progn (magit-section-show section) + (magit-section-hide-children section)) + (let ((children (oref section children))) + (cond ((and (--any-p (oref it hidden) children) + (--any-p (oref it children) children)) + (magit-section-show-headings section)) + ((seq-some #'magit-section-hidden-body children) + (magit-section-show-children section)) + (t + (magit-section-hide section)))))) + +(defun magit-section-cycle-global () + "Cycle visibility of all sections in the current buffer." + (interactive) + (let ((children (oref magit-root-section children))) + (cond ((and (--any-p (oref it hidden) children) + (--any-p (oref it children) children)) + (magit-section-show-headings magit-root-section)) + ((seq-some #'magit-section-hidden-body children) + (magit-section-show-children magit-root-section)) + (t + (mapc #'magit-section-hide children))))) + +(defun magit-section-hidden-body (section &optional pred) + (--if-let (oref section children) + (funcall (or pred #'-any-p) #'magit-section-hidden-body it) + (and (oref section content) + (oref section hidden)))) + +(defun magit-section-content-p (section) + "Return non-nil if SECTION has content or an unused washer function." + (with-slots (content end washer) section + (and content (or (not (= content end)) washer)))) + +(defun magit-section-invisible-p (section) + "Return t if the SECTION's body is invisible. +When the body of an ancestor of SECTION is collapsed then +SECTION's body (and heading) obviously cannot be visible." + (or (oref section hidden) + (and-let* ((parent (oref section parent))) + (magit-section-invisible-p parent)))) + +(defun magit-section-show-level (level) + "Show surrounding sections up to LEVEL. +If LEVEL is negative, show up to the absolute value. +Sections at higher levels are hidden." + (if (< level 0) + (let ((s (magit-current-section))) + (setq level (- level)) + (while (> (1- (length (magit-section-ident s))) level) + (setq s (oref s parent)) + (goto-char (oref s start))) + (magit-section-show-children magit-root-section (1- level))) + (cl-do* ((s (magit-current-section) + (oref s parent)) + (i (1- (length (magit-section-ident s))) + (cl-decf i))) + ((cond ((< i level) (magit-section-show-children s (- level i 1)) t) + ((= i level) (magit-section-hide s) t)) + (magit-section-goto s))))) + +(defun magit-section-show-level-1 () + "Show surrounding sections on first level." + (interactive) + (magit-section-show-level 1)) + +(defun magit-section-show-level-1-all () + "Show all sections on first level." + (interactive) + (magit-section-show-level -1)) + +(defun magit-section-show-level-2 () + "Show surrounding sections up to second level." + (interactive) + (magit-section-show-level 2)) + +(defun magit-section-show-level-2-all () + "Show all sections up to second level." + (interactive) + (magit-section-show-level -2)) + +(defun magit-section-show-level-3 () + "Show surrounding sections up to third level." + (interactive) + (magit-section-show-level 3)) + +(defun magit-section-show-level-3-all () + "Show all sections up to third level." + (interactive) + (magit-section-show-level -3)) + +(defun magit-section-show-level-4 () + "Show surrounding sections up to fourth level." + (interactive) + (magit-section-show-level 4)) + +(defun magit-section-show-level-4-all () + "Show all sections up to fourth level." + (interactive) + (magit-section-show-level -4)) + +(defun magit-mouse-toggle-section (event) + "Toggle visibility of the clicked section. +Clicks outside either the section heading or the left fringe are +silently ignored." + (interactive "e") + (let* ((pos (event-start event)) + (section (magit-section-at (posn-point pos)))) + (if (eq (posn-area pos) 'left-fringe) + (when section + (while (not (magit-section-content-p section)) + (setq section (oref section parent))) + (unless (eq section magit-root-section) + (goto-char (oref section start)) + (magit-section-toggle section))) + (magit-section-toggle section)))) + +;;;; Auxiliary + +(defun magit-describe-section-briefly (section &optional ident) + "Show information about the section at point. +With a prefix argument show the section identity instead of the +section lineage. This command is intended for debugging purposes." + (interactive (list (magit-current-section) current-prefix-arg)) + (let ((str (format "#<%s %S %S %s-%s%s>" + (eieio-object-class section) + (let ((val (oref section value))) + (cond ((stringp val) + (substring-no-properties val)) + ((and (eieio-object-p val) + (fboundp 'cl-prin1-to-string)) + (cl-prin1-to-string val)) + (t + val))) + (if ident + (magit-section-ident section) + (apply #'vector (magit-section-lineage section))) + (and-let* ((m (oref section start))) + (marker-position m)) + (if-let ((m (oref section content))) + (format "[%s-]" (marker-position m)) + "") + (and-let* ((m (oref section end))) + (marker-position m))))) + (if (called-interactively-p 'any) + (message "%s" str) + str))) + +(cl-defmethod cl-print-object ((section magit-section) stream) + "Print `magit-describe-section' result of SECTION." + ;; Used by debug and edebug as of Emacs 26. + (princ (magit-describe-section-briefly section) stream)) + +(defun magit-describe-section (section &optional interactive-p) + "Show information about the section at point." + (interactive (list (magit-current-section) t)) + (let ((inserter-section section)) + (while (and inserter-section (not (oref inserter-section inserter))) + (setq inserter-section (oref inserter-section parent))) + (when (and inserter-section (oref inserter-section inserter)) + (setq section inserter-section))) + (pcase (oref section inserter) + (`((,hook ,fun) . ,src-src) + (help-setup-xref `(magit-describe-section ,section) interactive-p) + (with-help-window (help-buffer) + (with-current-buffer standard-output + (insert (format-message + "%s\n is inserted by `%s'\n from `%s'" + (magit-describe-section-briefly section) + (make-text-button (symbol-name fun) nil + :type 'help-function + 'help-args (list fun)) + (make-text-button (symbol-name hook) nil + :type 'help-variable + 'help-args (list hook)))) + (pcase-dolist (`(,hook ,fun) src-src) + (insert (format-message + ",\n called by `%s'\n from `%s'" + (make-text-button (symbol-name fun) nil + :type 'help-function + 'help-args (list fun)) + (make-text-button (symbol-name hook) nil + :type 'help-variable + 'help-args (list hook))))) + (insert ".\n\n") + (insert + (format-message + "`%s' is " + (make-text-button (symbol-name fun) nil + :type 'help-function 'help-args (list fun)))) + (describe-function-1 fun)))) + (_ (message "%s, inserter unknown" + (magit-describe-section-briefly section))))) + +;;; Match + +(cl-defun magit-section-match + (condition &optional (section (magit-current-section))) + "Return t if SECTION matches CONDITION. + +SECTION defaults to the section at point. If SECTION is not +specified and there also is no section at point, then return +nil. + +CONDITION can take the following forms: + (CONDITION...) matches if any of the CONDITIONs matches. + [CLASS...] matches if the section's class is the same + as the first CLASS or a subclass of that; + the section's parent class matches the + second CLASS; and so on. + [* CLASS...] matches sections that match [CLASS...] and + also recursively all their child sections. + CLASS matches if the section's class is the same + as CLASS or a subclass of that; regardless + of the classes of the parent sections. + +Each CLASS should be a class symbol, identifying a class that +derives from `magit-section'. For backward compatibility CLASS +can also be a \"type symbol\". A section matches such a symbol +if the value of its `type' slot is `eq'. If a type symbol has +an entry in `magit--section-type-alist', then a section also +matches that type if its class is a subclass of the class that +corresponds to the type as per that alist. + +Note that it is not necessary to specify the complete section +lineage as printed by `magit-describe-section-briefly', unless +of course you want to be that precise." + (and section (magit-section-match-1 condition section))) + +(defun magit-section-match-1 (condition section) + (cl-assert condition) + (and section + (if (listp condition) + (--first (magit-section-match-1 it section) condition) + (magit-section-match-2 (if (symbolp condition) + (list condition) + (cl-coerce condition 'list)) + section)))) + +(defun magit-section-match-2 (condition section) + (if (eq (car condition) '*) + (or (magit-section-match-2 (cdr condition) section) + (and-let* ((parent (oref section parent))) + (magit-section-match-2 condition parent))) + (and (let ((c (car condition))) + (if (class-p c) + (cl-typep section c) + (if-let ((class (cdr (assq c magit--section-type-alist)))) + (cl-typep section class) + (eq (oref section type) c)))) + (or (not (setq condition (cdr condition))) + (and-let* ((parent (oref section parent))) + (magit-section-match-2 condition parent)))))) + +(defun magit-section-value-if (condition &optional section) + "If the section at point matches CONDITION, then return its value. + +If optional SECTION is non-nil then test whether that matches +instead. If there is no section at point and SECTION is nil, +then return nil. If the section does not match, then return +nil. + +See `magit-section-match' for the forms CONDITION can take." + (and-let* ((section (or section (magit-current-section)))) + (and (magit-section-match condition section) + (oref section value)))) + +(defmacro magit-section-when (condition &rest body) + "If the section at point matches CONDITION, evaluate BODY. + +If the section matches, then evaluate BODY forms sequentially +with `it' bound to the section and return the value of the last +form. If there are no BODY forms, then return the value of the +section. If the section does not match or if there is no section +at point, then return nil. + +See `magit-section-match' for the forms CONDITION can take." + (declare (obsolete + "instead use `magit-section-match' or `magit-section-value-if'." + "Magit 2.90.0") + (indent 1) + (debug (sexp body))) + `(--when-let (magit-current-section) + ;; Quoting CONDITION here often leads to double-quotes, which + ;; isn't an issue because `magit-section-match-1' implicitly + ;; deals with that. We shouldn't force users of this function + ;; to not quote CONDITION because that would needlessly break + ;; backward compatibility. + (when (magit-section-match ',condition it) + ,@(or body '((oref it value)))))) + +(defmacro magit-section-case (&rest clauses) + "Choose among clauses on the type of the section at point. + +Each clause looks like (CONDITION BODY...). The type of the +section is compared against each CONDITION; the BODY forms of the +first match are evaluated sequentially and the value of the last +form is returned. Inside BODY the symbol `it' is bound to the +section at point. If no clause succeeds or if there is no +section at point, return nil. + +See `magit-section-match' for the forms CONDITION can take. +Additionally a CONDITION of t is allowed in the final clause, and +matches if no other CONDITION match, even if there is no section +at point." + (declare (indent 0) + (debug (&rest (sexp body)))) + `(let* ((it (magit-current-section))) + (cond ,@(mapcar (lambda (clause) + `(,(or (eq (car clause) t) + `(and it + (magit-section-match-1 ',(car clause) it))) + ,@(cdr clause))) + clauses)))) + +(defun magit-section-match-assoc (section alist) + "Return the value associated with SECTION's type or lineage in ALIST." + (seq-some (pcase-lambda (`(,key . ,val)) + (and (magit-section-match-1 key section) val)) + alist)) + +;;; Create + +(defvar magit-insert-section-hook nil + "Hook run after `magit-insert-section's BODY. +Avoid using this hook and only ever do so if you know +what you are doing and are sure there is no other way.") + +(defmacro magit-insert-section (&rest args) + "Insert a section at point. + +Create a section object of type CLASS, storing VALUE in its +`value' slot, and insert the section at point. CLASS is a +subclass of `magit-section' or has the form `(eval FORM)', in +which case FORM is evaluated at runtime and should return a +subclass. In other places a sections class is oftern referred +to as its \"type\". + +Many commands behave differently depending on the class of the +current section and sections of a certain class can have their +own keymap, which is specified using the `keymap' class slot. +The value of that slot should be a variable whose value is a +keymap. + +For historic reasons Magit and Forge in most cases use symbols +as CLASS that don't actually identify a class and that lack the +appropriate package prefix. This works due to some undocumented +kludges, which are not available to other packages. + +When optional HIDE is non-nil collapse the section body by +default, i.e. when first creating the section, but not when +refreshing the buffer. Else expand it by default. This can be +overwritten using `magit-section-set-visibility-hook'. When a +section is recreated during a refresh, then the visibility of +predecessor is inherited and HIDE is ignored (but the hook is +still honored). + +BODY is any number of forms that actually insert the section's +heading and body. Optional NAME, if specified, has to be a +symbol, which is then bound to the object of the section being +inserted. + +Before BODY is evaluated the `start' of the section object is set +to the value of `point' and after BODY was evaluated its `end' is +set to the new value of `point'; BODY is responsible for moving +`point' forward. + +If it turns out inside BODY that the section is empty, then +`magit-cancel-section' can be used to abort and remove all traces +of the partially inserted section. This can happen when creating +a section by washing Git's output and Git didn't actually output +anything this time around. + +\(fn [NAME] (CLASS &optional VALUE HIDE) &rest BODY)" + (declare (indent defun) + (debug ([&optional symbolp] + (&or [("eval" form) &optional form form] + [symbolp &optional form form]) + body))) + (let ((tp (cl-gensym "type")) + (s* (and (symbolp (car args)) + (pop args))) + (s (cl-gensym "section"))) + `(let* ((,tp ,(let ((type (nth 0 (car args)))) + (if (eq (car-safe type) 'eval) + (cadr type) + `',type))) + (,s (funcall (if (class-p ,tp) + ,tp + (or (cdr (assq ,tp magit--section-type-alist)) + 'magit-section)) + :type + (or (and (class-p ,tp) + (car (rassq ,tp magit--section-type-alist))) + ,tp) + :value ,(nth 1 (car args)) + :start (point-marker) + :parent magit-insert-section--parent))) + (oset ,s hidden + (let ((value (run-hook-with-args-until-success + 'magit-section-set-visibility-hook ,s))) + (if value + (eq value 'hide) + (let ((incarnation (and magit-insert-section--oldroot + (magit-get-section + (magit-section-ident ,s) + magit-insert-section--oldroot)))) + (if incarnation + (oref incarnation hidden) + (let ((value (magit-section-match-assoc + ,s magit-section-initial-visibility-alist))) + (if value + (progn + (when (functionp value) + (setq value (funcall value ,s))) + (eq value 'hide)) + ,(nth 2 (car args))))))))) + (let ((magit-insert-section--current ,s) + (magit-insert-section--parent ,s) + (magit-insert-section--oldroot + (or magit-insert-section--oldroot + (unless magit-insert-section--parent + (prog1 magit-root-section + (setq magit-root-section ,s)))))) + (catch 'cancel-section + ,@(if s* + `((let ((,s* ,s)) + ,@(cdr args))) + (cdr args)) + ;; `magit-insert-section-hook' should *not* be run with + ;; `magit-run-section-hook' because it's a hook that runs + ;; on section insertion, not a section inserting hook. + (run-hooks 'magit-insert-section-hook) + (magit-insert-child-count ,s) + (set-marker-insertion-type (oref ,s start) t) + (let* ((end (oset ,s end (point-marker))) + (class-map (oref ,s keymap)) + (magit-map (intern (format "magit-%s-section-map" + (oref ,s type)))) + (forge-map (intern (format "forge-%s-section-map" + (oref ,s type)))) + (map (and class-map (symbol-value class-map)))) + (unless map + (setq map (or (and (boundp magit-map) (symbol-value magit-map)) + (and (boundp forge-map) (symbol-value forge-map)))) + (oset ,s keymap map)) + (save-excursion + (goto-char (oref ,s start)) + (while (< (point) end) + (let ((next (or (next-single-property-change + (point) 'magit-section) + end))) + (unless (magit-section-at) + (put-text-property (point) next 'magit-section ,s) + (when map + (put-text-property (point) next 'keymap map))) + (magit-section-maybe-add-heading-map ,s) + (goto-char next))))) + (if (eq ,s magit-root-section) + (let ((magit-section-cache-visibility nil)) + (magit-section-show ,s)) + (oset (oref ,s parent) children + (nconc (oref (oref ,s parent) children) + (list ,s))))) + ,s)))) + +(defun magit-cancel-section () + "Cancel inserting the section that is currently being inserted. +Remove all traces of that section." + (when magit-insert-section--current + (if (not (oref magit-insert-section--current parent)) + (insert "(empty)\n") + (delete-region (oref magit-insert-section--current start) + (point)) + (setq magit-insert-section--current nil) + (throw 'cancel-section nil)))) + +(defun magit-insert-heading (&rest args) + "Insert the heading for the section currently being inserted. + +This function should only be used inside `magit-insert-section'. + +When called without any arguments, then just set the `content' +slot of the object representing the section being inserted to +a marker at `point'. The section should only contain a single +line when this function is used like this. + +When called with arguments ARGS, which have to be strings, or +nil, then insert those strings at point. The section should not +contain any text before this happens and afterwards it should +again only contain a single line. If the `face' property is set +anywhere inside any of these strings, then insert all of them +unchanged. Otherwise use the `magit-section-heading' face for +all inserted text. + +The `content' property of the section object is the end of the +heading (which lasts from `start' to `content') and the beginning +of the the body (which lasts from `content' to `end'). If the +value of `content' is nil, then the section has no heading and +its body cannot be collapsed. If a section does have a heading, +then its height must be exactly one line, including a trailing +newline character. This isn't enforced, you are responsible for +getting it right. The only exception is that this function does +insert a newline character if necessary." + (declare (indent defun)) + (when args + (let ((heading (apply #'concat args))) + (insert (if (or (text-property-not-all 0 (length heading) + 'font-lock-face nil heading) + (text-property-not-all 0 (length heading) + 'face nil heading)) + heading + (propertize heading 'font-lock-face 'magit-section-heading))))) + (unless (bolp) + (insert ?\n)) + (when (fboundp 'magit-maybe-make-margin-overlay) + (magit-maybe-make-margin-overlay)) + (oset magit-insert-section--current content (point-marker))) + +(defmacro magit-insert-section-body (&rest body) + "Use BODY to insert the section body, once the section is expanded. +If the section is expanded when it is created, then this is +like `progn'. Otherwise BODY isn't evaluated until the section +is explicitly expanded." + (declare (indent 0)) + (let ((f (cl-gensym)) + (s (cl-gensym))) + `(let ((,f (lambda () ,@body)) + (,s magit-insert-section--current)) + (if (oref ,s hidden) + (oset ,s washer + (lambda () + (funcall ,f) + (magit-section-maybe-remove-heading-map ,s) + (magit-section-maybe-remove-visibility-indicator ,s))) + (funcall ,f))))) + +(defun magit-insert-headers (hook) + (let* ((header-sections nil) + (magit-insert-section-hook + (cons (lambda () + (push magit-insert-section--current + header-sections)) + (if (listp magit-insert-section-hook) + magit-insert-section-hook + (list magit-insert-section-hook))))) + (magit-run-section-hook hook) + (when header-sections + (insert "\n") + ;; Make the first header into the parent of the rest. + (when (cdr header-sections) + (cl-callf nreverse header-sections) + (let* ((1st-header (pop header-sections)) + (header-parent (oref 1st-header parent))) + (oset header-parent children (list 1st-header)) + (oset 1st-header children header-sections) + (oset 1st-header content (oref (car header-sections) start)) + (oset 1st-header end (oref (car (last header-sections)) end)) + (dolist (sub-header header-sections) + (oset sub-header parent 1st-header)) + (magit-section-maybe-add-heading-map 1st-header)))))) + +(defun magit-section-maybe-add-heading-map (section) + (when (magit-section-content-p section) + (let ((start (oref section start)) + (map (oref section keymap))) + (when (symbolp map) + (setq map (symbol-value map))) + (put-text-property + start + (save-excursion + (goto-char start) + (line-end-position)) + 'keymap (if map + (make-composed-keymap + (list map magit-section-heading-map)) + magit-section-heading-map))))) + +(defun magit-section-maybe-remove-heading-map (section) + (with-slots (start content end keymap) section + (when (= content end) + (put-text-property start end 'keymap keymap)))) + +(defun magit-insert-child-count (section) + "Modify SECTION's heading to contain number of child sections. + +If `magit-section-show-child-count' is non-nil and the SECTION +has children and its heading ends with \":\", then replace that +with \" (N)\", where N is the number of child sections. + +This function is called by `magit-insert-section' after that has +evaluated its BODY. Admittedly that's a bit of a hack." + ;; This has to be fast, not pretty! + (let (content count) + (when (and magit-section-show-child-count + (setq count (length (oref section children))) + (> count 0) + (setq content (oref section content)) + (eq (char-before (1- content)) ?:)) + (save-excursion + (goto-char (- content 2)) + (insert (concat (magit--propertize-face " " 'magit-section-heading) + (magit--propertize-face (format "(%s)" count) + 'magit-section-child-count))) + (delete-char 1))))) + +;;; Highlight + +(defun magit-section-pre-command-hook () + (when (and (not (bound-and-true-p transient--prefix)) + (or magit--context-menu-buffer + magit--context-menu-section) + (not (eq (ignore-errors + (event-basic-type (aref (this-command-keys) 0))) + 'mouse-3))) + ;; This is the earliest opportunity to clean up after an aborted + ;; context-menu because that neither causes the command that created + ;; the menu to abort nor some abortion hook to be run. It is not + ;; possible to update highlighting before the first command invoked + ;; after the menu is aborted. Here we can only make sure it is + ;; updated afterwards. + (magit-menu-highlight-point-section)) + (setq magit-section-pre-command-region-p (region-active-p)) + (setq magit-section-pre-command-section (magit-current-section))) + +(defun magit-section-post-command-hook () + (unless (bound-and-true-p transient--prefix) + (when (or magit--context-menu-buffer + magit--context-menu-section) + (magit-menu-highlight-point-section)) + (unless (memq this-command '(magit-refresh magit-refresh-all)) + (magit-section-update-highlight)))) + +(defun magit-section-deactivate-mark () + (setq magit-section-highlight-force-update t)) + +(defun magit-section-update-highlight (&optional force) + (let ((section (magit-current-section))) + (when (or force + magit-section-highlight-force-update + (xor magit-section-pre-command-region-p (region-active-p)) + (not (eq magit-section-pre-command-section section))) + (let ((inhibit-read-only t) + (deactivate-mark nil) + (selection (magit-region-sections))) + (mapc #'delete-overlay magit-section-highlight-overlays) + (setq magit-section-highlight-overlays nil) + (setq magit-section-unhighlight-sections + magit-section-highlighted-sections) + (setq magit-section-highlighted-sections nil) + (unless (eq section magit-root-section) + (run-hook-with-args-until-success + 'magit-section-highlight-hook section selection)) + (dolist (s magit-section-unhighlight-sections) + (run-hook-with-args-until-success + 'magit-section-unhighlight-hook s selection)) + (restore-buffer-modified-p nil))) + (setq magit-section-highlight-force-update nil) + (magit-section-maybe-paint-visibility-ellipses))) + +(defun magit-section-highlight (section selection) + "Highlight SECTION and if non-nil all sections in SELECTION. +This function works for any section but produces undesirable +effects for diff related sections, which by default are +highlighted using `magit-diff-highlight'. Return t." + (when-let ((face (oref section heading-highlight-face))) + (dolist (section (or selection (list section))) + (magit-section-make-overlay + (oref section start) + (or (oref section content) + (oref section end)) + face))) + (cond (selection + (magit-section-make-overlay (oref (car selection) start) + (oref (car (last selection)) end) + 'magit-section-highlight) + (magit-section-highlight-selection nil selection)) + (t + (magit-section-make-overlay (oref section start) + (oref section end) + 'magit-section-highlight))) + t) + +(defun magit-section-highlight-selection (_ selection) + "Highlight the section-selection region. +If SELECTION is non-nil, then it is a list of sections selected by +the region. The headings of these sections are then highlighted. + +This is a fallback for people who don't want to highlight the +current section and therefore removed `magit-section-highlight' +from `magit-section-highlight-hook'. + +This function is necessary to ensure that a representation of +such a region is visible. If neither of these functions were +part of the hook variable, then such a region would be +invisible." + (when (and selection + (not (and (eq this-command 'mouse-drag-region)))) + (dolist (section selection) + (magit-section-make-overlay (oref section start) + (or (oref section content) + (oref section end)) + 'magit-section-heading-selection)) + t)) + +(defun magit-section-make-overlay (start end face) + ;; Yes, this doesn't belong here. But the alternative of + ;; spreading this hack across the code base is even worse. + (when (and magit-section-keep-region-overlay + (memq face '(magit-section-heading-selection + magit-diff-file-heading-selection + magit-diff-hunk-heading-selection))) + (setq face (list :foreground (face-foreground face)))) + (let ((ov (make-overlay start end nil t))) + (overlay-put ov 'font-lock-face face) + (overlay-put ov 'evaporate t) + (push ov magit-section-highlight-overlays) + ov)) + +(cl-defgeneric magit-section-get-relative-position (section)) + +(cl-defmethod magit-section-get-relative-position ((section magit-section)) + (let ((start (oref section start)) + (point (magit-point))) + (list (- (line-number-at-pos point) + (line-number-at-pos start)) + (- point (line-beginning-position))))) + +(cl-defgeneric magit-section-goto-successor ()) + +(cl-defmethod magit-section-goto-successor ((section magit-section) + line char &optional _arg) + (or (magit-section-goto-successor--same section line char) + (magit-section-goto-successor--related section))) + +(defun magit-section-goto-successor--same (section line char) + (let ((ident (magit-section-ident section))) + (and-let* ((found (magit-get-section ident))) + (let ((start (oref found start))) + (goto-char start) + (unless (eq found magit-root-section) + (ignore-errors + (forward-line line) + (forward-char char)) + (unless (eq (magit-current-section) found) + (goto-char start))) + t)))) + +(defun magit-section-goto-successor--related (section) + (and-let* ((found (magit-section-goto-successor--related-1 section))) + (goto-char (if (eq (oref found type) 'button) + (point-min) + (oref found start))))) + +(defun magit-section-goto-successor--related-1 (section) + (or (and-let* ((alt (pcase (oref section type) + ('staged 'unstaged) + ('unstaged 'staged) + ('unpushed 'unpulled) + ('unpulled 'unpushed)))) + (magit-get-section `((,alt) (status)))) + (and-let* ((next (car (magit-section-siblings section 'next)))) + (magit-get-section (magit-section-ident next))) + (and-let* ((prev (car (magit-section-siblings section 'prev)))) + (magit-get-section (magit-section-ident prev))) + (and-let* ((parent (oref section parent))) + (or (magit-get-section (magit-section-ident parent)) + (magit-section-goto-successor--related-1 parent))))) + +;;; Region + +(defvar-local magit-section--region-overlays nil) + +(defun magit-section--delete-region-overlays () + (mapc #'delete-overlay magit-section--region-overlays) + (setq magit-section--region-overlays nil)) + +(defun magit-section--highlight-region (start end window rol) + (magit-section--delete-region-overlays) + (if (and (not magit-section-keep-region-overlay) + (or (magit-region-sections) + (run-hook-with-args-until-success 'magit-region-highlight-hook + (magit-current-section))) + (not (= (line-number-at-pos start) + (line-number-at-pos end))) + ;; (not (eq (car-safe last-command-event) 'mouse-movement)) + ) + (funcall (default-value 'redisplay-unhighlight-region-function) rol) + (funcall (default-value 'redisplay-highlight-region-function) + start end window rol))) + +(defun magit-section--unhighlight-region (rol) + (magit-section--delete-region-overlays) + (funcall (default-value 'redisplay-unhighlight-region-function) rol)) + +;;; Visibility + +(defvar-local magit-section-visibility-cache nil) +(put 'magit-section-visibility-cache 'permanent-local t) + +(defun magit-section-cached-visibility (section) + "Set SECTION's visibility to the cached value." + (cdr (assoc (magit-section-ident section) + magit-section-visibility-cache))) + +(cl-defun magit-section-cache-visibility + (&optional (section magit-insert-section--current)) + (setf (compat-alist-get (magit-section-ident section) + magit-section-visibility-cache + nil nil #'equal) + (if (oref section hidden) 'hide 'show))) + +(cl-defun magit-section-maybe-cache-visibility + (&optional (section magit-insert-section--current)) + (when (or (eq magit-section-cache-visibility t) + (memq (oref section type) + magit-section-cache-visibility)) + (magit-section-cache-visibility section))) + +(defun magit-section-maybe-update-visibility-indicator (section) + (when (and magit-section-visibility-indicator + (magit-section-content-p section)) + (let* ((beg (oref section start)) + (eoh (save-excursion + (goto-char beg) + (line-end-position)))) + (cond + ((symbolp (car-safe magit-section-visibility-indicator)) + (let ((ov (magit--overlay-at beg 'magit-vis-indicator 'fringe))) + (unless ov + (setq ov (make-overlay beg eoh nil t)) + (overlay-put ov 'evaporate t) + (overlay-put ov 'magit-vis-indicator 'fringe)) + (overlay-put + ov 'before-string + (propertize "fringe" 'display + (list 'left-fringe + (if (oref section hidden) + (car magit-section-visibility-indicator) + (cdr magit-section-visibility-indicator)) + 'fringe))))) + ((stringp (car-safe magit-section-visibility-indicator)) + (let ((ov (magit--overlay-at (1- eoh) 'magit-vis-indicator 'eoh))) + (cond ((oref section hidden) + (unless ov + (setq ov (make-overlay (1- eoh) eoh)) + (overlay-put ov 'evaporate t) + (overlay-put ov 'magit-vis-indicator 'eoh)) + (overlay-put ov 'after-string + (car magit-section-visibility-indicator))) + (ov + (delete-overlay ov))))))))) + +(defvar-local magit--ellipses-sections nil) + +(defun magit-section-maybe-paint-visibility-ellipses () + ;; This is needed because we hide the body instead of "the body + ;; except the final newline and additionally the newline before + ;; the body"; otherwise we could use `buffer-invisibility-spec'. + (when (stringp (car-safe magit-section-visibility-indicator)) + (let* ((sections (append magit--ellipses-sections + (setq magit--ellipses-sections + (or (magit-region-sections) + (list (magit-current-section)))))) + (beg (--map (oref it start) sections)) + (end (--map (oref it end) sections))) + (when (region-active-p) + ;; This ensures that the region face is removed from ellipses + ;; when the region becomes inactive, but fails to ensure that + ;; all ellipses within the active region use the region face, + ;; because the respective overlay has not yet been updated at + ;; this time. The magit-selection face is always applied. + (push (region-beginning) beg) + (push (region-end) end)) + (setq beg (apply #'min beg)) + (setq end (apply #'max end)) + (dolist (ov (overlays-in beg end)) + (when (eq (overlay-get ov 'magit-vis-indicator) 'eoh) + (overlay-put + ov 'after-string + (propertize + (car magit-section-visibility-indicator) 'font-lock-face + (let ((pos (overlay-start ov))) + (delq nil (nconc (--map (overlay-get it 'font-lock-face) + (overlays-at pos)) + (list (get-char-property + pos 'font-lock-face)))))))))))) + +(defun magit-section-maybe-remove-visibility-indicator (section) + (when (and magit-section-visibility-indicator + (= (oref section content) + (oref section end))) + (dolist (o (overlays-in (oref section start) + (save-excursion + (goto-char (oref section start)) + (1+ (line-end-position))))) + (when (overlay-get o 'magit-vis-indicator) + (delete-overlay o))))) + +(defvar-local magit-section--opened-sections nil) + +(defun magit-section--open-temporarily (beg end) + (save-excursion + (goto-char beg) + (let ((section (magit-current-section))) + (while section + (let ((content (oref section content))) + (if (and (magit-section-invisible-p section) + (<= (or content (oref section start)) + beg + (oref section end))) + (progn + (when content + (magit-section-show section) + (push section magit-section--opened-sections)) + (setq section (oref section parent))) + (setq section nil)))))) + (or (eq search-invisible t) + (not (isearch-range-invisible beg end)))) + +(defun isearch-clean-overlays@magit-mode (fn) + (if (derived-mode-p 'magit-mode) + (let ((pos (point))) + (dolist (section magit-section--opened-sections) + (unless (<= (oref section content) pos (oref section end)) + (magit-section-hide section))) + (setq magit-section--opened-sections nil)) + (funcall fn))) + +(advice-add 'isearch-clean-overlays :around + #'isearch-clean-overlays@magit-mode) + +;;; Utilities + +(cl-defun magit-section-selected-p (section &optional (selection nil sselection)) + (and (not (eq section magit-root-section)) + (or (eq section (magit-current-section)) + (memq section (if sselection + selection + (setq selection (magit-region-sections)))) + (and-let* ((parent (oref section parent))) + (magit-section-selected-p parent selection))))) + +(defun magit-section-parent-value (section) + (and-let* ((parent (oref section parent))) + (oref parent value))) + +(defun magit-section-siblings (section &optional direction) + "Return a list of the sibling sections of SECTION. + +If optional DIRECTION is `prev', then return siblings that come +before SECTION. If it is `next', then return siblings that come +after SECTION. For all other values, return all siblings +excluding SECTION itself." + (and-let* ((parent (oref section parent)) + (siblings (oref parent children))) + (pcase direction + ('prev (cdr (member section (reverse siblings)))) + ('next (cdr (member section siblings))) + (_ (remq section siblings))))) + +(defun magit-region-values (&optional condition multiple) + "Return a list of the values of the selected sections. + +Return the values that themselves would be returned by +`magit-region-sections' (which see)." + (--map (oref it value) + (magit-region-sections condition multiple))) + +(defun magit-region-sections (&optional condition multiple) + "Return a list of the selected sections. + +When the region is active and constitutes a valid section +selection, then return a list of all selected sections. This is +the case when the region begins in the heading of a section and +ends in the heading of the same section or in that of a sibling +section. If optional MULTIPLE is non-nil, then the region cannot +begin and end in the same section. + +When the selection is not valid, then return nil. In this case, +most commands that can act on the selected sections will instead +act on the section at point. + +When the region looks like it would in any other buffer then +the selection is invalid. When the selection is valid then the +region uses the `magit-section-highlight' face. This does not +apply to diffs where things get a bit more complicated, but even +here if the region looks like it usually does, then that's not +a valid selection as far as this function is concerned. + +If optional CONDITION is non-nil, then the selection not only +has to be valid; all selected sections additionally have to match +CONDITION, or nil is returned. See `magit-section-match' for the +forms CONDITION can take." + (when (region-active-p) + (let* ((rbeg (region-beginning)) + (rend (region-end)) + (sbeg (magit-section-at rbeg)) + (send (magit-section-at rend))) + (when (and send + (not (eq send magit-root-section)) + (not (and multiple (eq send sbeg)))) + (let ((siblings (cons sbeg (magit-section-siblings sbeg 'next))) + sections) + (when (and (memq send siblings) + (magit-section-position-in-heading-p sbeg rbeg) + (magit-section-position-in-heading-p send rend)) + (while siblings + (push (car siblings) sections) + (when (eq (pop siblings) send) + (setq siblings nil))) + (setq sections (nreverse sections)) + (when (or (not condition) + (--all-p (magit-section-match condition it) sections)) + sections))))))) + +(defun magit-section-position-in-heading-p (&optional section pos) + "Return t if POSITION is inside the heading of SECTION. +POSITION defaults to point and SECTION defaults to the +current section." + (unless section + (setq section (magit-current-section))) + (unless pos + (setq pos (point))) + (and section + (>= pos (oref section start)) + (< pos (or (oref section content) + (oref section end))) + t)) + +(defun magit-section-internal-region-p (&optional section) + "Return t if the region is active and inside SECTION's body. +If optional SECTION is nil, use the current section." + (and (region-active-p) + (or section (setq section (magit-current-section))) + (let ((beg (magit-section-at (region-beginning)))) + (and (eq beg (magit-section-at (region-end))) + (eq beg section))) + (not (or (magit-section-position-in-heading-p section (region-beginning)) + (magit-section-position-in-heading-p section (region-end)))) + t)) + +(defun magit-wash-sequence (function) + "Repeatedly call FUNCTION until it returns nil or eob is reached. +FUNCTION has to move point forward or return nil." + (while (and (not (eobp)) (funcall function)))) + +(defun magit-add-section-hook (hook function &optional at append local) + "Add to the value of section hook HOOK the function FUNCTION. + +Add FUNCTION at the beginning of the hook list unless optional +APPEND is non-nil, in which case FUNCTION is added at the end. +If FUNCTION already is a member, then move it to the new location. + +If optional AT is non-nil and a member of the hook list, then +add FUNCTION next to that instead. Add before or after AT, or +replace AT with FUNCTION depending on APPEND. If APPEND is the +symbol `replace', then replace AT with FUNCTION. For any other +non-nil value place FUNCTION right after AT. If nil, then place +FUNCTION right before AT. If FUNCTION already is a member of the +list but AT is not, then leave FUNCTION where ever it already is. + +If optional LOCAL is non-nil, then modify the hook's buffer-local +value rather than its global value. This makes the hook local by +copying the default value. That copy is then modified. + +HOOK should be a symbol. If HOOK is void, it is first set to nil. +HOOK's value must not be a single hook function. FUNCTION should +be a function that takes no arguments and inserts one or multiple +sections at point, moving point forward. FUNCTION may choose not +to insert its section(s), when doing so would not make sense. It +should not be abused for other side-effects. To remove FUNCTION +again use `remove-hook'." + (unless (boundp hook) + (error "Cannot add function to undefined hook variable %s" hook)) + (unless (default-boundp hook) + (set-default hook nil)) + (let ((value (if local + (if (local-variable-p hook) + (symbol-value hook) + (unless (local-variable-if-set-p hook) + (make-local-variable hook)) + (copy-sequence (default-value hook))) + (default-value hook)))) + (if at + (when (setq at (member at value)) + (setq value (delq function value)) + (cond ((eq append 'replace) + (setcar at function)) + (append + (push function (cdr at))) + (t + (push (car at) (cdr at)) + (setcar at function)))) + (setq value (delq function value))) + (unless (member function value) + (setq value (if append + (append value (list function)) + (cons function value)))) + (when (eq append 'replace) + (setq value (delq at value))) + (if local + (set hook value) + (set-default hook value)))) + +(defvar-local magit-disabled-section-inserters nil) + +(defun magit-disable-section-inserter (fn) + "Disable the section inserter FN in the current repository. +It is only intended for use in \".dir-locals.el\" and +\".dir-locals-2.el\". Also see info node `(magit)Per-Repository +Configuration'." + (cl-pushnew fn magit-disabled-section-inserters)) + +(put 'magit-disable-section-inserter 'safe-local-eval-function t) + +(defun magit-run-section-hook (hook &rest args) + "Run HOOK with ARGS, warning about invalid entries." + (let ((entries (symbol-value hook))) + (unless (listp entries) + (setq entries (list entries))) + (--when-let (-remove #'functionp entries) + (message "`%s' contains entries that are no longer valid. +%s\nUsing standard value instead. Please re-configure hook variable." + hook + (mapconcat (lambda (sym) (format " `%s'" sym)) it "\n")) + (sit-for 5) + (setq entries (eval (car (get hook 'standard-value))))) + (dolist (entry entries) + (let ((magit--current-section-hook (cons (list hook entry) + magit--current-section-hook))) + (unless (memq entry magit-disabled-section-inserters) + (if (bound-and-true-p magit-refresh-verbose) + (let ((time (benchmark-elapse (apply entry args)))) + (message " %-50s %s %s" entry time + (cond ((> time 0.03) "!!") + ((> time 0.01) "!") + (t "")))) + (apply entry args))))))) + +(cl-defun magit--overlay-at (pos prop &optional (val nil sval) testfn) + (cl-find-if (lambda (o) + (let ((p (overlay-properties o))) + (and (plist-member p prop) + (or (not sval) + (funcall (or testfn #'eql) + (plist-get p prop) + val))))) + (overlays-at pos t))) + +(defun magit-face-property-all (face string) + "Return non-nil if FACE is present in all of STRING." + (catch 'missing + (let ((pos 0)) + (while (setq pos (next-single-property-change pos 'font-lock-face string)) + (let ((val (get-text-property pos 'font-lock-face string))) + (unless (if (consp val) + (memq face val) + (eq face val)) + (throw 'missing nil)))) + (not pos)))) + +(defun magit--add-face-text-property (beg end face &optional append object) + "Like `add-face-text-property' but for `font-lock-face'." + (while (< beg end) + (let* ((pos (next-single-property-change beg 'font-lock-face object end)) + (val (get-text-property beg 'font-lock-face object)) + (val (if (listp val) val (list val)))) + (put-text-property beg pos 'font-lock-face + (if append + (append val (list face)) + (cons face val)) + object) + (setq beg pos)))) + +(defun magit--propertize-face (string face) + (propertize string 'face face 'font-lock-face face)) + +(defun magit--put-face (beg end face string) + (put-text-property beg end 'face face string) + (put-text-property beg end 'font-lock-face face string)) + +;;; Bitmaps + +(when (fboundp 'define-fringe-bitmap) + (define-fringe-bitmap 'magit-fringe-bitmap+ + [#b00000000 + #b00011000 + #b00011000 + #b01111110 + #b01111110 + #b00011000 + #b00011000 + #b00000000]) + (define-fringe-bitmap 'magit-fringe-bitmap- + [#b00000000 + #b00000000 + #b00000000 + #b01111110 + #b01111110 + #b00000000 + #b00000000 + #b00000000]) + + (define-fringe-bitmap 'magit-fringe-bitmap> + [#b01100000 + #b00110000 + #b00011000 + #b00001100 + #b00011000 + #b00110000 + #b01100000 + #b00000000]) + (define-fringe-bitmap 'magit-fringe-bitmapv + [#b00000000 + #b10000010 + #b11000110 + #b01101100 + #b00111000 + #b00010000 + #b00000000 + #b00000000]) + + (define-fringe-bitmap 'magit-fringe-bitmap-bold> + [#b11100000 + #b01110000 + #b00111000 + #b00011100 + #b00011100 + #b00111000 + #b01110000 + #b11100000]) + (define-fringe-bitmap 'magit-fringe-bitmap-boldv + [#b10000001 + #b11000011 + #b11100111 + #b01111110 + #b00111100 + #b00011000 + #b00000000 + #b00000000]) + ) + +;;; _ +(provide 'magit-section) +;;; magit-section.el ends here diff --git a/code/elpa/magit-section-20220810.1158/magit-section.info b/code/elpa/magit-section-20220810.1158/magit-section.info new file mode 100644 index 0000000..7e6ad9e --- /dev/null +++ b/code/elpa/magit-section-20220810.1158/magit-section.info @@ -0,0 +1,307 @@ +This is magit-section.info, produced by makeinfo version 6.7 from +magit-section.texi. + + Copyright (C) 2015-2022 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Magit-Section: (magit-section). Use Magit sections in your own packages. +END-INFO-DIR-ENTRY + + +File: magit-section.info, Node: Top, Next: Introduction, Up: (dir) + +Magit-Section Developer Manual +****************************** + +This package implements the main user interface of Magit — the +collapsible sections that make up its buffers. This package used to be +distributed as part of Magit but how it can also be used by other +packages that have nothing to do with Magit or Git. + + To learn more about the section abstraction and available commands +and user options see *note (magit)Sections::. This manual documents how +you can use sections in your own packages. + +This manual is for Magit-Section version 3.3.0-git. + + Copyright (C) 2015-2022 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +* Menu: + +* Introduction:: +* Creating Sections:: +* Core Functions:: +* Matching Functions:: + + +File: magit-section.info, Node: Introduction, Next: Creating Sections, Prev: Top, Up: Top + +1 Introduction +************** + +This package implements the main user interface of Magit — the +collapsible sections that make up its buffers. This package used to be +distributed as part of Magit but how it can also be used by other +packages that have nothing to do with Magit or Git. + + To learn more about the section abstraction and available commands +and user options see *note (magit)Sections::. This manual documents how +you can use sections in your own packages. + + When the documentation leaves something unaddressed, then please +consider that Magit uses this library extensively and search its source +for suitable examples before asking me for help. Thanks! + + +File: magit-section.info, Node: Creating Sections, Next: Core Functions, Prev: Introduction, Up: Top + +2 Creating Sections +******************* + + -- Macro: magit-insert-section [name] (type &optional value hide) &rest + body + Create a section object of type CLASS, storing VALUE in its ‘value’ + slot, and insert the section at point. CLASS is a subclass of + ‘magit-section’ or has the form ‘(eval FORM)’, in which case FORM + is evaluated at runtime and should return a subclass. In other + places a sections class is oftern referred to as its "type". + + Many commands behave differently depending on the class of the + current section and sections of a certain class can have their own + keymap, which is specified using the ‘keymap’ class slot. The + value of that slot should be a variable whose value is a keymap. + + For historic reasons Magit and Forge in most cases use symbols as + CLASS that don’t actually identify a class and that lack the + appropriate package prefix. This works due to some undocumented + kludges, which are not available to other packages. + + When optional HIDE is non-nil collapse the section body by default, + i.e. when first creating the section, but not when refreshing the + buffer. Else expand it by default. This can be overwritten using + ‘magit-section-set-visibility-hook’. When a section is recreated + during a refresh, then the visibility of predecessor is inherited + and HIDE is ignored (but the hook is still honored). + + BODY is any number of forms that actually insert the section’s + heading and body. Optional NAME, if specified, has to be a symbol, + which is then bound to the object of the section being inserted. + + Before BODY is evaluated the ‘start’ of the section object is set + to the value of ‘point’ and after BODY was evaluated its ‘end’ is + set to the new value of ‘point’; BODY is responsible for moving + ‘point’ forward. + + If it turns out inside BODY that the section is empty, then + ‘magit-cancel-section’ can be used to abort and remove all traces + of the partially inserted section. This can happen when creating a + section by washing Git’s output and Git didn’t actually output + anything this time around. + + -- Function: magit-insert-heading &rest args + Insert the heading for the section currently being inserted. + + This function should only be used inside ‘magit-insert-section’. + + When called without any arguments, then just set the ‘content’ slot + of the object representing the section being inserted to a marker + at ‘point’. The section should only contain a single line when + this function is used like this. + + When called with arguments ARGS, which have to be strings, or nil, + then insert those strings at point. The section should not contain + any text before this happens and afterwards it should again only + contain a single line. If the ‘face’ property is set anywhere + inside any of these strings, then insert all of them unchanged. + Otherwise use the ‘magit-section-heading’ face for all inserted + text. + + The ‘content’ property of the section object is the end of the + heading (which lasts from ‘start’ to ‘content’) and the beginning + of the the body (which lasts from ‘content’ to ‘end’). If the + value of ‘content’ is nil, then the section has no heading and its + body cannot be collapsed. If a section does have a heading, then + its height must be exactly one line, including a trailing newline + character. This isn’t enforced, you are responsible for getting it + right. The only exception is that this function does insert a + newline character if necessary. + + -- Macro: magit-insert-section-body &rest body + Use BODY to insert the section body, once the section is expanded. + If the section is expanded when it is created, then this is like + ‘progn’. Otherwise BODY isn’t evaluated until the section is + explicitly expanded. + + -- Function: magit-cancel-section + Cancel inserting the section that is currently being inserted. + Remove all traces of that section. + + -- Function: magit-wash-sequence function + Repeatedly call FUNCTION until it returns ‘nil’ or the end of the + buffer is reached. FUNCTION has to move point forward or return + ‘nil’. + + +File: magit-section.info, Node: Core Functions, Next: Matching Functions, Prev: Creating Sections, Up: Top + +3 Core Functions +**************** + + -- Function: magit-current-section + Return the section at point or where the context menu was invoked. + When using the context menu, return the section that the user + clicked on, provided the current buffer is the buffer in which the + click occurred. Otherwise return the section at point. + +Function magit-section-at &optional position + Return the section at POSITION, defaulting to point. Default to + point even when the context menu is used. + + -- Function: magit-section-ident section + Return an unique identifier for SECTION. The return value has the + form ‘((TYPE . VALUE)...)’. + + -- Function: magit-section-ident-value value + Return a constant representation of VALUE. + + VALUE is the value of a ‘magit-section’ object. If that is an + object itself, then that is not suitable to be used to identify the + section because two objects may represent the same thing but not be + equal. If possible a method should be added for such objects, + which returns a value that is equal. Otherwise the catch-all + method is used, which just returns the argument itself. + + -- Function: magit-get-section ident &optional root + Return the section identified by IDENT. IDENT has to be a list as + returned by ‘magit-section-ident’. If optional ROOT is non-nil, + then search in that section tree instead of in the one whose root + ‘magit-root-section’ is. + + -- Function: magit-section-lineage section + Return the lineage of SECTION. The return value has the form + ‘(TYPE...)’. + + -- Function: magit-section-content-p section + Return non-nil if SECTION has content or an unused washer function. + + The next two functions are replacements for the Emacs functions that +have the same name except for the ‘magit-’ prefix. Like +‘magit-current-section’ they do not act on point, the cursors position, +but on the position where the user clicked to invoke the context menu. + + If your package provides a context menu and some of its commands act +on the "thing at point", even if just as a default, then use the +prefixed functions to teach them to instead use the click location when +appropriate. + +Function magit-point + Return point or the position where the context menu was invoked. + When using the context menu, return the position the user clicked + on, provided the current buffer is the buffer in which the click + occurred. Otherwise return the same value as ‘point’. + +Function magit-thing-at-point thing &optional no-properties + Return the THING at point or where the context menu was invoked. + When using the context menu, return the thing the user clicked on, + provided the current buffer is the buffer in which the click + occurred. Otherwise return the same value as ‘thing-at-point’. + For the meaning of THING and NO-PROPERTIES see that function. + + +File: magit-section.info, Node: Matching Functions, Prev: Core Functions, Up: Top + +4 Matching Functions +******************** + + -- Function: magit-section-match condition &optional (section + (magit-current-section)) + Return t if SECTION matches CONDITION. + + SECTION defaults to the section at point. If SECTION is not + specified and there also is no section at point, then return nil. + + CONDITION can take the following forms: + + • ‘(CONDITION...)’ matches if any of the CONDITIONs matches. + • ‘[CLASS...]’ matches if the section’s class is the same as the + first CLASS or a subclass of that; the section’s parent class + matches the second CLASS; and so on. + + • ‘[* CLASS...]’ matches sections that match [CLASS...] and also + recursively all their child sections. + • ‘CLASS’ matches if the section’s class is the same as CLASS or + a subclass of that; regardless of the classes of the parent + sections. + + Each CLASS should be a class symbol, identifying a class that + derives from ‘magit-section’. For backward compatibility CLASS can + also be a "type symbol". A section matches such a symbol if the + value of its ‘type’ slot is ‘eq’. If a type symbol has an entry in + ‘magit--section-type-alist’, then a section also matches that type + if its class is a subclass of the class that corresponds to the + type as per that alist. + + Note that it is not necessary to specify the complete section + lineage as printed by ‘magit-describe-section-briefly’, unless of + course you want to be that precise. + + -- Function: magit-section-value-if condition &optional section + If the section at point matches CONDITION, then return its value. + + If optional SECTION is non-nil then test whether that matches + instead. If there is no section at point and SECTION is nil, then + return nil. If the section does not match, then return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + + -- Macro: magit-section-case &rest clauses + Choose among clauses on the type of the section at point. + + Each clause looks like ‘(CONDITION BODY...)’. The type of the + section is compared against each CONDITION; the BODY forms of the + first match are evaluated sequentially and the value of the last + form is returned. Inside BODY the symbol ‘it’ is bound to the + section at point. If no clause succeeds or if there is no section + at point, return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + Additionally a CONDITION of t is allowed in the final clause, and + matches if no other CONDITION match, even if there is no section at + point. + + + +Tag Table: +Node: Top788 +Node: Introduction2073 +Node: Creating Sections2843 +Node: Core Functions7352 +Node: Matching Functions10404 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/markdown-preview-mode-20210516.936/favicon.ico b/code/elpa/markdown-preview-mode-20210516.936/favicon.ico new file mode 100644 index 0000000..e1e43c3 Binary files /dev/null and b/code/elpa/markdown-preview-mode-20210516.936/favicon.ico differ diff --git a/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode-autoloads.el b/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode-autoloads.el new file mode 100644 index 0000000..537ba89 --- /dev/null +++ b/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode-autoloads.el @@ -0,0 +1,54 @@ +;;; markdown-preview-mode-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "markdown-preview-mode" "markdown-preview-mode.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from markdown-preview-mode.el + +(autoload 'markdown-preview-open-browser "markdown-preview-mode" "\ +Open the `markdown-preview' in the browser." t nil) + +(autoload 'markdown-preview-cleanup "markdown-preview-mode" "\ +Cleanup `markdown-preview' mode." t nil) + +(autoload 'markdown-preview-mode "markdown-preview-mode" "\ +Markdown preview mode. + +This is a minor mode. If called interactively, toggle the +`Markdown-Preview mode' mode. If the prefix argument is +positive, enable the mode, and if it is zero or negative, disable +the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `markdown-preview-mode'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "markdown-preview-mode" '("markdown-preview-")) + +;;;*** + +;;;### (autoloads nil nil ("markdown-preview-mode-pkg.el") (0 0 0 +;;;;;; 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; markdown-preview-mode-autoloads.el ends here diff --git a/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode-pkg.el b/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode-pkg.el new file mode 100644 index 0000000..d5fc4cb --- /dev/null +++ b/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode-pkg.el @@ -0,0 +1,16 @@ +(define-package "markdown-preview-mode" "20210516.936" "markdown realtime preview minor mode." + '((emacs "24.4") + (websocket "1.6") + (markdown-mode "2.0") + (cl-lib "0.5") + (web-server "0.1.1")) + :commit "dde87b96de9e81dd01d174da67ef68687b3a5eb5" :authors + '(("Igor Shymko" . "igor.shimko@gmail.com")) + :maintainer + '("Igor Shymko" . "igor.shimko@gmail.com") + :keywords + '("markdown" "gfm" "convenience") + :url "https://github.com/ancane/markdown-preview-mode") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode.el b/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode.el new file mode 100644 index 0000000..6fde898 --- /dev/null +++ b/code/elpa/markdown-preview-mode-20210516.936/markdown-preview-mode.el @@ -0,0 +1,418 @@ +;;; markdown-preview-mode.el --- markdown realtime preview minor mode. + +;; Copyright (C) 2014 + +;; Author: Igor Shymko +;; URL: https://github.com/ancane/markdown-preview-mode +;; Keywords: markdown, gfm, convenience +;; Version: 0.9.4 +;; Package-Requires: ((emacs "24.4") (websocket "1.6") (markdown-mode "2.0") (cl-lib "0.5") (web-server "0.1.1") ) + +;; 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, 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 GNU Emacs. If not, see . + +;;; Commentary: +;; +;; This package makes use of websockets to deliver rendered markdown to a web browser. +;; Updates happen upon buffer save or on idle. +;; +;;; Code: + +(eval-when-compile (require 'cl)) +(require 'cl-lib) +(require 'websocket) +(require 'markdown-mode) +(require 'web-server) + +(defgroup markdown-preview nil + "Markdown preview mode." + :group 'text + :prefix "markdown-preview-" + :link '(url-link "https://github.com/ancane/markdown-preview-mode")) + +(defcustom markdown-preview-host "localhost" + "Markdown preview websocket server address." + :group 'markdown-preview + :type 'string) + +(defcustom markdown-preview-ws-port 7379 + "Markdown preview websocket server port." + :group 'markdown-preview + :type 'integer) + +(defcustom markdown-preview-http-host "localhost" + "Markdown preview http server address." + :group 'markdown-preview + :type 'string) + +(defcustom markdown-preview-http-port 9000 + "Markdown preview http server port." + :group 'markdown-preview + :type 'integer) + +(defcustom markdown-preview-style nil + "Deprecated. Use `markdown-preview-stylesheets'." + :group 'markdown-preview + :type 'string) + +(defcustom markdown-preview-file-name ".markdown-preview.html" + "Markdown preview file name." + :group 'markdown-preview + :type 'string) + +(defcustom markdown-preview-auto-open 'http + "Markdown preview websocket server address." + :group 'markdown-preview + :type '(choice (const :tag "As local file" file) + (const :tag "Via http" http) + (const :tag "Off" nil))) + +(defcustom markdown-preview-delay-time 2.0 + "Refresh preview after this certain of time." + :group 'markdown-preview + :type 'float) + +(defvar markdown-preview-javascript '() + "List of client javascript libs for preview.") + +(defvar markdown-preview-stylesheets + (list "https://thomasf.github.io/solarized-css/solarized-dark.min.css") + "List of client stylesheets for preview.") + +(defvar markdown-preview--websocket-server nil + "`markdown-preview' Websocket server.") + +(defvar markdown-preview--http-server nil + "`markdown-preview' http server.") + +(defvar markdown-preview--local-client nil + "`markdown-preview' local client.") + +(defvar markdown-preview--remote-clients (make-hash-table :test 'equal) + "Remote clients hashtable. UUID -> WS.") + +(defvar markdown-preview--home-dir (file-name-directory load-file-name) + "`markdown-preview-mode' home directory.") + +(defvar markdown-preview--preview-template + (expand-file-name "preview.html" markdown-preview--home-dir) + "`markdown-preview-mode' html preview template.") + +(defvar markdown-preview--idle-timer nil + "Preview idle timer.") + +(defvar markdown-preview--uuid nil + "Unique preview identifier.") + +(defvar markdown-preview--preview-buffers (make-hash-table :test 'equal) + "Preview buffers hashtable. UUID -> buffer name.") + +(defun markdown-preview--stop-idle-timer () + "Stop the `markdown-preview' idle timer." + (when (timerp markdown-preview--idle-timer) + (cancel-timer markdown-preview--idle-timer))) + +(defun markdown-preview--css () + "Get list of styles for preview in backward compatible way." + (let* ((custom-style (list markdown-preview-style)) + (all-styles + (mapc (lambda (x) (add-to-list 'custom-style x t)) markdown-preview-stylesheets))) + (mapconcat + (lambda (x) + (if (string-match-p "^[\n\t ]*"))) + all-styles + "\n"))) + +(defun markdown-preview--scripts () + "Get list of javascript script tags for preview." + (mapconcat + (lambda (x) + (if (string-match-p "^[\n\t ]*"))) + markdown-preview-javascript + "\n")) + +(defun markdown-preview--read-preview-template (preview-uuid preview-file) + "Read preview template and writes identified by PREVIEW-UUID rendered copy to PREVIEW-FILE, ready to be open in browser." + (with-temp-file preview-file + (insert-file-contents markdown-preview--preview-template) + (when (search-forward "${MD_STYLE}" nil t) + (replace-match (markdown-preview--css) t)) + (when (search-forward "${MD_JS}" nil t) + (replace-match (markdown-preview--scripts) t)) + (when (search-forward "${WS_HOST}" nil t) + (replace-match markdown-preview-host t)) + (when (search-forward "${WS_PORT}" nil t) + (replace-match (format "%s" markdown-preview-ws-port) t)) + (when (search-forward "${MD_UUID}" nil t) + (replace-match (format "%s" preview-uuid) t)) + (buffer-string))) + +;; Emacs 26 async network workaround +(defun markdown-preview--fix-network-process-wait (plist) + "Ensure PLIST contain :nowait nil." + (if (and (>= emacs-major-version 26) + (equal (plist-get plist :name) "ws-server") + (plist-get plist :server) + (plist-get plist :nowait)) + (plist-put plist :nowait nil) + plist)) + +(defun markdown-preview--start-http-server (port) + "Start http server at PORT to serve preview file via http." + (unless markdown-preview--http-server + (lexical-let ((docroot default-directory)) + (advice-add 'make-network-process :filter-args #'markdown-preview--fix-network-process-wait) + (setq markdown-preview--http-server + (ws-start + (lambda (request) + (with-slots (process headers) request + (let* ((path (substring (cdr (assoc :GET headers)) 1)) + (filename (expand-file-name path docroot))) + (if (string= path "") + (progn + (ws-send-file + process + (expand-file-name + markdown-preview-file-name + (with-current-buffer + (gethash (markdown-preview--parse-uuid headers) + markdown-preview--preview-buffers) + default-directory + )))) + (if (string= path "favicon.ico") + (ws-send-file process (expand-file-name path markdown-preview--home-dir)) + (if (and (not (file-directory-p filename)) (file-exists-p filename)) + (ws-send-file process filename) + (ws-send-404 process) + )))))) + markdown-preview-http-port nil :host markdown-preview-http-host)) + (advice-remove 'make-network-process #'markdown-preview--fix-network-process-wait)))) + +(defun markdown-preview--parse-uuid (headers) + "Find uuid query param in HEADERS." + (let ((found (cl-find-if (lambda (x) + (when (stringp (car x)) + (equal "uuid" (format "%s" (car x))))) + headers))) + (when found (cdr found)))) + + +(defun markdown-preview--open-browser-preview () + "Open the markdown preview in the browser." + (when (eq markdown-preview-auto-open 'file) + (browse-url (expand-file-name markdown-preview-file-name default-directory))) + (when (eq markdown-preview-auto-open 'http) + (browse-url + (format "http://localhost:%d/?uuid=%s" markdown-preview-http-port markdown-preview--uuid))) + (unless markdown-preview-auto-open + (message + (format + "Preview address: http://0.0.0.0:%d/?uuid=%s" + markdown-preview-http-port + markdown-preview--uuid)))) + +(defun markdown-preview--stop-websocket-server () + "Stop the `markdown-preview' websocket server." + (clrhash markdown-preview--preview-buffers) + (when markdown-preview--local-client + (websocket-close markdown-preview--local-client)) + (when markdown-preview--websocket-server + (delete-process markdown-preview--websocket-server) + (setq markdown-preview--websocket-server nil) + (clrhash markdown-preview--remote-clients))) + +(defun markdown-preview--stop-http-server () + "Stop the `markdown-preview' http server." + (when markdown-preview--http-server + (ws-stop markdown-preview--http-server) + (setq markdown-preview--http-server nil))) + +(defun markdown-preview--drop-closed-clients () + "Clean closed clients in `markdown-preview--remote-clients' list." + (maphash (lambda (ws-uuid websocket) + (unless (websocket-openp websocket)) + (remhash ws-uuid markdown-preview--remote-clients)) + markdown-preview--remote-clients)) + +(defun markdown-preview--start-websocket-server () + "Start `markdown-preview' websocket server." + (when (not markdown-preview--websocket-server) + (setq markdown-preview--websocket-server + (websocket-server + markdown-preview-ws-port + :host markdown-preview-host + :on-message (lambda (websocket frame) + (let ((ws-frame-text (websocket-frame-payload frame))) + (if (and + (stringp ws-frame-text) + (string-prefix-p "MDPM-Register-UUID: " ws-frame-text)) + (let ((ws-uuid (substring ws-frame-text 20))) + (puthash ws-uuid websocket markdown-preview--remote-clients) + (markdown-preview--send-preview-to websocket ws-uuid)) + (progn + (websocket-send + (gethash markdown-preview--uuid markdown-preview--remote-clients) + frame)) + ))) + :on-open (lambda (websocket) (message "Websocket opened")) + :on-error (lambda (websocket type err) (message (format "====> Error: %s" err))) + :on-close (lambda (websocket) (markdown-preview--drop-closed-clients)))) + (add-hook 'kill-emacs-hook 'markdown-preview--stop-websocket-server)) + (markdown-preview--open-browser-preview)) + +(defun markdown-preview--start-local-client () + "Start the `markdown-preview' local client." + (when (not markdown-preview--local-client) + (setq markdown-preview--local-client + (websocket-open + (format "ws://%s:%d" markdown-preview-host markdown-preview-ws-port) + :on-error (lambda (ws type err) + (message "error connecting")) + :on-close (lambda (websocket) + (setq markdown-preview--local-client nil)))))) + +(defun markdown-preview--send-preview (preview-uuid) + "Send the `markdown-preview' with PREVIEW-UUID preview to clients." + (when (bound-and-true-p markdown-preview-mode) + (markdown-preview--send-preview-to markdown-preview--local-client preview-uuid))) + +(defun markdown-preview--send-preview-to (websocket preview-uuid) + "Send the `markdown-preview' with PREVIEW-UUID to a specific WEBSOCKET." + (let ((mark-position-percent + (number-to-string + (truncate + (* 100 + (/ + (float (- (line-number-at-pos) (/ (count-screen-lines (window-start) (point)) 2))) + (count-lines (point-min) (point-max)))))))) + + (let ((md-buffer (gethash preview-uuid markdown-preview--preview-buffers))) + (when md-buffer + (with-current-buffer md-buffer + + (markdown markdown-output-buffer-name)))) + + (with-current-buffer markdown-output-buffer-name ;; get-buffer + (websocket-send-text websocket + (concat + "
" + "" + mark-position-percent + "" + "
" + (buffer-substring-no-properties (point-min) (point-max)) + "
" + "
") + )))) + +(defun markdown-preview--start () + "Start `markdown-preview' mode." + (setq-local markdown-preview--uuid (markdown-preview--random-uuid)) + (puthash markdown-preview--uuid (buffer-name) markdown-preview--preview-buffers) + ;; (gethash markdown-preview--uuid markdown-preview--preview-buffers) + (markdown-preview--read-preview-template + markdown-preview--uuid + (expand-file-name markdown-preview-file-name default-directory)) + (markdown-preview--start-websocket-server) + (markdown-preview--start-local-client) + (markdown-preview--start-http-server markdown-preview-http-port) + (setq markdown-preview--idle-timer + (run-with-idle-timer markdown-preview-delay-time t + (lambda () + (when markdown-preview--uuid + (markdown-preview--send-preview markdown-preview--uuid))))) + (add-hook 'after-save-hook (lambda () + (when markdown-preview--uuid + (markdown-preview--send-preview markdown-preview--uuid))) nil t)) + +(defun markdown-preview--stop () + "Stop `markdown-preview' mode." + (remove-hook 'after-save-hook 'markdown-preview--send-preview t) + (markdown-preview--stop-idle-timer) + (remhash markdown-preview--uuid markdown-preview--preview-buffers) + (let* ((preview-file-dir (if (buffer-file-name) + (file-name-directory (buffer-file-name)) + default-directory)) + (preview-file (concat preview-file-dir markdown-preview-file-name))) + (if (file-exists-p preview-file) + (delete-file preview-file)))) + +(defun markdown-preview--random-uuid () + "Insert a UUID using a simple hashing of variable data. +Example of a UUID: 1df63142-a513-c850-31a3-535fc3520c3d +Note: this code uses https://en.wikipedia.org/wiki/Md5, +which is not cryptographically safe. I'm not sure what's +the implication of its use here. +Version 2015-01-30 +URL `http://ergoemacs.org/emacs/elisp_generate_uuid.html'" + ;; by Christopher Wellons, 2011-11-18. Editted by Xah Lee. + ;; Edited by Hideki Saito further to generate all valid variants for "N" in xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx format. + ;; (interactive) + (let ((myStr (md5 (format "%s%s%s%s%s%s%s%s%s%s" + (user-uid) + (emacs-pid) + (system-name) + (user-full-name) + (current-time) + (emacs-uptime) + (garbage-collect) + (buffer-string) + (random) + (recent-keys))))) + + (format "%s-%s-4%s-%s%s-%s" + (substring myStr 0 8) + (substring myStr 8 12) + (substring myStr 13 16) + (format "%x" (+ 8 (random 4))) + (substring myStr 17 20) + (substring myStr 20 32)))) + +;;;###autoload +(defun markdown-preview-open-browser () + "Open the `markdown-preview' in the browser." + (interactive) + (markdown-preview--open-browser-preview)) + +;;;###autoload +(defun markdown-preview-cleanup () + "Cleanup `markdown-preview' mode." + (interactive) + (markdown-preview--stop-websocket-server) + (markdown-preview--stop-http-server)) + +;;;###autoload +(define-minor-mode markdown-preview-mode + "Markdown preview mode." + :group 'markdown-preview + :init-value nil + (when (not (or + (equal major-mode 'markdown-mode) + (equal major-mode 'gfm-mode))) + (markdown-mode)) + (if markdown-preview-mode + (markdown-preview--start) + (markdown-preview--stop))) + +(provide 'markdown-preview-mode) + +;;; markdown-preview-mode.el ends here diff --git a/code/elpa/markdown-preview-mode-20210516.936/preview.html b/code/elpa/markdown-preview-mode-20210516.936/preview.html new file mode 100644 index 0000000..4ba0bef --- /dev/null +++ b/code/elpa/markdown-preview-mode-20210516.936/preview.html @@ -0,0 +1,41 @@ + + + + + + Markdown preview + ${MD_STYLE} + + ${MD_JS} + + + +
+

Markdown preview

+
+ + diff --git a/code/elpa/modus-themes-20220823.1919/dir b/code/elpa/modus-themes-20220823.1919/dir new file mode 100644 index 0000000..b0b7f0b --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/dir @@ -0,0 +1,19 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs misc features +* Modus Themes: (modus-themes). Elegant, highly legible and customizable + themes. diff --git a/code/elpa/modus-themes-20220823.1919/doclicense.info b/code/elpa/modus-themes-20220823.1919/doclicense.info new file mode 100644 index 0000000..b080c90 --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/doclicense.info @@ -0,0 +1,489 @@ +This is doclicense.info, produced by makeinfo version 6.7 from +doclicense.texi. + + Version 1.3, 3 November 2008 + + Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document "free" in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of "copyleft", which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. We + recommend this License principally for works whose purpose is + instruction or reference. + + 1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it can + be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + "Document", below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as "you". You accept + the license if you copy, modify or distribute the work in a way + requiring permission under copyright law. + + A "Modified Version" of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A "Secondary Section" is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document's overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The "Invariant Sections" are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in the + notice that says that the Document is released under this License. + If a section does not fit the above definition of Secondary then it + is not allowed to be designated as Invariant. The Document may + contain zero Invariant Sections. If the Document does not identify + any Invariant Sections then there are none. + + The "Cover Texts" are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A "Transparent" copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images composed + of pixels) generic paint programs or (for drawings) some widely + available drawing editor, and that is suitable for input to text + formatters or for automatic translation to a variety of formats + suitable for input to text formatters. A copy made in an otherwise + Transparent file format whose markup, or absence of markup, has + been arranged to thwart or discourage subsequent modification by + readers is not Transparent. An image format is not Transparent if + used for any substantial amount of text. A copy that is not + "Transparent" is called "Opaque". + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and standard-conforming + simple HTML, PostScript or PDF designed for human modification. + Examples of transparent image formats include PNG, XCF and JPG. + Opaque formats include proprietary formats that can be read and + edited only by proprietary word processors, SGML or XML for which + the DTD and/or processing tools are not generally available, and + the machine-generated HTML, PostScript or PDF produced by some word + processors for output purposes only. + + The "Title Page" means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, "Title + Page" means the text near the most prominent appearance of the + work's title, preceding the beginning of the body of the text. + + The "publisher" means any person or entity that distributes copies + of the Document to the public. + + A section "Entitled XYZ" means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + "Acknowledgements", "Dedications", "Endorsements", or "History".) + To "Preserve the Title" of such a section when you modify the + Document means that it remains a section "Entitled XYZ" according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + + 2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow the + conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + + 3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document's license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the title + equally prominent and visible. You may add other material on the + covers in addition. Copying with changes limited to the covers, as + long as they preserve the title of the Document and satisfy these + conditions, can be treated as verbatim copying in other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a machine-readable + Transparent copy along with each Opaque copy, or state in or with + each Opaque copy a computer-network location from which the general + network-using public has access to download using public-standard + network protocols a complete Transparent copy of the Document, free + of added material. If you use the latter option, you must take + reasonably prudent steps, when you begin distribution of Opaque + copies in quantity, to ensure that this Transparent copy will + remain thus accessible at the stated location until at least one + year after the last time you distribute an Opaque copy (directly or + through your agents or retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of copies, + to give them a chance to provide you with an updated version of the + Document. + + 4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with the + Modified Version filling the role of the Document, thus licensing + distribution and modification of the Modified Version to whoever + possesses a copy of it. In addition, you must do these things in + the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of previous + versions (which should, if there were any, be listed in the + History section of the Document). You may use the same title + as a previous version if the original publisher of that + version gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has fewer than five), unless they release you + from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified + Version under the terms of this License, in the form shown in + the Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document's + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled "History", Preserve its Title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on the + Title Page. If there is no section Entitled "History" in the + Document, create one stating the title, year, authors, and + publisher of the Document as given on its Title Page, then add + an item describing the Modified Version as stated in the + previous sentence. + + J. Preserve the network location, if any, given in the Document + for public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in the + "History" section. You may omit a network location for a work + that was published at least four years before the Document + itself, or if the original publisher of the version it refers + to gives permission. + + K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section + all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, unaltered + in their text and in their titles. Section numbers or the + equivalent are not considered part of the section titles. + + M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + "Endorsements" or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option designate + some or all of these sections as invariant. To do this, add their + titles to the list of Invariant Sections in the Modified Version's + license notice. These titles must be distinct from any other + section titles. + + You may add a section Entitled "Endorsements", provided it contains + nothing but endorsements of your Modified Version by various + parties--for example, statements of peer review or that the text + has been approved by an organization as the authoritative + definition of a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end of + the list of Cover Texts in the Modified Version. Only one passage + of Front-Cover Text and one of Back-Cover Text may be added by (or + through arrangements made by) any one entity. If the Document + already includes a cover text for the same cover, previously added + by you or by arrangement made by the same entity you are acting on + behalf of, you may not add another; but you may replace the old + one, on explicit permission from the previous publisher that added + the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + + 5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination all + of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + "History" in the various original documents, forming one section + Entitled "History"; likewise combine any sections Entitled + "Acknowledgements", and any sections Entitled "Dedications". You + must delete all sections Entitled "Endorsements." + + 6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the documents + in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow this + License in all other respects regarding verbatim copying of that + document. + + 7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of a + storage or distribution medium, is called an "aggregate" if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation's users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document's Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + + 8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of section + 4. Replacing Invariant Sections with translations requires special + permission from their copyright holders, but you may include + translations of some or all Invariant Sections in addition to the + original versions of these Invariant Sections. You may include a + translation of this License, and all the license notices in the + Document, and any Warranty Disclaimers, provided that you also + include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled "Acknowledgements", + "Dedications", or "History", the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + + 9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, or distribute it is void, + and will automatically terminate your rights under this License. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, receipt of a copy of some or all of the + same material does not give you any rights to use it. + + 10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. See + . + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If the + Document does not specify a version number of this License, you may + choose any version ever published (not as a draft) by the Free + Software Foundation. If the Document specifies that a proxy can + decide which future versions of this License can be used, that + proxy's public statement of acceptance of a version permanently + authorizes you to choose that version for the Document. + + 11. RELICENSING + + "Massive Multiauthor Collaboration Site" (or "MMC Site") means any + World Wide Web server that publishes copyrightable works and also + provides prominent facilities for anybody to edit those works. A + public wiki that anybody can edit is an example of such a server. + A "Massive Multiauthor Collaboration" (or "MMC") contained in the + site means any set of copyrightable works thus published on the MMC + site. + + "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 + license published by Creative Commons Corporation, a not-for-profit + corporation with a principal place of business in San Francisco, + California, as well as future copyleft versions of that license + published by that same organization. + + "Incorporate" means to publish or republish a Document, in whole or + in part, as part of another Document. + + An MMC is "eligible for relicensing" if it is licensed under this + License, and if all works that were first published under this + License somewhere other than this MMC, and subsequently + incorporated in whole or in part into the MMC, (1) had no cover + texts or invariant sections, and (2) were thus incorporated prior + to November 1, 2008. + + The operator of an MMC Site may republish an MMC contained in the + site under CC-BY-SA on the same site at any time before August 1, + 2009, provided the MMC is eligible for relicensing. + +ADDENDUM: How to use this License for your documents +==================================================== + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and license +notices just after the title page: + + Copyright (C) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + + If you have Invariant Sections, Front-Cover Texts and Back-Cover +Texts, replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with + the Front-Cover Texts being LIST, and with the Back-Cover Texts + being LIST. + + If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + + If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of free +software license, such as the GNU General Public License, to permit +their use in free software. + + + +Tag Table: + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/modus-themes-20220823.1919/modus-operandi-theme.el b/code/elpa/modus-themes-20220823.1919/modus-operandi-theme.el new file mode 100644 index 0000000..fd7ffff --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/modus-operandi-theme.el @@ -0,0 +1,74 @@ +;;; modus-operandi-theme.el --- Elegant, highly legible and customizable light theme -*- lexical-binding:t -*- + +;; Copyright (C) 2019-2022 Free Software Foundation, Inc. + +;; Author: Protesilaos Stavrou +;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht> +;; URL: https://git.sr.ht/~protesilaos/modus-themes +;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes +;; Version: 2.6.0 +;; Package-Requires: ((emacs "27.1")) +;; Keywords: faces, theme, accessibility + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: +;; +;; Modus Operandi is the light variant of the Modus themes (Modus +;; Vivendi is the dark one). The themes are designed for color-contrast +;; accessibility. More specifically: +;; +;; 1. Provide a consistent minimum contrast ratio between background +;; and foreground values of 7:1 or higher. This meets the highest +;; such accessibility criterion per the guidelines of the Worldwide +;; Web Consortium's Working Group on Accessibility (WCAG AAA +;; standard). +;; +;; 2. Offer as close to full face coverage as possible. The list is +;; already quite long, with more additions to follow as part of the +;; ongoing development process. +;; +;; For a complete view of the project, also refer to the following files +;; (should be distributed in the same repository/directory as the +;; current item): +;; +;; - modus-themes.el (Main code shared between the themes) +;; - modus-vivendi-theme.el (Dark theme) + +;;; Code: + + + +(eval-and-compile + (unless (and (fboundp 'require-theme) + load-file-name + (equal (file-name-directory load-file-name) + (expand-file-name "themes/" data-directory)) + (require-theme 'modus-themes t)) + (require 'modus-themes)) + + (deftheme modus-operandi + "Elegant, highly legible and customizable light theme. +Conforms with the highest legibility standard for color contrast +between background and foreground in any given piece of text, +which corresponds to a minimum contrast in relative luminance of +7:1 (WCAG AAA standard).") + + (modus-themes-theme modus-operandi) + + (provide-theme 'modus-operandi)) + +;;; modus-operandi-theme.el ends here diff --git a/code/elpa/modus-themes-20220823.1919/modus-themes-autoloads.el b/code/elpa/modus-themes-20220823.1919/modus-themes-autoloads.el new file mode 100644 index 0000000..a9197a2 --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/modus-themes-autoloads.el @@ -0,0 +1,86 @@ +;;; modus-themes-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "modus-themes" "modus-themes.el" (0 0 0 0)) +;;; Generated autoloads from modus-themes.el + +(autoload 'modus-themes-version "modus-themes" "\ +Print `modus-themes--version' in the echo area. +If optional INSERT argument is provided from Lisp or as a prefix +argument, insert the `modus-themes--version' at point. + +\(fn &optional INSERT)" t nil) + +(autoload 'modus-themes-report-bug "modus-themes" "\ +Submit a bug report or issue to the Modus themes developers." t nil) + +(autoload 'modus-themes-contrast "modus-themes" "\ +Measure WCAG contrast ratio between C1 and C2. +C1 and C2 are color values written in hexadecimal RGB. + +\(fn C1 C2)" nil nil) + +(autoload 'modus-themes-color "modus-themes" "\ +Return color value for COLOR from current palette. +COLOR is a key in `modus-themes-operandi-colors' or +`modus-themes-vivendi-colors'. + +\(fn COLOR)" nil nil) + +(autoload 'modus-themes-color-alts "modus-themes" "\ +Return color value from current palette. +When Modus Operandi is enabled, return color value for color +LIGHT-COLOR. When Modus Vivendi is enabled, return color value +for DARK-COLOR. LIGHT-COLOR and DARK-COLOR are keys in +`modus-themes-operandi-colors' or `modus-themes-vivendi-colors'. + +\(fn LIGHT-COLOR DARK-COLOR)" nil nil) + +(autoload 'modus-themes-load-themes "modus-themes" "\ +Ensure that the Modus themes are in `custom-enabled-themes'. + +This function is intended for use in package declarations such as +those defined with the help of `use-package'. The idea is to add +this function to the `:init' stage of the package's loading, so +that subsequent calls that assume the presence of a loaded theme, +like `modus-themes-toggle' or `modus-themes-load-operandi', will +continue to work as intended even if they are lazy-loaded (such +as when they are declared in the `:config' phase)." nil nil) + +(autoload 'modus-themes-load-operandi "modus-themes" "\ +Load `modus-operandi' and disable `modus-vivendi'. +Also run `modus-themes-after-load-theme-hook'." t nil) + +(autoload 'modus-themes-load-vivendi "modus-themes" "\ +Load `modus-vivendi' and disable `modus-operandi'. +Also run `modus-themes-after-load-theme-hook'." t nil) + +(autoload 'modus-themes-toggle "modus-themes" "\ +Toggle between `modus-operandi' and `modus-vivendi' themes. +Also runs `modus-themes-after-load-theme-hook' at its last stage +by virtue of calling either of `modus-themes-load-operandi' and +`modus-themes-load-vivendi' functions." t nil) + +(when load-file-name (let ((dir (file-name-directory load-file-name))) (unless (equal dir (expand-file-name "themes/" data-directory)) (add-to-list 'custom-theme-load-path dir)))) + +(register-definition-prefixes "modus-themes" '("modus-themes-")) + +;;;*** + +;;;### (autoloads nil nil ("modus-operandi-theme.el" "modus-themes-pkg.el" +;;;;;; "modus-vivendi-theme.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; modus-themes-autoloads.el ends here diff --git a/code/elpa/modus-themes-20220823.1919/modus-themes-pkg.el b/code/elpa/modus-themes-20220823.1919/modus-themes-pkg.el new file mode 100644 index 0000000..f135977 --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/modus-themes-pkg.el @@ -0,0 +1,12 @@ +(define-package "modus-themes" "20220823.1919" "Elegant, highly legible and customizable themes" + '((emacs "27.1")) + :commit "777089c0ffaabadc10cefead3737fabe24b9004c" :authors + '(("Protesilaos Stavrou" . "info@protesilaos.com")) + :maintainer + '("Modus-Themes Development" . "~protesilaos/modus-themes@lists.sr.ht") + :keywords + '("faces" "theme" "accessibility") + :url "https://git.sr.ht/~protesilaos/modus-themes") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/modus-themes-20220823.1919/modus-themes.el b/code/elpa/modus-themes-20220823.1919/modus-themes.el new file mode 100644 index 0000000..bc0271f --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/modus-themes.el @@ -0,0 +1,7560 @@ +;;; modus-themes.el --- Elegant, highly legible and customizable themes -*- lexical-binding:t -*- + +;; Copyright (C) 2019-2022 Free Software Foundation, Inc. + +;; Author: Protesilaos Stavrou +;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht> +;; URL: https://git.sr.ht/~protesilaos/modus-themes +;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes +;; Version: 2.6.0 +;; Package-Requires: ((emacs "27.1")) +;; Keywords: faces, theme, accessibility + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: +;; +;; The Modus themes conform with the highest standard for color-contrast +;; accessibility between background and foreground values (WCAG AAA). +;; This file contains all customization variables, helper functions, +;; interactive commands, and face specifications. Please refer to the +;; official Info manual for further documentation (distributed with the +;; themes, or available at: ). +;; +;; The themes share the following customization variables: +;; +;; modus-themes-completions (alist) +;; modus-themes-headings (alist) +;; modus-themes-org-agenda (alist) +;; modus-themes-bold-constructs (boolean) +;; modus-themes-deuteranopia (boolean) +;; modus-themes-inhibit-reload (boolean) +;; modus-themes-intense-mouseovers (boolean) +;; modus-themes-italic-constructs (boolean) +;; modus-themes-mixed-fonts (boolean) +;; modus-themes-subtle-line-numbers (boolean) +;; modus-themes-variable-pitch-ui (boolean) +;; modus-themes-box-buttons (choice) +;; modus-themes-diffs (choice) +;; modus-themes-fringes (choice) +;; modus-themes-hl-line (choice) +;; modus-themes-lang-checkers (choice) +;; modus-themes-links (choice) +;; modus-themes-mail-citations (choice) +;; modus-themes-markup (choice) +;; modus-themes-mode-line (choice) +;; modus-themes-org-blocks (choice) +;; modus-themes-paren-match (choice) +;; modus-themes-prompts (choice) +;; modus-themes-region (choice) +;; modus-themes-syntax (choice) +;; +;; There also exist two unique customization variables for overriding +;; color palette values. The specifics are documented in the manual. +;; The symbols are: +;; +;; modus-themes-operandi-color-overrides (alist) +;; modus-themes-vivendi-color-overrides (alist) +;; +;; Check the manual for all supported packages (there are hundreds of +;; them). +;; +;; For a complete view of the project, also refer to the following files +;; (should be distributed in the same repository/directory as the +;; current item): +;; +;; - modus-operandi-theme.el (Light theme) +;; - modus-vivendi-theme.el (Dark theme) + +;;; Code: + + + +(eval-when-compile + (require 'cl-lib) + (require 'subr-x)) + +(defgroup modus-themes () + "Options for `modus-operandi', `modus-vivendi'. +The Modus themes conform with the WCAG AAA standard for color +contrast between background and foreground combinations (a +minimum contrast of 7:1---the highest standard of its kind). The +themes also strive to empower users with red-green color +deficiency: this is achieved through customization variables that +replace all relevant instances of green with blue, as well as the +overall design of the themes which relies mostly on colors that +cover the blue-cyan-magenta side of the spectrum." + :group 'faces + :link '(info-link "(modus-themes) Top") + :prefix "modus-themes-" + :tag "Modus Themes") + +(defgroup modus-themes-faces () + "Faces defined by `modus-operandi' and `modus-vivendi'." + :group 'modus-themes + :link '(info-link "(modus-themes) Top") + :prefix "modus-themes-" + :tag "Modus Themes Faces") + +(defvar modus-themes--version "2.6.0" + "Current version of the Modus themes. + +The version either is the last tagged release, such as '1.0.0', +or an in-development version like '1.1.0-dev'. As we use +semantic versioning, tags of the '1.0.1' sort are not reported: +those would count as part of '1.1.0-dev'.") + +;;;###autoload +(defun modus-themes-version (&optional insert) + "Print `modus-themes--version' in the echo area. +If optional INSERT argument is provided from Lisp or as a prefix +argument, insert the `modus-themes--version' at point." + (interactive "P") + (if-let ((version modus-themes--version) + ((or insert current-prefix-arg))) + (insert version) + (message version))) + +;;;###autoload +(defun modus-themes-report-bug () + "Submit a bug report or issue to the Modus themes developers." + (interactive) + (reporter-submit-bug-report + "~protesilaos/modus-themes@lists.sr.ht" + (format "modus-themes (%s)\n" modus-themes--version) + ;; I am just getting started with this. Let's first see what people + ;; think about it. + nil nil nil nil)) + +;;; Variables for each theme variant + +;;;; Modus Operandi + +(defconst modus-themes-operandi-colors + '(;; base values + (bg-main . "#ffffff") (fg-main . "#000000") + (bg-dim . "#f8f8f8") (fg-dim . "#282828") + (bg-alt . "#f0f0f0") (fg-alt . "#505050") + ;; specifically for on/off states and must be combined with + ;; themselves, though the backgrounds are also meant to be used with + ;; other "active" values, defined further below; bg-active-accent + ;; can work as a substitute for bg-active + (bg-active . "#d7d7d7") (fg-active . "#0a0a0a") + (bg-inactive . "#efefef") (fg-inactive . "#404148") + (bg-active-accent . "#d0d6ff") + ;; these special values are intended as alternatives to the base + ;; values for cases where we need to avoid confusion between the + ;; highlighted constructs; they must either be used as pairs based + ;; on their name or each can be combined with {fg,bg}-{main,alt,dim} + ;; always in accordance with their role as background or foreground + (bg-special-cold . "#dde3f4") (bg-special-faint-cold . "#f0f1ff") (fg-special-cold . "#093060") + (bg-special-mild . "#c4ede0") (bg-special-faint-mild . "#ebf5eb") (fg-special-mild . "#184034") + (bg-special-warm . "#f0e0d4") (bg-special-faint-warm . "#fef2ea") (fg-special-warm . "#5d3026") + (bg-special-calm . "#f8ddea") (bg-special-faint-calm . "#faeff9") (fg-special-calm . "#61284f") + ;; foregrounds that can be combined with bg-main, bg-dim, bg-alt + (red . "#a60000") + (red-alt . "#972500") + (red-alt-other . "#a0132f") + (red-faint . "#7f1010") + (red-alt-faint . "#702f00") + (red-alt-other-faint . "#7f002f") + (green . "#005e00") + (green-alt . "#315b00") + (green-alt-other . "#145c33") + (green-faint . "#104410") + (green-alt-faint . "#30440f") + (green-alt-other-faint . "#0f443f") + (yellow . "#813e00") + (yellow-alt . "#70480f") + (yellow-alt-other . "#863927") + (yellow-faint . "#5f4400") + (yellow-alt-faint . "#5d5000") + (yellow-alt-other-faint . "#5e3a20") + (blue . "#0031a9") + (blue-alt . "#2544bb") + (blue-alt-other . "#0000c0") + (blue-faint . "#003497") + (blue-alt-faint . "#0f3d8c") + (blue-alt-other-faint . "#001087") + (magenta . "#721045") + (magenta-alt . "#8f0075") + (magenta-alt-other . "#5317ac") + (magenta-faint . "#752f50") + (magenta-alt-faint . "#7b206f") + (magenta-alt-other-faint . "#55348e") + (cyan . "#00538b") + (cyan-alt . "#30517f") + (cyan-alt-other . "#005a5f") + (cyan-faint . "#005077") + (cyan-alt-faint . "#354f6f") + (cyan-alt-other-faint . "#125458") + ;; these foreground values can only be combined with bg-main and are + ;; thus not suitable for general purpose highlighting + (red-intense . "#b60000") + (orange-intense . "#904200") + (green-intense . "#006800") + (yellow-intense . "#605b00") + (blue-intense . "#1f1fce") + (magenta-intense . "#a8007f") + (purple-intense . "#7f10d0") + (cyan-intense . "#005f88") + ;; those foregrounds are meant exclusively for bg-active, bg-inactive + (red-active . "#8a0000") + (green-active . "#004c2e") + (yellow-active . "#702f00") + (blue-active . "#0030b4") + (magenta-active . "#5c2092") + (cyan-active . "#003f8a") + ;; the "subtle" values below be combined with fg-dim, while the + ;; "intense" should be paired with fg-main + (red-subtle-bg . "#f2b0a2") + (red-intense-bg . "#ff9f9f") + (green-subtle-bg . "#aecf90") + (green-intense-bg . "#5ada88") + (yellow-subtle-bg . "#e4c340") + (yellow-intense-bg . "#f5df23") + (blue-subtle-bg . "#b5d0ff") + (blue-intense-bg . "#77baff") + (magenta-subtle-bg . "#f0d3ff") + (magenta-intense-bg . "#d5baff") + (cyan-subtle-bg . "#c0efff") + (cyan-intense-bg . "#42cbd4") + ;; those background values must be combined with fg-main and should + ;; only be used for indicators that are placed on the fringes + (red-fringe-bg . "#f08290") + (green-fringe-bg . "#62c86a") + (yellow-fringe-bg . "#dbba3f") + (blue-fringe-bg . "#82afff") + (magenta-fringe-bg . "#e0a3ff") + (cyan-fringe-bg . "#2fcddf") + ;; those background values should only be used for graphs or similar + ;; applications where colored blocks are expected to be positioned + ;; next to each other + (red-graph-0-bg . "#ef7969") + (red-graph-1-bg . "#ffaab4") + (green-graph-0-bg . "#4faa09") + (green-graph-1-bg . "#8fef00") + (yellow-graph-0-bg . "#ffcf00") + (yellow-graph-1-bg . "#f9ff00") + (blue-graph-0-bg . "#7090ff") + (blue-graph-1-bg . "#9fc6ff") + (magenta-graph-0-bg . "#e07fff") + (magenta-graph-1-bg . "#fad0ff") + (cyan-graph-0-bg . "#70d3f0") + (cyan-graph-1-bg . "#afefff") + ;; the following are for cases where both the foreground and the + ;; background need to have a similar hue and so must be combined + ;; with themselves, even though the foregrounds can be paired with + ;; any of the base backgrounds + (red-refine-bg . "#ffcccc") (red-refine-fg . "#780000") + (green-refine-bg . "#aceaac") (green-refine-fg . "#004c00") + (yellow-refine-bg . "#fff29a") (yellow-refine-fg . "#604000") + (blue-refine-bg . "#8fcfff") (blue-refine-fg . "#002f88") + (magenta-refine-bg . "#ffccff") (magenta-refine-fg . "#770077") + (cyan-refine-bg . "#8eecf4") (cyan-refine-fg . "#004850") + ;; the "nuanced" backgrounds can be combined with all of the above + ;; foregrounds, as well as those included here, while the "nuanced" + ;; foregrounds can in turn also be combined with bg-main, bg-dim, + ;; bg-alt + (red-nuanced-bg . "#fff1f0") (red-nuanced-fg . "#5f0000") + (green-nuanced-bg . "#ecf7ed") (green-nuanced-fg . "#004000") + (yellow-nuanced-bg . "#fff3da") (yellow-nuanced-fg . "#3f3000") + (blue-nuanced-bg . "#f3f3ff") (blue-nuanced-fg . "#201f55") + (magenta-nuanced-bg . "#fdf0ff") (magenta-nuanced-fg . "#541f4f") + (cyan-nuanced-bg . "#ebf6fa") (cyan-nuanced-fg . "#0f3360") + ;; the following are reserved for specific cases + ;; + ;; bg-hl-line is between bg-dim and bg-alt, so it should + ;; work with all accents that cover those two, plus bg-main + ;; + ;; bg-hl-alt and bg-hl-alt-intense should only be used when no + ;; other grayscale or fairly neutral background is available to + ;; properly draw attention to a given construct + ;; + ;; bg-header is between bg-active and bg-inactive, so it + ;; can be combined with any of the "active" values, plus the + ;; "special" and base foreground colors + ;; + ;; bg-paren-match, bg-paren-match-intense, bg-region, + ;; bg-region-accent and bg-tab-active must be combined with fg-main, + ;; while bg-tab-inactive should be combined with fg-dim, whereas + ;; bg-tab-inactive-alt goes together with fg-main + ;; + ;; bg-completion-* and bg-char-* variants are meant to be combined + ;; with fg-main + ;; + ;; fg-escape-char-construct and fg-escape-char-backslash can + ;; be combined bg-main, bg-dim, bg-alt + ;; + ;; fg-lang-error, fg-lang-warning, fg-lang-note can be + ;; combined with bg-main, bg-dim, bg-alt + ;; + ;; fg-mark-sel, fg-mark-del, fg-mark-alt can be combined + ;; with bg-main, bg-dim, bg-alt, bg-hl-line + ;; + ;; fg-unfocused must be combined with bg-main + ;; + ;; fg-docstring, fg-comment-yellow can be combined with + ;; bg-main, bg-dim, bg-alt + ;; + ;; the window divider colors apply to faces with just an fg value + ;; + ;; all pairs are combinable with themselves + (bg-hl-line . "#f2eff3") + (bg-hl-line-intense . "#e0e0e0") + (bg-hl-line-intense-accent . "#cfe2ff") + (bg-hl-alt . "#fbeee0") + (bg-hl-alt-intense . "#e8dfd1") + (bg-paren-match . "#e0af82") + (bg-paren-match-intense . "#c488ff") + (bg-paren-expression . "#dff0ff") + (bg-region . "#bcbcbc") + (bg-region-accent . "#afafef") + (bg-region-accent-subtle . "#efdfff") + + (bg-completion . "#b7dbff") + (bg-completion-subtle . "#def3ff") + + (bg-char-0 . "#7feaff") + (bg-char-1 . "#ffaaff") + (bg-char-2 . "#dff000") + + (bg-tab-active . "#f6f6f6") + (bg-tab-inactive . "#b7b7b7") + (bg-tab-inactive-accent . "#a9b4f6") + (bg-tab-inactive-alt . "#9f9f9f") + (bg-tab-inactive-alt-accent . "#9fa6d0") + + (red-tab . "#680000") + (green-tab . "#003900") + (yellow-tab . "#393000") + (orange-tab . "#502300") + (blue-tab . "#000080") + (cyan-tab . "#052f60") + (magenta-tab . "#5f004d") + (purple-tab . "#400487") + + (fg-escape-char-construct . "#8b1030") + (fg-escape-char-backslash . "#654d0f") + + (fg-lang-error . "#9f004f") + (fg-lang-warning . "#604f0f") + (fg-lang-note . "#4040ae") + (fg-lang-underline-error . "#ef4f54") + (fg-lang-underline-warning . "#cf9f00") + (fg-lang-underline-note . "#3f6fef") + + (fg-window-divider-inner . "#888888") + (fg-window-divider-outer . "#585858") + + (fg-unfocused . "#56576d") + + (fg-docstring . "#2a486a") + (fg-comment-yellow . "#794319") + + (bg-header . "#e5e5e5") (fg-header . "#2a2a2a") + + (bg-whitespace . "#f5efef") (fg-whitespace . "#624956") + + (bg-diff-heading . "#b7cfe0") (fg-diff-heading . "#041645") + (bg-diff-added . "#d4fad4") (fg-diff-added . "#004500") + (bg-diff-added-deuteran . "#daefff") (fg-diff-added-deuteran . "#002044") + (bg-diff-changed . "#fcefcf") (fg-diff-changed . "#524200") + (bg-diff-removed . "#ffe8ef") (fg-diff-removed . "#691616") + + (bg-diff-refine-added . "#94cf94") (fg-diff-refine-added . "#002a00") + (bg-diff-refine-added-deuteran . "#77c0ef") (fg-diff-refine-added-deuteran . "#000035") + (bg-diff-refine-changed . "#cccf8f") (fg-diff-refine-changed . "#302010") + (bg-diff-refine-removed . "#daa2b0") (fg-diff-refine-removed . "#400000") + + (bg-diff-focus-added . "#bbeabb") (fg-diff-focus-added . "#002c00") + (bg-diff-focus-added-deuteran . "#bacfff") (fg-diff-focus-added-deuteran . "#001755") + (bg-diff-focus-changed . "#ecdfbf") (fg-diff-focus-changed . "#392900") + (bg-diff-focus-removed . "#efcbcf") (fg-diff-focus-removed . "#4a0000") + + (bg-mark-sel . "#a0f0cf") (fg-mark-sel . "#005040") + (bg-mark-del . "#ffccbb") (fg-mark-del . "#840040") + (bg-mark-alt . "#f5d88f") (fg-mark-alt . "#782900")) + "The entire palette of the `modus-operandi' theme. +Each element has the form (NAME . HEX) with the former as a +symbol and the latter as a string.") + +;;;; Modus Vivendi + +(defconst modus-themes-vivendi-colors + '(;; base values + (bg-main . "#000000") (fg-main . "#ffffff") + (bg-dim . "#100f10") (fg-dim . "#e0e6f0") + (bg-alt . "#191a1b") (fg-alt . "#a8a8a8") + ;; specifically for on/off states and must be combined with + ;; themselves, though the backgrounds are also meant to be used with + ;; other "active" values, defined further below; bg-active-accent + ;; can work as a substitute for bg-active + (bg-active . "#323232") (fg-active . "#f4f4f4") + (bg-inactive . "#1e1e1e") (fg-inactive . "#bfc0c4") + (bg-active-accent . "#2a2a66") + ;; these special values are intended as alternatives to the base + ;; values for cases where we need to avoid confusion between the + ;; highlighted constructs; they must either be used as pairs based + ;; on their name or each can be combined with {fg,bg}-{main,alt,dim} + ;; always in accordance with their role as background or foreground + (bg-special-cold . "#203448") (bg-special-faint-cold . "#0e183a") (fg-special-cold . "#c6eaff") + (bg-special-mild . "#00322e") (bg-special-faint-mild . "#001f1a") (fg-special-mild . "#bfebe0") + (bg-special-warm . "#382f27") (bg-special-faint-warm . "#241613") (fg-special-warm . "#f8dec0") + (bg-special-calm . "#392a48") (bg-special-faint-calm . "#251232") (fg-special-calm . "#fbd6f4") + ;; foregrounds that can be combined with bg-main, bg-dim, bg-alt + (red . "#ff8059") + (red-alt . "#ef8b50") + (red-alt-other . "#ff9077") + (red-faint . "#ffa0a0") + (red-alt-faint . "#f5aa80") + (red-alt-other-faint . "#ff9fbf") + (green . "#44bc44") + (green-alt . "#70b900") + (green-alt-other . "#00c06f") + (green-faint . "#78bf78") + (green-alt-faint . "#99b56f") + (green-alt-other-faint . "#88bf99") + (yellow . "#d0bc00") + (yellow-alt . "#c0c530") + (yellow-alt-other . "#d3b55f") + (yellow-faint . "#d2b580") + (yellow-alt-faint . "#cabf77") + (yellow-alt-other-faint . "#d0ba95") + (blue . "#2fafff") + (blue-alt . "#79a8ff" ) + (blue-alt-other . "#00bcff") + (blue-faint . "#82b0ec") + (blue-alt-faint . "#a0acef") + (blue-alt-other-faint . "#80b2f0") + (magenta . "#feacd0") + (magenta-alt . "#f78fe7") + (magenta-alt-other . "#b6a0ff") + (magenta-faint . "#e0b2d6") + (magenta-alt-faint . "#ef9fe4") + (magenta-alt-other-faint . "#cfa6ff") + (cyan . "#00d3d0") + (cyan-alt . "#4ae2f0") + (cyan-alt-other . "#6ae4b9") + (cyan-faint . "#90c4ed") + (cyan-alt-faint . "#a0bfdf") + (cyan-alt-other-faint . "#a4d0bb") + ;; these foreground values can only be combined with bg-main and are + ;; thus not suitable for general purpose highlighting + (red-intense . "#fe6060") + (orange-intense . "#fba849") + (green-intense . "#4fe42f") + (yellow-intense . "#f0dd60") + (blue-intense . "#4fafff") + (magenta-intense . "#ff62d4") + (purple-intense . "#9f80ff") + (cyan-intense . "#3fdfd0") + ;; those foregrounds are meant exclusively for bg-active, bg-inactive + (red-active . "#ffa7ba") + (green-active . "#70d73f") + (yellow-active . "#dbbe5f") + (blue-active . "#34cfff") + (magenta-active . "#d5b1ff") + (cyan-active . "#00d8b4") + ;; the "subtle" values below be combined with fg-dim, while the + ;; "intense" should be paired with fg-main + (red-subtle-bg . "#762422") + (red-intense-bg . "#a4202a") + (green-subtle-bg . "#2f4a00") + (green-intense-bg . "#006800") + (yellow-subtle-bg . "#604200") + (yellow-intense-bg . "#874900") + (blue-subtle-bg . "#10387c") + (blue-intense-bg . "#2a40b8") + (magenta-subtle-bg . "#49366e") + (magenta-intense-bg . "#7042a2") + (cyan-subtle-bg . "#00415e") + (cyan-intense-bg . "#005f88") + ;; those background values must be combined with fg-main and should + ;; only be used for indicators that are placed on the fringes + (red-fringe-bg . "#8f1f4b") + (green-fringe-bg . "#006700") + (yellow-fringe-bg . "#6f4f00") + (blue-fringe-bg . "#3f33af") + (magenta-fringe-bg . "#6f2f89") + (cyan-fringe-bg . "#004f8f") + ;; those background values should only be used for graphs or similar + ;; applications where colored blocks are expected to be positioned + ;; next to each other + (red-graph-0-bg . "#b52c2c") + (red-graph-1-bg . "#702020") + (green-graph-0-bg . "#4fd100") + (green-graph-1-bg . "#007800") + (yellow-graph-0-bg . "#f1e00a") + (yellow-graph-1-bg . "#b08600") + (blue-graph-0-bg . "#2fafef") + (blue-graph-1-bg . "#1f2f8f") + (magenta-graph-0-bg . "#bf94fe") + (magenta-graph-1-bg . "#5f509f") + (cyan-graph-0-bg . "#47dfea") + (cyan-graph-1-bg . "#00808f") + ;; the following are for cases where both the foreground and the + ;; background need to have a similar hue and so must be combined + ;; with themselves, even though the foregrounds can be paired with + ;; any of the base backgrounds + (red-refine-bg . "#77002a") (red-refine-fg . "#ffb9ab") + (green-refine-bg . "#00422a") (green-refine-fg . "#9ff0cf") + (yellow-refine-bg . "#693200") (yellow-refine-fg . "#e2d980") + (blue-refine-bg . "#242679") (blue-refine-fg . "#8ecfff") + (magenta-refine-bg . "#71206a") (magenta-refine-fg . "#ffcaf0") + (cyan-refine-bg . "#004065") (cyan-refine-fg . "#8ae4f2") + ;; the "nuanced" backgrounds can be combined with all of the above + ;; foregrounds, as well as those included here, while the "nuanced" + ;; foregrounds can in turn also be combined with bg-main, bg-dim, + ;; bg-alt + (red-nuanced-bg . "#2c0614") (red-nuanced-fg . "#ffcccc") + (green-nuanced-bg . "#001904") (green-nuanced-fg . "#b8e2b8") + (yellow-nuanced-bg . "#221000") (yellow-nuanced-fg . "#dfdfb0") + (blue-nuanced-bg . "#0f0e39") (blue-nuanced-fg . "#bfd9ff") + (magenta-nuanced-bg . "#230631") (magenta-nuanced-fg . "#e5cfef") + (cyan-nuanced-bg . "#041529") (cyan-nuanced-fg . "#a8e5e5") + ;; the following are reserved for specific cases + ;; + ;; bg-hl-line is between bg-dim and bg-alt, so it should + ;; work with all accents that cover those two, plus bg-main + ;; + ;; bg-hl-alt and bg-hl-alt-intense should only be used when no + ;; other grayscale or fairly neutral background is available to + ;; properly draw attention to a given construct + ;; + ;; bg-header is between bg-active and bg-inactive, so it + ;; can be combined with any of the "active" values, plus the + ;; "special" and base foreground colors + ;; + ;; bg-paren-match, bg-paren-match-intense, bg-region, + ;; bg-region-accent and bg-tab-active must be combined with fg-main, + ;; while bg-tab-inactive should be combined with fg-dim, whereas + ;; bg-tab-inactive-alt goes together with fg-main + ;; + ;; bg-completion-* and bg-char-* variants are meant to be combined + ;; with fg-main + ;; + ;; fg-escape-char-construct and fg-escape-char-backslash can + ;; be combined bg-main, bg-dim, bg-alt + ;; + ;; fg-lang-error, fg-lang-warning, fg-lang-note can be + ;; combined with bg-main, bg-dim, bg-alt + ;; + ;; fg-mark-sel, fg-mark-del, fg-mark-alt can be combined + ;; with bg-main, bg-dim, bg-alt, bg-hl-line + ;; + ;; fg-unfocused must be combined with bg-main + ;; + ;; fg-docstring, fg-comment-yellow can be combined with + ;; bg-main, bg-dim, bg-alt + ;; + ;; the window divider colors apply to faces with just an fg value + ;; + ;; all pairs are combinable with themselves + (bg-hl-line . "#151823") + (bg-hl-line-intense . "#292929") + (bg-hl-line-intense-accent . "#002a4f") + (bg-hl-alt . "#181732") + (bg-hl-alt-intense . "#282e46") + (bg-paren-match . "#6f3355") + (bg-paren-match-intense . "#7416b5") + (bg-paren-expression . "#221044") + (bg-region . "#3c3c3c") + (bg-region-accent . "#4f3d88") + (bg-region-accent-subtle . "#240f55") + + (bg-completion . "#142f69") + (bg-completion-subtle . "#0e194b") + + (bg-char-0 . "#0050af") + (bg-char-1 . "#7f1f7f") + (bg-char-2 . "#625a00") + + (bg-tab-active . "#0e0e0e") + (bg-tab-inactive . "#424242") + (bg-tab-inactive-accent . "#35398f") + (bg-tab-inactive-alt . "#595959") + (bg-tab-inactive-alt-accent . "#505588") + + (red-tab . "#ffc0bf") + (green-tab . "#88ef88") + (yellow-tab . "#d2e580") + (orange-tab . "#f5ca80") + (blue-tab . "#92d9ff") + (cyan-tab . "#60e7e0") + (magenta-tab . "#ffb8ff") + (purple-tab . "#cfcaff") + + (fg-escape-char-construct . "#e7a59a") + (fg-escape-char-backslash . "#abab00") + + (fg-lang-error . "#ef8690") + (fg-lang-warning . "#b0aa00") + (fg-lang-note . "#9d9def") + (fg-lang-underline-error . "#ff4a6f") + (fg-lang-underline-warning . "#d0de00") + (fg-lang-underline-note . "#5f6fff") + + (fg-window-divider-inner . "#646464") + (fg-window-divider-outer . "#969696") + + (fg-unfocused . "#93959b") + + (fg-docstring . "#b0d6f5") + (fg-comment-yellow . "#d0a070") + + (bg-header . "#212121") (fg-header . "#dddddd") + + (bg-whitespace . "#101424") (fg-whitespace . "#aa9e9f") + + (bg-diff-heading . "#304466") (fg-diff-heading . "#dae7ff") + (bg-diff-added . "#0a280a") (fg-diff-added . "#94ba94") + (bg-diff-added-deuteran . "#001a3f") (fg-diff-added-deuteran . "#c4cdf2") + (bg-diff-changed . "#2a2000") (fg-diff-changed . "#b0ba9f") + (bg-diff-removed . "#40160f") (fg-diff-removed . "#c6adaa") + + (bg-diff-refine-added . "#005a36") (fg-diff-refine-added . "#e0f6e0") + (bg-diff-refine-added-deuteran . "#234f8f") (fg-diff-refine-added-deuteran . "#dde4ff") + (bg-diff-refine-changed . "#585800") (fg-diff-refine-changed . "#ffffcc") + (bg-diff-refine-removed . "#852828") (fg-diff-refine-removed . "#ffd9eb") + + (bg-diff-focus-added . "#1d3c25") (fg-diff-focus-added . "#b4ddb4") + (bg-diff-focus-added-deuteran . "#003959") (fg-diff-focus-added-deuteran . "#bfe4ff") + (bg-diff-focus-changed . "#424200") (fg-diff-focus-changed . "#d0daaf") + (bg-diff-focus-removed . "#500f29") (fg-diff-focus-removed . "#eebdba") + + (bg-mark-sel . "#002f2f") (fg-mark-sel . "#60cfa2") + (bg-mark-del . "#5a0000") (fg-mark-del . "#ff99aa") + (bg-mark-alt . "#3f2210") (fg-mark-alt . "#f0aa20")) + "The entire palette of the `modus-vivendi' theme. +Each element has the form (NAME . HEX) with the former as a +symbol and the latter as a string.") + + + +;;; Custom faces + +;; These faces are used internally to ensure consistency between various +;; groups and to streamline the evaluation of relevant customization +;; options. +(defface modus-themes-subtle-red nil + "Subtle red background combined with a dimmed foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-subtle-green nil + "Subtle green background combined with a dimmed foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-subtle-yellow nil + "Subtle yellow background combined with a dimmed foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-subtle-blue nil + "Subtle blue background combined with a dimmed foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-subtle-magenta nil + "Subtle magenta background combined with a dimmed foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-subtle-cyan nil + "Subtle cyan background combined with a dimmed foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-subtle-neutral nil + "Subtle gray background combined with a dimmed foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-intense-red nil + "Intense red background combined with the main foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-intense-green nil + "Intense green background combined with the main foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-intense-yellow nil + "Intense yellow background combined with the main foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-intense-blue nil + "Intense blue background combined with the main foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-intense-magenta nil + "Intense magenta background combined with the main foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-intense-cyan nil + "Intense cyan background combined with the main foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-intense-neutral nil + "Intense gray background combined with the main foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-refine-red nil + "Combination of accented red background and foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-refine-green nil + "Combination of accented green background and foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-refine-yellow nil + "Combination of accented yellow background and foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-refine-blue nil + "Combination of accented blue background and foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-refine-magenta nil + "Combination of accented magenta background and foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-refine-cyan nil + "Combination of accented cyan background and foreground. +This is used for general purpose highlighting, mostly in buffers +or for completion interfaces. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-active-red nil + "A red background meant for use on the mode line or similar. +This is combined with the mode lines primary foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-active-green nil + "A green background meant for use on the mode line or similar. +This is combined with the mode lines primary foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-active-yellow nil + "A yellow background meant for use on the mode line or similar. +This is combined with the mode lines primary foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-active-blue nil + "A blue background meant for use on the mode line or similar. +This is combined with the mode lines primary foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-active-magenta nil + "A magenta background meant for use on the mode line or similar. +This is combined with the mode lines primary foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-active-cyan nil + "A cyan background meant for use on the mode line or similar. +This is combined with the mode lines primary foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-fringe-red nil + "A red background meant for use on the fringe or similar. +This is combined with the main foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-fringe-green nil + "A green background meant for use on the fringe or similar. +This is combined with the main foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-fringe-yellow nil + "A yellow background meant for use on the fringe or similar. +This is combined with the main foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-fringe-blue nil + "A blue background meant for use on the fringe or similar. +This is combined with the main foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-fringe-magenta nil + "A magenta background meant for use on the fringe or similar. +This is combined with the main foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-fringe-cyan nil + "A cyan background meant for use on the fringe or similar. +This is combined with the main foreground value. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-nuanced-red nil + "A nuanced red background. +This does not specify a foreground of its own. Instead it is +meant to serve as the backdrop for elements such as Org blocks, +headings, and any other surface that needs to retain the colors +on display. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-nuanced-green nil + "A nuanced green background. +This does not specify a foreground of its own. Instead it is +meant to serve as the backdrop for elements such as Org blocks, +headings, and any other surface that needs to retain the colors +on display. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-nuanced-yellow nil + "A nuanced yellow background. +This does not specify a foreground of its own. Instead it is +meant to serve as the backdrop for elements such as Org blocks, +headings, and any other surface that needs to retain the colors +on display. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-nuanced-blue nil + "A nuanced blue background. +This does not specify a foreground of its own. Instead it is +meant to serve as the backdrop for elements such as Org blocks, +headings, and any other surface that needs to retain the colors +on display. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-nuanced-magenta nil + "A nuanced magenta background. +This does not specify a foreground of its own. Instead it is +meant to serve as the backdrop for elements such as Org blocks, +headings, and any other surface that needs to retain the colors +on display. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-nuanced-cyan nil + "A nuanced cyan background. +This does not specify a foreground of its own. Instead it is +meant to serve as the backdrop for elements such as Org blocks, +headings, and any other surface that needs to retain the colors +on display. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-special-cold nil + "Combines the special cold background and foreground values. +This is intended for cases when a neutral gray background is not +suitable and where a combination of more saturated colors would +not be appropriate. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-special-mild nil + "Combines the special mild background and foreground values. +This is intended for cases when a neutral gray background is not +suitable and where a combination of more saturated colors would +not be appropriate. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-special-warm nil + "Combines the special warm background and foreground values. +This is intended for cases when a neutral gray background is not +suitable and where a combination of more saturated colors would +not be appropriate. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-special-calm nil + "Combines the special calm background and foreground values. +This is intended for cases when a neutral gray background is not +suitable and where a combination of more saturated colors would +not be appropriate. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-added nil + "Combines green colors for the added state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-changed nil + "Combines yellow colors for the changed state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-removed nil + "Combines red colors for the removed state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-refine-added nil + "Combines green colors for word-wise added state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-refine-changed nil + "Combines yellow colors for word-wise changed state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-refine-removed nil + "Combines red colors for word-wise removed state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-focus-added nil + "Combines green colors for the focused added state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-focus-changed nil + "Combines yellow colors for the focused changed state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-focus-removed nil + "Combines red colors for the focused removed state in diffs. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-diff-heading nil + "Combines blue colors for the diff hunk heading. +The applied colors are contingent on the value assigned to +`modus-themes-diffs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-pseudo-header nil + "Generic style for some elements that function like headings. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-mark-alt nil + "Combines yellow colors for marking special lines. +This is intended for use in modes such as Dired, Ibuffer, Proced. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-mark-del nil + "Combines red colors for marking deletable lines. +This is intended for use in modes such as Dired, Ibuffer, Proced. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-mark-sel nil + "Combines green colors for marking lines. +This is intended for use in modes such as Dired, Ibuffer, Proced. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-mark-symbol nil + "Applies a blue color and other styles for mark indicators. +This is intended for use in modes such as Dired, Ibuffer, Proced. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-0 nil + "General purpose face for use as the document's title. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-1 nil + "General purpose face for use in headings level 1. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-2 nil + "General purpose face for use in headings level 2. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-3 nil + "General purpose face for use in headings level 3. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-4 nil + "General purpose face for use in headings level 4. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-5 nil + "General purpose face for use in headings level 5. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-6 nil + "General purpose face for use in headings level 6. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-7 nil + "General purpose face for use in headings level 7. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-heading-8 nil + "General purpose face for use in headings level 8. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-headings' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-hl-line nil + "General purpose face for the current line. +The exact attributes assigned to this face are contingent on the +values assigned to the `modus-themes-hl-line' variable. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-bold nil + "Generic face for applying a conditional bold weight. +This behaves in accordance with `modus-themes-bold-constructs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-slant nil + "Generic face for applying a conditional slant (italics). +This behaves in accordance with `modus-themes-italic-constructs'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-variable-pitch nil + "Generic face for applying a conditional `variable-pitch'. +This behaves in accordance with `modus-themes-mixed-fonts' and/or +`modus-themes-variable-pitch-ui'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-fixed-pitch nil + "Generic face for applying a conditional `fixed-pitch'. +This behaves in accordance with `modus-themes-mixed-fonts'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-ui-variable-pitch nil + "Face for `modus-themes-variable-pitch-ui'. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-lang-note nil + "Generic face for linter or spell checker notes. +The exact attributes and color combinations are controlled by +`modus-themes-lang-checkers'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-lang-warning nil + "Generic face for linter or spell checker warnings. +The exact attributes and color combinations are controlled by +`modus-themes-lang-checkers'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-lang-error nil + "Generic face for linter or spell checker errors. +The exact attributes and color combinations are controlled by +`modus-themes-lang-checkers'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-reset-soft nil + "Generic face to set most face properties to nil. + +This is intended to be inherited by faces that should not retain +properties from their context (e.g. an overlay over an underlined +text should not be underlined as well) yet still blend in. Also +see `modus-themes-reset-hard'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-reset-hard nil + "Generic face to set all face properties to nil. + +This is intended to be inherited by faces that should not retain +properties from their context (e.g. an overlay over an underlined +text should not be underlined as well) and not blend in. Also +see `modus-themes-reset-soft'. + +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-key-binding nil + "Generic face for key bindings. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-search-success nil + "Generic face for successful search. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-search-success-modeline nil + "Generic mode line indicator for successful search. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-search-success-lazy nil + "Generic face for successful, lazily highlighted search. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-prompt nil + "Generic face for command prompts. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +;; "Grue" is "green" and "blue". +(defface modus-themes-grue nil + "Generic face for `modus-themes-deuteranopia' foreground. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-grue-active nil + "Face for `modus-themes-deuteranopia' active foreground. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-grue-nuanced nil + "Face for `modus-themes-deuteranopia' nuanced foreground. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-grue-background-active nil + "Face for `modus-themes-deuteranopia' active background. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-grue-background-intense nil + "Face for `modus-themes-deuteranopia' intense background. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-grue-background-subtle nil + "Face for `modus-themes-deuteranopia' subtle background. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-grue-background-refine nil + "Face for `modus-themes-deuteranopia' refined background. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-link-symlink nil + "Face for `modus-themes-links' symbolic link. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-link-broken nil + "Face for `modus-themes-links' broken link. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-tab-backdrop nil + "Face of backdrop in tabbed interfaces. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-tab-active nil + "Face of active tab. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-tab-inactive nil + "Face of inactive tab. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-markup-code nil + "Face of inline code markup. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-markup-macro nil + "Face of macro markup. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-markup-verbatim nil + "Face of verbatim markup. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(define-obsolete-face-alias + 'modus-themes-completion-standard-first-match + 'modus-themes-completion-selected + "2.2.0") + +(define-obsolete-face-alias + 'modus-themes-completion-standard-selected + 'modus-themes-completion-selected + "2.2.0") + +(define-obsolete-face-alias + 'modus-themes-completion-extra-selected + 'modus-themes-completion-selected + "2.2.0") + +(define-obsolete-face-alias + 'modus-themes-completion-key-binding + 'modus-themes-key-binding + "2.2.0") + +(defface modus-themes-completion-selected nil + "Face for current selection in completion UIs. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-completion-selected-popup nil + "Face for current selection in completion UI popups. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-completion-match-0 nil + "Face for completions matches 0. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-completion-match-1 nil + "Face for completions matches 1. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-completion-match-2 nil + "Face for completions matches 2. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-completion-match-3 nil + "Face for completions matches 3. +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-box-button nil + "Face for widget buttons (e.g. in the Custom UI). +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + +(defface modus-themes-box-button-pressed nil + "Face for pressed widget buttons (e.g. in the Custom UI). +The actual styling of the face is done by `modus-themes-faces'." + :group 'modus-themes-faces) + + + +;;; Customization variables + +(defcustom modus-themes-inhibit-reload t + "Control theme reload when setting options with Customize. + +By default, customizing a theme-related user option through the +Custom interfaces or with `customize-set-variable' will not +reload the currently active Modus theme. + +Enable this behaviour by setting this variable to nil." + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type 'boolean + :link '(info-link "(modus-themes) Custom reload theme")) + +(defun modus-themes--set-option (sym val) + "Custom setter for theme related user options. +Will set SYM to VAL, and reload the current theme, unless +`modus-themes-inhibit-reload' is non-nil." + (set-default sym val) + (unless (or modus-themes-inhibit-reload + ;; Check if a theme is being loaded, in which case we + ;; don't want to reload a theme if the setter is + ;; invoked. `custom--inhibit-theme-enable' is set to nil + ;; by `enable-theme'. + (null (bound-and-true-p custom--inhibit-theme-enable))) + (let ((modus-themes-inhibit-reload t)) + (pcase (modus-themes--current-theme) + ('modus-operandi (modus-themes-load-operandi)) + ('modus-vivendi (modus-themes-load-vivendi)))))) + +(defcustom modus-themes-operandi-color-overrides nil + "Override colors in the Modus Operandi palette. + +For form, see `modus-themes-operandi-colors'." + :group 'modus-themes + :package-version '(modus-themes . "1.1.0") + :version "28.1" + :type '(alist :key-type symbol :value-type color) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Override colors")) + +(defcustom modus-themes-vivendi-color-overrides nil + "Override colors in the Modus Vivendi palette. + +For form, see `modus-themes-vivendi-colors'." + :group 'modus-themes + :package-version '(modus-themes . "1.1.0") + :version "28.1" + :type '(alist :key-type symbol :value-type color) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Override colors")) + +;; The byte compiler complains when a defcustom isn't a top level form +(let* ((names (mapcar (lambda (pair) + (symbol-name (car pair))) + modus-themes-operandi-colors)) + (colors (mapcar #'intern (sort names #'string<)))) + (put 'modus-themes-operandi-color-overrides + 'custom-options (copy-sequence colors)) + (put 'modus-themes-vivendi-color-overrides + 'custom-options (copy-sequence colors))) + +(defvaralias 'modus-themes-slanted-constructs 'modus-themes-italic-constructs) + +(defcustom modus-themes-italic-constructs nil + "Use italic font forms in more code constructs." + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Italic constructs")) + +(defcustom modus-themes-bold-constructs nil + "Use bold text in more code constructs." + :group 'modus-themes + :package-version '(modus-themes . "1.0.0") + :version "28.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Bold constructs")) + +(defcustom modus-themes-variable-pitch-ui nil + "Use proportional fonts (variable-pitch) in UI elements. +This includes the mode line, header line, tab bar, and tab line." + :group 'modus-themes + :package-version '(modus-themes . "1.1.0") + :version "28.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) UI typeface")) + +(defcustom modus-themes-mixed-fonts nil + "Non-nil to enable inheritance from `fixed-pitch' in some faces. + +This is done to allow spacing-sensitive constructs, such as Org +tables and code blocks, to remain monospaced when users opt for +something like the command `variable-pitch-mode'. + +Users may need to explicitly configure the font family of +`fixed-pitch' in order to get a consistent experience." + :group 'modus-themes + :package-version '(modus-themes . "1.7.0") + :version "29.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Mixed fonts")) + +(defcustom modus-themes-intense-mouseovers nil + "When non-nil use more intense style for mouse hover effects. + +This affects the generic `highlight' face which, strictly +speaking, is not limited to mouse usage." + :group 'modus-themes + :package-version '(modus-themes . "2.3.0") + :version "29.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Mouse hover effects")) + +(defconst modus-themes--headings-choice + '(set :tag "Properties" :greedy t + (const :tag "Background color" background) + (const :tag "Proportionately spaced font (variable-pitch)" variable-pitch) + (const :tag "Overline" overline) + (choice :tag "Font weight (must be supported by the typeface)" + (const :tag "Bold (default)" nil) + (const :tag "Thin" thin) + (const :tag "Ultra-light" ultralight) + (const :tag "Extra-light" extralight) + (const :tag "Light" light) + (const :tag "Semi-light" semilight) + (const :tag "Regular" regular) + (const :tag "Medium" medium) + (const :tag "Semi-bold" semibold) + (const :tag "Extra-bold" extrabold) + (const :tag "Ultra-bold" ultrabold)) + (radio :tag "Height" + (float :tag "Floating point to adjust height by") + (cons :tag "Cons cell of `(height . FLOAT)'" + (const :tag "The `height' key (constant)" height) + (float :tag "Floating point"))) + (choice :tag "Colors" + (const :tag "Subtle colors" nil) + (const :tag "Rainbow colors" rainbow) + (const :tag "Monochrome" monochrome))) + "Refer to the doc string of `modus-themes-headings'. +This is a helper variable intended for internal use.") + +(defcustom modus-themes-headings nil + "Heading styles with optional list of values for levels 0-8. + +This is an alist that accepts a (key . list-of-values) +combination. The key is either a number, representing the +heading's level (0-8) or t, which pertains to the fallback style. + +Level 0 is a special heading: it is used for what counts as a +document title or equivalent, such as the #+title construct we +find in Org files. Levels 1-8 are regular headings. + +The list of values covers symbols that refer to properties, as +described below. Here is a complete sample, followed by a +presentation of all available properties: + + (setq modus-themes-headings + (quote ((1 . (background overline variable-pitch 1.5)) + (2 . (overline rainbow 1.3)) + (3 . (overline 1.1)) + (t . (monochrome))))) + +By default (a nil value for this variable), all headings have a +bold typographic weight, use a desaturated text color, have a +font family that is the same as the `default' face (typically +monospaced), and a height that is equal to the `default' face's +height. + +A `rainbow' property makes the text color more saturated. + +An `overline' property draws a line above the area of the +heading. + +A `background' property applies a subtle tinted color to the +background of the heading. + +A `monochrome' property makes the heading the same as the base +color, which is that of the `default' face's foreground. When +`background' is also set, `monochrome' changes its color to gray. +If both `monochrome' and `rainbow' are set, the former takes +precedence. + +A `variable-pitch' property changes the font family of the +heading to that of the `variable-pitch' face (normally a +proportionately spaced typeface). + +The symbol of a weight attribute adjusts the font of the heading +accordingly, such as `light', `semibold', etc. Valid symbols are +defined in the variable `modus-themes-weights'. The absence of a +weight means that bold will be used by virtue of inheriting the +`bold' face (check the manual for tweaking bold and italic +faces). For backward compatibility, the `no-bold' value is +accepted, though users are encouraged to specify a `regular' +weight instead. + +A number, expressed as a floating point (e.g. 1.5), adjusts the +height of the heading to that many times the base font size. The +default height is the same as 1.0, though it need not be +explicitly stated. Instead of a floating point, an acceptable +value can be in the form of a cons cell like (height . FLOAT) +or (height FLOAT), where FLOAT is the given number. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (semibold) + (rainbow background) + (overline monochrome semibold 1.3) + (overline monochrome semibold (height 1.3)) ; same as above + (overline monochrome semibold (height . 1.3)) ; same as above + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-headings + (quote ((1 . (background overline rainbow 1.5)) + (2 . (background overline 1.3)) + (t . (overline semibold))))) + +When defining the styles per heading level, it is possible to +pass a non-nil value (t) instead of a list of properties. This +will retain the original aesthetic for that level. For example: + + (setq modus-themes-headings + (quote ((1 . t) ; keep the default style + (2 . (background overline)) + (t . (rainbow))))) ; style for all other headings + + (setq modus-themes-headings + (quote ((1 . (background overline)) + (2 . (rainbow semibold)) + (t . t)))) ; default style for all other levels + +For Org users, the extent of the heading depends on the variable +`org-fontify-whole-heading-line'. This affects the `overline' +and `background' properties. Depending on the version of Org, +there may be others, such as `org-fontify-done-headline'." + :group 'modus-themes + :package-version '(modus-themes . "2.5.0") + :version "29.1" + :type `(alist + :options ,(mapcar (lambda (el) + (list el modus-themes--headings-choice)) + '(0 1 2 3 4 5 6 7 8 t)) + :key-type symbol + :value-type ,modus-themes--headings-choice) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Heading styles")) + +(defcustom modus-themes-org-agenda nil + "Control the style of individual Org agenda constructs. + +This is an alist that accepts a (key . value) combination. Here +is a sample, followed by a description of all possible +combinations: + + (setq modus-themes-org-agenda + (quote ((header-block . (variable-pitch 1.5 semibold)) + (header-date . (grayscale workaholic bold-today 1.2)) + (event . (accented italic varied)) + (scheduled . uniform) + (habit . traffic-light)))) + +A `header-block' key applies to elements that concern the +headings which demarcate blocks in the structure of the agenda. +By default (a nil value) those are rendered in a bold typographic +weight, plus a height that is slightly taller than the default +font size. Acceptable values come in the form of a list that can +include either or both of those properties: + +- `variable-pitch' to use a proportionately spaced typeface; + +- A number as a floating point (e.g. 1.5) to set the height of + the text to that many times the default font height. A float + of 1.0 or the symbol `no-scale' have the same effect of making + the font the same height as the rest of the buffer. When + neither a number nor `no-scale' are present, the default is a + small increase in height (a value of 1.15). + + Instead of a floating point, an acceptable value can be in the + form of a cons cell like (height . FLOAT) or (height FLOAT), + where FLOAT is the given number. + +- The symbol of a weight attribute adjusts the font of the + heading accordingly, such as `light', `semibold', etc. Valid + symbols are defined in the variable `modus-themes-weights'. + The absence of a weight means that bold will be used by virtue + of inheriting the `bold' face (check the manual for tweaking + bold and italic faces). + +In case both a number and `no-scale' are in the list, the latter +takes precedence. If two numbers are specified, the first one is +applied. + +Example usage: + + (header-block . nil) + (header-block . (1.5)) + (header-block . (no-scale)) + (header-block . (variable-pitch 1.5)) + (header-block . (variable-pitch 1.5 semibold)) + +A `header-date' key covers date headings. Dates use only a +foreground color by default (a nil value), with weekdays and +weekends having a slight difference in hueness. The current date +has an added gray background. This key accepts a list of values +that can include any of the following properties: + +- `grayscale' to make weekdays use the main foreground color and + weekends a more subtle gray; + +- `workaholic' to make weekdays and weekends look the same in + terms of color; + +- `bold-today' to apply a bold typographic weight to the current + date; + +- `bold-all' to render all date headings in a bold weight; + +- `underline-today' applies an underline to the current date + while removing the background it has by default; + +- A number as a floating point (e.g. 1.2) to set the height of + the text to that many times the default font height. The + default is the same as the base font height (the equivalent of + 1.0). Instead of a floating point, an acceptable value can be + in the form of a cons cell like (height . FLOAT) or (height + FLOAT), where FLOAT is the given number. + +For example: + + (header-date . nil) + (header-date . (workaholic)) + (header-date . (grayscale bold-all)) + (header-date . (grayscale workaholic)) + (header-date . (grayscale workaholic bold-today)) + (header-date . (grayscale workaholic bold-today 1.2)) + +An `event' key covers (i) headings with a plain time stamp that +are shown on the agenda, also known as events, (ii) entries +imported from the diary, and (iii) other items that derive from a +symbolic expression or sexp (phases of the moon, holidays, etc.). +By default all those look the same and have a subtle foreground +color (the default is a nil value or an empty list). This key +accepts a list of properties. Those are: + +- `accented' applies an accent value to the event's foreground, + replacing the original gray. It makes all entries stand out more. +- `italic' adds a slant to the font's forms (italic or oblique + forms, depending on the typeface). +- `varied' differentiates between events with a plain time stamp + and entries that are generated from either the diary or a + symbolic expression. It generally puts more emphasis on + events. When `varied' is combined with `accented', it makes + only events use an accent color, while diary/sexp entries + retain their original subtle foreground. When `varied' is used + in tandem with `italic', it applies a slant only to diary and + sexp entries, not events. And when `varied' is the sole + property passed to the `event' key, it has the same meaning as + the list (italic varied). The combination of `varied', + `accented', `italic' covers all of the aforementioned cases. + +For example: + + (event . nil) + (event . (italic)) + (event . (accented italic)) + (event . (accented italic varied)) + +A `scheduled' key applies to tasks with a scheduled date. By +default (a nil value), these use varying shades of yellow to +denote (i) a past or current date and (ii) a future date. Valid +values are symbols: + +- nil (default); +- `uniform' to make all scheduled dates the same color; +- `rainbow' to use contrasting colors for past, present, future + scheduled dates. + +For example: + + (scheduled . nil) + (scheduled . uniform) + (scheduled . rainbow) + +A `habit' key applies to the `org-habit' graph. All possible +value are passed as a symbol. Those are: + +- The default (nil) is meant to conform with the original + aesthetic of `org-habit'. It employs all four color codes that + correspond to the org-habit states---clear, ready, alert, and + overdue---while distinguishing between their present and future + variants. This results in a total of eight colors in use: red, + yellow, green, blue, in tinted and shaded versions. They cover + the full set of information provided by the `org-habit' + consistency graph. + +- `simplified' is like the default except that it removes the + dichotomy between current and future variants by applying + uniform color-coded values. It applies a total of four colors: + red, yellow, green, blue. They produce a simplified + consistency graph that is more legible (or less \"busy\") than + the default. The intent is to shift focus towards the + distinction between the four states of a habit task, rather + than each state's present/future outlook. + +- `traffic-light' further reduces the available colors to red, + yellow, and green. As in `simplified', present and future + variants appear uniformly, but differently from it, the CLEAR + state is rendered in a green hue, instead of the original blue. + This is meant to capture the use-case where a habit task being + too early is less important than it being too late. The + difference between READY and CLEAR states is attenuated by + painting both of them using shades of green. This option thus + highlights the alert and overdue states. + +- When `modus-themes-deuteranopia' is non-nil the exact style of + the habit graph adapts to the needs of users with red-green + color deficiency by substituting every instance of green with + blue or cyan (depending on the specifics). + +For example: + + (habit . nil) + (habit . simplified) + (habit . traffic-light)" + :group 'modus-themes + :package-version '(modus-themes . "2.3.0") + :version "29.1" + :type '(set + (cons :tag "Block header" + (const header-block) + (set :tag "Header presentation" :greedy t + (choice :tag "Font style" + (const :tag "Use the original typeface (default)" nil) + (const :tag "Use `variable-pitch' font" variable-pitch)) + (choice :tag "Font weight (must be supported by the typeface)" + (const :tag "Bold (default)" nil) + (const :tag "Thin" thin) + (const :tag "Ultra-light" ultralight) + (const :tag "Extra-light" extralight) + (const :tag "Light" light) + (const :tag "Semi-light" semilight) + (const :tag "Regular" regular) + (const :tag "Medium" medium) + (const :tag "Semi-bold" semibold) + (const :tag "Extra-bold" extrabold) + (const :tag "Ultra-bold" ultrabold)) + (radio :tag "Scaling" + (const :tag "Slight increase in height (default)" nil) + (const :tag "Do not scale" no-scale) + (radio :tag "Number (float) to adjust height by" + (float :tag "Just the number") + (cons :tag "Cons cell of `(height . FLOAT)'" + (const :tag "The `height' key (constant)" height) + (float :tag "Floating point")))))) + (cons :tag "Date header" :greedy t + (const header-date) + (set :tag "Header presentation" :greedy t + (const :tag "Use grayscale for date headers" grayscale) + (const :tag "Do not differentiate weekdays from weekends" workaholic) + (const :tag "Make today bold" bold-today) + (const :tag "Make all dates bold" bold-all) + (const :tag "Make today underlined; remove the background" underline-today) + (radio :tag "Number (float) to adjust height by" + (float :tag "Just the number") + (cons :tag "Cons cell of `(height . FLOAT)'" + (const :tag "The `height' key (constant)" height) + (float :tag "Floating point"))))) + (cons :tag "Event entry" :greedy t + (const event) + (set :tag "Text presentation" :greedy t + (const :tag "Apply an accent color" accented) + (const :tag "Italic font slant (oblique forms)" italic) + (const :tag "Differentiate events from diary/sexp entries" varied))) + (cons :tag "Scheduled tasks" + (const scheduled) + (choice (const :tag "Yellow colors to distinguish current and future tasks (default)" nil) + (const :tag "Uniform subtle warm color for all scheduled tasks" uniform) + (const :tag "Rainbow-colored scheduled tasks" rainbow))) + (cons :tag "Habit graph" + (const habit) + (choice (const :tag "Follow the original design of `org-habit' (default)" nil) + (const :tag "Do not distinguish between present and future variants" simplified) + (const :tag "Use only red, yellow, green" traffic-light)))) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Org agenda")) + +(defcustom modus-themes-fringes nil + "Define the visibility of fringes. + +Nil means the fringes have no background color. Option `subtle' +will apply a grayscale value that is visible yet close to the +main buffer background color. Option `intense' will use a more +pronounced grayscale value." + :group 'modus-themes + :package-version '(modus-themes . "1.0.0") + :version "28.1" + :type '(choice + (const :format "[%v] %t\n" :tag "No visible fringes (default)" nil) + (const :format "[%v] %t\n" :tag "Subtle grayscale background" subtle) + (const :format "[%v] %t\n" :tag "Intense grayscale background" intense)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Fringes")) + +(defcustom modus-themes-lang-checkers nil + "Control the style of spelling and code checkers/linters. + +The value is a list of properties, each designated by a symbol. +The default (nil) applies a color-coded underline to the affected +text, while it leaves the original foreground intact. If the +display spec of Emacs has support for it, the underline's style +is that of a wave, otherwise it is a straight line. + +The property `straight-underline' ensures that the underline +under the affected text is always drawn as a straight line. + +The property `text-also' applies the same color of the underline +to the affected text. + +The property `background' adds a color-coded background. + +The property `intense' amplifies the applicable colors if +`background' and/or `text-also' are set. If `intense' is set on +its own, then it implies `text-also'. + +The property `faint' uses nuanced colors for the underline and +for the foreground when `text-also' is included. If both `faint' +and `intense' are specified, the former takes precedence. + +Combinations of any of those properties can be expressed in a +list, as in those examples: + + (background) + (straight-underline intense) + (background text-also straight-underline) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-lang-checkers (quote (text-also background))) + +NOTE: The placement of the straight underline, though not the +wave style, is controlled by the built-in variables +`underline-minimum-offset', `x-underline-at-descent-line', +`x-use-underline-position-properties'. + +To disable fringe indicators for Flymake or Flycheck, refer to +variables `flymake-fringe-indicator-position' and +`flycheck-indication-mode', respectively." + :group 'modus-themes + :package-version '(modus-themes . "1.7.0") + :version "29.1" + :type '(set :tag "Properties" :greedy t + (const :tag "Straight underline" straight-underline) + (const :tag "Colorise text as well" text-also) + (const :tag "With background" background) + (choice :tag "Overall coloration" + (const :tag "Intense colors" intense) + (const :tag "Faint colors" faint))) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Language checkers")) + +(defcustom modus-themes-org-blocks nil + "Set the overall style of Org code blocks, quotes, and the like. + +Nil (the default) means that the block has no background of its +own: it uses the one that applies to the rest of the buffer. In +this case, the delimiter lines have a gray color for their text, +making them look exactly like all other Org properties. + +Option `gray-background' applies a subtle gray background to the +block's contents. It also affects the begin and end lines of the +block as they get another shade of gray as their background, +which differentiates them from the contents of the block. All +background colors extend to the edge of the window, giving the +area a rectangular, \"blocky\" presentation. + +Option `tinted-background' uses a slightly colored background for +the contents of the block. The exact color will depend on the +programming language and is controlled by the variable +`org-src-block-faces' (refer to the theme's source code for the +current association list). For this to take effect, the Org +buffer needs to be restarted with `org-mode-restart'. In this +scenario, it may be better to inhibit the extension of the +delimiter lines' background to the edge of the window because Org +does not provide a mechanism to update their colors depending on +the contents of the block. Disable the extension of such +backgrounds by setting `org-fontify-whole-block-delimiter-line' +to nil. + +Code blocks use their major mode's colors only when the variable +`org-src-fontify-natively' is non-nil. While quote/verse blocks +require setting `org-fontify-quote-and-verse-blocks' to a non-nil +value. + +Older versions of the themes provided options `grayscale' (or +`greyscale') and `rainbow'. Those will continue to work as they +are aliases for `gray-background' and `tinted-background', +respectively." + :group 'modus-themes + :package-version '(modus-themes . "2.1.0") + :version "28.1" + :type '(choice + (const :format "[%v] %t\n" :tag "No Org block background (default)" nil) + (const :format "[%v] %t\n" :tag "Subtle gray block background" gray-background) + (const :format "[%v] %t\n" :tag "Alias for `gray-background'" grayscale) ; for backward compatibility + (const :format "[%v] %t\n" :tag "Alias for `gray-background'" greyscale) + (const :format "[%v] %t\n" :tag "Color-coded background per programming language" tinted-background) + (const :format "[%v] %t\n" :tag "Alias for `tinted-background'" rainbow)) ; back compat + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Org mode blocks")) + +(defcustom modus-themes-mode-line nil + "Control the overall style of the mode line. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is a two-dimensional +rectangle with a border around it. The active and the inactive +mode lines use different shades of grayscale values for the +background, foreground, border. + +The `3d' property applies a three-dimensional effect to the +active mode line. The inactive mode lines remain two-dimensional +and are toned down a bit, relative to the default style. + +The `moody' property optimizes the mode line for use with the +library of the same name (hereinafter referred to as Moody). +In practice, it removes the box effect and replaces it with +underline and overline properties. It also tones down the +inactive mode lines. Despite its intended purpose, this option +can also be used without the Moody library (please consult the +themes' manual on this point for more details). If both `3d' and +`moody' properties are set, the latter takes precedence. + +The `borderless' property removes the color of the borders. It +does not actually remove the borders, but only makes their color +the same as the background, effectively creating some padding. + +The `accented' property ensures that the active mode line uses a +colored background instead of the standard shade of gray. + +A positive integer (natural number or natnum) applies a padding +effect of NATNUM pixels at the boundaries of the mode lines. The +default value is 1 and does not need to be specified explicitly. +The padding has no effect when the `moody' property is also used, +because Moody already applies its own tweaks. To ensure that the +underline is placed at the bottom of the mode line, set +`x-underline-at-descent-line' to non-nil (this is not needed when +the `borderless' property is also set). For users on Emacs 29, +the `x-use-underline-position-properties' variable must also be +set to nil. + +The padding can also be expressed as a cons cell in the form +of (padding . NATNUM) or (padding NATNUM) where the key is +constant and NATNUM is the desired natural number. + +A floating point (e.g. 0.9) applies an adjusted height to the +mode line's text as a multiple of the main font size. The +default rate is 1.0 and does not need to be specified. Apart +from a floating point, the height may also be expressed as a cons +cell in the form of (height . FLOAT) or (height FLOAT) where the +key is constant and the FLOAT is the desired number. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (accented) + (borderless 3d) + (moody accented borderless) + +Same as above, using the padding and height as an example (these +all yield the same result): + + (accented borderless 4 0.9) + (accented borderless (padding . 4) (height . 0.9)) + (accented borderless (padding 4) (height 0.9)) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-mode-line (quote (borderless accented))) + +Note that Moody does not expose any faces that the themes could +style directly. Instead it re-purposes existing ones to render +its tabs and ribbons. As such, there may be cases where the +contrast ratio falls below the 7:1 target that the themes conform +with (WCAG AAA). To hedge against this, we configure a fallback +foreground for the `moody' property, which will come into effect +when the background of the mode line changes to something less +accessible, such as Moody ribbons (read the doc string of +`set-face-attribute', specifically `:distant-foreground'). This +fallback is activated when Emacs determines that the background +and foreground of the given construct are too close to each other +in terms of color distance. In practice, users will need to +experiment with the variable `face-near-same-color-threshold' to +trigger the effect. We find that a value of 45000 shall suffice, +contrary to the default 30000. Though for the combinations that +involve the `accented' and `moody' properties, as mentioned +above, that should be raised up to 70000. Do not set it too +high, because it has the adverse effect of always overriding the +default colors (which have been carefully designed to be highly +accessible). + +Furthermore, because Moody expects an underline and overline +instead of a box style, it is strongly advised to set +`x-underline-at-descent-line' to a non-nil value." + :group 'modus-themes + :package-version '(modus-themes . "2.3.0") + :version "29.1" + :type '(set :tag "Properties" :greedy t + (choice :tag "Overall style" + (const :tag "Rectangular Border" nil) + (const :tag "3d borders" 3d) + (const :tag "No box effects (Moody-compatible)" moody)) + (const :tag "Colored background" accented) + (const :tag "Without border color" borderless) + (radio :tag "Padding" + (natnum :tag "Natural number (e.g. 4)") + (cons :tag "Cons cell of `(padding . NATNUM)'" + (const :tag "The `padding' key (constant)" padding) + (natnum :tag "Natural number"))) + (radio :tag "Height" + (float :tag "Floating point (e.g. 0.9)") + (cons :tag "Cons cell of `(height . FLOAT)'" + (const :tag "The `height' key (constant)" height) + (float :tag "Floating point")))) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Mode line")) + +(defcustom modus-themes-diffs nil + "Adjust the overall style of diffs. + +The default (nil) uses fairly intense color combinations for +diffs, by applying prominently colored backgrounds, with +appropriately tinted foregrounds. + +Option `desaturated' follows the same principles as with the +default (nil), though it tones down all relevant colors. + +Option `bg-only' applies a background but does not override the +text's foreground. This makes it suitable for a non-nil value +passed to `diff-font-lock-syntax' (note: Magit does not support +syntax highlighting in diffs---last checked on 2021-12-02). + +When the user option `modus-themes-deuteranopia' is non-nil, all +diffs will use a red/blue color-coding system instead of the +standard red/green. Other stylistic changes are made in the +interest of optimizing for such a use-case." + :group 'modus-themes + :package-version '(modus-themes . "2.0.0") + :version "29.1" + :type '(choice + (const :format "[%v] %t\n" :tag "Intensely colored backgrounds (default)" nil) + (const :format "[%v] %t\n" :tag "Slightly accented backgrounds with tinted text" desaturated) + (const :format "[%v] %t\n" :tag "Apply color-coded backgrounds; keep syntax colors intact" bg-only)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Diffs")) + +(defcustom modus-themes-completions nil + "Control the style of completion user interfaces. + +This affects Company, Corfu, Flx, Helm, Icomplete/Fido, Ido, Ivy, +Mct, Orderless, Selectrum, Vertico. The value is an alist that +takes the form of a (key . properties) combination. Here is a +sample, followed by a description of the particularities: + + (setq modus-themes-completions + (quote ((matches . (extrabold background intense)) + (selection . (semibold accented intense)) + (popup . (accented))))) + +The `matches' key refers to the highlighted characters that +correspond to the user's input. By default (nil or an empty +list), they have a bold weight and a colored foreground. The +list of properties may include any of the following symbols +regardless of the order they may appear in: + +- `background' to add a background color; + +- `intense' to increase the overall coloration (also amplifies + the `background', if present); + +- `underline' to draw a line below the characters; + +- `italic' to use a slanted font (italic or oblique forms); + +- The symbol of a font weight attribute such as `light', + `semibold', et cetera. Valid symbols are defined in the + variable `modus-themes-weights'. The absence of a weight means + that bold will be used. + +The `selection' key applies to the current line or currently +matched candidate, depending on the specifics of the User +Interface. By default (nil or an empty list), it has a subtle +gray background, a bold weight, and the base foreground value +for the text. The list of properties it accepts is as +follows (order is not significant): + +- `accented' to make the background colorful instead of gray; + +- `text-also' to apply extra color to the text of the selected + line; + +- `intense' to increase the overall coloration; + +- `underline' to draw a line below the characters; + +- `italic' to use a slanted font (italic or oblique forms); + +- The symbol of a font weight attribute such as `light', + `semibold', et cetera. Valid symbols are defined in the + variable `modus-themes-weights'. The absence of a weight means + that bold will be used. + +The `popup' key takes the same values as `selection'. + +Apart from specifying each key separately, a fallback list is +accepted. This is only useful when the desired aesthetic is the +same across all keys that are not explicitly referenced. For +example, this: + + (setq modus-themes-completions + (quote ((t . (extrabold intense))))) + +Is the same as: + + (setq modus-themes-completions + (quote ((matches . (extrabold intense)) + (selection . (extrabold intense)) + (popup . (extrabold intense))))) + +In the case of the fallback, any property that does not apply to +the corresponding key is simply ignored (`matches' does not have +`accented' and `text-also', while `selection' and `popup' do not +have `background'). + +A concise expression of those associations can be written as +follows, where the `car' is always the key and the `cdr' is the +list of properties (whatever order they may appear in): + + (setq modus-themes-completions + (quote ((matches extrabold background intense) + (selection semibold accented intense) + (popup accented)))) + +Check the manual for tweaking `bold' and `italic' faces: Info +node `(modus-themes) Configure bold and italic faces'. + +Also refer to the Orderless documentation for its intersection +with Company (if you choose to use those in tandem)." + :group 'modus-themes + :package-version '(modus-themes . "2.3.0") + :version "29.1" + :type `(set + (cons :tag "Matches" + (const matches) + (set :tag "Style of matches" :greedy t + (choice :tag "Font weight (must be supported by the typeface)" + (const :tag "Bold (default)" nil) + (const :tag "Thin" thin) + (const :tag "Ultra-light" ultralight) + (const :tag "Extra-light" extralight) + (const :tag "Light" light) + (const :tag "Semi-light" semilight) + (const :tag "Regular" regular) + (const :tag "Medium" medium) + (const :tag "Semi-bold" semibold) + (const :tag "Extra-bold" extrabold) + (const :tag "Ultra-bold" ultrabold)) + (const :tag "With added background" background) + (const :tag "Increased coloration" intense) + (const :tag "Italic font (oblique or slanted forms)" italic) + (const :tag "Underline" underline))) + (cons :tag "Selection" + (const selection) + (set :tag "Style of selection" :greedy t + (choice :tag "Font weight (must be supported by the typeface)" + (const :tag "Bold (default)" nil) + (const :tag "Thin" thin) + (const :tag "Ultra-light" ultralight) + (const :tag "Extra-light" extralight) + (const :tag "Light" light) + (const :tag "Semi-light" semilight) + (const :tag "Regular" regular) + (const :tag "Medium" medium) + (const :tag "Semi-bold" semibold) + (const :tag "Extra-bold" extrabold) + (const :tag "Ultra-bold" ultrabold)) + (const :tag "Apply color to the line's text" text-also) + (const :tag "With accented background" accented) + (const :tag "Increased coloration" intense) + (const :tag "Italic font (oblique or slanted forms)" italic) + (const :tag "Underline" underline))) + (cons :tag "Popup" + (const popup) + (set :tag "Style of completion pop-ups" :greedy t + (choice :tag "Font weight (must be supported by the typeface)" + (const :tag "Bold (default)" nil) + (const :tag "Thin" thin) + (const :tag "Ultra-light" ultralight) + (const :tag "Extra-light" extralight) + (const :tag "Light" light) + (const :tag "Semi-light" semilight) + (const :tag "Regular" regular) + (const :tag "Medium" medium) + (const :tag "Semi-bold" semibold) + (const :tag "Extra-bold" extrabold) + (const :tag "Ultra-bold" ultrabold)) + (const :tag "Apply color to the line's text" text-also) + (const :tag "With accented background" accented) + (const :tag "Increased coloration" intense) + (const :tag "Italic font (oblique or slanted forms)" italic) + (const :tag "Underline" underline)))) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Completion UIs")) + +(defcustom modus-themes-prompts nil + "Use subtle or intense styles for minibuffer and REPL prompts. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) means to only use a +subtle accented foreground color. + +The property `background' applies a background color to the +prompt's text. By default, this is a subtle accented value. + +The property `intense' makes the foreground color more prominent. +If the `background' property is also set, it amplifies the value +of the background as well. + +The property `gray' changes the prompt's colors to grayscale. +This affects the foreground and, if the `background' property is +also set, the background. Its effect is subtle, unless it is +combined with the `intense' property. + +The property `bold' makes the text use a bold typographic weight. +Similarly, `italic' adds a slant to the font's forms (italic or +oblique forms, depending on the typeface). + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (intense) + (bold intense) + (intense bold gray) + (intense background gray bold) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-prompts (quote (background gray)))" + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type '(set :tag "Properties" :greedy t + (const :tag "With Background" background) + (const :tag "Intense" intense) + (const :tag "Grayscale" gray) + (const :tag "Bold font weight" bold) + (const :tag "Italic font slant" italic)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Command prompts")) + +(defcustom modus-themes-hl-line nil + "Control the current line highlight of HL-line mode. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is a subtle gray +background color. + +The property `accented' changes the background to a colored +variant. + +An `underline' property draws a line below the highlighted area. +Its color is similar to the background, so gray by default or an +accent color when `accented' is also set. + +An `intense' property amplifies the colors in use, which may be +both the background and the underline. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (intense) + (underline intense) + (accented intense underline) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-hl-line (quote (underline accented))) + +Set `x-underline-at-descent-line' to a non-nil value for better +results with underlines." + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type '(set :tag "Properties" :greedy t + (const :tag "Colored background" accented) + (const :tag "Underline" underline) + (const :tag "Intense style" intense)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Line highlighting")) + +(defcustom modus-themes-subtle-line-numbers nil + "Use more subtle style for command `display-line-numbers-mode'." + :group 'modus-themes + :package-version '(modus-themes . "1.2.0") + :version "28.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Line numbers")) + +(defcustom modus-themes-markup nil + "Style markup in Org, Markdown, and others. + +This affects constructs such as Org's =verbatim= and ~code~. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is a foreground +color. + +The `italic' property applies a typographic slant (italics). + +The `bold' property applies a heavier typographic weight. + +The `background' property adds a background color. The +background is a shade of gray, unless the `intense' property is +also set. + +The `intense' property amplifies the existing coloration. When +`background' is used, the background color is enhanced as well +and becomes tinted instead of being gray. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (bold) + (bold italic) + (bold italic intense) + (bold italic intense background) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-markup (quote (bold italic))) + +Also check the variables `org-hide-emphasis-markers', +`org-hide-macro-markers'." + :group 'modus-themes + :package-version '(modus-themes . "2.1.0") + :version "29.1" + :type '(set :tag "Properties" :greedy t + (const :tag "Added background" background) + (const :tag "Intense colors" intense) + (const :tag "Bold weight" bold) + (const :tag "Italics (slanted text)" italic)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Markup")) + +(make-obsolete 'modus-themes-intense-markup 'modus-themes-markup "2.1.0") + +(defcustom modus-themes-paren-match nil + "Control the style of matching parentheses or delimiters. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is a subtle background +color. + +The `bold' property adds a bold weight to the characters of the +matching delimiters. + +The `intense' property applies a more prominent background color +to the delimiters. + +The `underline' property draws a straight line under the affected +text. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (bold) + (underline intense) + (bold intense underline) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-paren-match (quote (bold intense)))" + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type '(set :tag "Properties" :greedy t + (const :tag "Bold weight" bold) + (const :tag "Intense background color" intense) + (const :tag "Underline" underline)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Matching parentheses")) + +(defcustom modus-themes-syntax nil + "Control the overall style of code syntax highlighting. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is to use a balanced +combination of colors on the cyan-blue-magenta side of the +spectrum. There is little to no use of greens, yellows, and +reds. Comments are gray, strings are blue colored, doc strings +are a shade of cyan, while color combinations are designed to +avoid exaggerations. + +The property `faint' fades the saturation of all applicable +colors, where that is possible or appropriate. + +The property `yellow-comments' applies a yellow color to +comments. + +The property `green-strings' applies a green color to strings and +a green tint to doc strings. + +The property `alt-syntax' changes the combination of colors +beyond strings and comments, so that the effective palette is +broadened to provide greater variety relative to the default. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (faint) + (green-strings yellow-comments) + (alt-syntax green-strings yellow-comments) + (faint alt-syntax green-strings yellow-comments) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-syntax (quote (faint alt-syntax))) + +Independent of this variable, users may also control the use of a +bold weight or italic text: `modus-themes-bold-constructs' and +`modus-themes-italic-constructs'." + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type '(set :tag "Properties" :greedy t + (const :tag "Faint colors" faint) + (const :tag "Yellow comments" yellow-comments) + (const :tag "Green strings" green-strings) + (const :tag "Alternative set of colors" alt-syntax)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Syntax styles")) + +(defcustom modus-themes-links nil + "Set the style of links. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is a prominent text +color, typically blue, with an underline of the same color. + +For the style of the underline, a `neutral-underline' property +turns the color of the line into a subtle gray, while the +`no-underline' property removes the line altogether. If both of +those are set, the latter takes precedence. + +For text coloration, a `faint' property desaturates the color of +the text and the underline, unless the underline is affected by +the aforementioned properties. While a `no-color' property +removes the color from the text. If both of those are set, the +latter takes precedence. + +A `bold' property applies a heavy typographic weight to the text +of the link. + +An `italic' property adds a slant to the link's text (italic or +oblique forms, depending on the typeface). + +A `background' property applies a subtle tinted background color. + +In case both `no-underline' and `no-color' are set, then a subtle +gray background is applied to all links. This can still be +combined with the `bold' and `italic' properties. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (faint) + (no-underline faint) + (no-color no-underline bold) + (italic bold background no-color no-underline) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-links (quote (neutral-underline background))) + +The placement of the underline, meaning its proximity to the +text, is controlled by `x-use-underline-position-properties', +`x-underline-at-descent-line', `underline-minimum-offset'. +Please refer to their documentation strings." + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type '(set :tag "Properties" :greedy t + (choice :tag "Text coloration" + (const :tag "Saturared color (default)" nil) + (const :tag "Faint coloration" faint) + (const :tag "No color (use main black/white)" no-color)) + (choice :tag "Underline" + (const :tag "Same color as text (default)" nil) + (const :tag "Neutral (gray) underline color" neutral-underline) + (const :tag "No underline" no-underline)) + (const :tag "Bold font weight" bold) + (const :tag "Italic font slant" italic) + (const :tag "Subtle background color" background)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Link styles")) + +(defcustom modus-themes-region nil + "Control the overall style of the active region. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is a prominent gray +background that overrides all foreground colors in the area it +encompasses. Its reach extends to the edge of the window. + +The `no-extend' property limits the region to the end of the +line, so that it does not reach the edge of the window. + +The `bg-only' property makes the region's background color more +subtle to allow the underlying text to retain its foreground +colors. + +The `accented' property applies a more colorful background to the +region. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (no-extend) + (bg-only accented) + (accented bg-only no-extend) + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-region (quote (bg-only no-extend)))" + :group 'modus-themes + :package-version '(modus-themes . "1.5.0") + :version "28.1" + :type '(set :tag "Properties" :greedy t + (const :tag "Do not extend to the edge of the window" no-extend) + (const :tag "Background only (preserve underlying colors)" bg-only) + (const :tag "Accented background" accented)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Active region")) + +(defcustom modus-themes-deuteranopia nil + "When non-nil use red/blue color-coding instead of red/green. + +This is to account for red-green color deficiency, also know as +deuteranopia and variants. It applies to all contexts where +there can be a color-coded distinction between failure or +success, a to-do or done state, a mark for deletion versus a mark +for selection (e.g. in Dired), current and lazily highlighted +search matches, removed lines in diffs as opposed to added ones, +and so on. + +Note that this does not change all colors throughout the active +theme, but only applies to cases that have color-coding +significance. For example, regular code syntax highlighting is +not affected. There is no such need because of the themes' +overarching commitment to the highest legibility standard, which +ensures that text is readable regardless of hue, as well as the +predominance of colors on the blue-cyan-magenta-purple side of +the spectrum." + :group 'modus-themes + :package-version '(modus-themes . "2.0.0") + :version "29.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Deuteranopia style")) + +(defcustom modus-themes-mail-citations nil + "Control the color of citations/quotes in messages or emails. + +By default (a nil value) citations are styled with contrasting +hues to denote their depth. Colors are easy to tell apart +because they complement each other, but they otherwise are not +very prominent. + +Option `intense' is similar to the default in terms of using +contrasting and complementary hues, but applies more saturated +colors. + +Option `faint' maintains the same color-based distinction between +citation levels though the colors it uses have subtle differences +between them. + +Option `monochrome' turns all quotes into a shade of gray. + +Whatever the value assigned to this variable, citations in emails +are controlled by typographic elements and/or indentation, which +the themes do not touch." + :group 'modus-themes + :package-version '(modus-themes . "2.1.0") + :version "29.1" + :type '(choice + (const :format "[%v] %t\n" :tag "Colorful email citations with contrasting hues (default)" nil) + (const :format "[%v] %t\n" :tag "Like the default, but with more saturated colors" intense) + (const :format "[%v] %t\n" :tag "Like the default, but with less saturated colors" faint) + (const :format "[%v] %t\n" :tag "Deprecated alias of `faint'" desaturated) + (const :format "[%v] %t\n" :tag "Uniformly gray mail citations" monochrome)) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Mail citations")) + +(defcustom modus-themes-tabs-accented nil + "Toggle accented tab backgrounds, instead of the default gray. +This affects the built-in tab-bar mode and tab-line mode, as well +as the Centaur tabs package." + :group 'modus-themes + :package-version '(modus-themes . "1.6.0") + :version "28.1" + :type 'boolean + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Tab style")) + +(defcustom modus-themes-box-buttons nil + "Control the style of buttons in the Custom UI and related. + +The value is a list of properties, each designated by a symbol. +The default (a nil value or an empty list) is a gray background +combined with a pseudo three-dimensional effect. + +The `flat' property makes the button two dimensional. + +The `accented' property changes the background from gray to an +accent color. + +The `faint' property reduces the overall coloration. + +The `variable-pitch' property applies a proportionately spaced +typeface to the button's text. + +The `underline' property draws a line below the affected text and +removes whatever box effect. This is optimal when Emacs runs +inside a terminal emulator. If `flat' and `underline' are +defined together, the latter takes precedence. + +The symbol of a weight attribute adjusts the font of the button +accordingly, such as `light', `semibold', etc. Valid symbols are +defined in the variable `modus-themes-weights'. + +A number, expressed as a floating point (e.g. 0.9), adjusts the +height of the button's text to that many times the base font +size. The default height is the same as 1.0, though it need not +be explicitly stated. Instead of a floating point, an acceptable +value can be in the form of a cons cell like (height . FLOAT) +or (height FLOAT), where FLOAT is the given number. + +The `all-buttons' property extends the box button effect (or the +aforementioned properties) to the faces of the generic widget +library. By default, those do not look like the buttons of the +Custom UI as they are ordinary text wrapped in square brackets. + +Combinations of any of those properties are expressed as a list, +like in these examples: + + (flat) + (variable-pitch flat) + (variable-pitch flat semibold 0.9) + (variable-pitch flat semibold (height 0.9)) ; same as above + (variable-pitch flat semibold (height . 0.9)) ; same as above + +The order in which the properties are set is not significant. + +In user configuration files the form may look like this: + + (setq modus-themes-box-buttons (quote (variable-pitch flat 0.9)))" + :group 'modus-themes + :package-version '(modus-themes . "2.3.0") + :version "29.1" + :type '(set :tag "Properties" :greedy t + (const :tag "Two-dimensional button" flat) + (const :tag "Accented background instead of gray" accented) + (const :tag "Reduce overall coloration" faint) + (const :tag "Proportionately spaced font (variable-pitch)" variable-pitch) + (const :tag "Underline instead of a box effect" underline) + (const :tag "Apply box button style to generic widget faces" all-buttons) + (choice :tag "Font weight (must be supported by the typeface)" + (const :tag "Thin" thin) + (const :tag "Ultra-light" ultralight) + (const :tag "Extra-light" extralight) + (const :tag "Light" light) + (const :tag "Semi-light" semilight) + (const :tag "Regular (default)" nil) + (const :tag "Medium" medium) + (const :tag "Bold" bold) + (const :tag "Semi-bold" semibold) + (const :tag "Extra-bold" extrabold) + (const :tag "Ultra-bold" ultrabold)) + (radio :tag "Height" + (float :tag "Floating point to adjust height by") + (cons :tag "Cons cell of `(height . FLOAT)'" + (const :tag "The `height' key (constant)" height) + (float :tag "Floating point")))) + :set #'modus-themes--set-option + :initialize #'custom-initialize-default + :link '(info-link "(modus-themes) Box buttons")) + + + +;;; Internal functions + +(defun modus-themes--warn (option) + "Warn that OPTION has changed." + (prog1 nil + (display-warning + 'modus-themes + (format "`%s' has changed; please read the updated documentation" option) + :warning))) + +(defun modus-themes--list-or-warn (option) + "Return list or nil value of OPTION, else `modus-themes--warn'." + (let* ((value (symbol-value option))) + (if (or (null value) (listp value)) + value + (modus-themes--warn option)))) + +(defun modus-themes--property-lookup (properties alist-key list-pred default) + "Return value from property alist or list. +Check PROPERTIES for an alist value that corresponds to +ALIST-KEY. If no alist is present, search the PROPERTIES +list given LIST-PRED, using DEFAULT as a fallback." + (if-let* ((val (or (alist-get alist-key properties) + (cl-loop for x in properties + if (funcall list-pred x) return x) + default)) + ((listp val))) + (car val) + val)) + +(defun modus-themes--palette (theme) + "Return color palette for Modus theme THEME. +THEME is a symbol, either `modus-operandi' or `modus-vivendi'." + (pcase theme + ('modus-operandi + (append modus-themes-operandi-color-overrides + modus-themes-operandi-colors)) + ('modus-vivendi + (append modus-themes-vivendi-color-overrides + modus-themes-vivendi-colors)) + (_theme + (error "'%s' is not a Modus theme" theme)))) + +(defvar modus-themes-faces) +(defvar modus-themes-custom-variables) + +(defmacro modus-themes-theme (name) + "Bind NAME's color palette around face specs and variables. + +NAME should be the proper name of a Modus theme, either +`modus-operandi' or `modus-vivendi'. + +Face specifications are passed to `custom-theme-set-faces'. +While variables are handled by `custom-theme-set-variables'. +Those are stored in `modus-themes-faces' and +`modus-themes-custom-variables' respectively." + (declare (indent 0)) + (let ((palette-sym (gensym)) + (colors (mapcar #'car modus-themes-operandi-colors))) + `(let* ((class '((class color) (min-colors 89))) + (,palette-sym (modus-themes--palette ',name)) + ,@(mapcar (lambda (color) + (list color `(alist-get ',color ,palette-sym))) + colors)) + (custom-theme-set-faces ',name ,@modus-themes-faces) + (custom-theme-set-variables ',name ,@modus-themes-custom-variables)))) + +(defun modus-themes--current-theme () + "Return current modus theme." + (car + (seq-filter + (lambda (theme) + (string-match-p "^modus" (symbol-name theme))) + custom-enabled-themes))) + +;; Helper functions that are meant to ease the implementation of the +;; above customization variables. +(defun modus-themes--bold-weight () + "Conditional use of a heavier text weight." + (when modus-themes-bold-constructs + (list :inherit 'bold))) + +(defun modus-themes--slant () + "Conditional use of italics for slant attribute." + (when modus-themes-italic-constructs + (list :inherit 'italic))) + +(defun modus-themes--fixed-pitch () + "Conditional application of `fixed-pitch' inheritance." + (when modus-themes-mixed-fonts + (list :inherit 'fixed-pitch))) + +(defun modus-themes--variable-pitch-ui () + "Conditional use of `variable-pitch' in UI elements." + (when modus-themes-variable-pitch-ui + (list :inherit 'variable-pitch))) + +(defun modus-themes--fringe (mainbg subtlebg intensebg) + "Conditional use of background colors for fringes. +MAINBG is the default. SUBTLEBG should be a subtle grayscale +value. INTENSEBG must be a more pronounced grayscale color." + (pcase modus-themes-fringes + ('intense (list :background intensebg)) + ('subtle (list :background subtlebg)) + (_ (list :background mainbg)))) + +(defun modus-themes--line-numbers (mainfg mainbg altfg &optional altbg) + "Conditional use of colors for line numbers. +MAINBG and MAINFG are the default colors. ALTFG is a color that +combines with the theme's primary background (white/black)." + (if modus-themes-subtle-line-numbers + (list :background (or altbg 'unspecified) :foreground altfg) + (list :background mainbg :foreground mainfg))) + +(defun modus-themes--markup (mainfg intensefg subtlebg intensebg) + "Conditional use of colors for markup in Org and others. +MAINFG is the default foreground. SUBTLEBG is a gray background. +INTENSEBG is a colorful background for use with the main +foreground. INTENSEFG is an alternative to the default." + (let ((properties modus-themes-markup)) + (list + :inherit + (cond + ((and (memq 'bold properties) + (memq 'italic properties)) + (list 'bold-italic 'modus-themes-fixed-pitch)) + ((memq 'italic properties) + (list 'italic 'modus-themes-fixed-pitch)) + ((memq 'bold properties) + (list 'bold 'modus-themes-fixed-pitch)) + (t 'modus-themes-fixed-pitch)) + :background + (cond + ((and (memq 'background properties) + (memq 'intense properties)) + intensebg) + ((memq 'background properties) + subtlebg) + (t + 'unspecified)) + :foreground + (cond + ((and (memq 'background properties) + (memq 'intense properties)) + mainfg) + ((memq 'intense properties) + intensefg) + (t + mainfg))))) + +(defun modus-themes--lang-check (underline subtlefg intensefg intensefg-alt subtlebg intensebg faintfg) + "Conditional use of foreground colors for language checkers. +UNDERLINE is a color-code value for the affected text's underline +property. SUBTLEFG and INTENSEFG follow the same color-coding +pattern and represent a value that is faint or vibrant +respectively. INTENSEFG-ALT is used when the intensity is high. +SUBTLEBG and INTENSEBG are color-coded background colors that +differ in overall intensity. FAINTFG is a nuanced color." + (let ((properties (modus-themes--list-or-warn 'modus-themes-lang-checkers))) + (list :underline + (list :color + (if (memq 'faint properties) + faintfg underline) + :style + (if (memq 'straight-underline properties) + 'line 'wave)) + :background + (cond + ((and (memq 'background properties) + (memq 'faint properties)) + subtlebg) + ((and (memq 'background properties) + (memq 'intense properties)) + intensebg) + ((memq 'background properties) + subtlebg) + ('unspecified)) + :foreground + (cond + ((and (memq 'faint properties) + (memq 'text-also properties)) + faintfg) + ((and (memq 'background properties) + (memq 'intense properties)) + intensefg-alt) + ((memq 'intense properties) + intensefg) + ((memq 'text-also properties) + subtlefg) + ('unspecified))))) + +(defun modus-themes--prompt (mainfg intensefg grayfg subtlebg intensebg intensebg-fg subtlebggray intensebggray) + "Conditional use of colors for text prompt faces. +MAINFG is the prompt's standard foreground. INTENSEFG is a more +prominent alternative to the main foreground, while GRAYFG is a +less luminant shade of gray. + +SUBTLEBG is a subtle accented background that works with either +MAINFG or INTENSEFG. + +INTENSEBG is a more pronounced accented background color that +should be combinable with INTENSEBG-FG. + +SUBTLEBGGRAY and INTENSEBGGRAY are background values. The former +can be combined with GRAYFG, while the latter only works with the +theme's fallback text color." + (let ((properties (modus-themes--list-or-warn 'modus-themes-prompts))) + (list :foreground + (cond + ((and (memq 'gray properties) + (memq 'intense properties)) + 'unspecified) + ((memq 'gray properties) + grayfg) + ((and (memq 'background properties) + (memq 'intense properties)) + intensebg-fg) + ((memq 'intense properties) + intensefg) + (mainfg)) + :background + (cond + ((and (memq 'gray properties) + (memq 'background properties) + (memq 'intense properties)) + intensebggray) + ((and (memq 'gray properties) + (memq 'background properties)) + subtlebggray) + ((and (memq 'background properties) + (memq 'intense properties)) + intensebg) + ((memq 'background properties) + subtlebg) + ('unspecified)) + :inherit + (cond + ((and (memq 'bold properties) + (memq 'italic properties)) + 'bold-italic) + ((memq 'italic properties) + 'italic) + ((memq 'bold properties) + 'bold) + ('unspecified))))) + +(defun modus-themes--paren (normalbg intensebg) + "Conditional use of intense colors for matching parentheses. +NORMALBG should be the special palette color bg-paren-match or +something similar. INTENSEBG must be easier to discern next to +other backgrounds, such as the special palette color +bg-paren-match-intense." + (let ((properties (modus-themes--list-or-warn 'modus-themes-paren-match))) + (list :inherit + (if (memq 'bold properties) + 'bold + 'unspecified) + :background + (if (memq 'intense properties) + intensebg + normalbg) + :underline + (if (memq 'underline properties) + t + nil)))) + +(defun modus-themes--syntax-foreground (fg faint) + "Apply foreground value to code syntax. +FG is the default. FAINT is typically the same color in its +desaturated version." + (let ((properties (modus-themes--list-or-warn 'modus-themes-syntax))) + (list :foreground + (cond + ((memq 'faint properties) + faint) + (fg))))) + +(defun modus-themes--syntax-extra (fg faint alt &optional faint-alt) + "Apply foreground value to code syntax. +FG is the default. FAINT is typically the same color in its +desaturated version. ALT is another hue while optional FAINT-ALT +is its subtle alternative." + (let ((properties (modus-themes--list-or-warn 'modus-themes-syntax))) + (list :foreground + (cond + ((and (memq 'alt-syntax properties) + (memq 'faint properties)) + (or faint-alt alt)) + ((memq 'faint properties) + faint) + ((memq 'alt-syntax properties) + alt) + (fg))))) + +(defun modus-themes--syntax-string (fg faint green alt &optional faint-green faint-alt) + "Apply foreground value to strings in code syntax. +FG is the default. FAINT is typically the same color in its +desaturated version. GREEN is a color variant in that side of +the spectrum. ALT is another hue. Optional FAINT-GREEN is a +subtle alternative to GREEN. Optional FAINT-ALT is a subtle +alternative to ALT." + (let ((properties (modus-themes--list-or-warn 'modus-themes-syntax))) + (list :foreground + (cond + ((and (memq 'faint properties) + (memq 'green-strings properties)) + (or faint-green green)) + ((and (memq 'alt-syntax properties) + (memq 'faint properties)) + (or faint-alt faint)) + ((memq 'faint properties) + faint) + ((memq 'green-strings properties) + green) + ((memq 'alt-syntax properties) + alt) + (fg))))) + +(defun modus-themes--syntax-comment (fg yellow &optional faint-yellow faint) + "Apply foreground value to strings in code syntax. +FG is the default. YELLOW is a color variant of that name while +optional FAINT-YELLOW is its subtle variant. Optional FAINT is +an alternative to the default value." + (let ((properties (modus-themes--list-or-warn 'modus-themes-syntax))) + (list :foreground + (cond + ((and (memq 'faint properties) + (memq 'yellow-comments properties)) + (or faint-yellow yellow)) + ((and (memq 'alt-syntax properties) + (memq 'yellow-comments properties) + (not (memq 'green-strings properties))) + yellow) + ((memq 'yellow-comments properties) + yellow) + ((memq 'faint properties) + (or faint fg)) + (fg))))) + +(defun modus-themes--key-cdr (key alist) + "Get cdr of KEY in ALIST." + (cdr (assoc key alist))) + +(define-obsolete-variable-alias + 'modus-themes--heading-weights + 'modus-themes-weights + "2.1.0") + +(defconst modus-themes-weights + '( thin ultralight extralight light semilight regular medium + semibold bold heavy extrabold ultrabold) + "List of font weights.") + +(defun modus-themes--weight (list) + "Search for `modus-themes--heading' weight in LIST." + (catch 'found + (dolist (elt list) + (when (memq elt modus-themes-weights) + (throw 'found elt))))) + +(defun modus-themes--heading (level fg fg-alt bg bg-gray border) + "Conditional styles for `modus-themes-headings'. + +LEVEL is the heading's position in their order. FG is the +default text color. FG-ALT is an accented, more saturated value +than the default. BG is a nuanced, typically accented, +background that can work well with either of the foreground +values. BG-GRAY is a gray background. BORDER is a color value +that combines well with the background and foreground." + (let* ((key (modus-themes--key-cdr level modus-themes-headings)) + (style (or key (modus-themes--key-cdr t modus-themes-headings))) + (style-listp (listp style)) + (properties style) + (var (when (memq 'variable-pitch properties) 'variable-pitch)) + (varbold (if var + (append (list 'bold) (list var)) + 'bold)) + (weight (when style-listp (modus-themes--weight style)))) + (list :inherit + (cond + ;; `no-bold' is for backward compatibility because we cannot + ;; deprecate a variable's value. + ((or weight (memq 'no-bold properties)) + var) + (varbold)) + :background + (cond + ((and (memq 'monochrome properties) + (memq 'background properties)) + bg-gray) + ((memq 'background properties) + bg) + ('unspecified)) + :foreground + (cond + ((memq 'monochrome properties) + 'unspecified) + ((memq 'rainbow properties) + fg-alt) + (fg)) + :height + (modus-themes--property-lookup properties 'height #'floatp 'unspecified) + :weight + (or weight 'unspecified) + :overline + (if (memq 'overline properties) + border + 'unspecified)))) + +(defun modus-themes--agenda-structure (fg) + "Control the style of the Org agenda structure. +FG is the foreground color to use." + (let* ((properties (modus-themes--key-cdr 'header-block modus-themes-org-agenda)) + (weight (modus-themes--weight properties))) + (list :inherit + (cond + ((and weight (memq 'variable-pitch properties)) + 'variable-pitch) + (weight 'unspecified) + ((memq 'variable-pitch properties) + (list 'bold 'variable-pitch)) + ('bold)) + :weight + (or weight 'unspecified) + :height + (cond ((memq 'no-scale properties) 'unspecified) + ((modus-themes--property-lookup properties 'height #'floatp 1.15))) + :foreground fg))) + +(defun modus-themes--agenda-date (defaultfg grayscalefg &optional workaholicfg grayscaleworkaholicfg bg bold ul) + "Control the style of date headings in Org agenda buffers. +DEFAULTFG is the original accent color for the foreground. +GRAYSCALEFG is a neutral color. Optional WORKAHOLICFG and +GRAYSCALEWORKAHOLICFG are alternative foreground colors. +Optional BG is a background color. Optional BOLD applies a bold +weight. Optional UL applies an underline." + (let ((properties (modus-themes--key-cdr 'header-date modus-themes-org-agenda))) + (list :inherit + (cond + ((or (memq 'bold-all properties) + (and bold (memq 'bold-today properties))) + 'bold) + (t + 'unspecified)) + :background + (cond + ((memq 'underline-today properties) + 'unspecified) + ((or bg 'unspecified))) + :foreground + (cond + ((and (memq 'grayscale properties) + (memq 'workaholic properties)) + (or grayscaleworkaholicfg grayscalefg)) + ((memq 'grayscale properties) + grayscalefg) + ((memq 'workaholic properties) + (or workaholicfg defaultfg)) + (t + defaultfg)) + :height + (modus-themes--property-lookup properties 'height #'floatp 'unspecified) + :underline + (if (and ul (memq 'underline-today properties)) + t + 'unspecified)))) + +(defun modus-themes--agenda-event (fg-accent &optional varied) + "Control the style of the Org agenda events. +FG-ACCENT is the accent color to use. Optional VARIED is a +toggle to behave in accordance with the semantics of the `varied' +property that the `event' key accepts in +`modus-themes-org-agenda'." + (let ((properties (modus-themes--key-cdr 'event modus-themes-org-agenda))) + (list :foreground + (cond + ((or (and (memq 'varied properties) varied) + (and (memq 'accented properties) + (memq 'varied properties) + varied)) + 'unspecified) + ((memq 'accented properties) + fg-accent) + ('unspecified)) + :inherit + (cond + ((and (memq 'italic properties) + (memq 'varied properties) + varied) + '(shadow italic)) + ((and (memq 'accented properties) + (memq 'varied properties) + varied) + 'shadow) + ((or (and (memq 'varied properties) varied) + (and (memq 'italic properties) varied)) + '(shadow italic)) + ((and (memq 'italic properties) + (not (memq 'varied properties))) + '(shadow italic)) + ('shadow))))) + +(defun modus-themes--agenda-scheduled (defaultfg uniformfg rainbowfg) + "Control the style of the Org agenda scheduled tasks. +DEFAULTFG is an accented foreground color that is meant to +differentiate between past or present and future tasks. +UNIFORMFG is a more subtle color that eliminates the color coding +for scheduled tasks. RAINBOWFG is a prominent accent value that +clearly distinguishes past, present, future tasks." + (pcase (modus-themes--key-cdr 'scheduled modus-themes-org-agenda) + ('uniform (list :foreground uniformfg)) + ('rainbow (list :foreground rainbowfg)) + (_ (list :foreground defaultfg)))) + +(defun modus-themes--agenda-habit (default traffic simple &optional default-d traffic-d simple-d) + "Specify background values for `modus-themes-org-agenda' habits. +DEFAULT is the original foregrounc color. TRAFFIC is to be used +when the traffic-light style is applied, while SIMPLE corresponds +to the simplified style. + +Optional DEFAULT-D, TRAFFIC-D, SIMPLE-D are alternatives to the +main colors, meant for dopia when `modus-themes-deuteranopia' is +non-nil." + (let ((habit (modus-themes--key-cdr 'habit modus-themes-org-agenda))) + (cond + ((and modus-themes-deuteranopia (null habit)) + (list :background (or default-d default))) + ((and modus-themes-deuteranopia (eq habit 'traffic-light)) + (list :background (or traffic-d traffic))) + ((and modus-themes-deuteranopia (eq habit 'simplified)) + (list :background (or simple-d simple))) + (t + (pcase habit + ('traffic-light (list :background traffic)) + ('simplified (list :background simple)) + (_ (list :background default))))))) + +(defun modus-themes--org-block (bgblk fgdefault &optional fgblk) + "Conditionally set the background of Org blocks. +BGBLK applies to a distinct neutral background. Else blocks have +no background of their own (the default), so they look the same +as the rest of the buffer. FGDEFAULT is used when no distinct +background is present. While optional FGBLK specifies a +foreground value that can be combined with BGBLK. + +`modus-themes-org-blocks' also accepts `tinted-background' (alias +`rainbow') as a value which applies to `org-src-block-faces' (see +the theme's source code)." + (if (or (eq modus-themes-org-blocks 'gray-background) + (eq modus-themes-org-blocks 'grayscale) + (eq modus-themes-org-blocks 'greyscale)) + (list :background bgblk :foreground (or fgblk fgdefault) :extend t) + (list :background 'unspecified :foreground fgdefault))) + +(defun modus-themes--org-block-delim (bgaccent fgaccent bg fg) + "Conditionally set the styles of Org block delimiters. +BG, FG, BGACCENT, FGACCENT apply a background and foreground +color respectively. + +The former pair is a grayscale combination that should be more +distinct than the background of the block. It is applied to the +default styles or when `modus-themes-org-blocks' is set +to `grayscale' (or `greyscale'). + +The latter pair should be more subtle than the background of the +block, as it is used when `modus-themes-org-blocks' is +set to `rainbow'." + (pcase modus-themes-org-blocks + ('gray-background (list :background bg :foreground fg :extend t)) + ('grayscale (list :background bg :foreground fg :extend t)) + ('greyscale (list :background bg :foreground fg :extend t)) + ('tinted-background (list :background bgaccent :foreground fgaccent :extend nil)) + ('rainbow (list :background bgaccent :foreground fgaccent :extend nil)) + (_ (list :foreground fg :extend nil)))) + +(defun modus-themes--mode-line-attrs + (fg bg fg-alt bg-alt fg-accent bg-accent border border-3d &optional alt-style fg-distant) + "Color combinations for `modus-themes-mode-line'. + +FG and BG are the default colors. FG-ALT and BG-ALT are meant to +accommodate the options for a 3D mode line or a `moody' compliant +one. FG-ACCENT and BG-ACCENT are used for all variants. BORDER +applies to all permutations of the mode line, except the +three-dimensional effect, where BORDER-3D is used instead. + +Optional ALT-STYLE applies an appropriate style to the mode +line's box property. + +Optional FG-DISTANT should be close to the main background +values. It is intended to be used as a distant-foreground +property." + (let* ((properties (modus-themes--list-or-warn 'modus-themes-mode-line)) + (padding (modus-themes--property-lookup properties 'padding #'natnump 1)) + (height (modus-themes--property-lookup properties 'height #'floatp 'unspecified)) + (padded (> padding 1)) + (base (cond ((memq 'accented properties) + (cons fg-accent bg-accent)) + ((and (or (memq 'moody properties) + (memq '3d properties)) + (not (memq 'borderless properties))) + (cons fg-alt bg-alt)) + ((cons fg bg)))) + (line (cond ((not (or (memq 'moody properties) padded)) + 'unspecified) + ((and (not (memq 'moody properties)) + padded + (memq 'borderless properties)) + 'unspecified) + ((and (memq 'borderless properties) + (memq 'accented properties)) + bg-accent) + ((memq 'borderless properties) + bg) + (border)))) + (list :foreground (car base) + :background (cdr base) + :height height + :box + (cond ((memq 'moody properties) + 'unspecified) + ((and (memq '3d properties) padded) + (list :line-width padding + :color + (cond ((and (memq 'accented properties) + (memq 'borderless properties)) + bg-accent) + ((or (memq 'accented properties) + (memq 'borderless properties)) + bg) + (bg-alt)) + :style (when alt-style 'released-button))) + ((and (memq 'accented properties) padded) + (list :line-width padding :color bg-accent)) + ((memq '3d properties) + (list :line-width padding + :color + (cond ((and (memq 'accented properties) + (memq 'borderless properties)) + bg-accent) + ((memq 'borderless properties) bg) + (border-3d)) + :style (when alt-style 'released-button))) + ((and (memq 'accented properties) + (memq 'borderless properties)) + (list :line-width padding :color bg-accent)) + ((or (memq 'borderless properties) padded) + (list :line-width padding :color bg)) + (border)) + :overline line + :underline line + :distant-foreground + (if (memq 'moody properties) + fg-distant + 'unspecified)))) + +;; Basically this is just for the keycast key indicator. +(defun modus-themes--mode-line-padded-box (color) + "Set padding of mode line box attribute with given COLOR." + (list :box (list :color color + :line-width + (or (cl-loop + for x in modus-themes-mode-line + if (natnump x) return x) + 1)))) + +(defun modus-themes--diff (mainbg mainfg altbg altfg &optional deubg deufg deualtbg deualtfg bg-only-fg) + "Color combinations for `modus-themes-diffs'. + +MAINBG must be one of the dedicated backgrounds for diffs while +MAINFG must be the same for the foreground. + +ALTBG needs to be a slightly accented background that is meant to +be combined with ALTFG. Both must be less intense than MAINBG +and MAINFG respectively. + +DEUBG and DEUFG must be combinations of colors that account for +red-green color defficiency (deuteranopia). They are the +equivalent of MAINBG and MAINFG. + +DEUALTBG and DEUALTFG are the equivalent of ALTBG and ALTFG for +deuteranopia. + +Optional non-nil BG-ONLY-FG applies ALTFG else leaves the +foreground unspecified." + (if modus-themes-deuteranopia + (pcase modus-themes-diffs + ('desaturated (list :background (or deualtbg altbg) :foreground (or deualtfg altfg))) + ('bg-only (list :background (or deualtbg altbg) :foreground (if bg-only-fg (or deualtfg altfg) 'unspecified))) + (_ (list :background (or deubg mainbg) :foreground (or deufg mainfg)))) + (pcase modus-themes-diffs + ('desaturated (list :background altbg :foreground altfg)) + ('bg-only (list :background altbg :foreground (if bg-only-fg altfg 'unspecified))) + (_ (list :background mainbg :foreground mainfg))))) + +(defun modus-themes--deuteran (deuteran main) + "Determine whether to color-code success as DEUTERAN or MAIN." + (if modus-themes-deuteranopia + (list deuteran) + (list main))) + +(make-obsolete 'modus-themes--completion 'modus-themes--completion-line "2.3.0") +(make-obsolete 'modus-themes--completion 'modus-themes--completion-match "2.3.0") + +(defun modus-themes--completion-line (key bg fg bgintense fgintense &optional bgaccent bgaccentintense) + "Styles for `modus-themes-completions'. +KEY is the key of a cons cell. BG and FG are the main colors. +BGINTENSE works with the main foreground. FGINTENSE works on its +own. BGACCENT and BGACCENTINTENSE are colorful variants of the +other backgrounds." + (let* ((var (modus-themes--list-or-warn 'modus-themes-completions)) + (properties (or (alist-get key var) (alist-get t var))) + (popup (eq key 'popup)) + (selection (eq key 'selection)) + (line (or popup selection)) + (text (memq 'text-also properties)) + (accented (memq 'accented properties)) + (intense (memq 'intense properties)) + (italic (memq 'italic properties)) + (weight (modus-themes--weight properties)) + (bold (when (and weight (eq weight 'bold)) 'bold))) + (list + :inherit + (cond + ((and italic weight (not (eq weight 'bold))) + 'italic) + ((and weight (not (eq weight 'bold))) + 'unspecified) + (italic 'bold-italic) + ('bold)) + :background + (cond + ((and accented intense line) + bgaccentintense) + ((and accented line) + bgaccent) + (intense bgintense) + (bg)) + :foreground + (cond + ((and line text intense) + fgintense) + ((and line text) + fg) + ('unspecified)) + :underline + (if (memq 'underline properties) t 'unspecified) + :weight + (if (and weight (null bold)) weight 'unspecified)))) + +(defun modus-themes--completion-match (key bg fg bgintense fgintense) + "Styles for `modus-themes-completions'. +KEY is the key of a cons cell. BG and FG are the main colors. +BGINTENSE works with the main foreground. FGINTENSE works on its +own." + (let* ((var (modus-themes--list-or-warn 'modus-themes-completions)) + (properties (or (alist-get key var) (alist-get t var))) + (background (memq 'background properties)) + (intense (memq 'intense properties)) + (italic (memq 'italic properties)) + (weight (modus-themes--weight properties)) + (bold (when (and weight (eq weight 'bold)) 'bold))) + (list + :inherit + (cond + ((and italic weight (not (eq weight 'bold))) + 'italic) + ((and weight (not (eq weight 'bold))) + 'unspecified) + (italic 'bold-italic) + ('bold)) + :background + (cond + ((and background intense) + bgintense) + (background bg) + ('unspecified)) + :foreground + (cond + ((and background intense) + 'unspecified) + (background fg) + (intense fgintense) + (fg)) + :underline + (if (memq 'underline properties) t 'unspecified) + :weight + (if (and weight (null bold)) weight 'unspecified)))) + +(defun modus-themes--link (fg fgfaint underline bg bgneutral) + "Conditional application of link styles. +FG is the link's default color for its text and underline +property. FGFAINT is a desaturated color for the text and +underline. UNDERLINE is a gray color only for the undeline. BG +is a background color and BGNEUTRAL is its fallback value." + (let ((properties (modus-themes--list-or-warn 'modus-themes-links))) + (list :inherit + (cond + ((and (memq 'bold properties) + (memq 'italic properties)) + 'bold-italic) + ((memq 'italic properties) + 'italic) + ((memq 'bold properties) + 'bold) + ('unspecified)) + :background + (cond + ((and (memq 'no-color properties) + (memq 'no-underline properties)) + bgneutral) + ((memq 'background properties) + bg) + ('unspecified)) + :foreground + (cond + ((memq 'no-color properties) + 'unspecified) + ((memq 'faint properties) + fgfaint) + (fg)) + :underline + (cond + ((memq 'no-underline properties) + 'unspecified) + ((memq 'neutral-underline properties) + underline) + (t))))) + +(defun modus-themes--link-color (fg fgfaint &optional neutralfg) + "Extend `modus-themes--link'. +FG is the main accented foreground. FGFAINT is also accented, +yet desaturated. Optional NEUTRALFG is a gray value." + (let ((properties (modus-themes--list-or-warn 'modus-themes-links))) + (list :foreground + (cond + ((memq 'no-color properties) + (or neutralfg 'unspecified)) + ((memq 'faint properties) + fgfaint) + (fg)) + :underline + (cond + ((memq 'no-underline properties) + 'unspecified) + ((memq 'neutral-underline properties) + (or neutralfg 'unspecified)) + (t))))) + +(defun modus-themes--region (bg fg bgsubtle bgaccent bgaccentsubtle) + "Apply `modus-themes-region' styles. + +BG and FG are the main values that are used by default. BGSUBTLE +is a subtle background value that can be combined with all colors +used to fontify text and code syntax. BGACCENT is a colored +background that combines well with FG. BGACCENTSUBTLE can be +combined with all colors used to fontify text." + (let ((properties (modus-themes--list-or-warn 'modus-themes-region))) + (list :background + (cond + ((and (memq 'accented properties) + (memq 'bg-only properties)) + bgaccentsubtle) + ((memq 'accented properties) + bgaccent) + ((memq 'bg-only properties) + bgsubtle) + (bg)) + :foreground + (cond + ((and (memq 'accented properties) + (memq 'bg-only properties)) + 'unspecified) + ((memq 'bg-only properties) + 'unspecified) + (fg)) + :extend + (cond + ((memq 'no-extend properties) + nil) + (t))))) + +(defun modus-themes--hl-line + (bgdefault bgintense bgaccent bgaccentsubtle lineneutral lineaccent lineneutralintense lineaccentintense) + "Apply `modus-themes-hl-line' styles. + +BGDEFAULT is a subtle neutral background. BGINTENSE is like the +default, but more prominent. BGACCENT is a prominent accented +background, while BGACCENTSUBTLE is more subtle. LINENEUTRAL and +LINEACCENT are color values that can remain distinct against the +buffer's possible backgrounds: the former is neutral, the latter +is accented. LINENEUTRALINTENSE and LINEACCENTINTENSE are their +more prominent alternatives." + (let ((properties (modus-themes--list-or-warn 'modus-themes-hl-line))) + (list :background + (cond + ((and (memq 'intense properties) + (memq 'accented properties)) + bgaccent) + ((memq 'accented properties) + bgaccentsubtle) + ((memq 'intense properties) + bgintense) + (bgdefault)) + :underline + (cond + ((and (memq 'intense properties) + (memq 'accented properties) + (memq 'underline properties)) + lineaccentintense) + ((and (memq 'accented properties) + (memq 'underline properties)) + lineaccent) + ((and (memq 'intense properties) + (memq 'underline properties)) + lineneutralintense) + ((or (memq 'no-background properties) + (memq 'underline properties)) + lineneutral) + ('unspecified))))) + +(defun modus-themes--mail-cite (mainfg intensefg subtlefg) + "Combinations for `modus-themes-mail-citations'. + +MAINFG is an accented foreground value. SUBTLEFG is its +desaturated counterpart. INTENSEFG is a more saturated variant." + (pcase modus-themes-mail-citations + ('monochrome (list :inherit 'shadow)) + ('intense (list :foreground intensefg)) + ('faint (list :foreground subtlefg)) + ('desaturated (list :foreground subtlefg)) + (_ (list :foreground mainfg)))) + +(defun modus-themes--tab (bg &optional bgaccent fg fgaccent box-p bold-p var-p) + "Helper function for tabs. +BG is the default background, while BGACCENT is its more colorful +alternative. Optional FG is a foreground color that combines +with BG. Same principle FGACCENT. + +BOX-P and BOLD-P determine the use of a box property and the +application of a bold weight, respectively. VAR-P controls the +application of a variable-pitch font." + (let ((background (if modus-themes-tabs-accented (or bgaccent bg) bg)) + (foreground (if modus-themes-tabs-accented (or fgaccent fg) fg))) + (list + :inherit (cond + ((and bold-p var-p) + (if modus-themes-variable-pitch-ui + '(variable-pitch bold) + '(bold))) + (bold-p 'bold) + (var-p (when modus-themes-variable-pitch-ui 'variable-pitch)) + ('unspecified)) + :background background + :foreground (or foreground 'unspecified) + :box (if box-p (list :line-width 2 :color background) 'unspecified)))) + +(defun modus-themes--button (bg bgfaint bgaccent bgaccentfaint border &optional pressed-button-p) + "Apply `modus-themes-box-buttons' styles. + +BG is the main background. BGFAINT is its subtle alternative. +BGACCENT is its accented variant and BGACCENTFAINT is the same +but less intense. BORDER is the color around the box. + +When optional PRESSED-BUTTON-P is non-nil, the box uses the +pressed button style, else the released button." + (let* ((properties modus-themes-box-buttons) + (weight (modus-themes--weight properties))) + (list :inherit + (cond + ((and (memq 'variable-pitch properties) + (eq weight 'bold)) + (list 'bold 'variable-pitch)) + ((memq 'variable-pitch properties) + 'variable-pitch) + ((eq weight 'bold) + 'bold) + ('unspecified)) + :background + (cond + ((and (memq 'accented properties) + (memq 'faint properties) + bgaccentfaint)) + ((memq 'faint properties) + bgfaint) + ((memq 'accented properties) + bgaccent) + (bg)) + :box + (cond + ((memq 'underline properties) + 'unspecified) + ((memq 'flat properties) + (list :line-width -1 :color border)) + ((list :line-width -1 + :style (if pressed-button-p + 'pressed-button + 'released-button) + :color border))) + :weight + (cond + ((eq weight 'bold) + 'unspecified) ; we :inherit the `bold' face above + (weight weight) + ('unspecified)) + :height + (modus-themes--property-lookup properties 'height #'floatp 'unspecified) + :underline + (if (memq 'underline properties) + t + 'unspecified)))) + + + +;;;; Utilities for DIY users + +;;;;; List colors (a variant of M-x list-colors-display) + +(defun modus-themes--list-colors-render (buffer theme &rest _) + "Render colors in BUFFER from THEME. +Routine for `modus-themes-list-colors'." + (let ((palette (seq-uniq (modus-themes--palette theme) + (lambda (x y) + (eq (car x) (car y))))) + (current-buffer buffer) + (current-theme theme)) + (with-help-window buffer + (with-current-buffer standard-output + (erase-buffer) + (when (<= (display-color-cells) 256) + (insert (concat "Your display terminal may not render all color previews!\n" + "It seems to only support <= 256 colors.\n\n")) + (put-text-property (point-min) (point) 'face 'warning)) + ;; We need this to properly render the first line. + (insert " ") + (dolist (cell palette) + (let* ((name (car cell)) + (color (cdr cell)) + (fg (readable-foreground-color color)) + (pad (make-string 5 ?\s))) + (let ((old-point (point))) + (insert (format "%s %s" color pad)) + (put-text-property old-point (point) 'face `( :foreground ,color))) + (let ((old-point (point))) + (insert (format " %s %s %s\n" color pad name)) + (put-text-property old-point (point) + 'face `( :background ,color + :foreground ,fg + :extend t))) + ;; We need this to properly render the last line. + (insert " "))) + (setq-local revert-buffer-function + (lambda (_ignore-auto _noconfirm) + (modus-themes--list-colors-render current-buffer current-theme))))))) + +(defvar modus-themes--list-colors-prompt-history '() + "Minibuffer history for `modus-themes--list-colors-prompt'.") + +(defun modus-themes--list-colors-prompt () + "Prompt for Modus theme. +Helper function for `modus-themes-list-colors'." + (let ((def (format "%s" (modus-themes--current-theme)))) + (completing-read + (format "Use palette from theme [%s]: " def) + '(modus-operandi modus-vivendi) nil t nil + 'modus-themes--list-colors-prompt-history def))) + +(defun modus-themes-list-colors (theme) + "Preview palette of the Modus THEME of choice." + (interactive (list (intern (modus-themes--list-colors-prompt)))) + (modus-themes--list-colors-render + (format "*%s-list-colors*" theme) + theme)) + +(defun modus-themes-list-colors-current () + "Call `modus-themes-list-colors' for the current Modus theme." + (interactive) + (modus-themes-list-colors (modus-themes--current-theme))) + +;;;;; Formula to measure relative luminance + +;; This is the WCAG formula: https://www.w3.org/TR/WCAG20-TECHS/G18.html +(defun modus-themes-wcag-formula (hex) + "Get WCAG value of color value HEX. +The value is defined in hexadecimal RGB notation, such as those in +`modus-themes-operandi-colors' and `modus-themes-vivendi-colors'." + (cl-loop for k in '(0.2126 0.7152 0.0722) + for x in (color-name-to-rgb hex) + sum (* k (if (<= x 0.03928) + (/ x 12.92) + (expt (/ (+ x 0.055) 1.055) 2.4))))) + +;;;###autoload +(defun modus-themes-contrast (c1 c2) + "Measure WCAG contrast ratio between C1 and C2. +C1 and C2 are color values written in hexadecimal RGB." + (let ((ct (/ (+ (modus-themes-wcag-formula c1) 0.05) + (+ (modus-themes-wcag-formula c2) 0.05)))) + (max ct (/ ct)))) + +;;;;; Retrieve colors from the themes + +(defun modus-themes-current-palette () + "Return current color palette." + (modus-themes--palette (modus-themes--current-theme))) + +;;;###autoload +(defun modus-themes-color (color) + "Return color value for COLOR from current palette. +COLOR is a key in `modus-themes-operandi-colors' or +`modus-themes-vivendi-colors'." + (alist-get color (modus-themes-current-palette))) + +;;;###autoload +(defun modus-themes-color-alts (light-color dark-color) + "Return color value from current palette. +When Modus Operandi is enabled, return color value for color +LIGHT-COLOR. When Modus Vivendi is enabled, return color value +for DARK-COLOR. LIGHT-COLOR and DARK-COLOR are keys in +`modus-themes-operandi-colors' or `modus-themes-vivendi-colors'." + (let* ((theme (modus-themes--current-theme)) + (color (pcase theme + ('modus-operandi light-color) + ('modus-vivendi dark-color) + (_theme + (error "'%s' is not a Modus theme" theme))))) + (alist-get color (modus-themes--palette theme)))) + +(defmacro modus-themes-with-colors (&rest body) + "Evaluate BODY with colors from current palette bound. +For colors bound, see `modus-themes-operandi-colors' or +`modus-themes-vivendi-colors'." + (declare (indent 0)) + (let ((palette-sym (gensym)) + (colors (mapcar #'car modus-themes-operandi-colors))) + `(let* ((class '((class color) (min-colors 89))) + (,palette-sym (modus-themes-current-palette)) + ,@(mapcar (lambda (color) + (list color `(alist-get ',color ,palette-sym))) + colors)) + (ignore class ,@colors) ; Silence unused variable warnings + ,@body))) + + + +;;;; Commands + +;;;###autoload +(defun modus-themes-load-themes () + "Ensure that the Modus themes are in `custom-enabled-themes'. + +This function is intended for use in package declarations such as +those defined with the help of `use-package'. The idea is to add +this function to the `:init' stage of the package's loading, so +that subsequent calls that assume the presence of a loaded theme, +like `modus-themes-toggle' or `modus-themes-load-operandi', will +continue to work as intended even if they are lazy-loaded (such +as when they are declared in the `:config' phase)." + (unless (or (custom-theme-p 'modus-operandi) + (custom-theme-p 'modus-vivendi)) + (load-theme 'modus-operandi t t) + (load-theme 'modus-vivendi t t))) + +(defvar modus-themes-after-load-theme-hook nil + "Hook that runs after the `modus-themes-toggle' routines.") + +;;;###autoload +(defun modus-themes-load-operandi () + "Load `modus-operandi' and disable `modus-vivendi'. +Also run `modus-themes-after-load-theme-hook'." + (interactive) + (disable-theme 'modus-vivendi) + (load-theme 'modus-operandi t) + (run-hooks 'modus-themes-after-load-theme-hook)) + +;;;###autoload +(defun modus-themes-load-vivendi () + "Load `modus-vivendi' and disable `modus-operandi'. +Also run `modus-themes-after-load-theme-hook'." + (interactive) + (disable-theme 'modus-operandi) + (load-theme 'modus-vivendi t) + (run-hooks 'modus-themes-after-load-theme-hook)) + +(defun modus-themes--load-prompt () + "Helper for `modus-themes-toggle'." + (let ((theme + (intern + (completing-read "Load Modus theme (will disable all others): " + '(modus-operandi modus-vivendi) nil t)))) + (mapc #'disable-theme custom-enabled-themes) + (pcase theme + ('modus-operandi (modus-themes-load-operandi)) + ('modus-vivendi (modus-themes-load-vivendi))))) + +;;;###autoload +(defun modus-themes-toggle () + "Toggle between `modus-operandi' and `modus-vivendi' themes. +Also runs `modus-themes-after-load-theme-hook' at its last stage +by virtue of calling either of `modus-themes-load-operandi' and +`modus-themes-load-vivendi' functions." + (interactive) + (modus-themes-load-themes) + (pcase (modus-themes--current-theme) + ('modus-operandi (modus-themes-load-vivendi)) + ('modus-vivendi (modus-themes-load-operandi)) + (_ (modus-themes--load-prompt)))) + + + +;;;; Face specifications + +(defconst modus-themes-faces + '( +;;;; custom faces + ;; these bespoke faces are inherited by other constructs below +;;;;; subtle colored backgrounds + `(modus-themes-subtle-red ((,class :background ,red-subtle-bg :foreground ,fg-dim))) + `(modus-themes-subtle-green ((,class :background ,green-subtle-bg :foreground ,fg-dim))) + `(modus-themes-subtle-yellow ((,class :background ,yellow-subtle-bg :foreground ,fg-dim))) + `(modus-themes-subtle-blue ((,class :background ,blue-subtle-bg :foreground ,fg-dim))) + `(modus-themes-subtle-magenta ((,class :background ,magenta-subtle-bg :foreground ,fg-dim))) + `(modus-themes-subtle-cyan ((,class :background ,cyan-subtle-bg :foreground ,fg-dim))) + `(modus-themes-subtle-neutral ((,class :background ,bg-inactive :foreground ,fg-inactive))) +;;;;; intense colored backgrounds + `(modus-themes-intense-red ((,class :background ,red-intense-bg :foreground ,fg-main))) + `(modus-themes-intense-green ((,class :background ,green-intense-bg :foreground ,fg-main))) + `(modus-themes-intense-yellow ((,class :background ,yellow-intense-bg :foreground ,fg-main))) + `(modus-themes-intense-blue ((,class :background ,blue-intense-bg :foreground ,fg-main))) + `(modus-themes-intense-magenta ((,class :background ,magenta-intense-bg :foreground ,fg-main))) + `(modus-themes-intense-cyan ((,class :background ,cyan-intense-bg :foreground ,fg-main))) + `(modus-themes-intense-neutral ((,class :background ,bg-active :foreground ,fg-main))) +;;;;; refined background and foreground combinations + ;; general purpose styles that use an accented foreground against an + ;; accented background + `(modus-themes-refine-red ((,class :background ,red-refine-bg :foreground ,red-refine-fg))) + `(modus-themes-refine-green ((,class :background ,green-refine-bg :foreground ,green-refine-fg))) + `(modus-themes-refine-yellow ((,class :background ,yellow-refine-bg :foreground ,yellow-refine-fg))) + `(modus-themes-refine-blue ((,class :background ,blue-refine-bg :foreground ,blue-refine-fg))) + `(modus-themes-refine-magenta ((,class :background ,magenta-refine-bg :foreground ,magenta-refine-fg))) + `(modus-themes-refine-cyan ((,class :background ,cyan-refine-bg :foreground ,cyan-refine-fg))) +;;;;; "active" combinations, mostly for use on the mode line + `(modus-themes-active-red ((,class :background ,red-active :foreground ,bg-active))) + `(modus-themes-active-green ((,class :background ,green-active :foreground ,bg-active))) + `(modus-themes-active-yellow ((,class :background ,yellow-active :foreground ,bg-active))) + `(modus-themes-active-blue ((,class :background ,blue-active :foreground ,bg-active))) + `(modus-themes-active-magenta ((,class :background ,magenta-active :foreground ,bg-active))) + `(modus-themes-active-cyan ((,class :background ,cyan-active :foreground ,bg-active))) +;;;;; nuanced backgrounds + ;; useful for adding an accented background that is suitable for all + ;; main foreground colors (intended for use in Org source blocks) + `(modus-themes-nuanced-red ((,class :background ,red-nuanced-bg :extend t))) + `(modus-themes-nuanced-green ((,class :background ,green-nuanced-bg :extend t))) + `(modus-themes-nuanced-yellow ((,class :background ,yellow-nuanced-bg :extend t))) + `(modus-themes-nuanced-blue ((,class :background ,blue-nuanced-bg :extend t))) + `(modus-themes-nuanced-magenta ((,class :background ,magenta-nuanced-bg :extend t))) + `(modus-themes-nuanced-cyan ((,class :background ,cyan-nuanced-bg :extend t))) +;;;;; fringe-specific combinations + `(modus-themes-fringe-red ((,class :background ,red-fringe-bg :foreground ,fg-main))) + `(modus-themes-fringe-green ((,class :background ,green-fringe-bg :foreground ,fg-main))) + `(modus-themes-fringe-yellow ((,class :background ,yellow-fringe-bg :foreground ,fg-main))) + `(modus-themes-fringe-blue ((,class :background ,blue-fringe-bg :foreground ,fg-main))) + `(modus-themes-fringe-magenta ((,class :background ,magenta-fringe-bg :foreground ,fg-main))) + `(modus-themes-fringe-cyan ((,class :background ,cyan-fringe-bg :foreground ,fg-main))) +;;;;; special base values + ;; these are closer to the grayscale than the accents defined above + ;; and should only be used when the next closest alternative would be + ;; a grayscale value than an accented one + `(modus-themes-special-cold ((,class :background ,bg-special-cold :foreground ,fg-special-cold))) + `(modus-themes-special-mild ((,class :background ,bg-special-mild :foreground ,fg-special-mild))) + `(modus-themes-special-warm ((,class :background ,bg-special-warm :foreground ,fg-special-warm))) + `(modus-themes-special-calm ((,class :background ,bg-special-calm :foreground ,fg-special-calm))) +;;;;; diff-specific combinations + ;; intended for `diff-mode' or equivalent + `(modus-themes-diff-added + ((,class ,@(modus-themes--diff + bg-diff-focus-added fg-diff-focus-added + green-nuanced-bg fg-diff-added + bg-diff-focus-added-deuteran fg-diff-focus-added-deuteran + blue-nuanced-bg fg-diff-added-deuteran)))) + `(modus-themes-diff-changed + ((,class ,@(modus-themes--diff + bg-diff-focus-changed fg-diff-focus-changed + yellow-nuanced-bg fg-diff-changed)))) + `(modus-themes-diff-removed + ((,class ,@(modus-themes--diff + bg-diff-focus-removed fg-diff-focus-removed + red-nuanced-bg fg-diff-removed)))) + `(modus-themes-diff-refine-added + ((,class ,@(modus-themes--diff + bg-diff-refine-added fg-diff-refine-added + bg-diff-focus-added fg-diff-focus-added + bg-diff-refine-added-deuteran fg-diff-refine-added-deuteran + bg-diff-focus-added-deuteran fg-diff-focus-added-deuteran)))) + `(modus-themes-diff-refine-changed + ((,class ,@(modus-themes--diff + bg-diff-refine-changed fg-diff-refine-changed + bg-diff-focus-changed fg-diff-focus-changed)))) + `(modus-themes-diff-refine-removed + ((,class ,@(modus-themes--diff + bg-diff-refine-removed fg-diff-refine-removed + bg-diff-focus-removed fg-diff-focus-removed)))) + `(modus-themes-diff-focus-added + ((,class ,@(modus-themes--diff + bg-diff-focus-added fg-diff-focus-added + bg-diff-added fg-diff-added + bg-diff-focus-added-deuteran fg-diff-focus-added-deuteran + bg-diff-added-deuteran fg-diff-added-deuteran)))) + `(modus-themes-diff-focus-changed + ((,class ,@(modus-themes--diff + bg-diff-focus-changed fg-diff-focus-changed + bg-diff-changed fg-diff-changed)))) + `(modus-themes-diff-focus-removed + ((,class ,@(modus-themes--diff + bg-diff-focus-removed fg-diff-focus-removed + bg-diff-removed fg-diff-removed)))) + `(modus-themes-diff-heading + ((,class ,@(modus-themes--diff + bg-diff-heading fg-diff-heading + cyan-nuanced-bg cyan-nuanced-fg + bg-header fg-main + bg-header fg-main + t)))) +;;;;; deuteranopia-specific + `(modus-themes-grue ((,class :foreground ,@(modus-themes--deuteran blue green)))) + `(modus-themes-grue-active ((,class :foreground ,@(modus-themes--deuteran blue-active green-active)))) + `(modus-themes-grue-nuanced ((,class :foreground ,@(modus-themes--deuteran blue-nuanced-fg green-nuanced-fg)))) + `(modus-themes-grue-background-active ((,class :inherit ,@(modus-themes--deuteran + 'modus-themes-fringe-blue + 'modus-themes-fringe-green)))) + `(modus-themes-grue-background-intense ((,class :inherit ,@(modus-themes--deuteran + 'modus-themes-intense-blue + 'modus-themes-intense-green)))) + `(modus-themes-grue-background-subtle ((,class :inherit ,@(modus-themes--deuteran + 'modus-themes-subtle-blue + 'modus-themes-subtle-green)))) + `(modus-themes-grue-background-subtle ((,class :inherit ,@(modus-themes--deuteran + 'modus-themes-refine-blue + 'modus-themes-refine-green)))) +;;;;; mark indicators + ;; color combinations intended for Dired, Ibuffer, or equivalent + `(modus-themes-pseudo-header ((,class :inherit bold :foreground ,fg-main))) + `(modus-themes-mark-alt ((,class :inherit bold :background ,bg-mark-alt :foreground ,fg-mark-alt))) + `(modus-themes-mark-del ((,class :inherit bold :background ,bg-mark-del :foreground ,fg-mark-del))) + `(modus-themes-mark-sel ((,class :inherit bold + :background ,@(modus-themes--deuteran + cyan-refine-bg + bg-mark-sel) + :foreground ,fg-mark-sel))) + `(modus-themes-mark-symbol ((,class :inherit bold :foreground ,blue-alt))) +;;;;; heading levels + ;; styles for regular headings used in Org, Markdown, Info, etc. + `(modus-themes-heading-0 + ((,class ,@(modus-themes--heading + 0 cyan-alt-other blue-alt + cyan-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-1 + ((,class ,@(modus-themes--heading + 1 fg-main magenta-alt-other + magenta-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-2 + ((,class ,@(modus-themes--heading + 2 fg-special-warm magenta-alt + red-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-3 + ((,class ,@(modus-themes--heading + 3 fg-special-cold blue + blue-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-4 + ((,class ,@(modus-themes--heading + 4 fg-special-mild cyan + cyan-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-5 + ((,class ,@(modus-themes--heading + 5 fg-special-calm green-alt-other + green-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-6 + ((,class ,@(modus-themes--heading + 6 yellow-nuanced-fg yellow-alt-other + yellow-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-7 + ((,class ,@(modus-themes--heading + 7 red-nuanced-fg red-alt + red-nuanced-bg bg-alt bg-region)))) + `(modus-themes-heading-8 + ((,class ,@(modus-themes--heading + 8 magenta-nuanced-fg magenta + bg-alt bg-alt bg-region)))) +;;;;; language checkers + `(modus-themes-lang-error ((,class ,@(modus-themes--lang-check + fg-lang-underline-error fg-lang-error + red red-refine-fg red-nuanced-bg red-refine-bg red-faint)))) + `(modus-themes-lang-note ((,class ,@(modus-themes--lang-check + fg-lang-underline-note fg-lang-note + blue-alt blue-refine-fg blue-nuanced-bg blue-refine-bg blue-faint)))) + `(modus-themes-lang-warning ((,class ,@(modus-themes--lang-check + fg-lang-underline-warning fg-lang-warning + yellow yellow-refine-fg yellow-nuanced-bg yellow-refine-bg yellow-faint)))) +;;;;; links + `(modus-themes-link-broken ((,class :inherit button ,@(modus-themes--link-color red red-faint)))) + `(modus-themes-link-symlink ((,class :inherit button ,@(modus-themes--link-color cyan cyan-faint)))) +;;;;; markup + `(modus-themes-markup-code + ((,class ,@(modus-themes--markup cyan-alt-other cyan-intense bg-alt + bg-special-faint-mild)))) + `(modus-themes-markup-macro + ((,class ,@(modus-themes--markup magenta-alt-other purple-intense bg-alt + bg-special-faint-cold)))) + `(modus-themes-markup-verbatim + ((,class ,@(modus-themes--markup magenta-alt magenta-intense bg-alt + bg-special-faint-calm)))) +;;;;; search + `(modus-themes-search-success ((,class :inherit modus-themes-intense-yellow))) + `(modus-themes-search-success-lazy ((,class :inherit modus-themes-subtle-cyan))) + `(modus-themes-search-success-modeline ((,class :foreground ,@(modus-themes--deuteran + blue-active + green-active)))) +;;;;; tabs + `(modus-themes-tab-active ((,class ,@(modus-themes--tab bg-tab-active nil nil nil t t)))) + `(modus-themes-tab-backdrop ((,class ,@(modus-themes--tab bg-active bg-active-accent nil nil nil nil t)))) + `(modus-themes-tab-inactive ((,class ,@(modus-themes--tab bg-tab-inactive bg-tab-inactive-accent fg-dim nil t)))) +;;;;; completion frameworks + `(modus-themes-completion-match-0 + ((,class ,@(modus-themes--completion-match + 'matches bg-special-faint-calm magenta-alt + magenta-subtle-bg magenta-intense)))) + `(modus-themes-completion-match-1 + ((,class ,@(modus-themes--completion-match + 'matches bg-special-faint-cold cyan + cyan-subtle-bg cyan-intense)))) + `(modus-themes-completion-match-2 + ((,class ,@(modus-themes--completion-match + 'matches bg-special-faint-mild green + green-subtle-bg green-intense)))) + `(modus-themes-completion-match-3 + ((,class ,@(modus-themes--completion-match + 'matches bg-special-faint-warm yellow + yellow-subtle-bg orange-intense)))) + `(modus-themes-completion-selected + ((,class ,@(modus-themes--completion-line + 'selection bg-inactive blue-alt + bg-active blue-active + bg-completion-subtle bg-completion)))) + `(modus-themes-completion-selected-popup + ((,class ,@(modus-themes--completion-line + 'popup bg-active blue-alt + bg-region blue-active + cyan-subtle-bg cyan-refine-bg)))) +;;;;; buttons + `(modus-themes-box-button + ((,class ,@(modus-themes--button bg-active bg-main bg-active-accent + bg-special-cold bg-region)))) + `(modus-themes-box-button-pressed + ((,class ,@(modus-themes--button bg-active bg-main bg-active-accent + bg-special-cold bg-region t)))) +;;;;; typography + `(modus-themes-bold ((,class ,@(modus-themes--bold-weight)))) + `(modus-themes-fixed-pitch ((,class ,@(modus-themes--fixed-pitch)))) + `(modus-themes-slant ((,class ,@(modus-themes--slant)))) + `(modus-themes-ui-variable-pitch ((,class ,@(modus-themes--variable-pitch-ui)))) +;;;;; other custom faces + `(modus-themes-hl-line ((,class ,@(modus-themes--hl-line + bg-hl-line bg-hl-line-intense + bg-hl-line-intense-accent blue-nuanced-bg + bg-region blue-intense-bg + fg-alt blue-intense) + :extend t))) + `(modus-themes-key-binding ((,class :inherit (bold modus-themes-fixed-pitch) + :foreground ,blue-alt-other))) + `(modus-themes-prompt ((,class ,@(modus-themes--prompt + cyan-alt-other blue-alt-other fg-alt + cyan-nuanced-bg blue-refine-bg fg-main + bg-alt bg-active)))) + `(modus-themes-reset-hard ((,class :inherit (fixed-pitch modus-themes-reset-soft) + :family ,(face-attribute 'default :family)))) + `(modus-themes-reset-soft ((,class :background ,bg-main :foreground ,fg-main + :weight normal :slant normal :strike-through nil + :box nil :underline nil :overline nil :extend nil))) +;;;; standard faces +;;;;; absolute essentials + `(default ((,class :background ,bg-main :foreground ,fg-main))) + `(cursor ((,class :background ,fg-main))) + `(fringe ((,class ,@(modus-themes--fringe bg-main bg-inactive bg-active) + :foreground ,fg-main))) + `(vertical-border ((,class :foreground ,fg-window-divider-inner))) +;;;;; basic and/or ungrouped styles + `(bold ((,class :weight bold))) + `(bold-italic ((,class :inherit (bold italic)))) + `(underline ((,class :underline ,fg-alt))) + `(buffer-menu-buffer ((,class :inherit bold))) + `(child-frame-border ((,class :background ,fg-window-divider-inner))) + `(comint-highlight-input ((,class :inherit bold))) + `(comint-highlight-prompt ((,class :inherit modus-themes-prompt))) + `(confusingly-reordered ((,class :inherit modus-themes-lang-error))) + `(edmacro-label ((,class :inherit bold :foreground ,cyan))) + `(elisp-shorthand-font-lock-face ((,class :inherit font-lock-variable-name-face))) + `(error ((,class :inherit bold :foreground ,red))) + `(escape-glyph ((,class :foreground ,fg-escape-char-construct))) + `(file-name-shadow ((,class :inherit shadow))) + `(header-line ((,class :inherit modus-themes-ui-variable-pitch + :background ,bg-header :foreground ,fg-header))) + `(header-line-highlight ((,class :inherit highlight))) + `(help-argument-name ((,class :inherit modus-themes-slant :foreground ,cyan))) + `(help-key-binding ((,class :inherit modus-themes-key-binding))) + `(homoglyph ((,class :foreground ,red-alt-faint))) + `(ibuffer-locked-buffer ((,class :foreground ,yellow-alt-other-faint))) + `(italic ((,class :slant italic))) + `(nobreak-hyphen ((,class :foreground ,fg-escape-char-construct))) + `(nobreak-space ((,class :foreground ,fg-escape-char-construct :underline t))) + `(menu ((,class :inverse-video unspecified :inherit modus-themes-intense-neutral))) + `(minibuffer-prompt ((,class :inherit modus-themes-prompt))) + `(mm-command-output ((,class :foreground ,red-alt-other))) + `(mm-uu-extract ((,class :background ,bg-dim :foreground ,fg-special-mild))) + `(next-error ((,class :inherit modus-themes-subtle-red :extend t))) + `(pgtk-im-0 ((,class :inherit modus-themes-refine-cyan))) + `(rectangle-preview ((,class :inherit modus-themes-special-warm))) + `(region ((,class ,@(modus-themes--region bg-region fg-main + bg-hl-alt-intense bg-region-accent + bg-region-accent-subtle)))) + `(secondary-selection ((,class :inherit modus-themes-special-cold))) + `(separator-line ((,class :underline ,bg-region))) + `(shadow ((,class :foreground ,fg-alt))) + `(success ((,class :inherit (bold modus-themes-grue)))) + `(trailing-whitespace ((,class :background ,red-intense-bg))) + `(warning ((,class :inherit bold :foreground ,yellow))) +;;;;; buttons, links, widgets + `(button ((,class ,@(modus-themes--link + blue-alt-other blue-alt-other-faint + bg-region blue-nuanced-bg bg-alt)))) + `(link ((,class :inherit button))) + `(link-visited ((,class :inherit button + ,@(modus-themes--link-color + magenta-alt-other magenta-alt-other-faint fg-alt)))) + `(tooltip ((,class :background ,bg-special-cold :foreground ,fg-main))) + `(widget-button ((,class ,@(if (memq 'all-buttons modus-themes-box-buttons) + (list :inherit 'modus-themes-box-button) + (list :inherit 'bold :foreground blue-alt))))) + `(widget-button-pressed ((,class ,@(if (memq 'all-buttons modus-themes-box-buttons) + (list :inherit 'modus-themes-box-button-pressed) + (list :inherit 'bold :foreground magenta-alt))))) + `(widget-documentation ((,class :foreground ,green))) + `(widget-field ((,class :background ,bg-alt :foreground ,fg-main :extend nil))) + `(widget-inactive ((,class :inherit shadow :background ,bg-dim))) + `(widget-single-line-field ((,class :inherit widget-field))) +;;;;; alert + `(alert-high-face ((,class :inherit bold :foreground ,red-alt))) + `(alert-low-face ((,class :foreground ,fg-special-mild))) + `(alert-moderate-face ((,class :inherit bold :foreground ,yellow))) + `(alert-trivial-face ((,class :foreground ,fg-special-calm))) + `(alert-urgent-face ((,class :inherit bold :foreground ,red-intense))) +;;;;; all-the-icons + `(all-the-icons-blue ((,class :foreground ,blue-alt-other))) + `(all-the-icons-blue-alt ((,class :foreground ,blue-alt))) + `(all-the-icons-cyan ((,class :foreground ,cyan-intense))) + `(all-the-icons-cyan-alt ((,class :foreground ,cyan-alt))) + `(all-the-icons-dblue ((,class :foreground ,blue-faint))) + `(all-the-icons-dcyan ((,class :foreground ,cyan-faint))) + `(all-the-icons-dgreen ((,class :foreground ,green))) + `(all-the-icons-dmaroon ((,class :foreground ,magenta-alt-faint))) + `(all-the-icons-dorange ((,class :foreground ,red-alt-faint))) + `(all-the-icons-dpink ((,class :foreground ,magenta-faint))) + `(all-the-icons-dpurple ((,class :foreground ,magenta-alt-other-faint))) + `(all-the-icons-dred ((,class :foreground ,red-faint))) + `(all-the-icons-dsilver ((,class :foreground ,cyan-alt-faint))) + `(all-the-icons-dyellow ((,class :foreground ,yellow-alt-faint))) + `(all-the-icons-green ((,class :foreground ,green-intense))) + `(all-the-icons-lblue ((,class :foreground ,blue-alt-other))) + `(all-the-icons-lcyan ((,class :foreground ,cyan))) + `(all-the-icons-lgreen ((,class :foreground ,green-alt-other))) + `(all-the-icons-lmaroon ((,class :foreground ,magenta-alt))) + `(all-the-icons-lorange ((,class :foreground ,red-alt))) + `(all-the-icons-lpink ((,class :foreground ,magenta))) + `(all-the-icons-lpurple ((,class :foreground ,magenta-faint))) + `(all-the-icons-lred ((,class :foreground ,red))) + `(all-the-icons-lsilver ((,class :foreground ,fg-docstring))) + `(all-the-icons-lyellow ((,class :foreground ,yellow-alt))) + `(all-the-icons-maroon ((,class :foreground ,magenta-intense))) + `(all-the-icons-orange ((,class :foreground ,orange-intense))) + `(all-the-icons-pink ((,class :foreground ,fg-special-calm))) + `(all-the-icons-purple ((,class :foreground ,magenta-alt-other))) + `(all-the-icons-purple-alt ((,class :foreground ,purple-intense))) + `(all-the-icons-red ((,class :foreground ,red-intense))) + `(all-the-icons-red-alt ((,class :foreground ,red-alt-other))) + `(all-the-icons-silver ((,class :foreground ,fg-special-cold))) + `(all-the-icons-yellow ((,class :foreground ,yellow))) +;;;;; all-the-icons-dired + `(all-the-icons-dired-dir-face ((,class :foreground ,cyan-faint))) +;;;;; all-the-icons-ibuffer + `(all-the-icons-ibuffer-dir-face ((,class :foreground ,cyan-faint))) + `(all-the-icons-ibuffer-file-face ((,class :foreground ,blue-faint))) + `(all-the-icons-ibuffer-mode-face ((,class :foreground ,cyan))) + `(all-the-icons-ibuffer-size-face ((,class :foreground ,cyan-alt-other))) +;;;;; annotate + `(annotate-annotation ((,class :inherit modus-themes-subtle-blue))) + `(annotate-annotation-secondary ((,class :inherit modus-themes-subtle-green))) + `(annotate-highlight ((,class :background ,blue-nuanced-bg :underline ,blue-intense))) + `(annotate-highlight-secondary ((,class :background ,green-nuanced-bg :underline ,green-intense))) +;;;;; ansi-color + ;; Those are in Emacs28. + `(ansi-color-black ((,class :background "black" :foreground "black"))) + `(ansi-color-blue ((,class :background ,blue :foreground ,blue))) + `(ansi-color-bold ((,class :inherit bold))) + `(ansi-color-bright-black ((,class :background "gray35" :foreground "gray35"))) + `(ansi-color-bright-blue ((,class :background ,blue-alt :foreground ,blue-alt))) + `(ansi-color-bright-cyan ((,class :background ,cyan-alt-other :foreground ,cyan-alt-other))) + `(ansi-color-bright-green ((,class :background ,green-alt-other :foreground ,green-alt-other))) + `(ansi-color-bright-magenta ((,class :background ,magenta-alt-other :foreground ,magenta-alt-other))) + `(ansi-color-bright-red ((,class :background ,red-alt :foreground ,red-alt))) + `(ansi-color-bright-white ((,class :background "white" :foreground "white"))) + `(ansi-color-bright-yellow ((,class :background ,yellow-alt :foreground ,yellow-alt))) + `(ansi-color-cyan ((,class :background ,cyan :foreground ,cyan))) + `(ansi-color-green ((,class :background ,green :foreground ,green))) + `(ansi-color-magenta ((,class :background ,magenta :foreground ,magenta))) + `(ansi-color-red ((,class :background ,red :foreground ,red))) + `(ansi-color-white ((,class :background "gray65" :foreground "gray65"))) + `(ansi-color-yellow ((,class :background ,yellow :foreground ,yellow))) +;;;;; anzu + `(anzu-match-1 ((,class :inherit modus-themes-subtle-cyan))) + `(anzu-match-2 ((,class :inherit modus-themes-search-success))) + `(anzu-match-3 ((,class :inherit modus-themes-subtle-yellow))) + `(anzu-mode-line ((,class :inherit (bold modus-themes-search-success-modeline)))) + `(anzu-mode-line-no-match ((,class :inherit bold :foreground ,red-active))) + `(anzu-replace-highlight ((,class :inherit modus-themes-refine-red :underline t))) + `(anzu-replace-to ((,class :inherit modus-themes-search-success))) +;;;;; apropos + `(apropos-button ((,class :foreground ,magenta-alt-other))) + `(apropos-function-button ((,class :foreground ,magenta))) + `(apropos-keybinding ((,class :inherit modus-themes-key-binding))) + `(apropos-misc-button ((,class :foreground ,green-alt-other))) + `(apropos-property ((,class :inherit modus-themes-bold :foreground ,magenta-alt))) + `(apropos-symbol ((,class :inherit modus-themes-pseudo-header))) + `(apropos-user-option-button ((,class :foreground ,cyan))) + `(apropos-variable-button ((,class :foreground ,blue-alt))) +;;;;; artbollocks-mode + `(artbollocks-face ((,class :inherit modus-themes-lang-note))) + `(artbollocks-lexical-illusions-face ((,class :background ,bg-alt :foreground ,red-alt :underline t))) + `(artbollocks-passive-voice-face ((,class :inherit modus-themes-lang-warning))) + `(artbollocks-weasel-words-face ((,class :inherit modus-themes-lang-error))) +;;;;; auctex and Tex + `(font-latex-bold-face ((,class :inherit bold))) + `(font-latex-doctex-documentation-face ((,class :inherit font-lock-doc-face))) + `(font-latex-doctex-preprocessor-face ((,class :inherit font-lock-preprocessor-face))) + `(font-latex-italic-face ((,class :inherit italic))) + `(font-latex-math-face ((,class :inherit font-lock-constant-face))) + `(font-latex-script-char-face ((,class :inherit font-lock-builtin-face))) + `(font-latex-sectioning-5-face ((,class :inherit (bold modus-themes-variable-pitch) :foreground ,blue-nuanced-fg))) + `(font-latex-sedate-face ((,class :inherit font-lock-keyword-face))) + `(font-latex-slide-title-face ((,class :inherit modus-themes-heading-1))) + `(font-latex-string-face ((,class :inherit font-lock-string-face))) + `(font-latex-subscript-face ((,class :height 0.95))) + `(font-latex-superscript-face ((,class :height 0.95))) + `(font-latex-verbatim-face ((,class :inherit modus-themes-markup-verbatim))) + `(font-latex-warning-face ((,class :inherit font-lock-warning-face))) + `(tex-match ((,class :foreground ,blue-alt-other))) + `(tex-verbatim ((,class :inherit modus-themes-markup-verbatim))) + `(texinfo-heading ((,class :foreground ,magenta))) + `(TeX-error-description-error ((,class :inherit error))) + `(TeX-error-description-help ((,class :inherit success))) + `(TeX-error-description-tex-said ((,class :inherit success))) + `(TeX-error-description-warning ((,class :inherit warning))) +;;;;; auto-dim-other-buffers + `(auto-dim-other-buffers-face ((,class :background ,bg-alt))) +;;;;; avy + `(avy-background-face ((,class :background ,bg-dim :foreground ,fg-dim :extend t))) + `(avy-goto-char-timer-face ((,class :inherit (modus-themes-intense-neutral bold)))) + `(avy-lead-face ((,class :inherit (bold modus-themes-reset-soft) :background ,bg-char-0))) + `(avy-lead-face-0 ((,class :inherit (bold modus-themes-reset-soft) :background ,bg-char-1))) + `(avy-lead-face-1 ((,class :inherit (modus-themes-special-warm modus-themes-reset-soft)))) + `(avy-lead-face-2 ((,class :inherit (bold modus-themes-reset-soft) :background ,bg-char-2))) +;;;;; aw (ace-window) + `(aw-background-face ((,class :foreground ,fg-unfocused))) + `(aw-key-face ((,class :inherit modus-themes-key-binding))) + `(aw-leading-char-face ((,class :inherit (bold modus-themes-reset-soft) :height 1.5 + :foreground ,red-intense))) + `(aw-minibuffer-leading-char-face ((,class :inherit (modus-themes-intense-red bold)))) + `(aw-mode-line-face ((,class :inherit bold))) +;;;;; awesome-tray + `(awesome-tray-module-awesome-tab-face ((,class :inherit bold :foreground ,red-alt-other))) + `(awesome-tray-module-battery-face ((,class :inherit bold :foreground ,cyan-alt-other))) + `(awesome-tray-module-buffer-name-face ((,class :inherit bold :foreground ,yellow-alt-other))) + `(awesome-tray-module-circe-face ((,class :inherit bold :foreground ,blue-alt))) + `(awesome-tray-module-date-face ((,class :inherit bold :foreground ,fg-dim))) + `(awesome-tray-module-evil-face ((,class :inherit bold :foreground ,green-alt))) + `(awesome-tray-module-git-face ((,class :inherit bold :foreground ,magenta))) + `(awesome-tray-module-last-command-face ((,class :inherit bold :foreground ,blue-alt-other))) + `(awesome-tray-module-location-face ((,class :inherit bold :foreground ,yellow))) + `(awesome-tray-module-mode-name-face ((,class :inherit bold :foreground ,green))) + `(awesome-tray-module-parent-dir-face ((,class :inherit bold :foreground ,cyan))) + `(awesome-tray-module-rvm-face ((,class :inherit bold :foreground ,magenta-alt-other))) +;;;;; bbdb + `(bbdb-name ((,class :foreground ,magenta-alt-other))) + `(bbdb-organization ((,class :foreground ,red-alt-other))) + `(bbdb-field-name ((,class :foreground ,cyan-alt-other))) +;;;;; binder + `(binder-sidebar-highlight ((,class :inherit modus-themes-subtle-cyan))) + `(binder-sidebar-marked ((,class :inherit modus-themes-mark-sel))) + `(binder-sidebar-missing ((,class :inherit modus-themes-subtle-red))) + `(binder-sidebar-tags ((,class :foreground ,cyan))) +;;;;; bm + `(bm-face ((,class :inherit modus-themes-subtle-yellow :extend t))) + `(bm-fringe-face ((,class :inherit modus-themes-fringe-yellow))) + `(bm-fringe-persistent-face ((,class :inherit modus-themes-fringe-blue))) + `(bm-persistent-face ((,class :inherit modus-themes-intense-blue :extend t))) +;;;;; bongo + `(bongo-album-title ((,class :foreground ,fg-active))) + `(bongo-artist ((,class :foreground ,magenta-active))) + `(bongo-currently-playing-track ((,class :inherit bold))) + `(bongo-elapsed-track-part ((,class :inherit modus-themes-subtle-magenta :underline t))) + `(bongo-filled-seek-bar ((,class :background ,blue-intense-bg :foreground ,fg-main))) + `(bongo-marked-track ((,class :foreground ,fg-mark-alt))) + `(bongo-marked-track-line ((,class :background ,bg-mark-alt))) + `(bongo-played-track ((,class :foreground ,fg-unfocused :strike-through t))) + `(bongo-track-length ((,class :inherit shadow))) + `(bongo-track-title ((,class :foreground ,blue-active))) + `(bongo-unfilled-seek-bar ((,class :background ,bg-special-cold :foreground ,fg-main))) +;;;;; boon + `(boon-modeline-cmd ((,class :inherit modus-themes-active-blue))) + `(boon-modeline-ins ((,class :inherit modus-themes-active-red))) + `(boon-modeline-off ((,class :inherit modus-themes-active-yellow))) + `(boon-modeline-spc ((,class :inherit modus-themes-active-green))) +;;;;; bookmark + `(bookmark-face ((,class :inherit modus-themes-fringe-cyan))) + `(bookmark-menu-bookmark ((,class :inherit bold))) +;;;;; breakpoint (built-in gdb-mi.el) + `(breakpoint-disabled ((,class :inherit shadow))) + `(breakpoint-enabled ((,class :inherit bold :foreground ,red))) +;;;;; calendar and diary + `(calendar-month-header ((,class :inherit modus-themes-pseudo-header))) + `(calendar-today ((,class :inherit bold :underline t))) + `(calendar-weekday-header ((,class :foreground ,fg-unfocused))) + `(calendar-weekend-header ((,class :foreground ,red-faint))) + `(diary ((,class :background ,blue-nuanced-bg :foreground ,blue-alt-other))) + `(diary-anniversary ((,class :foreground ,red-alt-other))) + `(diary-time ((,class :foreground ,cyan))) + `(holiday ((,class :background ,magenta-nuanced-bg :foreground ,magenta-alt))) +;;;;; calfw + `(cfw:face-annotation ((,class :foreground ,fg-special-warm))) + `(cfw:face-day-title ((,class :foreground ,fg-main))) + `(cfw:face-default-content ((,class :foreground ,green-alt))) + `(cfw:face-default-day ((,class :inherit (cfw:face-day-title bold)))) + `(cfw:face-disable ((,class :foreground ,fg-unfocused))) + `(cfw:face-grid ((,class :foreground ,fg-window-divider-outer))) + `(cfw:face-header ((,class :inherit bold :foreground ,fg-main))) + `(cfw:face-holiday ((,class :foreground ,magenta-alt-other))) + `(cfw:face-periods ((,class :foreground ,cyan-alt-other))) + `(cfw:face-saturday ((,class :inherit bold :foreground ,cyan-alt-other))) + `(cfw:face-select ((,class :inherit modus-themes-intense-blue))) + `(cfw:face-sunday ((,class :inherit bold :foreground ,cyan-alt-other))) + `(cfw:face-title ((,class :inherit modus-themes-heading-1 :background ,bg-main :overline nil :foreground ,fg-special-cold))) + `(cfw:face-today ((,class :background ,bg-inactive))) + `(cfw:face-today-title ((,class :background ,bg-active))) + `(cfw:face-toolbar ((,class :background ,bg-alt :foreground ,bg-alt))) + `(cfw:face-toolbar-button-off ((,class :inherit shadow))) + `(cfw:face-toolbar-button-on ((,class :inherit bold :background ,blue-nuanced-bg + :foreground ,blue-alt))) +;;;;; calibredb + `(calibredb-archive-face ((,class :foreground ,magenta-alt-faint))) + `(calibredb-author-face ((,class :foreground ,blue-faint))) + `(calibredb-comment-face ((,class :inherit shadow))) + `(calibredb-date-face ((,class :foreground ,cyan))) + `(calibredb-edit-annotation-header-title-face ((,class :inherit bold))) + `(calibredb-favorite-face ((,class :foreground ,red-alt))) + `(calibredb-file-face (( ))) + `(calibredb-format-face ((,class :foreground ,cyan-faint))) + `(calibredb-highlight-face ((,class :inherit success))) + `(calibredb-id-face (( ))) + `(calibredb-ids-face (( ))) + `(calibredb-search-header-highlight-face ((,class :inherit modus-themes-hl-line))) + `(calibredb-search-header-library-name-face ((,class :foreground ,blue-active))) + `(calibredb-search-header-library-path-face ((,class :inherit bold))) + `(calibredb-search-header-sort-face ((,class :inherit bold :foreground ,magenta-active))) + `(calibredb-search-header-total-face ((,class :inherit bold :foreground ,cyan-active))) + `(calibredb-search-header-filter-face ((,class :inherit bold))) + `(calibredb-mark-face ((,class :inherit modus-themes-mark-sel))) + `(calibredb-size-face (( ))) + `(calibredb-tag-face ((,class :foreground ,magenta-alt-faint))) +;;;;; cfrs + `(cfrs-border-color ((,class :background ,fg-window-divider-inner))) +;;;;; change-log and log-view (`vc-print-log' and `vc-print-root-log') + `(change-log-acknowledgment ((,class :inherit shadow))) + `(change-log-conditionals ((,class :foreground ,yellow))) + `(change-log-date ((,class :foreground ,cyan))) + `(change-log-email ((,class :foreground ,cyan-alt-other))) + `(change-log-file ((,class :inherit bold :foreground ,fg-special-cold))) + `(change-log-function ((,class :foreground ,green-alt-other))) + `(change-log-list ((,class :foreground ,magenta-alt))) + `(change-log-name ((,class :foreground ,magenta-alt-other))) + `(log-edit-header ((,class :foreground ,fg-special-warm))) + `(log-edit-summary ((,class :inherit bold :foreground ,blue))) + `(log-edit-unknown-header ((,class :inherit shadow))) + `(log-view-commit-body ((,class :foreground ,blue-nuanced-fg))) + `(log-view-file ((,class :inherit bold :foreground ,fg-special-cold))) + `(log-view-message ((,class :background ,bg-alt :foreground ,fg-alt))) +;;;;; cider + `(cider-debug-code-overlay-face ((,class :background ,bg-alt))) + `(cider-debug-prompt-face ((,class :foreground ,magenta-alt :underline t))) + `(cider-deprecated-face ((,class :inherit modus-themes-refine-yellow))) + `(cider-docview-emphasis-face ((,class :inherit italic :foreground ,fg-special-cold))) + `(cider-docview-literal-face ((,class :foreground ,blue-alt))) + `(cider-docview-strong-face ((,class :inherit bold :foreground ,fg-special-cold))) + `(cider-docview-table-border-face ((,class :inherit shadow))) + `(cider-enlightened-face ((,class :box (:line-width -1 :color ,yellow-alt :style nil) :background ,bg-dim))) + `(cider-enlightened-local-face ((,class :inherit bold :foreground ,yellow-alt-other))) + `(cider-error-highlight-face ((,class :foreground ,red :underline t))) + `(cider-fragile-button-face ((,class :box (:line-width 3 :color ,fg-alt :style released-button) :foreground ,yellow))) + `(cider-fringe-good-face ((,class :foreground ,green-active))) + `(cider-instrumented-face ((,class :box (:line-width -1 :color ,red :style nil) :background ,bg-dim))) + `(cider-reader-conditional-face ((,class :inherit italic :foreground ,fg-special-warm))) + `(cider-repl-input-face ((,class :inherit bold))) + `(cider-repl-prompt-face ((,class :inherit modus-themes-prompt))) + `(cider-repl-stderr-face ((,class :inherit bold :foreground ,red))) + `(cider-repl-stdout-face ((,class :foreground ,blue))) + `(cider-result-overlay-face ((,class :box (:line-width -1 :color ,blue :style nil) :background ,bg-dim))) + `(cider-stacktrace-error-class-face ((,class :inherit bold :foreground ,red))) + `(cider-stacktrace-error-message-face ((,class :inherit italic :foreground ,red-alt-other))) + `(cider-stacktrace-face ((,class :foreground ,fg-main))) + `(cider-stacktrace-filter-active-face ((,class :foreground ,cyan-alt :underline t))) + `(cider-stacktrace-filter-inactive-face ((,class :foreground ,cyan-alt))) + `(cider-stacktrace-fn-face ((,class :inherit bold :foreground ,fg-main))) + `(cider-stacktrace-ns-face ((,class :inherit (shadow italic)))) + `(cider-stacktrace-promoted-button-face ((,class :box (:line-width 3 :color ,fg-alt :style released-button) :foreground ,red))) + `(cider-stacktrace-suppressed-button-face ((,class :box (:line-width 3 :color ,fg-alt :style pressed-button) + :background ,bg-alt :foreground ,fg-alt))) + `(cider-test-error-face ((,class :inherit modus-themes-subtle-red))) + `(cider-test-failure-face ((,class :inherit (modus-themes-intense-red bold)))) + `(cider-test-success-face ((,class :inherit modus-themes-grue-background-intense))) + `(cider-traced-face ((,class :box (:line-width -1 :color ,cyan :style nil) :background ,bg-dim))) + `(cider-warning-highlight-face ((,class :foreground ,yellow :underline t))) +;;;;; circe (and lui) + `(circe-fool-face ((,class :inherit shadow))) + `(circe-highlight-nick-face ((,class :inherit bold :foreground ,blue))) + `(circe-prompt-face ((,class :inherit modus-themes-prompt))) + `(circe-server-face ((,class :foreground ,fg-unfocused))) + `(lui-button-face ((,class :inherit button))) + `(lui-highlight-face ((,class :foreground ,magenta-alt))) + `(lui-time-stamp-face ((,class :foreground ,blue-nuanced-fg))) +;;;;; citar + `(citar ((,class :inherit shadow))) + `(citar-highlight (( ))) +;;;;; color-rg + `(color-rg-font-lock-column-number ((,class :foreground ,magenta-alt-other))) + `(color-rg-font-lock-command ((,class :inherit bold :foreground ,fg-main))) + `(color-rg-font-lock-file ((,class :inherit bold :foreground ,fg-special-cold))) + `(color-rg-font-lock-flash ((,class :inherit modus-themes-intense-blue))) + `(color-rg-font-lock-function-location ((,class :inherit modus-themes-special-calm))) + `(color-rg-font-lock-header-line-directory ((,class :foreground ,blue-active))) + `(color-rg-font-lock-header-line-edit-mode ((,class :foreground ,magenta-active))) + `(color-rg-font-lock-header-line-keyword ((,class :foreground ,green-active))) + `(color-rg-font-lock-header-line-text ((,class :foreground ,fg-active))) + `(color-rg-font-lock-line-number ((,class :foreground ,fg-special-warm))) + `(color-rg-font-lock-mark-changed ((,class :inherit bold :foreground ,blue))) + `(color-rg-font-lock-mark-deleted ((,class :inherit bold :foreground ,red))) + `(color-rg-font-lock-match ((,class :inherit modus-themes-special-calm))) + `(color-rg-font-lock-position-splitter ((,class :inherit shadow))) +;;;;; column-enforce-mode + `(column-enforce-face ((,class :inherit modus-themes-refine-yellow))) +;;;;; company-mode + `(company-echo-common ((,class :inherit modus-themes-completion-match-0))) + `(company-preview ((,class :background ,bg-dim :foreground ,fg-dim))) + `(company-preview-common ((,class :inherit company-echo-common))) + `(company-preview-search ((,class :inherit modus-themes-special-calm))) + `(company-template-field ((,class :inherit modus-themes-intense-magenta))) + `(company-tooltip ((,class :background ,bg-alt))) + `(company-tooltip-annotation ((,class :inherit completions-annotations))) + `(company-tooltip-common ((,class :inherit company-echo-common))) + `(company-tooltip-deprecated ((,class :inherit company-tooltip :strike-through t))) + `(company-tooltip-mouse ((,class :inherit highlight))) + `(company-tooltip-scrollbar-thumb ((,class :background ,fg-active))) + `(company-tooltip-scrollbar-track ((,class :background ,bg-active))) + `(company-tooltip-search ((,class :inherit (modus-themes-search-success-lazy bold)))) + `(company-tooltip-search-selection ((,class :inherit modus-themes-search-success :underline t))) + `(company-tooltip-selection ((,class :inherit modus-themes-completion-selected-popup))) +;;;;; company-posframe + `(company-posframe-active-backend-name ((,class :inherit bold :background ,bg-active :foreground ,blue-active))) + `(company-posframe-inactive-backend-name ((,class :background ,bg-active :foreground ,fg-active))) + `(company-posframe-metadata ((,class :background ,bg-inactive :foreground ,fg-inactive))) +;;;;; compilation + `(compilation-column-number ((,class :inherit compilation-line-number))) + `(compilation-error ((,class :inherit modus-themes-bold :foreground ,red))) + `(compilation-info ((,class :inherit modus-themes-bold :foreground ,fg-special-cold))) + `(compilation-line-number ((,class :foreground ,fg-special-warm))) + `(compilation-mode-line-exit ((,class :inherit bold))) + `(compilation-mode-line-fail ((,class :inherit modus-themes-bold :foreground ,red-active))) + `(compilation-mode-line-run ((,class :inherit modus-themes-bold :foreground ,cyan-active))) + `(compilation-warning ((,class :inherit modus-themes-bold :foreground ,yellow-alt))) +;;;;; completions + `(completions-annotations ((,class :inherit modus-themes-slant :foreground ,cyan-faint))) + `(completions-common-part ((,class :inherit modus-themes-completion-match-0))) + `(completions-first-difference ((,class :inherit modus-themes-completion-match-1))) +;;;;; consult + `(consult-async-running ((,class :inherit bold :foreground ,blue))) + `(consult-async-split ((,class :foreground ,magenta-alt))) + `(consult-bookmark ((,class :foreground ,blue))) + `(consult-file ((,class :foreground ,fg-special-cold))) + `(consult-imenu-prefix ((,class :inherit shadow))) + `(consult-key ((,class :inherit modus-themes-key-binding))) + `(consult-line-number ((,class :foreground ,fg-special-warm))) + `(consult-line-number-prefix ((,class :foreground ,fg-unfocused))) + `(consult-narrow-indicator ((,class :foreground ,magenta-alt))) + `(consult-preview-cursor ((,class :inherit modus-themes-intense-blue))) + `(consult-preview-error ((,class :inherit modus-themes-intense-red))) + `(consult-preview-insertion ((,class :inherit modus-themes-special-warm))) + `(consult-preview-line ((,class :background ,bg-hl-alt-intense))) +;;;;; corfu + `(corfu-current ((,class :inherit modus-themes-completion-selected-popup))) + `(corfu-bar ((,class :background ,fg-alt))) + `(corfu-border ((,class :background ,bg-active))) + `(corfu-default ((,class :background ,bg-alt))) +;;;;; corfu-quick + `(corfu-quick1 ((,class :inherit bold :background ,bg-char-0))) + `(corfu-quick2 ((,class :inherit bold :background ,bg-char-1))) +;;;;; counsel + `(counsel-active-mode ((,class :foreground ,magenta-alt-other))) + `(counsel-application-name ((,class :foreground ,red-alt-other))) + `(counsel-key-binding ((,class :inherit modus-themes-key-binding))) + `(counsel-outline-1 ((,class :inherit org-level-1))) + `(counsel-outline-2 ((,class :inherit org-level-2))) + `(counsel-outline-3 ((,class :inherit org-level-3))) + `(counsel-outline-4 ((,class :inherit org-level-4))) + `(counsel-outline-5 ((,class :inherit org-level-5))) + `(counsel-outline-6 ((,class :inherit org-level-6))) + `(counsel-outline-7 ((,class :inherit org-level-7))) + `(counsel-outline-8 ((,class :inherit org-level-8))) + `(counsel-outline-default ((,class :foreground ,fg-main))) + `(counsel-variable-documentation ((,class :inherit modus-themes-slant :foreground ,yellow-alt-other))) +;;;;; counsel-css + `(counsel-css-selector-depth-face-1 ((,class :foreground ,blue))) + `(counsel-css-selector-depth-face-2 ((,class :foreground ,cyan))) + `(counsel-css-selector-depth-face-3 ((,class :foreground ,green))) + `(counsel-css-selector-depth-face-4 ((,class :foreground ,yellow))) + `(counsel-css-selector-depth-face-5 ((,class :foreground ,magenta))) + `(counsel-css-selector-depth-face-6 ((,class :foreground ,red))) +;;;;; cov + `(cov-coverage-not-run-face ((,class :foreground ,red-intense))) + `(cov-coverage-run-face ((,class :foreground ,green-intense))) + `(cov-heavy-face ((,class :foreground ,magenta-intense))) + `(cov-light-face ((,class :foreground ,blue-intense))) + `(cov-med-face ((,class :foreground ,yellow-intense))) + `(cov-none-face ((,class :foreground ,cyan-intense))) +;;;;; cperl-mode + `(cperl-nonoverridable-face ((,class :foreground unspecified))) + `(cperl-array-face ((,class :inherit font-lock-keyword-face))) + `(cperl-hash-face ((,class :inherit font-lock-variable-name-face))) +;;;;; css-mode + `(css-property ((,class :inherit font-lock-type-face))) + `(css-selector ((,class :inherit font-lock-keyword-face))) +;;;;; csv-mode + `(csv-separator-face ((,class :foreground ,red-intense))) +;;;;; ctrlf + `(ctrlf-highlight-active ((,class :inherit modus-themes-search-success))) + `(ctrlf-highlight-line ((,class :inherit modus-themes-hl-line))) + `(ctrlf-highlight-passive ((,class :inherit modus-themes-search-success-lazy))) +;;;;; custom (M-x customize) + `(custom-button ((,class :inherit modus-themes-box-button))) + `(custom-button-mouse ((,class :inherit (highlight custom-button)))) + `(custom-button-pressed ((,class :inherit modus-themes-box-button-pressed))) + `(custom-changed ((,class :inherit modus-themes-subtle-cyan))) + `(custom-comment ((,class :inherit shadow))) + `(custom-comment-tag ((,class :background ,bg-alt :foreground ,yellow-alt-other))) + `(custom-face-tag ((,class :inherit bold :foreground ,blue-intense))) + `(custom-group-tag ((,class :inherit modus-themes-pseudo-header :foreground ,magenta-alt))) + `(custom-group-tag-1 ((,class :inherit modus-themes-special-warm))) + `(custom-invalid ((,class :inherit (modus-themes-intense-red bold)))) + `(custom-modified ((,class :inherit modus-themes-subtle-cyan))) + `(custom-rogue ((,class :inherit modus-themes-refine-magenta))) + `(custom-set ((,class :foreground ,blue-alt))) + `(custom-state ((,class :foreground ,red-alt-faint))) + `(custom-themed ((,class :inherit modus-themes-subtle-blue))) + `(custom-variable-obsolete ((,class :inherit shadow))) + `(custom-variable-tag ((,class :foreground ,cyan))) +;;;;; dap-mode + `(dap-mouse-eval-thing-face ((,class :box (:line-width -1 :color ,blue-active :style nil) + :background ,bg-active :foreground ,fg-main))) + `(dap-result-overlay-face ((,class :box (:line-width -1 :color ,bg-active :style nil) + :background ,bg-active :foreground ,fg-main))) + `(dap-ui-breakpoint-verified-fringe ((,class :inherit bold :foreground ,green-active))) + `(dap-ui-compile-errline ((,class :inherit bold :foreground ,red-intense))) + `(dap-ui-locals-scope-face ((,class :inherit bold :foreground ,magenta :underline t))) + `(dap-ui-locals-variable-face ((,class :inherit bold :foreground ,cyan))) + `(dap-ui-locals-variable-leaf-face ((,class :inherit italic :foreground ,cyan-alt-other))) + `(dap-ui-marker-face ((,class :inherit modus-themes-subtle-blue))) + `(dap-ui-sessions-stack-frame-face ((,class :inherit bold :foreground ,magenta-alt))) + `(dap-ui-sessions-terminated-active-face ((,class :inherit bold :foreground ,fg-alt))) + `(dap-ui-sessions-terminated-face ((,class :inherit shadow))) +;;;;; deadgrep + `(deadgrep-filename-face ((,class :inherit bold :foreground ,fg-special-cold))) + `(deadgrep-match-face ((,class :inherit modus-themes-special-calm))) + `(deadgrep-meta-face ((,class :inherit shadow))) + `(deadgrep-regexp-metachar-face ((,class :inherit bold :foreground ,yellow-intense))) + `(deadgrep-search-term-face ((,class :inherit bold :foreground ,green-intense))) +;;;;; debbugs + `(debbugs-gnu-archived ((,class :inverse-video t))) + `(debbugs-gnu-done ((,class :inherit shadow))) + `(debbugs-gnu-forwarded ((,class :foreground ,fg-special-warm))) + `(debbugs-gnu-handled ((,class :foreground ,blue))) + `(debbugs-gnu-new ((,class :foreground ,red))) + `(debbugs-gnu-pending ((,class :foreground ,cyan))) + `(debbugs-gnu-stale-1 ((,class :foreground ,yellow-nuanced-fg))) + `(debbugs-gnu-stale-2 ((,class :foreground ,yellow))) + `(debbugs-gnu-stale-3 ((,class :foreground ,yellow-alt))) + `(debbugs-gnu-stale-4 ((,class :foreground ,yellow-alt-other))) + `(debbugs-gnu-stale-5 ((,class :foreground ,red-alt))) + `(debbugs-gnu-tagged ((,class :foreground ,magenta-alt))) +;;;;; deft + `(deft-filter-string-face ((,class :inherit bold :foreground ,blue))) + `(deft-header-face ((,class :foreground ,fg-special-warm))) + `(deft-separator-face ((,class :foreground "gray50"))) + `(deft-summary-face ((,class :inherit (shadow modus-themes-slant)))) + `(deft-time-face ((,class :foreground ,cyan))) + `(deft-title-face ((,class :inherit bold))) +;;;;; denote + `(denote-faces-date ((,class :foreground ,cyan))) + `(denote-faces-keywords ((,class :inherit modus-themes-bold :foreground ,magenta-alt))) +;;;;; devdocs + `(devdocs-code-block ((,class :inherit modus-themes-fixed-pitch :background ,bg-dim :extend t))) +;;;;; dictionary + `(dictionary-button-face ((,class :inherit bold :foreground ,fg-special-cold))) + `(dictionary-reference-face ((,class :inherit button))) + `(dictionary-word-definition-face (())) + `(dictionary-word-entry-face ((,class :inherit font-lock-comment-face))) +;;;;; diff-hl + `(diff-hl-change ((,class :inherit modus-themes-fringe-yellow))) + `(diff-hl-delete ((,class :inherit modus-themes-fringe-red))) + `(diff-hl-dired-change ((,class :inherit diff-hl-change))) + `(diff-hl-dired-delete ((,class :inherit diff-hl-delete))) + `(diff-hl-dired-ignored ((,class :inherit dired-ignored))) + `(diff-hl-dired-insert ((,class :inherit diff-hl-insert))) + `(diff-hl-dired-unknown ((,class :inherit dired-ignored))) + `(diff-hl-insert ((,class :inherit modus-themes-grue-background-active))) + `(diff-hl-reverted-hunk-highlight ((,class :background ,fg-main :foreground ,bg-main))) +;;;;; diff-mode + `(diff-added ((,class :inherit modus-themes-diff-added))) + `(diff-changed ((,class :inherit modus-themes-diff-changed :extend t))) + `(diff-context ((,class ,@(unless (eq modus-themes-diffs 'bg-only) (list :foreground fg-unfocused))))) + `(diff-error ((,class :inherit modus-themes-intense-red))) + `(diff-file-header ((,class :inherit (bold diff-header)))) + `(diff-function ((,class :inherit modus-themes-diff-heading))) + `(diff-header ((,class :foreground ,fg-main))) + `(diff-hunk-header ((,class :inherit (bold modus-themes-diff-heading)))) + `(diff-index ((,class :inherit bold :foreground ,blue-alt))) + `(diff-indicator-added ((,class :inherit (modus-themes-grue diff-added bold)))) + `(diff-indicator-changed ((,class :inherit (diff-changed bold) :foreground ,yellow))) + `(diff-indicator-removed ((,class :inherit (diff-removed bold) :foreground ,red))) + `(diff-nonexistent ((,class :inherit (modus-themes-neutral bold)))) + `(diff-refine-added ((,class :inherit modus-themes-diff-refine-added))) + `(diff-refine-changed ((,class :inherit modus-themes-diff-refine-changed))) + `(diff-refine-removed ((,class :inherit modus-themes-diff-refine-removed))) + `(diff-removed ((,class :inherit modus-themes-diff-removed))) +;;;;; dim-autoload + `(dim-autoload-cookie-line ((,class :inherit font-lock-comment-face))) +;;;;; dir-treeview + `(dir-treeview-archive-face ((,class :foreground ,fg-special-warm))) + `(dir-treeview-archive-icon-face ((,class :inherit dir-treeview-default-icon-face :foreground ,yellow))) + `(dir-treeview-audio-face ((,class :foreground ,magenta))) + `(dir-treeview-audio-icon-face ((,class :inherit dir-treeview-default-icon-face :foreground ,magenta-alt))) + `(dir-treeview-control-face ((,class :inherit shadow))) + `(dir-treeview-control-mouse-face ((,class :inherit highlight))) + `(dir-treeview-default-icon-face ((,class :inherit (shadow bold) :family "Font Awesome"))) + `(dir-treeview-default-filename-face ((,class :foreground ,fg-main))) + `(dir-treeview-directory-face ((,class :foreground ,blue))) + `(dir-treeview-directory-icon-face ((,class :inherit dir-treeview-default-icon-face :foreground ,blue-alt))) + `(dir-treeview-executable-face ((,class :foreground ,red-alt))) + `(dir-treeview-executable-icon-face ((,class :inherit dir-treeview-default-icon-face :foreground ,red-alt-other))) + `(dir-treeview-image-face ((,class :foreground ,green-alt-other))) + `(dir-treeview-image-icon-face ((,class :inherit dir-treeview-default-icon-face :foreground ,green-alt))) + `(dir-treeview-indent-face ((,class :inherit shadow))) + `(dir-treeview-label-mouse-face ((,class :inherit highlight))) + `(dir-treeview-start-dir-face ((,class :inherit modus-themes-pseudo-header))) + `(dir-treeview-symlink-face ((,class :inherit modus-themes-link-symlink))) + `(dir-treeview-video-face ((,class :foreground ,magenta-alt-other))) + `(dir-treeview-video-icon-face ((,class :inherit dir-treeview-default-icon-face :foreground ,magenta-alt-other))) +;;;;; dired + `(dired-broken-symlink ((,class :inherit modus-themes-link-broken))) + `(dired-directory ((,class :foreground ,blue))) + `(dired-flagged ((,class :inherit modus-themes-mark-del))) + `(dired-header ((,class :inherit modus-themes-pseudo-header))) + `(dired-ignored ((,class :inherit shadow))) + `(dired-mark ((,class :inherit modus-themes-mark-symbol))) + `(dired-marked ((,class :inherit modus-themes-mark-sel))) + `(dired-perm-write ((,class :foreground ,fg-special-warm))) + `(dired-symlink ((,class :inherit modus-themes-link-symlink))) + `(dired-warning ((,class :inherit bold :foreground ,yellow))) +;;;;; dired-async + `(dired-async-failures ((,class :inherit bold :foreground ,red-active))) + `(dired-async-message ((,class :inherit bold :foreground ,blue-active))) + `(dired-async-mode-message ((,class :inherit bold :foreground ,cyan-active))) +;;;;; dired-git + `(dired-git-branch-else ((,class :inherit bold :foreground ,magenta-alt))) + `(dired-git-branch-master ((,class :inherit bold :foreground ,magenta-alt-other))) +;;;;; dired-git-info + `(dgi-commit-message-face ((,class :foreground ,cyan-alt-other))) +;;;;; dired-narrow + `(dired-narrow-blink ((,class :inherit (modus-themes-subtle-cyan bold)))) +;;;;; dired-subtree + ;; remove backgrounds from dired-subtree faces, else they break + ;; dired-{flagged,marked} and any other face that sets a background + ;; such as hl-line. Also, denoting depth by varying shades of gray + ;; is not good for accessibility. + `(dired-subtree-depth-1-face (())) + `(dired-subtree-depth-2-face (())) + `(dired-subtree-depth-3-face (())) + `(dired-subtree-depth-4-face (())) + `(dired-subtree-depth-5-face (())) + `(dired-subtree-depth-6-face (())) +;;;;; diredfl + `(diredfl-autofile-name ((,class :inherit modus-themes-special-cold))) + `(diredfl-compressed-file-name ((,class :foreground ,fg-special-warm))) + `(diredfl-compressed-file-suffix ((,class :foreground ,red-alt))) + `(diredfl-date-time ((,class :foreground ,cyan))) + `(diredfl-deletion ((,class :inherit modus-themes-mark-del))) + `(diredfl-deletion-file-name ((,class :inherit modus-themes-mark-del))) + `(diredfl-dir-heading ((,class :inherit modus-themes-pseudo-header))) + `(diredfl-dir-name ((,class :inherit dired-directory))) + `(diredfl-dir-priv ((,class :foreground ,blue-alt))) + `(diredfl-exec-priv ((,class :foreground ,magenta-alt))) + `(diredfl-executable-tag ((,class :foreground ,magenta-alt))) + `(diredfl-file-name ((,class :foreground ,fg-main))) + `(diredfl-file-suffix ((,class :foreground ,magenta-alt-other))) + `(diredfl-flag-mark ((,class :inherit modus-themes-mark-sel))) + `(diredfl-flag-mark-line ((,class :inherit modus-themes-mark-sel))) + `(diredfl-ignored-file-name ((,class :inherit shadow))) + `(diredfl-link-priv ((,class :foreground ,blue-alt-other))) + `(diredfl-no-priv ((,class :foreground "gray50"))) + `(diredfl-number ((,class :foreground ,cyan-alt-other-faint))) + `(diredfl-other-priv ((,class :foreground ,yellow))) + `(diredfl-rare-priv ((,class :foreground ,red))) + `(diredfl-read-priv ((,class :foreground ,fg-main))) + `(diredfl-symlink ((,class :inherit dired-symlink))) + `(diredfl-tagged-autofile-name ((,class :inherit modus-themes-refine-magenta))) + `(diredfl-write-priv ((,class :foreground ,cyan))) +;;;;; dired+ + `(diredp-autofile-name ((,class :inherit modus-themes-special-cold))) + `(diredp-compressed-file-name ((,class :foreground ,fg-special-warm))) + `(diredp-compressed-file-suffix ((,class :foreground ,red-alt))) + `(diredp-date-time ((,class :foreground ,cyan))) + `(diredp-deletion ((,class :inherit modus-themes-mark-del))) + `(diredp-deletion-file-name ((,class :inherit modus-themes-mark-del))) + `(diredp-dir-heading ((,class :inherit modus-themes-pseudo-header))) + `(diredp-dir-name ((,class :inherit dired-directory))) + `(diredp-dir-priv ((,class :foreground ,blue-alt))) + `(diredp-exec-priv ((,class :foreground ,magenta-alt))) + `(diredp-executable-tag ((,class :foreground ,magenta-alt))) + `(diredp-file-name ((,class :foreground ,fg-main))) + `(diredp-file-suffix ((,class :foreground ,magenta-alt-other))) + `(diredp-flag-mark ((,class :inherit modus-themes-mark-sel))) + `(diredp-flag-mark-line ((,class :inherit modus-themes-mark-sel))) + `(diredp-ignored-file-name ((,class :inherit shadow))) + `(diredp-link-priv ((,class :foreground ,blue-alt-other))) + `(diredp-mode-line-flagged ((,class :foreground ,red-active))) + `(diredp-mode-line-marked ((,class :foreground ,green-active))) + `(diredp-no-priv ((,class :foreground "gray50"))) + `(diredp-number ((,class :foreground ,cyan-alt-other-faint))) + `(diredp-omit-file-name ((,class :inherit shadow :strike-through t))) + `(diredp-other-priv ((,class :foreground ,yellow))) + `(diredp-rare-priv ((,class :foreground ,red))) + `(diredp-read-priv ((,class :foreground ,fg-main))) + `(diredp-symlink ((,class :inherit dired-symlink))) + `(diredp-tagged-autofile-name ((,class :inherit modus-themes-refine-magenta))) + `(diredp-write-priv ((,class :foreground ,cyan))) +;;;;; display-fill-column-indicator-mode + `(fill-column-indicator ((,class :height 1 :background ,bg-inactive :foreground ,bg-inactive))) +;;;;; doom-modeline + `(doom-modeline-bar ((,class :inherit modus-themes-active-blue))) + `(doom-modeline-bar-inactive ((,class :background ,fg-inactive :foreground ,bg-main))) + `(doom-modeline-battery-charging ((,class :foreground ,green-active))) + `(doom-modeline-battery-critical ((,class :inherit bold :foreground ,red-active))) + `(doom-modeline-battery-error ((,class :inherit bold :box (:line-width -2) + :foreground ,red-active))) + `(doom-modeline-battery-full ((,class :foreground ,blue-active))) + `(doom-modeline-battery-normal ((,class :foreground ,fg-active))) + `(doom-modeline-battery-warning ((,class :inherit bold :foreground ,yellow-active))) + `(doom-modeline-buffer-file ((,class :inherit bold :foreground ,fg-active))) + `(doom-modeline-buffer-major-mode ((,class :inherit bold :foreground ,cyan-active))) + `(doom-modeline-buffer-minor-mode ((,class :foreground ,fg-inactive))) + `(doom-modeline-buffer-modified ((,class :inherit bold :foreground ,magenta-active))) + `(doom-modeline-buffer-path ((,class :inherit bold :foreground ,fg-active))) + `(doom-modeline-debug ((,class :inherit bold :foreground ,yellow-active))) + `(doom-modeline-debug-visual ((,class :inherit bold :foreground ,red-active))) + `(doom-modeline-evil-emacs-state ((,class :inherit bold :foreground ,magenta-active))) + `(doom-modeline-evil-insert-state ((,class :inherit bold :foreground ,green-active))) + `(doom-modeline-evil-motion-state ((,class :inherit bold :foreground ,fg-inactive))) + `(doom-modeline-evil-normal-state ((,class :inherit bold :foreground ,fg-active))) + `(doom-modeline-evil-operator-state ((,class :inherit bold :foreground ,blue-active))) + `(doom-modeline-evil-replace-state ((,class :inherit bold :foreground ,red-active))) + `(doom-modeline-evil-visual-state ((,class :inherit bold :foreground ,cyan-active))) + `(doom-modeline-highlight ((,class :inherit bold :foreground ,blue-active))) + `(doom-modeline-host ((,class :inherit italic))) + `(doom-modeline-info ((,class :foreground ,green-active))) + `(doom-modeline-lsp-error ((,class :inherit bold :foreground ,red-active))) + `(doom-modeline-lsp-success ((,class :inherit (bold modus-themes-grue-active)))) + `(doom-modeline-lsp-warning ((,class :inherit bold :foreground ,yellow-active))) + `(doom-modeline-panel ((,class :inherit modus-themes-active-blue))) + `(doom-modeline-persp-buffer-not-in-persp ((,class :inherit italic :foreground ,yellow-active))) + `(doom-modeline-persp-name ((,class :foreground ,fg-active))) + `(doom-modeline-project-dir ((,class :inherit bold :foreground ,blue-active))) + `(doom-modeline-project-parent-dir ((,class :foreground ,blue-active))) + `(doom-modeline-project-root-dir ((,class :foreground ,fg-active))) + `(doom-modeline-unread-number ((,class :inherit italic :foreground ,fg-active))) + `(doom-modeline-urgent ((,class :inherit bold :foreground ,red-active))) + `(doom-modeline-warning ((,class :inherit bold :foreground ,yellow-active))) +;;;;; easy-jekyll + `(easy-jekyll-help-face ((,class :background ,bg-dim :foreground ,blue-alt-other))) +;;;;; ebdb + `(ebdb-address-default ((,class :foreground ,fg-special-calm))) + `(ebdb-defunct ((,class :inherit shadow))) + `(ebdb-field-hidden ((,class :foreground ,magenta))) + `(ebdb-label ((,class :foreground ,cyan-alt-other))) + `(ebdb-mail-default ((,class :foreground ,fg-main))) + `(ebdb-mail-primary ((,class :foreground ,magenta-alt))) + `(ebdb-marked ((,class :background ,cyan-intense-bg))) + `(ebdb-organization-name ((,class :foreground ,red-alt-other))) + `(ebdb-person-name ((,class :foreground ,magenta-alt-other))) + `(ebdb-phone-default ((,class :foreground ,cyan))) + `(eieio-custom-slot-tag-face ((,class :foreground ,red-alt))) +;;;;; ediff + `(ediff-current-diff-A ((,class :inherit modus-themes-diff-removed))) + `(ediff-current-diff-Ancestor ((,class ,@(modus-themes--diff + bg-special-cold fg-special-cold + blue-nuanced-bg blue)))) + `(ediff-current-diff-B ((,class :inherit modus-themes-diff-added))) + `(ediff-current-diff-C ((,class :inherit modus-themes-diff-changed))) + `(ediff-even-diff-A ((,class :background ,bg-alt))) + `(ediff-even-diff-Ancestor ((,class :background ,bg-alt))) + `(ediff-even-diff-B ((,class :background ,bg-alt))) + `(ediff-even-diff-C ((,class :background ,bg-alt))) + `(ediff-fine-diff-A ((,class :inherit modus-themes-diff-refine-removed))) + `(ediff-fine-diff-Ancestor ((,class :inherit modus-themes-refine-cyan))) + `(ediff-fine-diff-B ((,class :inherit modus-themes-diff-refine-added))) + `(ediff-fine-diff-C ((,class :inherit modus-themes-diff-refine-changed))) + `(ediff-odd-diff-A ((,class :inherit ediff-even-diff-A))) + `(ediff-odd-diff-Ancestor ((,class :inherit ediff-even-diff-Ancestor))) + `(ediff-odd-diff-B ((,class :inherit ediff-even-diff-B))) + `(ediff-odd-diff-C ((,class :inherit ediff-even-diff-C))) +;;;;; ein (Emacs IPython Notebook) + `(ein:basecell-input-area-face ((,class :background ,bg-dim :extend t))) + `(ein:cell-output-area (( ))) + `(ein:cell-output-area-error ((,class :background ,red-nuanced-bg :extend t))) + `(ein:cell-output-stderr ((,class :background ,red-nuanced-bg :extend t))) + `(ein:markdowncell-input-area-face (( ))) + `(ein:notification-tab-normal ((,class :underline t))) +;;;;; eglot + `(eglot-mode-line ((,class :inherit modus-themes-bold :foreground ,magenta-active))) +;;;;; el-search + `(el-search-highlight-in-prompt-face ((,class :inherit bold :foreground ,magenta-alt))) + `(el-search-match ((,class :inherit modus-themes-search-success))) + `(el-search-other-match ((,class :inherit modus-themes-special-mild))) + `(el-search-occur-match ((,class :inherit modus-themes-special-calm))) +;;;;; eldoc + ;; NOTE: see https://github.com/purcell/package-lint/issues/187 + (list 'eldoc-highlight-function-argument `((,class :inherit bold + :background ,yellow-nuanced-bg + :foreground ,yellow-alt-other))) +;;;;; eldoc-box + `(eldoc-box-body ((,class :background ,bg-alt :foreground ,fg-main))) + `(eldoc-box-border ((,class :background ,fg-alt))) +;;;;; elfeed + `(elfeed-log-date-face ((,class :inherit elfeed-search-date-face))) + `(elfeed-log-debug-level-face ((,class :inherit elfeed-search-filter-face))) + `(elfeed-log-error-level-face ((,class :inherit error))) + `(elfeed-log-info-level-face ((,class :inherit success))) + `(elfeed-log-warn-level-face ((,class :inherit warning))) + `(elfeed-search-date-face ((,class :foreground ,cyan))) + `(elfeed-search-feed-face ((,class :foreground ,blue-faint))) + `(elfeed-search-filter-face ((,class :inherit bold :foreground ,magenta-active))) + `(elfeed-search-last-update-face ((,class :inherit bold :foreground ,cyan-active))) + `(elfeed-search-tag-face ((,class :foreground ,magenta-alt-faint))) + `(elfeed-search-title-face ((,class :foreground ,fg-dim))) + `(elfeed-search-unread-count-face ((,class :inherit bold :foreground ,fg-active))) + `(elfeed-search-unread-title-face ((,class :inherit bold :foreground ,fg-main))) +;;;;; elfeed-score + `(elfeed-score-date-face ((,class :foreground ,blue))) + `(elfeed-score-debug-level-face ((,class :foreground ,magenta-alt-other))) + `(elfeed-score-error-level-face ((,class :foreground ,red))) + `(elfeed-score-info-level-face ((,class :foreground ,cyan))) + `(elfeed-score-warn-level-face ((,class :foreground ,yellow))) +;;;;; elpher + `(elpher-gemini-heading1 ((,class :inherit modus-themes-heading-1))) + `(elpher-gemini-heading2 ((,class :inherit modus-themes-heading-2))) + `(elpher-gemini-heading3 ((,class :inherit modus-themes-heading-3))) +;;;;; embark + `(embark-keybinding ((,class :inherit modus-themes-key-binding))) + `(embark-collect-marked ((,class :inherit modus-themes-mark-sel))) +;;;;; ement (ement.el) + `(ement-room-fully-read-marker ((,class :background ,cyan-subtle-bg))) + `(ement-room-membership ((,class :inherit shadow))) + `(ement-room-mention ((,class :background ,bg-hl-alt-intense))) + `(ement-room-name ((,class :inherit bold))) + `(ement-room-reactions ((,class :inherit shadow))) + `(ement-room-read-receipt-marker ((,class :background ,yellow-subtle-bg))) + `(ement-room-self ((,class :inherit bold :foreground ,magenta))) + `(ement-room-self-message ((,class :foreground ,magenta-faint))) + `(ement-room-timestamp ((,class :inherit shadow))) + `(ement-room-timestamp-header ((,class :inherit bold :foreground ,cyan))) + `(ement-room-user ((,class :inherit bold :foreground ,blue))) +;;;;; emms + `(emms-browser-album-face ((,class :foreground ,magenta-alt-other))) + `(emms-browser-artist-face ((,class :foreground ,cyan))) + `(emms-browser-composer-face ((,class :foreground ,magenta-alt))) + `(emms-browser-performer-face ((,class :inherit emms-browser-artist-face))) + `(emms-browser-track-face ((,class :inherit emms-playlist-track-face))) + `(emms-browser-year/genre-face ((,class :foreground ,cyan-alt-other))) + `(emms-playlist-track-face ((,class :foreground ,blue-alt))) + `(emms-playlist-selected-face ((,class :inherit bold :foreground ,blue-alt-other))) + `(emms-metaplaylist-mode-current-face ((,class :inherit emms-playlist-selected-face))) + `(emms-metaplaylist-mode-face ((,class :foreground ,cyan))) +;;;;; enh-ruby-mode (enhanced-ruby-mode) + `(enh-ruby-heredoc-delimiter-face ((,class :inherit font-lock-constant-face))) + `(enh-ruby-op-face ((,class :foreground ,fg-main))) + `(enh-ruby-regexp-delimiter-face ((,class :inherit font-lock-regexp-grouping-construct))) + `(enh-ruby-regexp-face ((,class :inherit font-lock-string-face))) + `(enh-ruby-string-delimiter-face ((,class :inherit font-lock-string-face))) + `(erm-syn-errline ((,class :inherit modus-themes-lang-error))) + `(erm-syn-warnline ((,class :inherit modus-themes-lang-warning))) +;;;;; epa + `(epa-field-body ((,class :foreground ,fg-main))) + `(epa-field-name ((,class :inherit bold :foreground ,fg-dim))) + `(epa-mark ((,class :inherit bold :foreground ,magenta))) + `(epa-string ((,class :foreground ,blue-alt))) + `(epa-validity-disabled ((,class :foreground ,red))) + `(epa-validity-high ((,class :inherit bold :foreground ,cyan))) + `(epa-validity-low ((,class :inherit shadow))) + `(epa-validity-medium ((,class :foreground ,green-alt))) +;;;;; equake + `(equake-buffer-face ((,class :background ,bg-main :foreground ,fg-main))) + `(equake-shell-type-eshell ((,class :background ,bg-inactive :foreground ,blue-active))) + `(equake-shell-type-rash ((,class :background ,bg-inactive :foreground ,red-active))) + `(equake-shell-type-shell ((,class :background ,bg-inactive :foreground ,cyan-active))) + `(equake-shell-type-term ((,class :background ,bg-inactive :foreground ,yellow-active))) + `(equake-shell-type-vterm ((,class :background ,bg-inactive :foreground ,magenta-active))) + `(equake-tab-active ((,class :background ,fg-alt :foreground ,bg-alt))) + `(equake-tab-inactive ((,class :foreground ,fg-inactive))) +;;;;; erc + `(erc-action-face ((,class :foreground ,cyan-alt-other))) + `(erc-bold-face ((,class :inherit bold))) + `(erc-button ((,class :inherit button))) + `(erc-command-indicator-face ((,class :inherit bold :foreground ,cyan-alt))) + `(erc-current-nick-face ((,class :inherit bold :foreground ,red-alt))) + `(erc-dangerous-host-face ((,class :inherit modus-themes-intense-red))) + `(erc-direct-msg-face ((,class :foreground ,fg-special-warm))) + `(erc-error-face ((,class :inherit bold :foreground ,red))) + `(erc-fool-face ((,class :inherit shadow))) + `(erc-header-line ((,class :background ,bg-header :foreground ,fg-header))) + `(erc-input-face ((,class :foreground ,magenta))) + `(erc-inverse-face ((,class :inherit erc-default-face :inverse-video t))) + `(erc-keyword-face ((,class :inherit bold :foreground ,magenta-alt-other))) + `(erc-my-nick-face ((,class :inherit bold :foreground ,magenta))) + `(erc-my-nick-prefix-face ((,class :inherit erc-my-nick-face))) + `(erc-nick-default-face ((,class :inherit bold :foreground ,blue))) + `(erc-nick-msg-face ((,class :inherit warning))) + `(erc-nick-prefix-face ((,class :inherit erc-nick-default-face))) + `(erc-notice-face ((,class :inherit font-lock-comment-face))) + `(erc-pal-face ((,class :inherit bold :foreground ,magenta-alt))) + `(erc-prompt-face ((,class :inherit modus-themes-prompt))) + `(erc-timestamp-face ((,class :foreground ,cyan))) + `(erc-underline-face ((,class :underline t))) + `(bg:erc-color-face0 ((,class :background "white"))) + `(bg:erc-color-face1 ((,class :background "black"))) + `(bg:erc-color-face10 ((,class :background ,cyan-subtle-bg))) + `(bg:erc-color-face11 ((,class :background ,cyan-intense-bg))) + `(bg:erc-color-face12 ((,class :background ,blue-subtle-bg))) + `(bg:erc-color-face13 ((,class :background ,magenta-subtle-bg))) + `(bg:erc-color-face14 ((,class :background "gray60"))) + `(bg:erc-color-face15 ((,class :background "gray80"))) + `(bg:erc-color-face2 ((,class :background ,blue-intense-bg))) + `(bg:erc-color-face3 ((,class :background ,green-intense-bg))) + `(bg:erc-color-face4 ((,class :background ,red-subtle-bg))) + `(bg:erc-color-face5 ((,class :background ,red-intense-bg))) + `(bg:erc-color-face6 ((,class :background ,magenta-refine-bg))) + `(bg:erc-color-face7 ((,class :background ,yellow-subtle-bg))) + `(bg:erc-color-face8 ((,class :background ,yellow-refine-bg))) + `(bg:erc-color-face9 ((,class :background ,green-subtle-bg))) + `(fg:erc-color-face0 ((,class :foreground "white"))) + `(fg:erc-color-face1 ((,class :foreground "black"))) + `(fg:erc-color-face10 ((,class :foreground ,cyan))) + `(fg:erc-color-face11 ((,class :foreground ,cyan-alt-other))) + `(fg:erc-color-face12 ((,class :foreground ,blue))) + `(fg:erc-color-face13 ((,class :foreground ,magenta-alt))) + `(fg:erc-color-face14 ((,class :foreground "gray60"))) + `(fg:erc-color-face15 ((,class :foreground "gray80"))) + `(fg:erc-color-face2 ((,class :foreground ,blue-alt-other))) + `(fg:erc-color-face3 ((,class :foreground ,green))) + `(fg:erc-color-face4 ((,class :foreground ,red))) + `(fg:erc-color-face5 ((,class :foreground ,red-alt))) + `(fg:erc-color-face6 ((,class :foreground ,magenta-alt-other))) + `(fg:erc-color-face7 ((,class :foreground ,yellow-alt-other))) + `(fg:erc-color-face8 ((,class :foreground ,yellow-alt))) + `(fg:erc-color-face9 ((,class :foreground ,green-alt-other))) +;;;;; eros + `(eros-result-overlay-face ((,class :box (:line-width -1 :color ,blue) + :background ,bg-dim :foreground ,fg-dim))) +;;;;; ert + `(ert-test-result-expected ((,class :inherit modus-themes-intense-green))) + `(ert-test-result-unexpected ((,class :inherit modus-themes-intense-red))) +;;;;; eshell + `(eshell-ls-archive ((,class :foreground ,cyan-alt))) + `(eshell-ls-backup ((,class :inherit shadow))) + `(eshell-ls-clutter ((,class :foreground ,red-alt))) + `(eshell-ls-directory ((,class :foreground ,blue-alt))) + `(eshell-ls-executable ((,class :foreground ,magenta-alt))) + `(eshell-ls-missing ((,class :inherit modus-themes-intense-red))) + `(eshell-ls-product ((,class :inherit shadow))) + `(eshell-ls-readonly ((,class :foreground ,yellow-faint))) + `(eshell-ls-special ((,class :foreground ,magenta))) + `(eshell-ls-symlink ((,class :inherit modus-themes-link-symlink))) + `(eshell-ls-unreadable ((,class :background ,bg-inactive :foreground ,fg-inactive))) + `(eshell-prompt ((,class :inherit modus-themes-prompt))) +;;;;; eshell-fringe-status + `(eshell-fringe-status-failure ((,class :inherit error))) + `(eshell-fringe-status-success ((,class :inherit success))) +;;;;; eshell-git-prompt + `(eshell-git-prompt-add-face ((,class :foreground ,magenta-alt-other))) + `(eshell-git-prompt-branch-face ((,class :foreground ,magenta-alt))) + `(eshell-git-prompt-directory-face ((,class :inherit bold :foreground ,blue))) + `(eshell-git-prompt-exit-fail-face ((,class :inherit error))) + `(eshell-git-prompt-exit-success-face ((,class :inherit success))) + `(eshell-git-prompt-modified-face ((,class :foreground ,yellow))) + `(eshell-git-prompt-powerline-clean-face ((,class :background ,green-refine-bg))) + `(eshell-git-prompt-powerline-dir-face ((,class :background ,blue-refine-bg))) + `(eshell-git-prompt-powerline-not-clean-face ((,class :background ,yellow-fringe-bg))) + `(eshell-git-prompt-robyrussell-branch-face ((,class :foreground ,magenta-alt))) + `(eshell-git-prompt-robyrussell-git-dirty-face ((,class :foreground ,yellow))) + `(eshell-git-prompt-robyrussell-git-face ((,class :foreground ,magenta-alt-other))) +;;;;; eshell-prompt-extras (epe) + `(epe-dir-face ((,class :inherit bold :foreground ,blue))) + `(epe-git-dir-face ((,class :foreground ,red-alt-other))) + `(epe-git-face ((,class :foreground ,magenta-alt))) + `(epe-pipeline-delimiter-face ((,class :inherit shadow))) + `(epe-pipeline-host-face ((,class :foreground ,fg-main))) + `(epe-pipeline-time-face ((,class :foreground ,fg-main))) + `(epe-pipeline-user-face ((,class :foreground ,magenta-alt-other))) + `(epe-remote-face ((,class :inherit (shadow modus-themes-slant)))) + `(epe-status-face ((,class :foreground ,magenta-alt-other))) + `(epe-venv-face ((,class :inherit (shadow modus-themes-slant)))) +;;;;; eshell-syntax-highlighting + `(eshell-syntax-highlighting-directory-face ((,class :inherit eshell-ls-directory))) + `(eshell-syntax-highlighting-invalid-face ((,class :foreground ,red))) + `(eshell-syntax-highlighting-shell-command-face ((,class :foreground ,fg-main))) +;;;;; evil-mode + `(evil-ex-commands ((,class :foreground ,magenta-alt-other))) + `(evil-ex-info ((,class :foreground ,cyan-alt-other))) + `(evil-ex-lazy-highlight ((,class :inherit modus-themes-search-success-lazy))) + `(evil-ex-search ((,class :inherit modus-themes-search-success))) + `(evil-ex-substitute-matches ((,class :inherit modus-themes-refine-yellow :underline t))) + `(evil-ex-substitute-replacement ((,class :inherit modus-themes-search-success))) +;;;;; evil-goggles + `(evil-goggles-change-face ((,class :inherit modus-themes-refine-yellow))) + `(evil-goggles-commentary-face ((,class :inherit (modus-themes-subtle-neutral modus-themes-slant)))) + `(evil-goggles-default-face ((,class :inherit modus-themes-subtle-neutral))) + `(evil-goggles-delete-face ((,class :inherit modus-themes-refine-red))) + `(evil-goggles-fill-and-move-face ((,class :inherit evil-goggles-default-face))) + `(evil-goggles-indent-face ((,class :inherit evil-goggles-default-face))) + `(evil-goggles-join-face ((,class :inherit modus-themes-subtle-green))) + `(evil-goggles-nerd-commenter-face ((,class :inherit evil-goggles-commentary-face))) + `(evil-goggles-paste-face ((,class :inherit modus-themes-subtle-cyan))) + `(evil-goggles-record-macro-face ((,class :inherit modus-themes-special-cold))) + `(evil-goggles-replace-with-register-face ((,class :inherit modus-themes-refine-magenta))) + `(evil-goggles-set-marker-face ((,class :inherit modus-themes-intense-magenta))) + `(evil-goggles-shift-face ((,class :inherit evil-goggles-default-face))) + `(evil-goggles-surround-face ((,class :inherit evil-goggles-default-face))) + `(evil-goggles-yank-face ((,class :inherit modus-themes-subtle-blue))) +;;;;; evil-snipe + `(evil-snipe-first-match-face ((,class :inherit (bold modus-themes-intense-blue)))) + `(evil-snipe-matches-face ((,class :inherit modus-themes-refine-magenta))) +;;;;; evil-visual-mark-mode + `(evil-visual-mark-face ((,class :inherit modus-themes-intense-magenta))) +;;;;; eww + `(eww-invalid-certificate ((,class :foreground ,red-faint))) + `(eww-valid-certificate ((,class :foreground ,blue-faint))) + `(eww-form-checkbox ((,class :inherit eww-form-text))) + `(eww-form-file ((,class :inherit eww-form-submit))) + `(eww-form-select ((,class :inherit eww-form-submit))) + `(eww-form-submit ((,class :inherit modus-themes-box-button))) + `(eww-form-text ((,class :inherit widget-field))) + `(eww-form-textarea ((,class :inherit eww-form-text))) +;;;;; eyebrowse + `(eyebrowse-mode-line-active ((,class :inherit bold :foreground ,blue-active))) +;;;;; fancy-dabbrev + `(fancy-dabbrev-menu-face ((,class :background ,bg-alt :foreground ,fg-alt))) + `(fancy-dabbrev-preview-face ((,class :inherit shadow :underline t))) + `(fancy-dabbrev-selection-face ((,class :inherit (modus-themes-intense-cyan bold)))) +;;;;; flycheck + `(flycheck-error ((,class :inherit modus-themes-lang-error))) + `(flycheck-error-list-checker-name ((,class :foreground ,magenta-active))) + `(flycheck-error-list-column-number ((,class :foreground ,fg-special-cold))) + `(flycheck-error-list-error ((,class :inherit modus-themes-bold :foreground ,red))) + `(flycheck-error-list-filename ((,class :foreground ,blue))) + `(flycheck-error-list-highlight ((,class :inherit modus-themes-hl-line))) + `(flycheck-error-list-id ((,class :foreground ,magenta-alt-other))) + `(flycheck-error-list-id-with-explainer ((,class :inherit flycheck-error-list-id :box t))) + `(flycheck-error-list-info ((,class :foreground ,cyan))) + `(flycheck-error-list-line-number ((,class :foreground ,fg-special-warm))) + `(flycheck-error-list-warning ((,class :foreground ,yellow))) + `(flycheck-fringe-error ((,class :inherit modus-themes-fringe-red))) + `(flycheck-fringe-info ((,class :inherit modus-themes-fringe-cyan))) + `(flycheck-fringe-warning ((,class :inherit modus-themes-fringe-yellow))) + `(flycheck-info ((,class :inherit modus-themes-lang-note))) + `(flycheck-verify-select-checker ((,class :box (:line-width 1 :color nil :style released-button)))) + `(flycheck-warning ((,class :inherit modus-themes-lang-warning))) +;;;;; flycheck-color-mode-line + `(flycheck-color-mode-line-error-face ((,class :inherit flycheck-fringe-error))) + `(flycheck-color-mode-line-info-face ((,class :inherit flycheck-fringe-info))) + `(flycheck-color-mode-line-running-face ((,class :inherit italic :foreground ,fg-inactive))) + `(flycheck-color-mode-line-info-face ((,class :inherit flycheck-fringe-warning))) +;;;;; flycheck-indicator + `(flycheck-indicator-disabled ((,class :inherit modus-themes-slant :foreground ,fg-inactive))) + `(flycheck-indicator-error ((,class :inherit modus-themes-bold :foreground ,red-active))) + `(flycheck-indicator-info ((,class :inherit modus-themes-bold :foreground ,blue-active))) + `(flycheck-indicator-running ((,class :inherit modus-themes-bold :foreground ,magenta-active))) + `(flycheck-indicator-success ((,class :inherit (modus-themes-bold modus-themes-grue-active)))) + `(flycheck-indicator-warning ((,class :inherit modus-themes-bold :foreground ,yellow-active))) +;;;;; flycheck-posframe + `(flycheck-posframe-background-face ((,class :background ,bg-alt))) + `(flycheck-posframe-border-face ((,class :inherit shadow))) + `(flycheck-posframe-error-face ((,class :inherit bold :foreground ,red))) + `(flycheck-posframe-face ((,class :inherit modus-themes-slant :foreground ,fg-main))) + `(flycheck-posframe-info-face ((,class :inherit bold :foreground ,cyan))) + `(flycheck-posframe-warning-face ((,class :inherit bold :foreground ,yellow))) +;;;;; flymake + `(flymake-error ((,class :inherit modus-themes-lang-error))) + `(flymake-note ((,class :inherit modus-themes-lang-note))) + `(flymake-warning ((,class :inherit modus-themes-lang-warning))) +;;;;; flyspell + `(flyspell-duplicate ((,class :inherit modus-themes-lang-warning))) + `(flyspell-incorrect ((,class :inherit modus-themes-lang-error))) +;;;;; flx + `(flx-highlight-face ((,class :inherit modus-themes-completion-match-0))) +;;;;; freeze-it + `(freeze-it-show ((,class :background ,bg-dim :foreground ,fg-special-warm))) +;;;;; focus + `(focus-unfocused ((,class :foreground ,fg-unfocused))) +;;;;; fold-this + `(fold-this-overlay ((,class :inherit modus-themes-special-mild))) +;;;;; font-lock + `(font-lock-builtin-face ((,class :inherit modus-themes-bold + ,@(modus-themes--syntax-extra + magenta-alt magenta-alt-faint + magenta magenta-faint)))) + `(font-lock-comment-delimiter-face ((,class :inherit font-lock-comment-face))) + `(font-lock-comment-face ((,class :inherit modus-themes-slant + ,@(modus-themes--syntax-comment + fg-alt fg-comment-yellow yellow-alt-other-faint)))) + `(font-lock-constant-face ((,class ,@(modus-themes--syntax-extra + blue-alt-other blue-alt-other-faint + magenta-alt-other magenta-alt-other-faint)))) + `(font-lock-doc-face ((,class :inherit modus-themes-slant + ,@(modus-themes--syntax-string + fg-docstring fg-special-cold + fg-special-mild fg-special-calm + fg-special-mild magenta-nuanced-fg)))) + `(font-lock-function-name-face ((,class ,@(modus-themes--syntax-extra + magenta magenta-faint + magenta-alt magenta-alt-faint)))) + `(font-lock-keyword-face ((,class :inherit modus-themes-bold + ,@(modus-themes--syntax-extra + magenta-alt-other magenta-alt-other-faint + cyan cyan-faint)))) + `(font-lock-negation-char-face ((,class :inherit modus-themes-bold + ,@(modus-themes--syntax-foreground + yellow yellow-faint)))) + `(font-lock-preprocessor-face ((,class ,@(modus-themes--syntax-extra + red-alt-other red-alt-other-faint + cyan-alt-other cyan-alt-faint)))) + `(font-lock-regexp-grouping-backslash ((,class :inherit modus-themes-bold + ,@(modus-themes--syntax-string + fg-escape-char-backslash yellow-alt-faint + yellow-alt magenta-alt + red-faint green-alt-other-faint)))) + `(font-lock-regexp-grouping-construct ((,class :inherit modus-themes-bold + ,@(modus-themes--syntax-string + fg-escape-char-construct red-alt-other-faint + red-alt-other blue-alt-other + blue-faint blue-alt-other-faint)))) + `(font-lock-string-face ((,class ,@(modus-themes--syntax-string + blue-alt blue-alt-faint + green-alt-other red-alt-other + green-alt-faint red-alt-faint)))) + `(font-lock-type-face ((,class :inherit modus-themes-bold + ,@(modus-themes--syntax-extra + cyan-alt-other cyan-alt-faint + magenta-alt-other magenta-alt-other-faint)))) + `(font-lock-variable-name-face ((,class ,@(modus-themes--syntax-extra + cyan cyan-faint + blue-alt blue-alt-faint)))) + `(font-lock-warning-face ((,class :inherit modus-themes-bold + ,@(modus-themes--syntax-comment + yellow red yellow-alt-faint red-faint)))) +;;;;; forge + `(forge-post-author ((,class :inherit bold :foreground ,fg-main))) + `(forge-post-date ((,class :foreground ,fg-special-cold))) + `(forge-topic-closed ((,class :inherit shadow))) + `(forge-topic-merged ((,class :inherit shadow))) + `(forge-topic-open ((,class :foreground ,fg-special-mild))) + `(forge-topic-unmerged ((,class :inherit modus-themes-slant :foreground ,magenta))) + `(forge-topic-unread ((,class :inherit bold :foreground ,fg-main))) +;;;;; fountain-mode + `(fountain-character ((,class :foreground ,blue-alt-other))) + `(fountain-comment ((,class :inherit font-lock-comment-face))) + `(fountain-dialog ((,class :foreground ,blue-alt))) + `(fountain-metadata-key ((,class :foreground ,green-alt-other))) + `(fountain-metadata-value ((,class :foreground ,blue))) + `(fountain-non-printing ((,class :inherit shadow))) + `(fountain-note ((,class :inherit modus-themes-slant :foreground ,yellow))) + `(fountain-page-break ((,class :inherit bold :foreground ,red-alt))) + `(fountain-page-number ((,class :inherit bold :foreground ,red-alt-other))) + `(fountain-paren ((,class :foreground ,cyan))) + `(fountain-scene-heading ((,class :inherit bold :foreground ,blue-nuanced-fg))) + `(fountain-section-heading ((,class :inherit modus-themes-heading-1))) + `(fountain-section-heading-1 ((,class :inherit modus-themes-heading-1))) + `(fountain-section-heading-2 ((,class :inherit modus-themes-heading-2))) + `(fountain-section-heading-3 ((,class :inherit modus-themes-heading-3))) + `(fountain-section-heading-4 ((,class :inherit modus-themes-heading-4))) + `(fountain-section-heading-5 ((,class :inherit modus-themes-heading-5))) + `(fountain-synopsis ((,class :foreground ,cyan-alt))) + `(fountain-trans ((,class :foreground ,yellow-alt-other))) +;;;;; geiser + `(geiser-font-lock-autodoc-current-arg ((,class :inherit bold + :background ,yellow-nuanced-bg + :foreground ,yellow-alt-other))) + `(geiser-font-lock-autodoc-identifier ((,class :foreground ,cyan))) + `(geiser-font-lock-doc-button ((,class :inherit button :foreground ,fg-docstring))) + `(geiser-font-lock-doc-link ((,class :inherit button))) + `(geiser-font-lock-error-link ((,class :inherit button :foreground ,red))) + `(geiser-font-lock-image-button ((,class :inherit button :foreground ,green-alt))) + `(geiser-font-lock-repl-input ((,class :inherit bold))) + `(geiser-font-lock-repl-output ((,class :inherit font-lock-keyword-face))) + `(geiser-font-lock-repl-prompt ((,class :inherit modus-themes-prompt))) + `(geiser-font-lock-xref-header ((,class :inherit bold))) + `(geiser-font-lock-xref-link ((,class :inherit button))) +;;;;; git-commit + `(git-commit-comment-action ((,class :inherit font-lock-comment-face))) + `(git-commit-comment-branch-local ((,class :inherit font-lock-comment-face :foreground ,blue-alt))) + `(git-commit-comment-branch-remote ((,class :inherit font-lock-comment-face :foreground ,magenta-alt))) + `(git-commit-comment-detached ((,class :inherit font-lock-comment-face :foreground ,cyan-alt))) + `(git-commit-comment-file ((,class :inherit font-lock-comment-face :foreground ,cyan))) + `(git-commit-comment-heading ((,class :inherit (bold font-lock-comment-face)))) + `(git-commit-keyword ((,class :foreground ,magenta))) + `(git-commit-known-pseudo-header ((,class :foreground ,cyan-alt-other))) + `(git-commit-nonempty-second-line ((,class :inherit error))) + `(git-commit-overlong-summary ((,class :inherit warning))) + `(git-commit-pseudo-header ((,class :foreground ,blue))) + `(git-commit-summary ((,class :inherit bold :foreground ,blue))) +;;;;; git-gutter + `(git-gutter:added ((,class :inherit modus-themes-grue-background-active))) + `(git-gutter:deleted ((,class :inherit modus-themes-fringe-red))) + `(git-gutter:modified ((,class :inherit modus-themes-fringe-yellow))) + `(git-gutter:separator ((,class :inherit modus-themes-fringe-cyan))) + `(git-gutter:unchanged ((,class :inherit modus-themes-fringe-magenta))) +;;;;; git-gutter-fr + `(git-gutter-fr:added ((,class :inherit modus-themes-grue-background-active))) + `(git-gutter-fr:deleted ((,class :inherit modus-themes-fringe-red))) + `(git-gutter-fr:modified ((,class :inherit modus-themes-fringe-yellow))) +;;;;; git-rebase + `(git-rebase-comment-hash ((,class :inherit font-lock-comment-face :foreground ,cyan))) + `(git-rebase-comment-heading ((,class :inherit (bold font-lock-comment-face)))) + `(git-rebase-description ((,class :foreground ,fg-main))) + `(git-rebase-hash ((,class :foreground ,cyan-alt-other))) +;;;;; git-timemachine + `(git-timemachine-commit ((,class :inherit bold :foreground ,yellow-active))) + `(git-timemachine-minibuffer-author-face ((,class :foreground ,fg-special-warm))) + `(git-timemachine-minibuffer-detail-face ((,class :foreground ,red-alt))) +;;;;; gnus + `(gnus-button ((,class :inherit button))) + `(gnus-cite-1 ((,class :inherit message-cited-text-1))) + `(gnus-cite-2 ((,class :inherit message-cited-text-2))) + `(gnus-cite-3 ((,class :inherit message-cited-text-3))) + `(gnus-cite-4 ((,class :inherit message-cited-text-4))) + `(gnus-cite-5 ((,class :inherit gnus-cite-1))) + `(gnus-cite-6 ((,class :inherit gnus-cite-2))) + `(gnus-cite-7 ((,class :inherit gnus-cite-3))) + `(gnus-cite-8 ((,class :inherit gnus-cite-4))) + `(gnus-cite-9 ((,class :inherit gnus-cite-1))) + `(gnus-cite-10 ((,class :inherit gnus-cite-2))) + `(gnus-cite-11 ((,class :inherit gnus-cite-3))) + `(gnus-cite-attribution ((,class :inherit italic :foreground ,fg-main))) + `(gnus-emphasis-bold ((,class :inherit bold))) + `(gnus-emphasis-bold-italic ((,class :inherit bold-italic))) + `(gnus-emphasis-highlight-words ((,class :inherit modus-themes-refine-yellow))) + `(gnus-emphasis-italic ((,class :inherit italic))) + `(gnus-emphasis-underline-bold ((,class :inherit gnus-emphasis-bold :underline t))) + `(gnus-emphasis-underline-bold-italic ((,class :inherit gnus-emphasis-bold-italic :underline t))) + `(gnus-emphasis-underline-italic ((,class :inherit gnus-emphasis-italic :underline t))) + `(gnus-group-mail-1 ((,class :inherit bold :foreground ,magenta-alt))) + `(gnus-group-mail-1-empty ((,class :foreground ,magenta-alt))) + `(gnus-group-mail-2 ((,class :inherit bold :foreground ,magenta))) + `(gnus-group-mail-2-empty ((,class :foreground ,magenta))) + `(gnus-group-mail-3 ((,class :inherit bold :foreground ,magenta-alt-other))) + `(gnus-group-mail-3-empty ((,class :foreground ,magenta-alt-other))) + `(gnus-group-mail-low ((,class :inherit bold :foreground ,magenta-nuanced-fg))) + `(gnus-group-mail-low-empty ((,class :foreground ,magenta-nuanced-fg))) + `(gnus-group-news-1 ((,class :inherit bold :foreground ,green))) + `(gnus-group-news-1-empty ((,class :foreground ,green))) + `(gnus-group-news-2 ((,class :inherit bold :foreground ,cyan))) + `(gnus-group-news-2-empty ((,class :foreground ,cyan))) + `(gnus-group-news-3 ((,class :inherit bold :foreground ,yellow-nuanced-fg))) + `(gnus-group-news-3-empty ((,class :foreground ,yellow-nuanced-fg))) + `(gnus-group-news-4 ((,class :inherit bold :foreground ,cyan-nuanced-fg))) + `(gnus-group-news-4-empty ((,class :foreground ,cyan-nuanced-fg))) + `(gnus-group-news-5 ((,class :inherit bold :foreground ,red-nuanced-fg))) + `(gnus-group-news-5-empty ((,class :foreground ,red-nuanced-fg))) + `(gnus-group-news-6 ((,class :inherit bold :foreground ,fg-unfocused))) + `(gnus-group-news-6-empty ((,class :foreground ,fg-unfocused))) + `(gnus-group-news-low ((,class :inherit bold :foreground ,green-nuanced-fg))) + `(gnus-group-news-low-empty ((,class :foreground ,green-nuanced-fg))) + `(gnus-header-content ((,class :inherit message-header-other))) + `(gnus-header-from ((,class :inherit message-header-to :underline nil))) + `(gnus-header-name ((,class :inherit message-header-name))) + `(gnus-header-newsgroups ((,class :inherit message-header-newsgroups))) + `(gnus-header-subject ((,class :inherit message-header-subject))) + `(gnus-server-agent ((,class :inherit bold :foreground ,cyan))) + `(gnus-server-closed ((,class :inherit bold :foreground ,magenta))) + `(gnus-server-cloud ((,class :inherit bold :foreground ,cyan-alt))) + `(gnus-server-cloud-host ((,class :inherit modus-themes-refine-cyan))) + `(gnus-server-denied ((,class :inherit bold :foreground ,red))) + `(gnus-server-offline ((,class :inherit bold :foreground ,yellow))) + `(gnus-server-opened ((,class :inherit bold :foreground ,green))) + `(gnus-signature ((,class :inherit italic :foreground ,fg-special-cold))) + `(gnus-splash ((,class :inherit shadow))) + `(gnus-summary-cancelled ((,class :inherit modus-themes-mark-alt :extend t))) + `(gnus-summary-high-ancient ((,class :inherit bold :foreground ,fg-alt))) + `(gnus-summary-high-read ((,class :inherit bold :foreground ,fg-special-cold))) + `(gnus-summary-high-ticked ((,class :inherit bold :foreground ,red-alt-other))) + `(gnus-summary-high-undownloaded ((,class :inherit bold :foreground ,yellow))) + `(gnus-summary-high-unread ((,class :inherit bold :foreground ,fg-main))) + `(gnus-summary-low-ancient ((,class :inherit italic :foreground ,fg-alt))) + `(gnus-summary-low-read ((,class :inherit italic :foreground ,fg-alt))) + `(gnus-summary-low-ticked ((,class :inherit italic :foreground ,red-refine-fg))) + `(gnus-summary-low-undownloaded ((,class :inherit italic :foreground ,yellow-refine-fg))) + `(gnus-summary-low-unread ((,class :inherit bold :foreground ,fg-special-cold))) + `(gnus-summary-normal-ancient ((,class :foreground ,fg-special-calm))) + `(gnus-summary-normal-read ((,class :inherit shadow))) + `(gnus-summary-normal-ticked ((,class :foreground ,red-alt-other))) + `(gnus-summary-normal-undownloaded ((,class :foreground ,yellow))) + `(gnus-summary-normal-unread ((,class :foreground ,fg-main))) + `(gnus-summary-selected ((,class :inherit highlight :extend t))) +;;;;; gotest + `(go-test--ok-face ((,class :inherit success))) + `(go-test--error-face ((,class :inherit error))) + `(go-test--warning-face ((,class :inherit warning))) + `(go-test--pointer-face ((,class :foreground ,magenta-alt-other))) + `(go-test--standard-face ((,class :foreground ,fg-special-cold))) +;;;;; golden-ratio-scroll-screen + `(golden-ratio-scroll-highlight-line-face ((,class :background ,cyan-subtle-bg :foreground ,fg-main))) +;;;;; helm + `(helm-M-x-key ((,class :inherit modus-themes-key-binding))) + `(helm-action ((,class :underline t))) + `(helm-bookmark-addressbook ((,class :foreground ,green-alt))) + `(helm-bookmark-directory ((,class :inherit bold :foreground ,blue))) + `(helm-bookmark-file ((,class :foreground ,fg-main))) + `(helm-bookmark-file-not-found ((,class :background ,bg-alt :foreground ,fg-alt))) + `(helm-bookmark-gnus ((,class :foreground ,magenta))) + `(helm-bookmark-info ((,class :foreground ,cyan-alt))) + `(helm-bookmark-man ((,class :foreground ,yellow-alt))) + `(helm-bookmark-w3m ((,class :foreground ,blue-alt))) + `(helm-buffer-archive ((,class :inherit bold :foreground ,cyan))) + `(helm-buffer-directory ((,class :inherit bold :foreground ,blue))) + `(helm-buffer-file ((,class :foreground ,fg-main))) + `(helm-buffer-modified ((,class :foreground ,yellow-alt))) + `(helm-buffer-not-saved ((,class :foreground ,red-alt))) + `(helm-buffer-process ((,class :foreground ,magenta))) + `(helm-buffer-saved-out ((,class :inherit bold :background ,bg-alt :foreground ,red))) + `(helm-buffer-size ((,class :inherit shadow))) + `(helm-candidate-number ((,class :foreground ,cyan-active))) + `(helm-candidate-number-suspended ((,class :foreground ,yellow-active))) + `(helm-comint-prompts-buffer-name ((,class :foreground ,green-active))) + `(helm-comint-prompts-promptidx ((,class :foreground ,cyan-active))) + `(helm-delete-async-message ((,class :inherit bold :foreground ,magenta-active))) + `(helm-eob-line ((,class :background ,bg-main :foreground ,fg-main))) + `(helm-eshell-prompts-buffer-name ((,class :foreground ,green-active))) + `(helm-eshell-prompts-promptidx ((,class :foreground ,cyan-active))) + `(helm-etags-file ((,class :foreground ,fg-dim :underline t))) + `(helm-ff-backup-file ((,class :inherit shadow))) + `(helm-ff-denied ((,class :inherit modus-themes-intense-red))) + `(helm-ff-directory ((,class :inherit helm-buffer-directory))) + `(helm-ff-dirs ((,class :inherit bold :foreground ,blue-alt-other))) + `(helm-ff-dotted-directory ((,class :inherit bold :background ,bg-alt :foreground ,fg-alt))) + `(helm-ff-dotted-symlink-directory ((,class :inherit (button helm-ff-dotted-directory)))) + `(helm-ff-executable ((,class :foreground ,magenta-alt))) + `(helm-ff-file ((,class :foreground ,fg-main))) + `(helm-ff-file-extension ((,class :foreground ,fg-special-warm))) + `(helm-ff-invalid-symlink ((,class :inherit modus-themes-link-broken))) + `(helm-ff-pipe ((,class :inherit modus-themes-special-calm))) + `(helm-ff-prefix ((,class :inherit modus-themes-special-warm))) + `(helm-ff-socket ((,class :foreground ,red-alt-other))) + `(helm-ff-suid ((,class :inherit modus-themes-special-warm))) + `(helm-ff-symlink ((,class :inherit modus-themes-link-symlink))) + `(helm-ff-truename ((,class :foreground ,blue-alt-other))) + `(helm-fd-finish ((,class :inherit success))) + `(helm-grep-cmd-line ((,class :foreground ,yellow-alt-other))) + `(helm-grep-file ((,class :inherit bold :foreground ,fg-special-cold))) + `(helm-grep-finish ((,class :inherit bold))) + `(helm-grep-lineno ((,class :foreground ,fg-special-warm))) + `(helm-grep-match ((,class :inherit modus-themes-special-calm))) + `(helm-header ((,class :inherit bold :foreground ,fg-special-cold))) + `(helm-header-line-left-margin ((,class :inherit bold :foreground ,yellow-intense))) + `(helm-history-deleted ((,class :inherit modus-themes-special-warm))) + `(helm-history-remote ((,class :foreground ,red-alt-other))) + `(helm-lisp-completion-info ((,class :inherit modus-themes-bold :foreground ,fg-special-cold))) + `(helm-lisp-show-completion ((,class :inherit modus-themes-special-warm))) + `(helm-locate-finish ((,class :inherit success))) + `(helm-match ((,class :inherit modus-themes-completion-match-0))) + `(helm-match-item ((,class :inherit helm-match))) + `(helm-minibuffer-prompt ((,class :inherit modus-themes-prompt))) + `(helm-moccur-buffer ((,class :inherit button :foreground ,cyan-alt-other))) + `(helm-mode-prefix ((,class :inherit modus-themes-special-calm))) + `(helm-non-file-buffer ((,class :inherit shadow))) + `(helm-prefarg ((,class :foreground ,red-active))) + `(helm-resume-need-update ((,class :inherit modus-themes-special-calm))) + `(helm-selection ((,class :inherit modus-themes-completion-selected))) + `(helm-selection-line ((,class :background ,bg-hl-alt-intense))) + `(helm-separator ((,class :foreground ,fg-special-mild))) + `(helm-time-zone-current ((,class :foreground ,green))) + `(helm-time-zone-home ((,class :foreground ,magenta))) + `(helm-source-header ((,class :inherit modus-themes-pseudo-header :foreground ,fg-special-warm))) + `(helm-top-columns ((,class :inherit helm-header))) + `(helm-ucs-char ((,class :foreground ,yellow-alt-other))) + `(helm-visible-mark ((,class :inherit modus-themes-subtle-cyan))) +;;;;; helm-ls-git + `(helm-ls-git-added-copied-face ((,class :foreground ,green-intense))) + `(helm-ls-git-added-modified-face ((,class :foreground ,yellow-intense))) + `(helm-ls-git-conflict-face ((,class :inherit bold :foreground ,red-intense))) + `(helm-ls-git-deleted-and-staged-face ((,class :foreground ,red-nuanced-fg))) + `(helm-ls-git-deleted-not-staged-face ((,class :foreground ,red))) + `(helm-ls-git-modified-and-staged-face ((,class :foreground ,yellow-nuanced-fg))) + `(helm-ls-git-modified-not-staged-face ((,class :foreground ,yellow))) + `(helm-ls-git-renamed-modified-face ((,class :foreground ,magenta))) + `(helm-ls-git-untracked-face ((,class :foreground ,fg-special-cold))) +;;;;; helm-switch-shell + `(helm-switch-shell-new-shell-face ((,class :inherit modus-themes-completion-match-0))) +;;;;; helm-xref + `(helm-xref-file-name ((,class :inherit modus-themes-bold :foreground ,fg-special-cold))) +;;;;; helpful + `(helpful-heading ((,class :inherit modus-themes-heading-1))) +;;;;; highlight region or ad-hoc regexp + ;; HACK 2022-06-23: The :inverse-video prevents hl-line-mode from + ;; overriding the background. Such an override really defeats the + ;; purpose of setting those highlights. + `(hi-aquamarine ((,class :background ,bg-main :foreground ,cyan :inverse-video t))) + `(hi-black-b ((,class :inverse-video t))) + `(hi-black-hb ((,class :background ,bg-main :foreground ,fg-alt :inverse-video t))) + `(hi-blue ((,class :background ,bg-main :foreground ,blue-alt :inverse-video t))) + `(hi-blue-b ((,class :inherit (bold hi-blue)))) + `(hi-green ((,class :background ,bg-main :foreground ,green :inverse-video t))) + `(hi-green-b ((,class :inherit (bold hi-green)))) + `(hi-pink ((,class :background ,bg-main :foreground ,magenta :inverse-video t))) + `(hi-red-b ((,class :inherit bold :background ,bg-main :foreground ,red :inverse-video t))) + `(hi-salmon ((,class :background ,bg-main :foreground ,red-alt-faint :inverse-video t))) + `(hi-yellow ((,class :background ,bg-main :foreground ,yellow-alt :inverse-video t))) + `(highlight ((,class ,@(if modus-themes-intense-mouseovers + (list :background blue-intense-bg :foreground fg-main) + (list :background cyan-subtle-bg :foreground fg-main))))) + `(highlight-changes ((,class :foreground ,red-alt :underline nil))) + `(highlight-changes-delete ((,class :background ,red-nuanced-bg + :foreground ,red :underline t))) + `(hl-line ((,class :inherit modus-themes-hl-line))) +;;;;; highlight-indentation + `(highlight-indentation-face ((,class :inherit modus-themes-hl-line))) + `(highlight-indentation-current-column-face ((,class :background ,bg-active))) +;;;;; highlight-numbers + `(highlight-numbers-number ((,class :foreground ,blue-alt-other))) +;;;;; highlight-thing + `(highlight-thing ((,class :inherit modus-themes-special-calm))) +;;;;; hl-defined + `(hdefd-functions ((,class :foreground ,blue))) + `(hdefd-undefined ((,class :foreground ,red-alt))) + `(hdefd-variables ((,class :foreground ,cyan-alt))) +;;;;; hl-fill-column + `(hl-fill-column-face ((,class :background ,bg-active :foreground ,fg-active))) +;;;;; hl-todo + `(hl-todo ((,class :inherit (bold modus-themes-slant) :foreground ,red-alt-other))) +;;;;; hydra + `(hydra-face-amaranth ((,class :inherit bold :foreground ,yellow-alt))) + `(hydra-face-blue ((,class :inherit bold :foreground ,blue))) + `(hydra-face-pink ((,class :inherit bold :foreground ,magenta-alt-faint))) + `(hydra-face-red ((,class :inherit bold :foreground ,red-faint))) + `(hydra-face-teal ((,class :inherit bold :foreground ,cyan-alt-other))) +;;;;; icomplete + `(icomplete-first-match ((,class :inherit modus-themes-completion-match-0))) + `(icomplete-selected-match ((,class :inherit modus-themes-completion-selected))) +;;;;; icomplete-vertical + `(icomplete-vertical-separator ((,class :inherit shadow))) +;;;;; ido-mode + `(ido-first-match ((,class :inherit modus-themes-completion-match-0))) + `(ido-incomplete-regexp ((,class :inherit error))) + `(ido-indicator ((,class :inherit modus-themes-subtle-yellow))) + `(ido-only-match ((,class :inherit ido-first-match))) + `(ido-subdir ((,class :foreground ,blue))) + `(ido-virtual ((,class :foreground ,magenta-alt-other))) +;;;;; iedit + `(iedit-occurrence ((,class :inherit modus-themes-refine-blue))) + `(iedit-read-only-occurrence ((,class :inherit modus-themes-intense-yellow))) +;;;;; iflipb + `(iflipb-current-buffer-face ((,class :inherit bold :foreground ,cyan-alt))) + `(iflipb-other-buffer-face ((,class :inherit shadow))) +;;;;; image-dired + `(image-dired-thumb-flagged ((,class :background ,red-intense-bg))) + `(image-dired-thumb-mark ((,class :inherit modus-themes-grue-background-intense))) +;;;;; imenu-list + `(imenu-list-entry-face-0 ((,class :foreground ,cyan))) + `(imenu-list-entry-face-1 ((,class :foreground ,blue))) + `(imenu-list-entry-face-2 ((,class :foreground ,cyan-alt-other))) + `(imenu-list-entry-face-3 ((,class :foreground ,blue-alt))) + `(imenu-list-entry-subalist-face-0 ((,class :inherit bold :foreground ,magenta-alt-other :underline t))) + `(imenu-list-entry-subalist-face-1 ((,class :inherit bold :foreground ,magenta :underline t))) + `(imenu-list-entry-subalist-face-2 ((,class :inherit bold :foreground ,green-alt-other :underline t))) + `(imenu-list-entry-subalist-face-3 ((,class :inherit bold :foreground ,red-alt-other :underline t))) +;;;;; indium + `(indium-breakpoint-face ((,class :foreground ,red-active))) + `(indium-frame-url-face ((,class :inherit (shadow button)))) + `(indium-keyword-face ((,class :inherit font-lock-keyword-face))) + `(indium-litable-face ((,class :inherit modus-themes-slant :foreground ,fg-special-warm))) + `(indium-repl-error-face ((,class :inherit error))) + `(indium-repl-prompt-face ((,class :inherit modus-themes-prompt))) + `(indium-repl-stdout-face ((,class :foreground ,fg-main))) +;;;;; info + `(Info-quoted ((,class :inherit modus-themes-markup-verbatim))) ; the capitalization is canonical + `(info-header-node ((,class :inherit (shadow bold)))) + `(info-header-xref ((,class :foreground ,blue-active))) + `(info-index-match ((,class :inherit match))) + `(info-menu-header ((,class :inherit modus-themes-pseudo-header))) + `(info-menu-star ((,class :foreground ,red))) + `(info-node ((,class :inherit bold))) + `(info-title-1 ((,class :inherit modus-themes-heading-1))) + `(info-title-2 ((,class :inherit modus-themes-heading-2))) + `(info-title-3 ((,class :inherit modus-themes-heading-3))) + `(info-title-4 ((,class :inherit modus-themes-heading-4))) +;;;;; info+ (info-plus) + `(info-command-ref-item ((,class :inherit font-lock-function-name-face))) + `(info-constant-ref-item ((,class :inherit font-lock-constant-face))) + `(info-custom-delimited ((,class :inherit modus-themes-markup-verbatim))) + `(info-double-quoted-name ((,class :inherit font-lock-string-face))) + `(info-file (( ))) + `(info-function-ref-item ((,class :inherit font-lock-function-name-face))) + `(info-glossary-word ((,class :inherit modus-themes-box-button))) + `(info-indented-text (( ))) + `(info-isolated-backquote (( ))) + `(info-isolated-quote (( ))) + `(info-macro-ref-item ((,class :inherit font-lock-keyword-face))) + `(info-menu ((,class :inherit bold))) + `(info-quoted-name ((,class :inherit modus-themes-markup-verbatim))) + `(info-reference-item ((,class :inherit bold))) + `(info-special-form-ref-item ((,class :inherit warning))) + `(info-string ((,class :inherit font-lock-string-face))) + `(info-syntax-class-item ((,class :inherit modus-themes-markup-code))) + `(info-user-option-ref-item ((,class :inherit font-lock-variable-name-face))) + `(info-variable-ref-item ((,class :inherit font-lock-variable-name-face))) +;;;;; info-colors + `(info-colors-lisp-code-block ((,class :inherit modus-themes-fixed-pitch))) + `(info-colors-ref-item-command ((,class :inherit font-lock-function-name-face))) + `(info-colors-ref-item-constant ((,class :inherit font-lock-constant-face))) + `(info-colors-ref-item-function ((,class :inherit font-lock-function-name-face))) + `(info-colors-ref-item-macro ((,class :inherit font-lock-keyword-face))) + `(info-colors-ref-item-other ((,class :inherit font-lock-doc-face))) + `(info-colors-ref-item-special-form ((,class :inherit font-lock-keyword-face))) + `(info-colors-ref-item-syntax-class ((,class :inherit font-lock-builtin-face))) + `(info-colors-ref-item-type ((,class :inherit font-lock-type-face))) + `(info-colors-ref-item-user-option ((,class :inherit font-lock-variable-name-face))) + `(info-colors-ref-item-variable ((,class :inherit font-lock-variable-name-face))) +;;;;; interaction-log + `(ilog-buffer-face ((,class :foreground ,magenta-alt-other))) + `(ilog-change-face ((,class :foreground ,magenta-alt))) + `(ilog-echo-face ((,class :foreground ,yellow-alt-other))) + `(ilog-load-face ((,class :foreground ,green))) + `(ilog-message-face ((,class :inherit shadow))) + `(ilog-non-change-face ((,class :foreground ,blue))) +;;;;; ioccur + `(ioccur-cursor ((,class :foreground ,fg-main))) + `(ioccur-invalid-regexp ((,class :foreground ,red))) + `(ioccur-match-face ((,class :inherit modus-themes-special-calm))) + `(ioccur-match-overlay-face ((,class :inherit modus-themes-special-cold :extend t))) + `(ioccur-num-line-face ((,class :foreground ,fg-special-warm))) + `(ioccur-overlay-face ((,class :inherit modus-themes-refine-blue :extend t))) + `(ioccur-regexp-face ((,class :inherit (modus-themes-intense-magenta bold)))) + `(ioccur-title-face ((,class :inherit modus-themes-pseudo-header :foreground ,fg-special-cold))) +;;;;; isearch, occur, and the like + `(isearch ((,class :inherit modus-themes-search-success))) + `(isearch-fail ((,class :inherit modus-themes-refine-red))) + `(isearch-group-1 ((,class :inherit modus-themes-refine-blue))) + `(isearch-group-2 ((,class :inherit modus-themes-refine-magenta))) + `(lazy-highlight ((,class :inherit modus-themes-search-success-lazy))) + `(match ((,class :inherit modus-themes-special-calm))) + `(query-replace ((,class :inherit modus-themes-intense-red))) +;;;;; ivy + `(ivy-action ((,class :inherit modus-themes-key-binding))) + `(ivy-confirm-face ((,class :inherit success))) + `(ivy-current-match ((,class :inherit modus-themes-completion-selected))) + `(ivy-cursor ((,class :background ,fg-main :foreground ,bg-main))) + `(ivy-highlight-face ((,class :foreground ,magenta))) + `(ivy-match-required-face ((,class :inherit error))) + `(ivy-minibuffer-match-face-1 (( ))) + `(ivy-minibuffer-match-face-2 ((,class :inherit modus-themes-completion-match-0))) + `(ivy-minibuffer-match-face-3 ((,class :inherit modus-themes-completion-match-1))) + `(ivy-minibuffer-match-face-4 ((,class :inherit modus-themes-completion-match-2))) + `(ivy-org ((,class :foreground ,cyan-alt-other))) + `(ivy-remote ((,class :foreground ,magenta))) + `(ivy-separator ((,class :inherit shadow))) + `(ivy-subdir ((,class :foreground ,blue))) + `(ivy-virtual ((,class :foreground ,magenta-alt-other))) +;;;;; ivy-posframe + `(ivy-posframe-border ((,class :background ,fg-window-divider-inner))) + `(ivy-posframe-cursor ((,class :background ,fg-main :foreground ,bg-main))) +;;;;; jira (org-jira) + `(jiralib-comment-face ((,class :background ,bg-alt))) + `(jiralib-comment-header-face ((,class :inherit bold))) + `(jiralib-issue-info-face ((,class :inherit modus-themes-special-warm))) + `(jiralib-issue-info-header-face ((,class :inherit (modus-themes-special-warm bold)))) + `(jiralib-issue-summary-face ((,class :inherit bold))) + `(jiralib-link-filter-face ((,class :underline t))) + `(jiralib-link-issue-face ((,class :underline t))) + `(jiralib-link-project-face ((,class :underline t))) +;;;;; journalctl-mode + `(journalctl-error-face ((,class :inherit error))) + `(journalctl-finished-face ((,class :inherit success))) + `(journalctl-host-face ((,class :foreground ,blue))) + `(journalctl-process-face ((,class :foreground ,cyan-alt-other))) + `(journalctl-starting-face ((,class :foreground ,green))) + `(journalctl-timestamp-face ((,class :foreground ,fg-special-cold))) + `(journalctl-warning-face ((,class :inherit warning))) +;;;;; js2-mode + `(js2-error ((,class :inherit modus-themes-lang-error))) + `(js2-external-variable ((,class :inherit font-lock-variable-name-face))) + `(js2-function-call ((,class :inherit font-lock-function-name-face))) + `(js2-function-param ((,class :inherit font-lock-constant-face))) + `(js2-instance-member ((,class :inherit font-lock-keyword-face))) + `(js2-jsdoc-html-tag-delimiter ((,class :foreground ,fg-main))) + `(js2-jsdoc-html-tag-name ((,class :inherit font-lock-function-name-face))) + `(js2-jsdoc-tag ((,class :inherit (font-lock-builtin-face font-lock-comment-face) :weight normal))) + `(js2-jsdoc-type ((,class :inherit (font-lock-type-face font-lock-comment-face) :weight normal))) + `(js2-jsdoc-value ((,class :inherit (font-lock-constant-face font-lock-comment-face) :weight normal))) + `(js2-object-property ((,class :foreground ,fg-main))) + `(js2-object-property-access ((,class :foreground ,fg-main))) + `(js2-private-function-call ((,class :inherit font-lock-preprocessor-face))) + `(js2-private-member ((,class :inherit font-lock-warning-face))) + `(js2-warning ((,class :inherit modus-themes-lang-warning))) +;;;;; julia + `(julia-macro-face ((,class :inherit font-lock-builtin-face))) + `(julia-quoted-symbol-face ((,class :inherit font-lock-constant-face))) +;;;;; jupyter + `(jupyter-eval-overlay ((,class :inherit bold :foreground ,blue))) + `(jupyter-repl-input-prompt ((,class :foreground ,cyan-alt-other))) + `(jupyter-repl-output-prompt ((,class :foreground ,magenta-alt-other))) + `(jupyter-repl-traceback ((,class :inherit modus-themes-intense-red))) +;;;;; kaocha-runner + `(kaocha-runner-error-face ((,class :inherit error))) + `(kaocha-runner-success-face ((,class :inherit success))) + `(kaocha-runner-warning-face ((,class :inherit warning))) +;;;;; keycast + `(keycast-command ((,class :inherit bold :foreground ,blue-active))) + ;; FIXME 2022-05-03: The padding breaks `keycast-tab-bar-mode' + `(keycast-key ((,class ;; ,@(modus-themes--mode-line-padded-box blue-active) + :background ,blue-active :foreground ,bg-main))) +;;;;; ledger-mode + `(ledger-font-auto-xact-face ((,class :foreground ,magenta))) + `(ledger-font-account-name-face ((,class :foreground ,fg-special-cold))) + `(ledger-font-directive-face ((,class :foreground ,magenta-alt-other))) + `(ledger-font-posting-date-face ((,class :inherit bold :foreground ,fg-main))) + `(ledger-font-periodic-xact-face ((,class :foreground ,cyan-alt-other))) + `(ledger-font-posting-amount-face ((,class :foreground ,fg-special-mild))) + `(ledger-font-payee-cleared-face ((,class :foreground ,blue-alt))) + `(ledger-font-payee-pending-face ((,class :foreground ,yellow))) + `(ledger-font-payee-uncleared-face ((,class :foreground ,red-alt-other))) + `(ledger-font-xact-highlight-face ((,class :background ,bg-hl-alt))) +;;;;; leerzeichen + `(leerzeichen ((,class :background ,bg-whitespace :foreground ,fg-whitespace))) +;;;;; line numbers (display-line-numbers-mode and global variant) + ;; Here we cannot inherit `modus-themes-fixed-pitch'. We need to + ;; fall back to `default' otherwise line numbers do not scale when + ;; using `text-scale-adjust'. + `(line-number + ((,class :inherit ,(if modus-themes-mixed-fonts '(fixed-pitch default) 'default) + ,@(modus-themes--line-numbers + fg-alt bg-dim + fg-unfocused)))) + `(line-number-current-line + ((,class :inherit (bold line-number) + ,@(modus-themes--line-numbers + fg-main bg-active + blue-alt-other)))) + `(line-number-major-tick + ((,class :inherit (bold line-number) + ,@(modus-themes--line-numbers + yellow-nuanced-fg yellow-nuanced-bg + red-alt)))) + `(line-number-minor-tick + ((,class :inherit (bold line-number) + ,@(modus-themes--line-numbers + fg-alt bg-inactive + fg-inactive)))) +;;;;; lsp-mode + `(lsp-face-highlight-read ((,class :inherit modus-themes-subtle-blue :underline t))) + `(lsp-face-highlight-textual ((,class :inherit modus-themes-subtle-blue))) + `(lsp-face-highlight-write ((,class :inherit (modus-themes-refine-blue bold)))) + `(lsp-face-semhl-constant ((,class :foreground ,blue-alt-other))) + `(lsp-face-semhl-deprecated ((,class :inherit modus-themes-lang-warning))) + `(lsp-face-semhl-enummember ((,class :foreground ,blue-alt-other))) + `(lsp-face-semhl-field ((,class :foreground ,cyan-alt))) + `(lsp-face-semhl-field-static ((,class :inherit modus-themes-slant :foreground ,cyan-alt))) + `(lsp-face-semhl-function ((,class :foreground ,magenta))) + `(lsp-face-semhl-method ((,class :foreground ,magenta))) + `(lsp-face-semhl-namespace ((,class :inherit modus-themes-bold :foreground ,magenta-alt))) + `(lsp-face-semhl-preprocessor ((,class :foreground ,red-alt-other))) + `(lsp-face-semhl-static-method ((,class :inherit modus-themes-slant :foreground ,magenta))) + `(lsp-face-semhl-type-class ((,class :foreground ,magenta-alt))) + `(lsp-face-semhl-type-enum ((,class :foreground ,magenta-alt))) + `(lsp-face-semhl-type-primitive ((,class :inherit modus-themes-slant :foreground ,magenta-alt))) + `(lsp-face-semhl-type-template ((,class :inherit modus-themes-slant :foreground ,magenta-alt))) + `(lsp-face-semhl-type-typedef ((,class :inherit modus-themes-slant :foreground ,magenta-alt))) + `(lsp-face-semhl-variable ((,class :foreground ,cyan))) + `(lsp-face-semhl-variable-local ((,class :foreground ,cyan))) + `(lsp-face-semhl-variable-parameter ((,class :foreground ,cyan-alt-other))) + `(lsp-lens-face ((,class :inherit shadow :height 0.8))) + `(lsp-lens-mouse-face ((,class :height 0.8 :foreground ,blue-alt-other :underline t))) + `(lsp-ui-doc-background ((,class :background ,bg-alt))) + `(lsp-ui-doc-header ((,class :background ,bg-header :foreground ,fg-header))) + `(lsp-ui-doc-url ((,class :inherit button))) + `(lsp-ui-peek-filename ((,class :foreground ,fg-special-warm))) + `(lsp-ui-peek-footer ((,class :background ,bg-header :foreground ,fg-header))) + `(lsp-ui-peek-header ((,class :background ,bg-header :foreground ,fg-header))) + `(lsp-ui-peek-highlight ((,class :inherit modus-themes-subtle-blue))) + `(lsp-ui-peek-line-number ((,class :inherit shadow))) + `(lsp-ui-peek-list ((,class :background ,bg-dim))) + `(lsp-ui-peek-peek ((,class :background ,bg-alt))) + `(lsp-ui-peek-selection ((,class :inherit modus-themes-subtle-cyan))) + `(lsp-ui-sideline-code-action ((,class :foreground ,yellow))) + `(lsp-ui-sideline-current-symbol ((,class :inherit bold :height 0.99 :box (:line-width -1 :style nil) :foreground ,fg-main))) + `(lsp-ui-sideline-symbol ((,class :inherit bold :height 0.99 :box (:line-width -1 :style nil) :foreground ,fg-alt))) + `(lsp-ui-sideline-symbol-info ((,class :inherit italic :height 0.99))) +;;;;; macrostep + `(macrostep-compiler-macro-face ((,class :inherit italic))) + `(macrostep-expansion-highlight-face ((,class :background ,blue-nuanced-bg))) + `(macrostep-gensym-1 ((,class :inherit bold :foreground ,blue :box t))) + `(macrostep-gensym-2 ((,class :inherit bold :foreground ,green :box t))) + `(macrostep-gensym-3 ((,class :inherit bold :foreground ,yellow :box t))) + `(macrostep-gensym-4 ((,class :inherit bold :foreground ,red :box t))) + `(macrostep-gensym-5 ((,class :inherit bold :foreground ,magenta :box t))) + `(macrostep-macro-face ((,class :inherit button :foreground ,green-alt))) +;;;;; magit + `(magit-bisect-bad ((,class :inherit error))) + `(magit-bisect-good ((,class :inherit success))) + `(magit-bisect-skip ((,class :inherit warning))) + `(magit-blame-date ((,class :foreground ,blue))) + `(magit-blame-dimmed ((,class :inherit (shadow modus-themes-reset-hard)))) + `(magit-blame-hash ((,class :foreground ,fg-special-warm))) + `(magit-blame-heading ((,class :inherit modus-themes-reset-hard :background ,bg-alt :extend t))) + `(magit-blame-highlight ((,class :inherit modus-themes-nuanced-cyan))) + `(magit-blame-margin ((,class :inherit (magit-blame-highlight modus-themes-reset-hard)))) + `(magit-blame-name ((,class :foreground ,magenta-alt-other))) + `(magit-blame-summary ((,class :foreground ,cyan-alt-other))) + ;; ;; NOTE 2021-11-23: we do not set the `magit-branch-current' + ;; ;; because its definition checks if the :box attribute can be set + ;; ;; and if not, it uses :inverse-video. Useful for terminal + ;; ;; emulators. + ;; + ;; `(magit-branch-current ((,class :foreground ,blue-alt-other :box t))) + `(magit-branch-local ((,class :foreground ,blue-alt))) + `(magit-branch-remote ((,class :foreground ,magenta-alt))) + `(magit-branch-remote-head ((,class :foreground ,magenta-alt-other :box t))) + `(magit-branch-upstream ((,class :inherit italic))) + `(magit-branch-warning ((,class :inherit warning))) + `(magit-cherry-equivalent ((,class :background ,bg-main :foreground ,magenta-intense))) + `(magit-cherry-unmatched ((,class :background ,bg-main :foreground ,cyan-intense))) + ;; NOTE: here we break from the pattern of inheriting from the + ;; modus-themes-diff-* faces, though only for the standard actions, + ;; not the highlighted ones. This is because Magit's interaction + ;; model relies on highlighting the current diff hunk. + `(magit-diff-added ((,class ,@(modus-themes--diff + bg-diff-added fg-diff-added + green-nuanced-bg fg-diff-added + bg-diff-added-deuteran fg-diff-added-deuteran + blue-nuanced-bg fg-diff-added-deuteran)))) + `(magit-diff-added-highlight ((,class :inherit modus-themes-diff-focus-added))) + `(magit-diff-base ((,class ,@(modus-themes--diff + bg-diff-changed fg-diff-changed + yellow-nuanced-bg fg-diff-changed)))) + `(magit-diff-base-highlight ((,class :inherit modus-themes-diff-focus-changed))) + `(magit-diff-context ((,class ,@(unless (eq modus-themes-diffs 'bg-only) (list :foreground fg-unfocused))))) + `(magit-diff-context-highlight ((,class ,@(modus-themes--diff + bg-inactive fg-inactive + bg-dim fg-alt + bg-dim fg-alt)))) + `(magit-diff-file-heading ((,class :inherit bold :foreground ,fg-special-cold))) + `(magit-diff-file-heading-highlight ((,class :inherit (modus-themes-special-cold bold)))) + `(magit-diff-file-heading-selection ((,class :inherit modus-themes-refine-cyan))) + ;; NOTE: here we break from the pattern of inheriting from the + ;; modus-themes-diff-* faces. + `(magit-diff-hunk-heading ((,class :inherit bold + ,@(modus-themes--diff + bg-active fg-inactive + bg-inactive fg-inactive + bg-inactive fg-inactive + nil nil + t)))) + ;; NOTE: we do not follow the pattern of inheriting from + ;; modus-themes-grue-* faces, as this is a special case. + `(magit-diff-hunk-heading-highlight + ((,class :inherit bold + :background ,@(modus-themes--deuteran bg-active bg-diff-heading) + :foreground ,@(modus-themes--deuteran fg-main fg-diff-heading)))) + `(magit-diff-hunk-heading-selection ((,class :inherit modus-themes-refine-blue))) + `(magit-diff-hunk-region ((,class :inherit bold))) + `(magit-diff-lines-boundary ((,class :background ,fg-main))) + `(magit-diff-lines-heading ((,class :inherit modus-themes-refine-magenta))) + `(magit-diff-removed ((,class ,@(modus-themes--diff + bg-diff-removed fg-diff-removed + red-nuanced-bg fg-diff-removed)))) + `(magit-diff-removed-highlight ((,class :inherit modus-themes-diff-focus-removed))) + `(magit-diffstat-added ((,class :inherit modus-themes-grue))) + `(magit-diffstat-removed ((,class :foreground ,red))) + `(magit-dimmed ((,class :foreground ,fg-unfocused))) + `(magit-filename ((,class :foreground ,fg-special-cold))) + `(magit-hash ((,class :inherit shadow))) + `(magit-head ((,class :inherit magit-branch-local))) + `(magit-header-line ((,class :inherit bold :foreground ,magenta-active))) + `(magit-header-line-key ((,class :inherit modus-themes-key-binding))) + `(magit-header-line-log-select ((,class :inherit bold :foreground ,fg-main))) + `(magit-keyword ((,class :foreground ,magenta))) + `(magit-keyword-squash ((,class :inherit bold :foreground ,yellow-alt-other))) + `(magit-log-author ((,class :foreground ,cyan))) + `(magit-log-date ((,class :inherit shadow))) + `(magit-log-graph ((,class :foreground ,fg-dim))) + `(magit-mode-line-process ((,class :inherit bold :foreground ,cyan-active))) + `(magit-mode-line-process-error ((,class :inherit bold :foreground ,red-active))) + `(magit-process-ng ((,class :inherit error))) + `(magit-process-ok ((,class :inherit success))) + `(magit-reflog-amend ((,class :inherit warning))) + `(magit-reflog-checkout ((,class :inherit bold :foreground ,blue-alt))) + `(magit-reflog-cherry-pick ((,class :inherit success))) + `(magit-reflog-commit ((,class :inherit bold))) + `(magit-reflog-merge ((,class :inherit success))) + `(magit-reflog-other ((,class :inherit bold :foreground ,cyan))) + `(magit-reflog-rebase ((,class :inherit bold :foreground ,magenta))) + `(magit-reflog-remote ((,class :inherit bold :foreground ,magenta-alt-other))) + `(magit-reflog-reset ((,class :inherit error))) + `(magit-refname ((,class :inherit shadow))) + `(magit-refname-pullreq ((,class :inherit shadow))) + `(magit-refname-stash ((,class :inherit shadow))) + `(magit-refname-wip ((,class :inherit shadow))) + `(magit-section ((,class :background ,bg-dim :foreground ,fg-main))) + `(magit-section-heading ((,class :inherit bold :foreground ,cyan))) + `(magit-section-heading-selection ((,class :inherit (modus-themes-refine-cyan bold)))) + `(magit-section-highlight ((,class :background ,bg-alt))) + `(magit-sequence-done ((,class :inherit success))) + `(magit-sequence-drop ((,class :inherit error))) + `(magit-sequence-exec ((,class :inherit bold :foreground ,magenta-alt))) + `(magit-sequence-head ((,class :inherit bold :foreground ,cyan-alt))) + `(magit-sequence-onto ((,class :inherit (bold shadow)))) + `(magit-sequence-part ((,class :inherit warning))) + `(magit-sequence-pick ((,class :inherit bold))) + `(magit-sequence-stop ((,class :inherit error))) + `(magit-signature-bad ((,class :inherit error))) + `(magit-signature-error ((,class :inherit error))) + `(magit-signature-expired ((,class :inherit warning))) + `(magit-signature-expired-key ((,class :foreground ,yellow))) + `(magit-signature-good ((,class :inherit success))) + `(magit-signature-revoked ((,class :inherit bold :foreground ,magenta))) + `(magit-signature-untrusted ((,class :inherit (bold shadow)))) + `(magit-tag ((,class :foreground ,yellow-alt-other))) +;;;;; magit-imerge + `(magit-imerge-overriding-value ((,class :inherit bold :foreground ,red-alt))) +;;;;; make-mode (makefiles) + `(makefile-makepp-perl ((,class :background ,cyan-nuanced-bg))) + `(makefile-space ((,class :background ,magenta-nuanced-bg))) +;;;;; man + `(Man-overstrike ((,class :inherit bold :foreground ,magenta-alt))) + `(Man-reverse ((,class :inherit modus-themes-subtle-magenta))) + `(Man-underline ((,class :foreground ,cyan-alt-other :underline t))) +;;;;; marginalia + `(marginalia-archive ((,class :foreground ,cyan-alt-other))) + `(marginalia-char ((,class :foreground ,magenta))) + `(marginalia-date ((,class :foreground ,cyan))) + `(marginalia-documentation ((,class :inherit modus-themes-slant :foreground ,fg-docstring))) + `(marginalia-file-name ((,class :foreground ,blue-faint))) + `(marginalia-file-owner ((,class :foreground ,red-faint))) + `(marginalia-file-priv-dir ((,class :foreground ,blue-alt))) + `(marginalia-file-priv-exec ((,class :foreground ,magenta-alt))) + `(marginalia-file-priv-link ((,class :foreground ,blue-alt-other))) + `(marginalia-file-priv-no ((,class :foreground "gray50"))) + `(marginalia-file-priv-other ((,class :foreground ,yellow))) + `(marginalia-file-priv-rare ((,class :foreground ,red))) + `(marginalia-file-priv-read ((,class :foreground ,fg-main))) + `(marginalia-file-priv-write ((,class :foreground ,cyan))) + `(marginalia-function ((,class :foreground ,magenta-alt-faint))) + `(marginalia-key ((,class :inherit modus-themes-key-binding))) + `(marginalia-lighter ((,class :foreground ,blue-alt))) + `(marginalia-list ((,class :foreground ,magenta-alt-other-faint))) + `(marginalia-mode ((,class :foreground ,cyan))) + `(marginalia-modified ((,class :foreground ,magenta-alt-faint))) + `(marginalia-null ((,class :inherit shadow))) + `(marginalia-number ((,class :foreground ,cyan))) + `(marginalia-size ((,class :foreground ,cyan-alt-other-faint))) + `(marginalia-string ((,class :foreground ,blue-alt))) + `(marginalia-symbol ((,class :foreground ,blue-alt-other-faint))) + `(marginalia-true ((,class :foreground ,fg-main))) + `(marginalia-type ((,class :foreground ,cyan-alt-other))) + `(marginalia-value ((,class :foreground ,cyan))) + `(marginalia-version ((,class :foreground ,cyan))) +;;;;; markdown-mode + `(markdown-blockquote-face ((,class :inherit modus-themes-slant :foreground ,fg-special-cold))) + `(markdown-bold-face ((,class :inherit bold))) + `(markdown-code-face ((,class :inherit modus-themes-fixed-pitch :background ,bg-dim :extend t))) + `(markdown-comment-face ((,class :inherit font-lock-comment-face))) + `(markdown-footnote-marker-face ((,class :inherit bold :foreground ,cyan-alt))) + `(markdown-footnote-text-face ((,class :inherit modus-themes-slant :foreground ,fg-main))) + `(markdown-gfm-checkbox-face ((,class :foreground ,yellow-alt-other))) + `(markdown-header-delimiter-face ((,class :inherit modus-themes-bold :foreground ,fg-dim))) + `(markdown-header-face ((t nil))) + `(markdown-header-face-1 ((,class :inherit modus-themes-heading-1))) + `(markdown-header-face-2 ((,class :inherit modus-themes-heading-2))) + `(markdown-header-face-3 ((,class :inherit modus-themes-heading-3))) + `(markdown-header-face-4 ((,class :inherit modus-themes-heading-4))) + `(markdown-header-face-5 ((,class :inherit modus-themes-heading-5))) + `(markdown-header-face-6 ((,class :inherit modus-themes-heading-6))) + `(markdown-header-rule-face ((,class :inherit bold :foreground ,fg-special-warm))) + `(markdown-highlighting-face ((,class :inherit modus-themes-refine-yellow))) + `(markdown-hr-face ((,class :inherit bold :foreground ,fg-special-warm))) + `(markdown-html-attr-name-face ((,class :inherit modus-themes-fixed-pitch + :foreground ,cyan))) + `(markdown-html-attr-value-face ((,class :inherit modus-themes-fixed-pitch + :foreground ,blue))) + `(markdown-html-entity-face ((,class :inherit modus-themes-fixed-pitch + :foreground ,cyan))) + `(markdown-html-tag-delimiter-face ((,class :inherit modus-themes-fixed-pitch + :foreground ,fg-special-mild))) + `(markdown-html-tag-name-face ((,class :inherit modus-themes-fixed-pitch + :foreground ,magenta-alt))) + `(markdown-inline-code-face ((,class :inherit modus-themes-markup-verbatim))) + `(markdown-italic-face ((,class :inherit italic))) + `(markdown-language-info-face ((,class :inherit modus-themes-fixed-pitch + :foreground ,fg-special-cold))) + `(markdown-language-keyword-face ((,class :inherit modus-themes-fixed-pitch + :background ,bg-alt + :foreground ,fg-alt))) + `(markdown-line-break-face ((,class :inherit modus-themes-refine-cyan :underline t))) + `(markdown-link-face ((,class :inherit button))) + `(markdown-link-title-face ((,class :inherit modus-themes-slant :foreground ,fg-special-cold))) + `(markdown-list-face ((,class :foreground ,fg-dim))) + `(markdown-markup-face ((,class :inherit shadow))) + `(markdown-math-face ((,class :foreground ,magenta-alt-other))) + `(markdown-metadata-key-face ((,class :foreground ,cyan-alt-other))) + `(markdown-metadata-value-face ((,class :foreground ,blue-alt))) + `(markdown-missing-link-face ((,class :inherit bold :foreground ,yellow))) + `(markdown-plain-url-face ((,class :inherit markdown-link-face))) + `(markdown-pre-face ((,class :inherit markdown-code-face :foreground ,fg-special-mild))) + `(markdown-reference-face ((,class :inherit markdown-markup-face))) + `(markdown-strike-through-face ((,class :strike-through t))) + `(markdown-table-face ((,class :inherit modus-themes-fixed-pitch + :foreground ,fg-special-cold))) + `(markdown-url-face ((,class :foreground ,blue-alt))) +;;;;; markup-faces (`adoc-mode') + `(markup-attribute-face ((,class :inherit (italic markup-meta-face)))) + `(markup-bold-face ((,class :inherit bold :foreground ,red-nuanced-fg))) + `(markup-code-face ((,class :foreground ,magenta))) + `(markup-comment-face ((,class :inherit font-lock-comment-face))) + `(markup-complex-replacement-face ((,class :background ,magenta-nuanced-bg :foreground ,magenta-alt-other))) + `(markup-emphasis-face ((,class :inherit markup-italic-face))) + `(markup-error-face ((,class :inherit error))) + `(markup-gen-face ((,class :foreground ,magenta-alt))) + `(markup-internal-reference-face ((,class :inherit modus-themes-slant :foreground ,fg-alt))) + `(markup-italic-face ((,class :inherit italic))) + `(markup-list-face ((,class :inherit modus-themes-special-cold))) + `(markup-meta-face ((,class :inherit (modus-themes-fixed-pitch shadow)))) + `(markup-meta-hide-face ((,class :foreground "gray50"))) + `(markup-reference-face ((,class :inherit modus-themes-slant :foreground ,blue-alt))) + `(markup-replacement-face ((,class :inherit modus-themes-fixed-pitch :foreground ,red-alt))) + `(markup-secondary-text-face ((,class :height 0.9 :foreground ,cyan-alt-other))) + `(markup-small-face ((,class :inherit markup-gen-face :height 0.9))) + `(markup-strong-face ((,class :inherit markup-bold-face))) + `(markup-subscript-face ((,class :height 0.9 :foreground ,magenta-alt-other))) + `(markup-superscript-face ((,class :height 0.9 :foreground ,magenta-alt-other))) + `(markup-table-cell-face ((,class :inherit modus-themes-subtle-neutral))) + `(markup-table-face ((,class :inherit modus-themes-subtle-neutral))) + `(markup-table-row-face ((,class :inherit modus-themes-special-cold))) + `(markup-title-0-face ((,class :inherit modus-themes-heading-1))) + `(markup-title-1-face ((,class :inherit modus-themes-heading-2))) + `(markup-title-2-face ((,class :inherit modus-themes-heading-3))) + `(markup-title-3-face ((,class :inherit modus-themes-heading-4))) + `(markup-title-4-face ((,class :inherit modus-themes-heading-5))) + `(markup-title-5-face ((,class :inherit modus-themes-heading-6))) + `(markup-verbatim-face ((,class :inherit modus-themes-fixed-pitch :background ,bg-alt))) +;;;;; mentor + `(mentor-download-message ((,class :foreground ,fg-special-warm))) + `(mentor-download-name ((,class :foreground ,fg-special-cold))) + `(mentor-download-progress ((,class :foreground ,blue-alt-other))) + `(mentor-download-size ((,class :foreground ,magenta-alt-other))) + `(mentor-download-speed-down ((,class :foreground ,cyan-alt))) + `(mentor-download-speed-up ((,class :foreground ,red-alt))) + `(mentor-download-state ((,class :foreground ,yellow-alt))) + `(mentor-highlight-face ((,class :inherit modus-themes-subtle-blue))) + `(mentor-tracker-name ((,class :foreground ,magenta-alt))) +;;;;; messages + `(message-cited-text-1 ((,class ,@(modus-themes--mail-cite blue-faint blue fg-special-cold)))) + `(message-cited-text-2 ((,class ,@(modus-themes--mail-cite yellow-faint yellow yellow-alt-faint)))) + `(message-cited-text-3 ((,class ,@(modus-themes--mail-cite magenta-alt-faint magenta-alt fg-special-calm)))) + `(message-cited-text-4 ((,class ,@(modus-themes--mail-cite cyan-alt-other-faint cyan-alt-other fg-special-mild)))) + `(message-header-cc ((,class :foreground ,blue-alt-other))) + `(message-header-name ((,class :inherit bold :foreground ,cyan))) + `(message-header-newsgroups ((,class :inherit message-header-other))) + `(message-header-other ((,class :foreground ,fg-special-calm))) + `(message-header-subject ((,class :inherit bold :foreground ,magenta-alt))) + `(message-header-to ((,class :inherit bold :foreground ,magenta-alt-other))) + `(message-header-xheader ((,class :foreground ,blue-alt))) + `(message-mml ((,class :foreground ,cyan-alt-other))) + `(message-separator ((,class :inherit modus-themes-intense-neutral))) +;;;;; mini-modeline + `(mini-modeline-mode-line ((,class :background ,blue-intense :height 0.14))) + `(mini-modeline-mode-line-inactive ((,class :background ,fg-window-divider-inner :height 0.1))) +;;;;; minimap + `(minimap-active-region-background ((,class :background ,bg-active))) + `(minimap-current-line-face ((,class :background ,cyan-intense-bg :foreground ,fg-main))) +;;;;; mmm-mode + `(mmm-cleanup-submode-face ((,class :background ,yellow-nuanced-bg))) + `(mmm-code-submode-face ((,class :background ,bg-alt))) + `(mmm-comment-submode-face ((,class :background ,blue-nuanced-bg))) + `(mmm-declaration-submode-face ((,class :background ,cyan-nuanced-bg))) + `(mmm-default-submode-face ((,class :background ,bg-dim))) + `(mmm-init-submode-face ((,class :background ,magenta-nuanced-bg))) + `(mmm-output-submode-face ((,class :background ,red-nuanced-bg))) + `(mmm-special-submode-face ((,class :background ,green-nuanced-bg))) +;;;;; mode-line + `(mode-line ((,class :inherit modus-themes-ui-variable-pitch + ,@(modus-themes--mode-line-attrs + fg-active bg-active + fg-dim bg-active + fg-main bg-active-accent + fg-alt bg-active + 'alt-style bg-main)))) + `(mode-line-active ((,class :inherit mode-line))) + `(mode-line-buffer-id ((,class :inherit bold))) + `(mode-line-emphasis ((,class :inherit bold :foreground ,magenta-active))) + `(mode-line-highlight ((,class ,@(if modus-themes-intense-mouseovers + (list :inherit 'modus-themes-active-blue) + (list :inherit 'highlight))))) + `(mode-line-inactive ((,class :inherit modus-themes-ui-variable-pitch + ,@(modus-themes--mode-line-attrs + fg-inactive bg-inactive + fg-alt bg-dim + fg-inactive bg-inactive + bg-region bg-active)))) +;;;;; mood-line + `(mood-line-modified ((,class :foreground ,magenta-active))) + `(mood-line-status-error ((,class :inherit bold :foreground ,red-active))) + `(mood-line-status-info ((,class :foreground ,cyan-active))) + `(mood-line-status-neutral ((,class :foreground ,blue-active))) + `(mood-line-status-success ((,class :inherit modus-themes-grue-active))) + `(mood-line-status-warning ((,class :inherit bold :foreground ,yellow-active))) + `(mood-line-unimportant ((,class :foreground ,fg-inactive))) +;;;;; mpdel + `(mpdel-browser-directory-face ((,class :foreground ,blue))) + `(mpdel-playlist-current-song-face ((,class :inherit bold :foreground ,blue-alt-other))) +;;;;; mu4e + `(mu4e-attach-number-face ((,class :inherit bold :foreground ,fg-dim))) + `(mu4e-cited-1-face ((,class :inherit message-cited-text-1))) + `(mu4e-cited-2-face ((,class :inherit message-cited-text-2))) + `(mu4e-cited-3-face ((,class :inherit message-cited-text-3))) + `(mu4e-cited-4-face ((,class :inherit message-cited-text-4))) + `(mu4e-cited-5-face ((,class :inherit message-cited-text-1))) + `(mu4e-cited-6-face ((,class :inherit message-cited-text-2))) + `(mu4e-cited-7-face ((,class :inherit message-cited-text-3))) + `(mu4e-compose-header-face ((,class :inherit mu4e-compose-separator-face))) + `(mu4e-compose-separator-face ((,class :inherit modus-themes-intense-neutral))) + `(mu4e-contact-face ((,class :inherit message-header-to))) + `(mu4e-context-face ((,class :foreground ,blue-active))) + `(mu4e-draft-face ((,class :foreground ,magenta-alt))) + `(mu4e-flagged-face ((,class :foreground ,red-alt-other))) + `(mu4e-footer-face ((,class :inherit modus-themes-slant :foreground ,fg-special-cold))) + `(mu4e-forwarded-face ((,class :foreground ,magenta-alt-other))) + `(mu4e-header-face ((,class :inherit shadow))) + `(mu4e-header-highlight-face ((,class :inherit modus-themes-hl-line))) + `(mu4e-header-key-face ((,class :inherit message-header-name))) + `(mu4e-header-marks-face ((,class :inherit mu4e-special-header-value-face))) + `(mu4e-header-title-face ((,class :foreground ,fg-special-mild))) + `(mu4e-header-value-face ((,class :inherit message-header-other))) + `(mu4e-highlight-face ((,class :inherit modus-themes-key-binding))) + `(mu4e-link-face ((,class :inherit button))) + `(mu4e-modeline-face ((,class :foreground ,magenta-active))) + `(mu4e-moved-face ((,class :inherit modus-themes-slant :foreground ,yellow))) + `(mu4e-ok-face ((,class :inherit bold :foreground ,green))) + `(mu4e-region-code ((,class :inherit modus-themes-special-calm))) + `(mu4e-related-face ((,class :inherit (italic shadow)))) + `(mu4e-replied-face ((,class :foreground ,blue))) + `(mu4e-special-header-value-face ((,class :inherit message-header-subject))) + `(mu4e-system-face ((,class :inherit modus-themes-slant :foreground ,fg-mark-del))) + `(mu4e-title-face ((,class :foreground ,fg-main))) + `(mu4e-trashed-face ((,class :foreground ,red))) + `(mu4e-unread-face ((,class :inherit bold))) + `(mu4e-url-number-face ((,class :inherit shadow))) + `(mu4e-view-body-face ((,class :foreground ,fg-main))) + `(mu4e-warning-face ((,class :inherit warning))) +;;;;; multiple-cursors + `(mc/cursor-bar-face ((,class :height 1 :background ,fg-main))) + `(mc/cursor-face ((,class :inverse-video t))) + `(mc/region-face ((,class :inherit region))) +;;;;; nano-modeline + `(nano-modeline-active-primary ((,class :inherit mode-line :foreground ,fg-special-mild))) + `(nano-modeline-active-secondary ((,class :inherit mode-line :foreground ,fg-special-cold))) + `(nano-modeline-active-status-** ((,class :inherit mode-line :background ,yellow-subtle-bg))) + `(nano-modeline-active-status-RO ((,class :inherit mode-line :background ,red-subtle-bg))) + `(nano-modeline-active-status-RW ((,class :inherit mode-line :background ,cyan-subtle-bg))) + `(nano-modeline-inactive-primary ((,class :inherit mode-line-inactive :foreground ,fg-inactive))) + `(nano-modeline-inactive-secondary ((,class :inherit mode-line-inactive :foreground ,fg-inactive))) + `(nano-modeline-inactive-status-** ((,class :inherit mode-line-inactive :foreground ,yellow-active))) + `(nano-modeline-inactive-status-RO ((,class :inherit mode-line-inactive :foreground ,red-active))) + `(nano-modeline-inactive-status-RW ((,class :inherit mode-line-inactive :foreground ,cyan-active))) +;;;;; neotree + `(neo-banner-face ((,class :foreground ,magenta))) + `(neo-button-face ((,class :inherit button))) + `(neo-dir-link-face ((,class :inherit bold :foreground ,blue))) + `(neo-expand-btn-face ((,class :foreground ,cyan))) + `(neo-file-link-face ((,class :foreground ,fg-main))) + `(neo-header-face ((,class :inherit bold :foreground ,fg-main))) + `(neo-root-dir-face ((,class :inherit bold :foreground ,cyan-alt))) + `(neo-vc-added-face ((,class :inherit modus-themes-grue))) + `(neo-vc-conflict-face ((,class :inherit error))) + `(neo-vc-default-face ((,class :foreground ,fg-main))) + `(neo-vc-edited-face ((,class :foreground ,yellow))) + `(neo-vc-ignored-face ((,class :foreground ,fg-inactive))) + `(neo-vc-missing-face ((,class :foreground ,red-alt))) + `(neo-vc-needs-merge-face ((,class :foreground ,magenta-alt))) + `(neo-vc-needs-update-face ((,class :underline t))) + `(neo-vc-removed-face ((,class :strike-through t))) + `(neo-vc-unlocked-changes-face ((,class :inherit modus-themes-refine-blue))) + `(neo-vc-up-to-date-face ((,class :inherit shadow))) + `(neo-vc-user-face ((,class :foreground ,magenta))) +;;;;; notmuch + `(notmuch-crypto-decryption ((,class :inherit (shadow bold)))) + `(notmuch-crypto-part-header ((,class :foreground ,magenta-alt-other))) + `(notmuch-crypto-signature-bad ((,class :inherit error))) + `(notmuch-crypto-signature-good ((,class :inherit success))) + `(notmuch-crypto-signature-good-key ((,class :inherit bold :foreground ,cyan))) + `(notmuch-crypto-signature-unknown ((,class :inherit warning))) + `(notmuch-hello-logo-background ((,class :background "gray50"))) + `(notmuch-jump-key ((,class :inherit modus-themes-key-binding))) + `(notmuch-message-summary-face ((,class :inherit (bold modus-themes-nuanced-cyan)))) + `(notmuch-search-count ((,class :inherit shadow))) + `(notmuch-search-date ((,class :foreground ,cyan))) + `(notmuch-search-flagged-face ((,class :foreground ,red-alt-other))) + `(notmuch-search-matching-authors ((,class :foreground ,fg-special-cold))) + `(notmuch-search-non-matching-authors ((,class :inherit shadow))) + `(notmuch-search-subject ((,class :foreground ,fg-main))) + `(notmuch-search-unread-face ((,class :inherit bold))) + `(notmuch-tag-added ((,class :underline ,blue))) + `(notmuch-tag-deleted ((,class :strike-through ,red))) + `(notmuch-tag-face ((,class :foreground ,blue))) + `(notmuch-tag-flagged ((,class :foreground ,red-alt))) + `(notmuch-tag-unread ((,class :foreground ,magenta-alt))) + `(notmuch-tree-match-author-face ((,class :inherit notmuch-search-matching-authors))) + `(notmuch-tree-match-date-face ((,class :inherit notmuch-search-date))) + `(notmuch-tree-match-face ((,class :foreground ,fg-main))) + `(notmuch-tree-match-tag-face ((,class :inherit notmuch-tag-face))) + `(notmuch-tree-no-match-face ((,class :inherit shadow))) + `(notmuch-tree-no-match-date-face ((,class :inherit shadow))) + `(notmuch-wash-cited-text ((,class :inherit message-cited-text-1))) + `(notmuch-wash-toggle-button ((,class :background ,bg-alt :foreground ,fg-alt))) +;;;;; num3-mode + `(num3-face-even ((,class :inherit bold :background ,bg-alt))) +;;;;; nxml-mode + `(nxml-attribute-colon ((,class :foreground ,fg-main))) + `(nxml-attribute-local-name ((,class :inherit font-lock-variable-name-face))) + `(nxml-attribute-prefix ((,class :inherit font-lock-type-face))) + `(nxml-attribute-value ((,class :inherit font-lock-constant-face))) + `(nxml-cdata-section-CDATA ((,class :inherit error))) + `(nxml-cdata-section-delimiter ((,class :inherit error))) + `(nxml-char-ref-delimiter ((,class :foreground ,fg-special-mild))) + `(nxml-char-ref-number ((,class :inherit modus-themes-bold :foreground ,fg-special-mild))) + `(nxml-delimited-data ((,class :inherit modus-themes-slant :foreground ,fg-special-cold))) + `(nxml-delimiter ((,class :foreground ,fg-dim))) + `(nxml-element-colon ((,class :foreground ,fg-main))) + `(nxml-element-local-name ((,class :inherit font-lock-function-name-face))) + `(nxml-element-prefix ((,class :inherit font-lock-builtin-face))) + `(nxml-entity-ref-delimiter ((,class :foreground ,fg-special-mild))) + `(nxml-entity-ref-name ((,class :inherit modus-themes-bold :foreground ,fg-special-mild))) + `(nxml-glyph ((,class :inherit modus-themes-intense-neutral))) + `(nxml-hash ((,class :inherit (bold font-lock-string-face)))) + `(nxml-heading ((,class :inherit bold))) + `(nxml-name ((,class :inherit font-lock-builtin-face))) + `(nxml-namespace-attribute-colon ((,class :foreground ,fg-main))) + `(nxml-namespace-attribute-prefix ((,class :inherit font-lock-variable-name-face))) + `(nxml-processing-instruction-target ((,class :inherit font-lock-keyword-face))) + `(nxml-prolog-keyword ((,class :inherit font-lock-keyword-face))) + `(nxml-ref ((,class :inherit modus-themes-bold :foreground ,fg-special-mild))) + `(rng-error ((,class :inherit error))) +;;;;; orderless + `(orderless-match-face-0 ((,class :inherit modus-themes-completion-match-0))) + `(orderless-match-face-1 ((,class :inherit modus-themes-completion-match-1))) + `(orderless-match-face-2 ((,class :inherit modus-themes-completion-match-2))) + `(orderless-match-face-3 ((,class :inherit modus-themes-completion-match-3))) +;;;;; org + `(org-agenda-calendar-event ((,class ,@(modus-themes--agenda-event blue-alt)))) + `(org-agenda-calendar-sexp ((,class ,@(modus-themes--agenda-event blue-alt t)))) + `(org-agenda-clocking ((,class :background ,yellow-nuanced-bg :foreground ,red-alt))) + `(org-agenda-column-dateline ((,class :background ,bg-alt))) + `(org-agenda-current-time ((,class :foreground ,blue-alt-other-faint))) + `(org-agenda-date ((,class ,@(modus-themes--agenda-date cyan fg-main)))) + `(org-agenda-date-today + ((,class ,@(modus-themes--agenda-date cyan fg-main nil nil bg-special-cold t t)))) + `(org-agenda-date-weekend + ((,class ,@(modus-themes--agenda-date cyan-alt-other-faint fg-alt cyan fg-main)))) + `(org-agenda-date-weekend-today + ((,class ,@(modus-themes--agenda-date cyan-alt-other-faint fg-alt cyan fg-main bg-special-cold t t)))) + `(org-agenda-diary ((,class :inherit org-agenda-calendar-sexp))) + `(org-agenda-dimmed-todo-face ((,class :inherit shadow))) + `(org-agenda-done ((,class :inherit modus-themes-grue-nuanced))) + `(org-agenda-filter-category ((,class :inherit bold :foreground ,cyan-active))) + `(org-agenda-filter-effort ((,class :inherit bold :foreground ,cyan-active))) + `(org-agenda-filter-regexp ((,class :inherit bold :foreground ,cyan-active))) + `(org-agenda-filter-tags ((,class :inherit bold :foreground ,cyan-active))) + `(org-agenda-restriction-lock ((,class :background ,bg-dim :foreground ,fg-dim))) + `(org-agenda-structure ((,class ,@(modus-themes--agenda-structure blue-alt)))) + `(org-agenda-structure-filter ((,class :inherit org-agenda-structure :foreground ,yellow))) + `(org-agenda-structure-secondary ((,class :foreground ,cyan))) + `(org-archived ((,class :background ,bg-alt :foreground ,fg-alt))) + `(org-block ((,class :inherit modus-themes-fixed-pitch + ,@(modus-themes--org-block bg-dim fg-main)))) + `(org-block-begin-line ((,class :inherit modus-themes-fixed-pitch + ,@(modus-themes--org-block-delim + bg-dim fg-special-cold + bg-alt fg-alt)))) + `(org-block-end-line ((,class :inherit org-block-begin-line))) + `(org-checkbox ((,class :foreground ,yellow-alt-other))) + `(org-checkbox-statistics-done ((,class :inherit org-done))) + `(org-checkbox-statistics-todo ((,class :inherit org-todo))) + `(org-clock-overlay ((,class :background ,yellow-nuanced-bg :foreground ,red-alt-faint))) + `(org-code ((,class :inherit modus-themes-markup-code :extend t))) + `(org-column ((,class :inherit (modus-themes-fixed-pitch default) + :background ,bg-alt))) + `(org-column-title ((,class :inherit (bold modus-themes-fixed-pitch default) + :underline t :background ,bg-alt))) + `(org-date ((,class :inherit (modus-themes-link-symlink modus-themes-fixed-pitch)))) + `(org-date-selected ((,class :foreground ,blue-alt :inverse-video t))) + `(org-dispatcher-highlight ((,class :inherit (bold modus-themes-mark-alt)))) + `(org-document-info ((,class :foreground ,fg-special-cold))) + `(org-document-info-keyword ((,class :inherit (shadow modus-themes-fixed-pitch)))) + `(org-document-title ((,class :inherit modus-themes-heading-0))) + `(org-done ((,class :inherit modus-themes-grue))) + `(org-drawer ((,class :inherit (shadow modus-themes-fixed-pitch)))) + `(org-ellipsis (())) ; inherits from the heading's color + `(org-footnote ((,class :inherit button + ,@(modus-themes--link-color + blue-alt blue-alt-faint)))) + `(org-formula ((,class :inherit modus-themes-fixed-pitch :foreground ,red-alt))) + `(org-habit-alert-face ((,class ,@(modus-themes--agenda-habit + yellow-graph-0-bg + yellow-graph-0-bg + yellow-graph-1-bg) + :foreground "black"))) ; special case + `(org-habit-alert-future-face ((,class ,@(modus-themes--agenda-habit + yellow-graph-1-bg + yellow-graph-0-bg + yellow-graph-1-bg)))) + `(org-habit-clear-face ((,class ,@(modus-themes--agenda-habit + blue-graph-0-bg + green-graph-1-bg + blue-graph-1-bg + blue-graph-1-bg + blue-graph-1-bg) + :foreground "black"))) ; special case + `(org-habit-clear-future-face ((,class ,@(modus-themes--agenda-habit + blue-graph-1-bg + green-graph-1-bg + blue-graph-1-bg + blue-graph-1-bg + blue-graph-1-bg)))) + `(org-habit-overdue-face ((,class ,@(modus-themes--agenda-habit + red-graph-0-bg + red-graph-0-bg + red-graph-1-bg)))) + `(org-habit-overdue-future-face ((,class ,@(modus-themes--agenda-habit + red-graph-1-bg + red-graph-0-bg + red-graph-1-bg)))) + `(org-habit-ready-face ((,class ,@(modus-themes--agenda-habit + green-graph-0-bg + green-graph-0-bg + green-graph-1-bg + cyan-graph-0-bg + blue-graph-0-bg + cyan-graph-1-bg) + :foreground "black"))) ; special case + `(org-habit-ready-future-face ((,class ,@(modus-themes--agenda-habit + green-graph-1-bg + green-graph-0-bg + green-graph-1-bg + cyan-graph-1-bg + blue-graph-0-bg + cyan-graph-1-bg)))) + `(org-headline-done ((,class :inherit (modus-themes-variable-pitch modus-themes-grue-nuanced)))) + `(org-headline-todo ((,class :inherit modus-themes-variable-pitch :foreground ,red-nuanced-fg))) + `(org-hide ((,class :foreground ,bg-main))) + `(org-indent ((,class :inherit (fixed-pitch org-hide)))) + `(org-imminent-deadline ((,class :foreground ,red-intense))) + `(org-latex-and-related ((,class :foreground ,magenta-faint))) + `(org-level-1 ((,class :inherit modus-themes-heading-1))) + `(org-level-2 ((,class :inherit modus-themes-heading-2))) + `(org-level-3 ((,class :inherit modus-themes-heading-3))) + `(org-level-4 ((,class :inherit modus-themes-heading-4))) + `(org-level-5 ((,class :inherit modus-themes-heading-5))) + `(org-level-6 ((,class :inherit modus-themes-heading-6))) + `(org-level-7 ((,class :inherit modus-themes-heading-7))) + `(org-level-8 ((,class :inherit modus-themes-heading-8))) + `(org-link ((,class :inherit button))) + `(org-list-dt ((,class :inherit bold))) + `(org-macro ((,class :inherit modus-themes-markup-macro))) + `(org-meta-line ((,class :inherit (shadow modus-themes-fixed-pitch)))) + `(org-mode-line-clock ((,class :foreground ,fg-main))) + `(org-mode-line-clock-overrun ((,class :inherit bold :foreground ,red-active))) + `(org-priority ((,class :foreground ,magenta))) + `(org-property-value ((,class :inherit modus-themes-fixed-pitch :foreground ,fg-special-cold))) + `(org-quote ((,class ,@(modus-themes--org-block bg-dim fg-special-cold fg-main)))) + `(org-scheduled ((,class ,@(modus-themes--agenda-scheduled yellow-faint fg-special-warm magenta-alt)))) + `(org-scheduled-previously ((,class ,@(modus-themes--agenda-scheduled yellow fg-special-warm yellow-alt-other)))) + `(org-scheduled-today ((,class ,@(modus-themes--agenda-scheduled yellow fg-special-warm magenta-alt-other)))) + `(org-sexp-date ((,class :foreground ,cyan-alt-other))) + `(org-special-keyword ((,class :inherit (shadow modus-themes-fixed-pitch)))) + `(org-table ((,class :inherit modus-themes-fixed-pitch :foreground ,fg-special-cold))) + `(org-table-header ((,class :inherit (fixed-pitch modus-themes-special-cold)))) + `(org-tag ((,class :foreground ,magenta-nuanced-fg))) + `(org-tag-group ((,class :inherit bold :foreground ,cyan-nuanced-fg))) + `(org-target ((,class :underline t))) + `(org-time-grid ((,class :inherit shadow))) + `(org-todo ((,class :foreground ,red))) + `(org-upcoming-deadline ((,class :foreground ,red-alt-other))) + `(org-upcoming-distant-deadline ((,class :foreground ,red-faint))) + `(org-verbatim ((,class :inherit modus-themes-markup-verbatim))) + `(org-verse ((,class :inherit org-quote))) + `(org-warning ((,class :inherit bold :foreground ,red-alt-other))) +;;;;; org-journal + `(org-journal-calendar-entry-face ((,class :inherit modus-themes-slant :foreground ,yellow-alt-other))) + `(org-journal-calendar-scheduled-face ((,class :inherit modus-themes-slant :foreground ,red-alt-other))) + `(org-journal-highlight ((,class :foreground ,magenta-alt))) +;;;;; org-noter + `(org-noter-no-notes-exist-face ((,class :inherit error))) + `(org-noter-notes-exist-face ((,class :inherit success))) +;;;;; org-pomodoro + `(org-pomodoro-mode-line ((,class :foreground ,red-active))) + `(org-pomodoro-mode-line-break ((,class :foreground ,cyan-active))) + `(org-pomodoro-mode-line-overtime ((,class :inherit bold :foreground ,red-active))) +;;;;; org-recur + `(org-recur ((,class :foreground ,magenta-active))) +;;;;; org-roam + `(org-roam-dim ((,class :foreground "gray50"))) + `(org-roam-header-line ((,class :inherit bold :foreground ,magenta-active))) + `(org-roam-olp ((,class :inherit shadow))) + `(org-roam-preview-heading ((,class :inherit modus-themes-subtle-neutral))) + `(org-roam-preview-heading-highlight ((,class :inherit modus-themes-intense-neutral))) + `(org-roam-preview-heading-selection ((,class :inherit modus-themes-special-cold))) + `(org-roam-preview-region ((,class :inherit bold))) + `(org-roam-title ((,class :inherit modus-themes-pseudo-header))) +;;;;; org-superstar + `(org-superstar-item ((,class :foreground ,fg-main))) + `(org-superstar-leading ((,class :foreground ,fg-whitespace))) +;;;;; org-table-sticky-header + `(org-table-sticky-header-face ((,class :inherit modus-themes-special-cold))) +;;;;; org-tree-slide + `(org-tree-slide-header-overlay-face ((,class :inherit org-document-title))) +;;;;; origami + `(origami-fold-header-face ((,class :background ,bg-dim :foreground ,fg-dim :box t))) + `(origami-fold-replacement-face ((,class :background ,bg-alt :foreground ,fg-alt))) +;;;;; outline-mode + `(outline-1 ((,class :inherit modus-themes-heading-1))) + `(outline-2 ((,class :inherit modus-themes-heading-2))) + `(outline-3 ((,class :inherit modus-themes-heading-3))) + `(outline-4 ((,class :inherit modus-themes-heading-4))) + `(outline-5 ((,class :inherit modus-themes-heading-5))) + `(outline-6 ((,class :inherit modus-themes-heading-6))) + `(outline-7 ((,class :inherit modus-themes-heading-7))) + `(outline-8 ((,class :inherit modus-themes-heading-8))) +;;;;; outline-minor-faces + `(outline-minor-0 (())) +;;;;; package (M-x list-packages) + `(package-description ((,class :foreground ,fg-special-cold))) + `(package-help-section-name ((,class :inherit bold :foreground ,cyan))) + `(package-name ((,class :inherit button))) + `(package-status-available ((,class :foreground ,cyan-alt-other))) + `(package-status-avail-obso ((,class :inherit error))) + `(package-status-built-in ((,class :foreground ,magenta))) + `(package-status-dependency ((,class :foreground ,magenta-alt-other))) + `(package-status-disabled ((,class :inherit modus-themes-subtle-red))) + `(package-status-external ((,class :foreground ,cyan-alt-other))) + `(package-status-held ((,class :foreground ,yellow-alt))) + `(package-status-incompat ((,class :inherit warning))) + `(package-status-installed ((,class :foreground ,fg-special-warm))) + `(package-status-new ((,class :inherit success))) + `(package-status-unsigned ((,class :inherit error))) +;;;;; page-break-lines + `(page-break-lines ((,class :inherit default :foreground ,fg-window-divider-outer))) +;;;;; pandoc-mode + `(pandoc-citation-key-face ((,class :background ,bg-dim :foreground ,magenta-alt))) + `(pandoc-directive-@@-face ((,class :background ,bg-dim :foreground ,blue-alt-other))) + `(pandoc-directive-braces-face ((,class :foreground ,blue-alt-other))) + `(pandoc-directive-contents-face ((,class :foreground ,cyan-alt-other))) + `(pandoc-directive-type-face ((,class :foreground ,magenta))) +;;;;; paren-face + `(parenthesis ((,class :foreground ,fg-unfocused))) +;;;;; pass + `(pass-mode-directory-face ((,class :inherit bold :foreground ,fg-special-cold))) + `(pass-mode-entry-face ((,class :background ,bg-main :foreground ,fg-main))) + `(pass-mode-header-face ((,class :foreground ,fg-special-warm))) +;;;;; pdf-tools + `(pdf-links-read-link ((,class :background ,fg-main :foreground ,magenta-intense-bg :inherit bold))) ; Foreground is background and vice versa + `(pdf-occur-document-face ((,class :inherit shadow))) + `(pdf-occur-page-face ((,class :inherit shadow))) +;;;;; persp-mode + `(persp-face-lighter-buffer-not-in-persp ((,class :inherit modus-themes-intense-red))) + `(persp-face-lighter-default ((,class :inherit bold :foreground ,blue-active))) + `(persp-face-lighter-nil-persp ((,class :inherit bold :foreground ,fg-active))) +;;;;; perspective + `(persp-selected-face ((,class :inherit bold :foreground ,blue-active))) +;;;;; phi-grep + `(phi-grep-heading-face ((,class :inherit modus-themes-pseudo-header :foreground ,fg-special-cold))) + `(phi-grep-line-number-face ((,class :foreground ,fg-special-warm))) + `(phi-grep-match-face ((,class :inherit modus-themes-special-calm))) + `(phi-grep-modified-face ((,class :inherit modus-themes-refine-yellow))) + `(phi-grep-overlay-face ((,class :inherit modus-themes-refine-blue))) +;;;;; pomidor + `(pomidor-break-face ((,class :foreground ,blue-alt-other))) + `(pomidor-overwork-face ((,class :foreground ,red-alt-other))) + `(pomidor-skip-face ((,class :inherit (shadow modus-themes-slant)))) + `(pomidor-work-face ((,class :inherit modus-themes-grue))) +;;;;; popup + `(popup-face ((,class :background ,bg-alt :foreground ,fg-main))) + `(popup-isearch-match ((,class :inherit modus-themes-search-success))) + `(popup-menu-mouse-face ((,class :inherit highlight))) + `(popup-menu-selection-face ((,class :inherit modus-themes-completion-selected-popup))) + `(popup-scroll-bar-background-face ((,class :background ,bg-active))) + `(popup-scroll-bar-foreground-face ((,class :foreground ,fg-active))) + `(popup-summary-face ((,class :background ,bg-active :foreground ,fg-inactive))) + `(popup-tip-face ((,class :inherit modus-themes-refine-yellow))) +;;;;; powerline + `(powerline-active0 ((,class :background ,fg-unfocused :foreground ,bg-main))) + `(powerline-active1 ((,class :inherit mode-line-active))) + `(powerline-active2 ((,class :inherit mode-line-inactive))) + `(powerline-inactive0 ((,class :background ,bg-active :foreground ,fg-alt))) + `(powerline-inactive1 ((,class :background ,bg-main :foreground ,fg-alt))) + `(powerline-inactive2 ((,class :inherit mode-line-inactive))) +;;;;; powerline-evil + `(powerline-evil-base-face ((,class :background ,fg-main :foreground ,bg-main))) + `(powerline-evil-emacs-face ((,class :inherit modus-themes-active-magenta))) + `(powerline-evil-insert-face ((,class :inherit modus-themes-active-green))) + `(powerline-evil-motion-face ((,class :inherit modus-themes-active-blue))) + `(powerline-evil-normal-face ((,class :background ,fg-alt :foreground ,bg-main))) + `(powerline-evil-operator-face ((,class :inherit modus-themes-active-yellow))) + `(powerline-evil-replace-face ((,class :inherit modus-themes-active-red))) + `(powerline-evil-visual-face ((,class :inherit modus-themes-active-cyan))) +;;;;; proced + `(proced-mark ((,class :inherit modus-themes-mark-symbol))) + `(proced-marked ((,class :inherit modus-themes-mark-alt))) + `(proced-sort-header ((,class :inherit bold :foreground ,fg-special-calm :underline t))) +;;;;; prodigy + `(prodigy-green-face ((,class :inherit success))) + `(prodigy-red-face ((,class :inherit error))) + `(prodigy-yellow-face ((,class :inherit warning))) +;;;;; pulse + `(pulse-highlight-start-face ((,class :background ,bg-active-accent :extend t))) +;;;;; pyim + `(pyim-page ((,class :background ,bg-active :foreground ,fg-active))) + `(pyim-page-selection ((,class :inherit bold :background ,bg-active :foreground ,blue-active))) + `(pyim-page-subword ((,class :background ,bg-inactive))) +;;;;; quick-peek + `(quick-peek-background-face ((,class :background ,bg-alt))) + `(quick-peek-border-face ((,class :background ,fg-window-divider-inner :height 1))) + `(quick-peek-padding-face ((,class :background ,bg-alt :height 0.15))) +;;;;; racket-mode + `(racket-debug-break-face ((,class :inherit modus-themes-intense-red))) + `(racket-debug-locals-face ((,class :box (:line-width -1 :color nil) + :foreground ,green-alt-other))) + `(racket-debug-result-face ((,class :inherit bold :box (:line-width -1 :color nil) + :foreground ,green))) + `(racket-here-string-face ((,class :foreground ,blue-alt))) + `(racket-keyword-argument-face ((,class :foreground ,red-alt))) + `(racket-logger-config-face ((,class :inherit (shadow modus-themes-slant)))) + `(racket-logger-debug-face ((,class :foreground ,blue-alt-other))) + `(racket-logger-info-face ((,class :foreground ,fg-lang-note))) + `(racket-logger-topic-face ((,class :inherit modus-themes-slant :foreground ,magenta))) + `(racket-selfeval-face ((,class :foreground ,green-alt))) + `(racket-xp-error-face ((,class :inherit modus-themes-lang-error))) +;;;;; rainbow-blocks + `(rainbow-blocks-depth-1-face ((,class :foreground ,magenta-alt-other))) + `(rainbow-blocks-depth-2-face ((,class :foreground ,blue))) + `(rainbow-blocks-depth-3-face ((,class :foreground ,magenta-alt))) + `(rainbow-blocks-depth-4-face ((,class :foreground ,green))) + `(rainbow-blocks-depth-5-face ((,class :foreground ,magenta))) + `(rainbow-blocks-depth-6-face ((,class :foreground ,cyan))) + `(rainbow-blocks-depth-7-face ((,class :foreground ,yellow))) + `(rainbow-blocks-depth-8-face ((,class :foreground ,cyan-alt))) + `(rainbow-blocks-depth-9-face ((,class :foreground ,red-alt))) + `(rainbow-blocks-unmatched-face ((,class :foreground ,red))) +;;;;; rainbow-delimiters + `(rainbow-delimiters-base-error-face ((,class :background ,red-subtle-bg :foreground ,fg-main))) + `(rainbow-delimiters-base-face ((,class :foreground ,fg-main))) + `(rainbow-delimiters-depth-1-face ((,class :foreground ,fg-main))) + `(rainbow-delimiters-depth-2-face ((,class :foreground ,magenta-intense))) + `(rainbow-delimiters-depth-3-face ((,class :foreground ,cyan-intense))) + `(rainbow-delimiters-depth-4-face ((,class :foreground ,orange-intense))) + `(rainbow-delimiters-depth-5-face ((,class :foreground ,purple-intense))) + `(rainbow-delimiters-depth-6-face ((,class :foreground ,green-intense))) + `(rainbow-delimiters-depth-7-face ((,class :foreground ,red-intense))) + `(rainbow-delimiters-depth-8-face ((,class :foreground ,blue-intense))) + `(rainbow-delimiters-depth-9-face ((,class :foreground ,yellow-intense))) + `(rainbow-delimiters-mismatched-face ((,class :inherit (bold modus-themes-refine-yellow)))) + `(rainbow-delimiters-unmatched-face ((,class :inherit (bold modus-themes-refine-red)))) +;;;;; rcirc + `(rcirc-bright-nick ((,class :inherit bold :foreground ,magenta-intense))) + `(rcirc-dim-nick ((,class :inherit shadow))) + `(rcirc-monospace-text ((,class :inherit fixed-pitch))) + `(rcirc-my-nick ((,class :inherit bold :foreground ,magenta))) + `(rcirc-nick-in-message ((,class :inherit bold :foreground ,red-alt))) + `(rcirc-nick-in-message-full-line ((,class :inherit bold :foreground ,cyan-alt-other))) + `(rcirc-other-nick ((,class :inherit bold :foreground ,blue))) + `(rcirc-prompt ((,class :inherit modus-themes-prompt))) + `(rcirc-server ((,class :inherit shadow))) + `(rcirc-timestamp ((,class :foreground ,cyan))) + `(rcirc-track-keyword ((,class :inherit bold))) + `(rcirc-track-nick ((,class :inherit bold :foreground ,red-active))) + `(rcirc-url ((,class :inherit link))) +;;;;; recursion-indicator + `(recursion-indicator-general ((,class :foreground ,blue-active))) + `(recursion-indicator-minibuffer ((,class :foreground ,red-active))) +;;;;; regexp-builder (re-builder) + `(reb-match-0 ((,class :inherit modus-themes-refine-cyan))) + `(reb-match-1 ((,class :inherit modus-themes-subtle-magenta))) + `(reb-match-2 ((,class :inherit modus-themes-subtle-green))) + `(reb-match-3 ((,class :inherit modus-themes-refine-yellow))) + `(reb-regexp-grouping-backslash ((,class :inherit font-lock-regexp-grouping-backslash))) + `(reb-regexp-grouping-construct ((,class :inherit font-lock-regexp-grouping-construct))) +;;;;; rg (rg.el) + `(rg-column-number-face ((,class :foreground ,magenta-alt-other))) + `(rg-context-face ((,class :foreground ,fg-unfocused))) + `(rg-error-face ((,class :inherit bold :foreground ,red))) + `(rg-file-tag-face ((,class :foreground ,fg-special-cold))) + `(rg-filename-face ((,class :inherit bold :foreground ,fg-special-cold))) + `(rg-line-number-face ((,class :foreground ,fg-special-warm))) + `(rg-literal-face ((,class :foreground ,blue-alt))) + `(rg-match-face ((,class :inherit modus-themes-special-calm))) + `(rg-regexp-face ((,class :foreground ,magenta-active))) + `(rg-toggle-off-face ((,class :inherit bold :foreground ,fg-inactive))) + `(rg-toggle-on-face ((,class :inherit bold :foreground ,cyan-active))) + `(rg-warning-face ((,class :inherit bold :foreground ,yellow))) +;;;;; ripgrep + `(ripgrep-context-face ((,class :foreground ,fg-unfocused))) + `(ripgrep-error-face ((,class :inherit bold :foreground ,red))) + `(ripgrep-hit-face ((,class :foreground ,cyan))) + `(ripgrep-match-face ((,class :inherit modus-themes-special-calm))) +;;;;; rmail + `(rmail-header-name ((,class :foreground ,cyan-alt-other))) + `(rmail-highlight ((,class :inherit bold :foreground ,magenta-alt))) +;;;;; ruler-mode + `(ruler-mode-column-number ((,class :inherit ruler-mode-default :foreground ,fg-main))) + `(ruler-mode-comment-column ((,class :inherit ruler-mode-default :foreground ,red))) + `(ruler-mode-current-column ((,class :inherit ruler-mode-default :background ,blue-subtle-bg :foreground ,fg-main))) + `(ruler-mode-default ((,class :inherit default :background ,bg-alt :foreground ,fg-unfocused))) + `(ruler-mode-fill-column ((,class :inherit ruler-mode-default :foreground ,green))) + `(ruler-mode-fringes ((,class :inherit ruler-mode-default :foreground ,cyan))) + `(ruler-mode-goal-column ((,class :inherit ruler-mode-default :foreground ,blue))) + `(ruler-mode-margins ((,class :inherit ruler-mode-default :foreground ,bg-main))) + `(ruler-mode-pad ((,class :inherit ruler-mode-default :background ,bg-active :foreground ,fg-inactive))) + `(ruler-mode-tab-stop ((,class :inherit ruler-mode-default :foreground ,fg-special-warm))) +;;;;; selectrum + `(selectrum-current-candidate ((,class :inherit modus-themes-completion-selected))) + `(selectrum-mouse-highlight ((,class :inherit highlight))) + `(selectrum-quick-keys-highlight ((,class :inherit bold :background ,bg-char-0))) + `(selectrum-quick-keys-match ((,class :inherit bold :background ,bg-char-1))) +;;;;; selectrum-prescient + `(selectrum-prescient-primary-highlight ((,class :inherit modus-themes-completion-match-0))) + `(selectrum-prescient-secondary-highlight ((,class :inherit modus-themes-completion-match-1))) +;;;;; semantic + `(semantic-complete-inline-face ((,class :foreground ,fg-special-warm :underline t))) + `(semantic-decoration-on-fileless-includes ((,class :inherit modus-themes-refine-green))) + `(semantic-decoration-on-private-members-face ((,class :inherit modus-themes-refine-cyan))) + `(semantic-decoration-on-protected-members-face ((,class :background ,bg-dim))) + `(semantic-decoration-on-unknown-includes ((,class :inherit modus-themes-refine-red))) + `(semantic-decoration-on-unparsed-includes ((,class :inherit modus-themes-refine-yellow))) + `(semantic-highlight-edits-face ((,class :background ,bg-alt))) + `(semantic-highlight-func-current-tag-face ((,class :background ,bg-alt))) + `(semantic-idle-symbol-highlight ((,class :inherit modus-themes-special-mild))) + `(semantic-tag-boundary-face ((,class :overline ,blue-intense))) + `(semantic-unmatched-syntax-face ((,class :underline ,fg-lang-error))) +;;;;; sesman + `(sesman-browser-button-face ((,class :inherit button))) + `(sesman-browser-highligh-face ((,class :inherit highlight))) + `(sesman-buffer-face ((,class :foreground ,magenta))) + `(sesman-directory-face ((,class :inherit bold :foreground ,blue))) + `(sesman-project-face ((,class :inherit bold :foreground ,magenta-alt-other))) +;;;;; shell-script-mode + `(sh-heredoc ((,class :foreground ,blue-alt))) + `(sh-quoted-exec ((,class :inherit modus-themes-bold :foreground ,magenta-alt))) +;;;;; shortdoc + `(shortdoc-heading ((,class :inherit modus-themes-pseudo-header))) + `(shortdoc-section (())) ; remove the default's variable-pitch style +;;;;; show-paren-mode + `(show-paren-match ((,class ,@(modus-themes--paren bg-paren-match + bg-paren-match-intense) + :foreground ,fg-main))) + `(show-paren-match-expression ((,class :background ,bg-paren-expression))) + `(show-paren-mismatch ((,class :inherit modus-themes-intense-red))) +;;;;; shr + `(shr-abbreviation ((,class :inherit modus-themes-lang-note))) + `(shr-code ((,class :inherit modus-themes-markup-verbatim))) + `(shr-h1 ((,class :inherit modus-themes-heading-1))) + `(shr-h2 ((,class :inherit modus-themes-heading-2))) + `(shr-h3 ((,class :inherit modus-themes-heading-3))) + `(shr-h4 ((,class :inherit modus-themes-heading-4))) + `(shr-h5 ((,class :inherit modus-themes-heading-5))) + `(shr-h6 ((,class :inherit modus-themes-heading-6))) + `(shr-selected-link ((,class :inherit modus-themes-subtle-red))) +;;;;; side-notes + `(side-notes ((,class :background ,bg-dim :foreground ,fg-dim))) +;;;;; sieve-mode + `(sieve-action-commands ((,class :inherit font-lock-builtin-face))) + `(sieve-control-commands ((,class :inherit font-lock-keyword-face))) + `(sieve-tagged-arguments ((,class :inherit font-lock-type-face))) + `(sieve-test-commands ((,class :inherit font-lock-function-name-face))) +;;;;; skewer-mode + `(skewer-error-face ((,class :foreground ,red :underline t))) +;;;;; slime (sldb) + `(sldb-condition-face ((,class :inherit font-lock-preprocessor-face))) + `(sldb-restart-number-face ((,class :inherit bold))) + `(sldb-restart-type-face ((,class :inherit font-lock-type-face))) + `(sldb-restartable-frame-line-face ((,class :inherit success))) + `(sldb-section-face ((,class :inherit modus-themes-pseudo-header))) + `(slime-error-face ((,class :inherit modus-themes-lang-error))) + `(slime-note-face ((,class :underline t))) + `(slime-repl-input-face ((,class :inherit bold))) + `(slime-repl-inputed-output-face ((,class :inherit font-lock-string-face))) + `(slime-repl-output-mouseover-face ((,class :inherit highlight))) + `(slime-repl-prompt-face ((,class :inherit modus-themes-prompt))) + `(slime-style-warning-face ((,class :inherit modus-themes-lang-note))) + `(slime-warning-face ((,class :inherit modus-themes-lang-warning))) +;;;;; sly + `(sly-action-face ((,class :inherit font-lock-type-face))) + `(sly-db-condition-face ((,class :inherit font-lock-preprocessor-face))) + `(sly-db-restartable-frame-line-face ((,class :inherit success))) + `(sly-error-face ((,class :inherit modus-themes-lang-error))) + `(sly-mode-line ((,class :inherit mode-line-emphasis))) + `(sly-mrepl-output-face ((,class :inherit font-lock-string-face))) + `(sly-mrepl-output-face ((,class :inherit font-lock-string-face))) + `(sly-mrepl-prompt-face ((,class :inherit modus-themes-prompt))) + `(sly-note-face ((,class :inherit modus-themes-lang-note))) + `(sly-stickers-placed-face ((,class :inherit modus-themes-subtle-neutral))) + `(sly-style-warning-face ((,class :inherit modus-themes-lang-note))) + `(sly-warning-face ((,class :inherit modus-themes-lang-warning))) +;;;;; smart-mode-line + `(sml/charging ((,class :foreground ,green-active))) + `(sml/discharging ((,class :foreground ,red-active))) + `(sml/filename ((,class :inherit bold :foreground ,blue-active))) + `(sml/folder ((,class :foreground ,fg-active))) + `(sml/git ((,class :inherit bold :foreground ,green-active))) + `(sml/global ((,class :foreground ,fg-active))) + `(sml/line-number ((,class :inherit sml/global))) + `(sml/minor-modes ((,class :inherit sml/global))) + `(sml/modes ((,class :inherit bold :foreground ,fg-active))) + `(sml/modified ((,class :inherit bold :foreground ,magenta-active))) + `(sml/mule-info ((,class :inherit sml/global))) + `(sml/name-filling ((,class :foreground ,yellow-active))) + `(sml/not-modified ((,class :inherit sml/global))) + `(sml/numbers-separator ((,class :inherit sml/global))) + `(sml/outside-modified ((,class :inherit modus-themes-intense-red))) + `(sml/position-percentage ((,class :inherit sml/global))) + `(sml/prefix ((,class :foreground ,green-active))) + `(sml/process ((,class :inherit sml/prefix))) + `(sml/projectile ((,class :inherit sml/git))) + `(sml/read-only ((,class :inherit bold :foreground ,cyan-active))) + `(sml/remote ((,class :inherit sml/global))) + `(sml/sudo ((,class :inherit modus-themes-subtle-red))) + `(sml/time ((,class :inherit sml/global))) + `(sml/vc ((,class :inherit sml/git))) + `(sml/vc-edited ((,class :inherit bold :foreground ,yellow-active))) +;;;;; smartparens + `(sp-pair-overlay-face ((,class :inherit modus-themes-special-warm))) + `(sp-show-pair-enclosing ((,class :inherit modus-themes-special-mild))) + `(sp-show-pair-match-face ((,class ,@(modus-themes--paren bg-paren-match + bg-paren-match-intense) + :foreground ,fg-main))) + `(sp-show-pair-mismatch-face ((,class :inherit modus-themes-intense-red))) + `(sp-wrap-overlay-closing-pair ((,class :inherit sp-pair-overlay-face))) + `(sp-wrap-overlay-face ((,class :inherit sp-pair-overlay-face))) + `(sp-wrap-overlay-opening-pair ((,class :inherit sp-pair-overlay-face))) + `(sp-wrap-tag-overlay-face ((,class :inherit sp-pair-overlay-face))) +;;;;; smerge + `(smerge-base ((,class :inherit modus-themes-diff-changed))) + `(smerge-lower ((,class :inherit modus-themes-diff-added))) + `(smerge-markers ((,class :inherit modus-themes-diff-heading))) + `(smerge-refined-added ((,class :inherit modus-themes-diff-refine-added))) + `(smerge-refined-changed (())) + `(smerge-refined-removed ((,class :inherit modus-themes-diff-refine-removed))) + `(smerge-upper ((,class :inherit modus-themes-diff-removed))) +;;;;; spaceline + `(spaceline-evil-emacs ((,class :inherit modus-themes-active-magenta))) + `(spaceline-evil-insert ((,class :inherit modus-themes-active-green))) + `(spaceline-evil-motion ((,class :inherit modus-themes-active-blue))) + `(spaceline-evil-normal ((,class :background ,fg-alt :foreground ,bg-alt))) + `(spaceline-evil-replace ((,class :inherit modus-themes-active-red))) + `(spaceline-evil-visual ((,class :inherit modus-themes-active-cyan))) + `(spaceline-flycheck-error ((,class :foreground ,red-active))) + `(spaceline-flycheck-info ((,class :foreground ,cyan-active))) + `(spaceline-flycheck-warning ((,class :foreground ,yellow-active))) + `(spaceline-highlight-face ((,class :inherit modus-themes-fringe-blue))) + `(spaceline-modified ((,class :inherit modus-themes-fringe-magenta))) + `(spaceline-python-venv ((,class :foreground ,magenta-active))) + `(spaceline-read-only ((,class :inherit modus-themes-fringe-red))) + `(spaceline-unmodified ((,class :inherit modus-themes-fringe-cyan))) +;;;;; speedbar + `(speedbar-button-face ((,class :inherit button))) + `(speedbar-directory-face ((,class :inherit bold :foreground ,blue))) + `(speedbar-file-face ((,class :foreground ,fg-main))) + `(speedbar-highlight-face ((,class :inherit highlight))) + `(speedbar-selected-face ((,class :inherit bold :foreground ,cyan))) + `(speedbar-separator-face ((,class :inherit modus-themes-intense-neutral))) + `(speedbar-tag-face ((,class :foreground ,yellow-alt-other))) +;;;;; spell-fu + `(spell-fu-incorrect-face ((,class :inherit modus-themes-lang-error))) +;;;;; stripes + `(stripes ((,class :background ,bg-alt))) +;;;;; suggest + `(suggest-heading ((,class :inherit bold :foreground ,yellow-alt-other))) +;;;;; switch-window + `(switch-window-background ((,class :background ,bg-dim))) + `(switch-window-label ((,class :height 3.0 :foreground ,blue-intense))) +;;;;; swiper + `(swiper-background-match-face-1 (( ))) + `(swiper-background-match-face-2 ((,class :inherit modus-themes-completion-match-0))) + `(swiper-background-match-face-3 ((,class :inherit modus-themes-completion-match-1))) + `(swiper-background-match-face-4 ((,class :inherit modus-themes-completion-match-2))) + `(swiper-line-face ((,class :background ,bg-hl-alt-intense))) + `(swiper-match-face-1 (( ))) + `(swiper-match-face-2 ((,class :inherit modus-themes-completion-match-0))) + `(swiper-match-face-3 ((,class :inherit modus-themes-completion-match-1))) + `(swiper-match-face-4 ((,class :inherit modus-themes-completion-match-2))) +;;;;; sx + `(sx-inbox-item-type ((,class :foreground ,magenta-alt-other))) + `(sx-inbox-item-type-unread ((,class :inherit (sx-inbox-item-type bold)))) + `(sx-question-list-answers ((,class :foreground ,green))) + `(sx-question-list-answers-accepted ((,class :box t :foreground ,green))) + `(sx-question-list-bounty ((,class :inherit bold :background ,bg-alt :foreground ,yellow))) + `(sx-question-list-date ((,class :foreground ,fg-special-cold))) + `(sx-question-list-favorite ((,class :inherit bold :foreground ,fg-special-warm))) + `(sx-question-list-parent ((,class :foreground ,fg-main))) + `(sx-question-list-read-question ((,class :inherit shadow))) + `(sx-question-list-score ((,class :foreground ,fg-special-mild))) + `(sx-question-list-score-upvoted ((,class :inherit (sx-question-list-score bold)))) + `(sx-question-list-unread-question ((,class :inherit bold :foreground ,fg-main))) + `(sx-question-mode-accepted ((,class :inherit bold :height 1.3 :foreground ,green))) + `(sx-question-mode-closed ((,class :inherit modus-themes-active-yellow :box (:line-width 2 :color nil)))) + `(sx-question-mode-closed-reason ((,class :box (:line-width 2 :color nil) :foreground ,fg-main))) + `(sx-question-mode-content-face ((,class :background ,bg-dim))) + `(sx-question-mode-date ((,class :foreground ,blue))) + `(sx-question-mode-header ((,class :inherit bold :foreground ,cyan))) + `(sx-question-mode-kbd-tag ((,class :inherit bold :height 0.9 :box (:line-width 3 :color ,fg-main :style released-button) :foreground ,fg-main))) + `(sx-question-mode-score ((,class :foreground ,fg-dim))) + `(sx-question-mode-score-downvoted ((,class :foreground ,yellow))) + `(sx-question-mode-score-upvoted ((,class :inherit bold :foreground ,magenta))) + `(sx-question-mode-title ((,class :inherit bold :foreground ,fg-main))) + `(sx-question-mode-title-comments ((,class :inherit (shadow bold)))) + `(sx-tag ((,class :foreground ,magenta-alt))) + `(sx-user-name ((,class :foreground ,blue-alt))) + `(sx-user-reputation ((,class :inherit shadow))) +;;;;; symbol-overlay + `(symbol-overlay-default-face ((,class :inherit modus-themes-special-warm))) + `(symbol-overlay-face-1 ((,class :inherit modus-themes-intense-blue))) + `(symbol-overlay-face-2 ((,class :inherit modus-themes-refine-magenta))) + `(symbol-overlay-face-3 ((,class :inherit modus-themes-intense-yellow))) + `(symbol-overlay-face-4 ((,class :inherit modus-themes-intense-magenta))) + `(symbol-overlay-face-5 ((,class :inherit modus-themes-intense-red))) + `(symbol-overlay-face-6 ((,class :inherit modus-themes-refine-red))) + `(symbol-overlay-face-7 ((,class :inherit modus-themes-intense-cyan))) + `(symbol-overlay-face-8 ((,class :inherit modus-themes-refine-cyan))) +;;;;; syslog-mode + `(syslog-debug ((,class :inherit bold :foreground ,cyan-alt-other))) + `(syslog-error ((,class :inherit error))) + `(syslog-file ((,class :inherit bold :foreground ,fg-special-cold))) + `(syslog-hide ((,class :background ,bg-main :foreground ,fg-main))) + `(syslog-hour ((,class :inherit bold :foreground ,magenta-alt-other))) + `(syslog-info ((,class :inherit success))) + `(syslog-ip ((,class :inherit bold :foreground ,fg-special-mild :underline t))) + `(syslog-su ((,class :inherit bold :foreground ,red-alt))) + `(syslog-warn ((,class :inherit warning))) +;;;;; tab-bar-groups + `(tab-bar-groups-tab-1 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,blue-tab))) + `(tab-bar-groups-tab-2 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,red-tab))) + `(tab-bar-groups-tab-3 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,green-tab))) + `(tab-bar-groups-tab-4 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,orange-tab))) + `(tab-bar-groups-tab-5 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,purple-tab))) + `(tab-bar-groups-tab-6 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,cyan-tab))) + `(tab-bar-groups-tab-7 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,yellow-tab))) + `(tab-bar-groups-tab-8 ((,class :inherit modus-themes-ui-variable-pitch :foreground ,magenta-tab))) +;;;;; tab-bar-mode + `(tab-bar ((,class :inherit modus-themes-tab-backdrop))) + `(tab-bar-tab-group-current ((,class ,@(modus-themes--tab bg-tab-active) + :box (:line-width (2 . -2) :color "gray50")))) + `(tab-bar-tab-group-inactive ((,class ,@(modus-themes--tab bg-tab-inactive bg-tab-inactive-accent fg-dim) + :box (:line-width (2 . -2) :color "gray50")))) + `(tab-bar-tab ((,class :inherit modus-themes-tab-active))) + `(tab-bar-tab-inactive ((,class :inherit modus-themes-tab-inactive))) +;;;;; tab-line-mode + `(tab-line ((,class :inherit modus-themes-tab-backdrop :height 0.95))) + `(tab-line-close-highlight ((,class :foreground ,red))) + `(tab-line-highlight ((,class :inherit modus-themes-active-blue))) + `(tab-line-tab ((,class :inherit modus-themes-tab-active))) + `(tab-line-tab-current ((,class :inherit tab-line-tab))) + `(tab-line-tab-inactive ((,class :inherit modus-themes-tab-inactive))) + `(tab-line-tab-inactive-alternate ((,class ,@(modus-themes--tab bg-tab-inactive-alt + bg-tab-inactive-alt-accent fg-main nil t)))) + `(tab-line-tab-modified ((,class :foreground ,red-alt-other-faint))) +;;;;; table (built-in table.el) + `(table-cell ((,class :background ,blue-nuanced-bg))) +;;;;; telega + ;; FIXME 2021-03-28: Some aspects of `telega' are not fully + ;; supported or have not been tested thoroughly. Please understand + ;; that I do not use that service because it requires a smartphone + ;; and I have none. Help with testing is appreciated. + `(telega-button ((,class :box t :foreground ,blue))) + `(telega-button-active ((,class :box ,blue-intense-bg :background ,blue-intense-bg :foreground ,fg-main))) + `(telega-button-highlight ((,class :inherit modus-themes-subtle-magenta))) + `(telega-chat-prompt ((,class :inherit bold))) + `(telega-entity-type-code ((,class :inherit modus-themes-fixed-pitch))) + `(telega-entity-type-mention ((,class :foreground ,cyan))) + `(telega-entity-type-pre ((,class :inherit modus-themes-fixed-pitch))) + `(telega-entity-type-spoiler ((,class :background ,fg-main :foreground ,fg-main))) + `(telega-msg-heading ((,class :background ,bg-alt))) + `(telega-msg-self-title ((,class :inherit bold))) + `(telega-root-heading ((,class :inherit modus-themes-subtle-neutral))) + `(telega-secret-title ((,class :foreground ,magenta-alt))) + `(telega-unmuted-count ((,class :foreground ,blue-alt-other))) + `(telega-user-online-status ((,class :foreground ,cyan-active))) + `(telega-username ((,class :foreground ,cyan-alt-other))) + `(telega-webpage-chat-link ((,class :background ,bg-alt))) + `(telega-webpage-fixed ((,class :inherit modus-themes-fixed-pitch :height 0.85))) + `(telega-webpage-header ((,class :inherit modus-themes-variable-pitch :height 1.3))) + `(telega-webpage-preformatted ((,class :inherit modus-themes-fixed-pitch :background ,bg-alt))) + `(telega-webpage-subheader ((,class :inherit modus-themes-variable-pitch :height 1.15))) +;;;;; telephone-line + `(telephone-line-accent-active ((,class :background ,fg-inactive :foreground ,bg-inactive))) + `(telephone-line-accent-inactive ((,class :background ,bg-active :foreground ,fg-active))) + `(telephone-line-error ((,class :inherit bold :foreground ,red-active))) + `(telephone-line-evil ((,class :foreground ,fg-main))) + `(telephone-line-evil-emacs ((,class :inherit telephone-line-evil :background ,magenta-intense-bg))) + `(telephone-line-evil-insert ((,class :inherit telephone-line-evil :background ,green-intense-bg))) + `(telephone-line-evil-motion ((,class :inherit telephone-line-evil :background ,yellow-intense-bg))) + `(telephone-line-evil-normal ((,class :inherit telephone-line-evil :background ,bg-alt))) + `(telephone-line-evil-operator ((,class :inherit telephone-line-evil :background ,yellow-subtle-bg))) + `(telephone-line-evil-replace ((,class :inherit telephone-line-evil :background ,red-intense-bg))) + `(telephone-line-evil-visual ((,class :inherit telephone-line-evil :background ,cyan-intense-bg))) + `(telephone-line-projectile ((,class :foreground ,cyan-active))) + `(telephone-line-unimportant ((,class :foreground ,fg-inactive))) + `(telephone-line-warning ((,class :inherit bold :foreground ,yellow-active))) +;;;;; terraform-mode + `(terraform--resource-name-face ((,class ,@(modus-themes--syntax-string + magenta-alt-other magenta-alt-other-faint + red-alt red-alt)))) + `(terraform--resource-type-face ((,class ,@(modus-themes--syntax-string + green green-faint + blue-alt magenta-alt)))) +;;;;; term + `(term ((,class :background ,bg-main :foreground ,fg-main))) + `(term-bold ((,class :inherit bold))) + `(term-color-black ((,class :background "gray35" :foreground "gray35"))) + `(term-color-blue ((,class :background ,blue :foreground ,blue))) + `(term-color-cyan ((,class :background ,cyan :foreground ,cyan))) + `(term-color-green ((,class :background ,green :foreground ,green))) + `(term-color-magenta ((,class :background ,magenta :foreground ,magenta))) + `(term-color-red ((,class :background ,red :foreground ,red))) + `(term-color-white ((,class :background "gray65" :foreground "gray65"))) + `(term-color-yellow ((,class :background ,yellow :foreground ,yellow))) + `(term-underline ((,class :underline t))) +;;;;; textsec + `(textsec-suspicious ((,class :inherit modus-themes-refine-red))) +;;;;; tomatinho + `(tomatinho-ok-face ((,class :foreground ,blue-intense))) + `(tomatinho-pause-face ((,class :foreground ,yellow-intense))) + `(tomatinho-reset-face ((,class :inherit shadow))) +;;;;; transient + `(transient-active-infix ((,class :inherit modus-themes-special-mild))) + `(transient-amaranth ((,class :inherit bold :foreground ,yellow-alt))) + ;; Placate the compiler for what is a spurious warning. We also + ;; have to do this with `eldoc-highlight-function-argument'. + (list 'transient-argument `((,class :inherit bold :background ,cyan-nuanced-bg :foreground ,cyan))) + `(transient-blue ((,class :inherit bold :foreground ,blue))) + `(transient-disabled-suffix ((,class :inherit modus-themes-intense-red))) + `(transient-enabled-suffix ((,class :inherit modus-themes-grue-background-subtle))) + `(transient-heading ((,class :inherit bold :foreground ,fg-main))) + `(transient-inactive-argument ((,class :inherit shadow))) + `(transient-inactive-value ((,class :inherit shadow))) + `(transient-key ((,class :inherit modus-themes-key-binding))) + `(transient-mismatched-key ((,class :underline t))) + `(transient-nonstandard-key ((,class :underline t))) + `(transient-pink ((,class :inherit bold :foreground ,magenta-alt-faint))) + `(transient-purple ((,class :inherit bold :foreground ,magenta-alt-other))) + `(transient-red ((,class :inherit bold :foreground ,red-faint))) + `(transient-teal ((,class :inherit bold :foreground ,cyan-alt-other))) + `(transient-unreachable ((,class :inherit shadow))) + `(transient-unreachable-key ((,class :inherit shadow))) + `(transient-value ((,class :inherit bold :background ,yellow-nuanced-bg :foreground ,yellow-alt-other))) +;;;;; trashed + `(trashed-deleted ((,class :inherit modus-themes-mark-del))) + `(trashed-directory ((,class :foreground ,blue))) + `(trashed-mark ((,class :inherit modus-themes-mark-symbol))) + `(trashed-marked ((,class :inherit modus-themes-mark-alt))) + `(trashed-restored ((,class :inherit modus-themes-mark-sel))) + `(trashed-symlink ((,class :inherit modus-themes-link-symlink))) +;;;;; tree-sitter + `(tree-sitter-hl-face:attribute ((,class :inherit font-lock-variable-name-face))) + `(tree-sitter-hl-face:constant.builtin ((,class :inherit tree-sitter-hl-face:constant))) + `(tree-sitter-hl-face:escape ((,class :inherit font-lock-regexp-grouping-backslash))) + `(tree-sitter-hl-face:function ((,class :inherit font-lock-function-name-face))) + `(tree-sitter-hl-face:function.call ((,class :inherit tree-sitter-hl-face:function))) + `(tree-sitter-hl-face:label (( ))) + `(tree-sitter-hl-face:method.call (( ))) + `(tree-sitter-hl-face:operator ((,class :inherit modus-themes-bold))) + `(tree-sitter-hl-face:property (( ))) + `(tree-sitter-hl-face:property.definition ((,class :inherit font-lock-variable-name-face))) + `(tree-sitter-hl-face:punctuation (( ))) + `(tree-sitter-hl-face:punctuation.bracket (( ))) + `(tree-sitter-hl-face:punctuation.delimiter (( ))) + `(tree-sitter-hl-face:punctuation.special ((,class :inherit font-lock-regexp-grouping-construct))) + `(tree-sitter-hl-face:string.special ((,class :inherit tree-sitter-hl-face:string))) + `(tree-sitter-hl-face:tag ((,class :inherit font-lock-function-name-face))) + `(tree-sitter-hl-face:type.argument (( ))) +;;;;; treemacs + `(treemacs-directory-collapsed-face ((,class :foreground ,magenta-alt))) + `(treemacs-directory-face ((,class :inherit dired-directory))) + `(treemacs-file-face ((,class :foreground ,fg-main))) + `(treemacs-fringe-indicator-face ((,class :foreground ,fg-main))) + `(treemacs-git-added-face ((,class :inherit success))) + `(treemacs-git-conflict-face ((,class :inherit error))) + `(treemacs-git-ignored-face ((,class :inherit shadow))) + `(treemacs-git-modified-face ((,class :inherit warning))) + `(treemacs-git-renamed-face ((,class :inherit italic))) + `(treemacs-git-unmodified-face ((,class :foreground ,fg-main))) + `(treemacs-git-untracked-face ((,class :inherit shadow))) + `(treemacs-help-column-face ((,class :inherit modus-themes-bold :foreground ,magenta-alt-other :underline t))) + `(treemacs-help-title-face ((,class :foreground ,blue-alt-other))) + `(treemacs-on-failure-pulse-face ((,class :inherit modus-themes-intense-red))) + `(treemacs-on-success-pulse-face ((,class :inherit modus-themes-grue-background-intense))) + `(treemacs-root-face ((,class :inherit bold :foreground ,blue-alt-other :height 1.2 :underline t))) + `(treemacs-root-remote-disconnected-face ((,class :inherit treemacs-root-remote-face :foreground ,yellow))) + `(treemacs-root-remote-face ((,class :inherit treemacs-root-face :foreground ,magenta))) + `(treemacs-root-remote-unreadable-face ((,class :inherit treemacs-root-unreadable-face))) + `(treemacs-root-unreadable-face ((,class :inherit treemacs-root-face :strike-through t))) + `(treemacs-tags-face ((,class :foreground ,blue-alt))) +;;;;; tty-menu + `(tty-menu-disabled-face ((,class :background ,bg-alt :foreground ,fg-alt))) + `(tty-menu-enabled-face ((,class :inherit bold :background ,bg-alt :foreground ,fg-main))) + `(tty-menu-selected-face ((,class :inherit modus-themes-intense-blue))) +;;;;; tuareg + `(caml-types-def-face ((,class :inherit modus-themes-subtle-red))) + `(caml-types-expr-face ((,class :inherit modus-themes-subtle-green))) + `(caml-types-occ-face ((,class :inherit modus-themes-subtle-green))) + `(caml-types-scope-face ((,class :inherit modus-themes-subtle-blue))) + `(caml-types-typed-face ((,class :inherit modus-themes-subtle-magenta))) + `(tuareg-font-double-semicolon-face ((,class :inherit font-lock-preprocessor-face))) + `(tuareg-font-lock-attribute-face ((,class :inherit font-lock-function-name-face))) + `(tuareg-font-lock-constructor-face ((,class :foreground ,fg-main))) + `(tuareg-font-lock-error-face ((,class :inherit (modus-themes-intense-red bold)))) + `(tuareg-font-lock-extension-node-face ((,class :background ,bg-alt :foreground ,magenta))) + `(tuareg-font-lock-governing-face ((,class :inherit bold :foreground ,fg-main))) + `(tuareg-font-lock-infix-extension-node-face ((,class :inherit font-lock-function-name-face))) + `(tuareg-font-lock-interactive-directive-face ((,class :foreground ,fg-special-cold))) + `(tuareg-font-lock-interactive-error-face ((,class :inherit error))) + `(tuareg-font-lock-interactive-output-face ((,class :inherit font-lock-constant-face))) + `(tuareg-font-lock-label-face ((,class :inherit font-lock-type-face))) + `(tuareg-font-lock-line-number-face ((,class :foreground ,fg-special-warm))) + `(tuareg-font-lock-module-face ((,class :inherit font-lock-builtin-face))) + `(tuareg-font-lock-multistage-face ((,class :inherit bold :background ,bg-alt :foreground ,blue))) + `(tuareg-font-lock-operator-face ((,class :inherit font-lock-preprocessor-face))) + `(tuareg-opam-error-face ((,class :inherit error))) + `(tuareg-opam-pkg-variable-name-face ((,class :inherit font-lock-variable-name-face))) +;;;;; typescript + `(typescript-jsdoc-tag ((,class :inherit (font-lock-builtin-face font-lock-comment-face) :weight normal))) + `(typescript-jsdoc-type ((,class :inherit (font-lock-type-face font-lock-comment-face) :weight normal))) + `(typescript-jsdoc-value ((,class :inherit (font-lock-constant-face font-lock-comment-face) :weight normal))) +;;;;; undo-tree + `(undo-tree-visualizer-active-branch-face ((,class :inherit bold :foreground ,fg-main))) + `(undo-tree-visualizer-current-face ((,class :foreground ,blue-intense))) + `(undo-tree-visualizer-default-face ((,class :inherit shadow))) + `(undo-tree-visualizer-register-face ((,class :foreground ,magenta-intense))) + `(undo-tree-visualizer-unmodified-face ((,class :foreground ,green-intense))) +;;;;; vc (vc-dir.el, vc-hooks.el) + `(vc-dir-directory ((,class :foreground ,blue))) + `(vc-dir-file ((,class :foreground ,fg-main))) + `(vc-dir-header ((,class :foreground ,cyan-alt-other))) + `(vc-dir-header-value ((,class :foreground ,magenta-alt-other))) + `(vc-dir-mark-indicator ((,class :foreground ,blue-alt-other))) + `(vc-dir-status-edited ((,class :foreground ,yellow))) + `(vc-dir-status-ignored ((,class :inherit shadow))) + `(vc-dir-status-up-to-date ((,class :foreground ,cyan))) + `(vc-dir-status-warning ((,class :inherit error))) + `(vc-conflict-state ((,class :inherit bold :foreground ,red-active))) + `(vc-edited-state ((,class :foreground ,yellow-active))) + `(vc-locally-added-state ((,class :foreground ,cyan-active))) + `(vc-locked-state ((,class :foreground ,blue-active))) + `(vc-missing-state ((,class :inherit modus-themes-slant :foreground ,magenta-active))) + `(vc-needs-update-state ((,class :inherit modus-themes-slant :foreground ,green-active))) + `(vc-removed-state ((,class :foreground ,red-active))) + `(vc-state-base ((,class :foreground ,fg-active))) + `(vc-up-to-date-state ((,class :foreground ,fg-special-cold))) +;;;;; vertico + `(vertico-current ((,class :inherit modus-themes-completion-selected))) +;;;;; vertico-quick + `(vertico-quick1 ((,class :inherit bold :background ,bg-char-0))) + `(vertico-quick2 ((,class :inherit bold :background ,bg-char-1))) +;;;;; vimish-fold + `(vimish-fold-fringe ((,class :foreground ,cyan-active))) + `(vimish-fold-mouse-face ((,class :inherit modus-themes-intense-blue))) + `(vimish-fold-overlay ((,class :background ,bg-alt :foreground ,fg-special-cold))) +;;;;; visible-mark + `(visible-mark-active ((,class :background ,blue-intense-bg))) + `(visible-mark-face1 ((,class :background ,cyan-intense-bg))) + `(visible-mark-face2 ((,class :background ,yellow-intense-bg))) + `(visible-mark-forward-face1 ((,class :background ,magenta-intense-bg))) + `(visible-mark-forward-face2 ((,class :background ,green-intense-bg))) +;;;;; visual-regexp + `(vr/group-0 ((,class :inherit modus-themes-intense-blue))) + `(vr/group-1 ((,class :inherit modus-themes-intense-magenta))) + `(vr/group-2 ((,class :inherit modus-themes-intense-green))) + `(vr/match-0 ((,class :inherit modus-themes-refine-yellow))) + `(vr/match-1 ((,class :inherit modus-themes-refine-yellow))) + `(vr/match-separator-face ((,class :inherit (modus-themes-intense-neutral bold)))) +;;;;; vterm + `(vterm-color-black ((,class :background "gray35" :foreground "gray35"))) + `(vterm-color-blue ((,class :background ,blue :foreground ,blue))) + `(vterm-color-cyan ((,class :background ,cyan :foreground ,cyan))) + `(vterm-color-default ((,class :background ,bg-main :foreground ,fg-main))) + `(vterm-color-green ((,class :background ,green :foreground ,green))) + `(vterm-color-inverse-video ((,class :background ,bg-main :inverse-video t))) + `(vterm-color-magenta ((,class :background ,magenta :foreground ,magenta))) + `(vterm-color-red ((,class :background ,red :foreground ,red))) + `(vterm-color-underline ((,class :foreground ,fg-special-warm :underline t))) + `(vterm-color-white ((,class :background "gray65" :foreground "gray65"))) + `(vterm-color-yellow ((,class :background ,yellow :foreground ,yellow))) +;;;;; vundo + `(vundo-highlight ((,class :inherit (bold vundo-node) :foreground ,red-intense))) +;;;;; wcheck-mode + `(wcheck-default-face ((,class :foreground ,red :underline t))) +;;;;; web-mode + `(web-mode-annotation-face ((,class :inherit web-mode-comment-face))) + `(web-mode-annotation-html-face ((,class :inherit web-mode-comment-face))) + `(web-mode-annotation-tag-face ((,class :inherit web-mode-comment-face :underline t))) + `(web-mode-block-attr-name-face ((,class :inherit font-lock-constant-face))) + `(web-mode-block-attr-value-face ((,class :inherit font-lock-type-face))) + `(web-mode-block-comment-face ((,class :inherit web-mode-comment-face))) + `(web-mode-block-control-face ((,class :inherit font-lock-builtin-face))) + `(web-mode-block-delimiter-face ((,class :foreground ,fg-main))) + `(web-mode-block-face ((,class :background ,bg-dim))) + `(web-mode-block-string-face ((,class :inherit web-mode-string-face))) + `(web-mode-bold-face ((,class :inherit bold))) + `(web-mode-builtin-face ((,class :inherit font-lock-builtin-face))) + `(web-mode-comment-face ((,class :inherit font-lock-comment-face))) + `(web-mode-comment-keyword-face ((,class :inherit font-lock-warning-face))) + `(web-mode-constant-face ((,class :inherit font-lock-constant-face))) + `(web-mode-css-at-rule-face ((,class :inherit font-lock-constant-face))) + `(web-mode-css-color-face ((,class :inherit font-lock-builtin-face))) + `(web-mode-css-comment-face ((,class :inherit web-mode-comment-face))) + `(web-mode-css-function-face ((,class :inherit font-lock-builtin-face))) + `(web-mode-css-priority-face ((,class :inherit font-lock-warning-face))) + `(web-mode-css-property-name-face ((,class :inherit font-lock-keyword-face))) + `(web-mode-css-pseudo-class-face ((,class :inherit font-lock-doc-face))) + `(web-mode-css-selector-face ((,class :inherit font-lock-keyword-face))) + `(web-mode-css-string-face ((,class :inherit web-mode-string-face))) + `(web-mode-css-variable-face ((,class :foreground ,fg-special-warm))) + `(web-mode-current-column-highlight-face ((,class :background ,bg-alt))) + `(web-mode-current-element-highlight-face ((,class :inherit modus-themes-special-mild))) + `(web-mode-doctype-face ((,class :inherit modus-themes-slant :foreground ,fg-special-cold))) + `(web-mode-error-face ((,class :inherit modus-themes-intense-red))) + `(web-mode-filter-face ((,class :inherit font-lock-function-name-face))) + `(web-mode-folded-face ((,class :underline t))) + `(web-mode-function-call-face ((,class :inherit font-lock-function-name-face))) + `(web-mode-function-name-face ((,class :inherit font-lock-function-name-face))) + `(web-mode-html-attr-custom-face ((,class :inherit font-lock-variable-name-face))) + `(web-mode-html-attr-engine-face ((,class :foreground ,fg-main))) + `(web-mode-html-attr-equal-face ((,class :foreground ,fg-main))) + `(web-mode-html-attr-name-face ((,class :inherit font-lock-variable-name-face))) + `(web-mode-html-attr-value-face ((,class :inherit font-lock-constant-face))) + `(web-mode-html-entity-face ((,class :inherit font-lock-negation-char-face))) + `(web-mode-html-tag-bracket-face ((,class :foreground ,fg-dim))) + `(web-mode-html-tag-custom-face ((,class :inherit font-lock-function-name-face))) + `(web-mode-html-tag-face ((,class :inherit font-lock-function-name-face))) + `(web-mode-html-tag-namespaced-face ((,class :inherit font-lock-builtin-face))) + `(web-mode-html-tag-unclosed-face ((,class :inherit error :underline t))) + `(web-mode-inlay-face ((,class :background ,bg-alt))) + `(web-mode-italic-face ((,class :inherit italic))) + `(web-mode-javascript-comment-face ((,class :inherit web-mode-comment-face))) + `(web-mode-javascript-string-face ((,class :inherit web-mode-string-face))) + `(web-mode-json-comment-face ((,class :inherit web-mode-comment-face))) + `(web-mode-json-context-face ((,class :inherit font-lock-builtin-face))) + `(web-mode-json-key-face ((,class :foreground ,blue-nuanced-fg))) + `(web-mode-json-string-face ((,class :inherit web-mode-string-face))) + `(web-mode-jsx-depth-1-face ((,class :background ,blue-intense-bg :foreground ,fg-main))) + `(web-mode-jsx-depth-2-face ((,class :background ,blue-subtle-bg :foreground ,fg-main))) + `(web-mode-jsx-depth-3-face ((,class :background ,bg-special-cold :foreground ,fg-special-cold))) + `(web-mode-jsx-depth-4-face ((,class :background ,bg-alt :foreground ,blue-refine-fg))) + `(web-mode-jsx-depth-5-face ((,class :background ,bg-alt :foreground ,blue-nuanced-fg))) + `(web-mode-keyword-face ((,class :inherit font-lock-keyword-face))) + `(web-mode-param-name-face ((,class :inherit font-lock-function-name-face))) + `(web-mode-part-comment-face ((,class :inherit web-mode-comment-face))) + `(web-mode-part-face ((,class :inherit web-mode-block-face))) + `(web-mode-part-string-face ((,class :inherit web-mode-string-face))) + `(web-mode-preprocessor-face ((,class :inherit font-lock-preprocessor-face))) + `(web-mode-script-face ((,class :inherit web-mode-part-face))) + `(web-mode-sql-keyword-face ((,class :inherit font-lock-negation-char-face))) + `(web-mode-string-face ((,class :inherit font-lock-string-face))) + `(web-mode-style-face ((,class :inherit web-mode-part-face))) + `(web-mode-symbol-face ((,class :inherit font-lock-constant-face))) + `(web-mode-type-face ((,class :inherit font-lock-builtin-face))) + `(web-mode-underline-face ((,class :underline t))) + `(web-mode-variable-name-face ((,class :inherit font-lock-variable-name-face))) + `(web-mode-warning-face ((,class :inherit font-lock-warning-face))) + `(web-mode-whitespace-face ((,class :background ,bg-whitespace :foreground ,fg-whitespace))) +;;;;; wgrep + `(wgrep-delete-face ((,class :inherit warning))) + `(wgrep-done-face ((,class :inherit success))) + `(wgrep-face ((,class :inherit bold))) + `(wgrep-file-face ((,class :foreground ,fg-special-warm))) + `(wgrep-reject-face ((,class :inherit error))) +;;;;; which-function-mode + `(which-func ((,class :foreground ,magenta-active))) +;;;;; which-key + `(which-key-command-description-face ((,class :foreground ,fg-main))) + `(which-key-group-description-face ((,class :foreground ,magenta-alt))) + `(which-key-highlighted-command-face ((,class :foreground ,yellow :underline t))) + `(which-key-key-face ((,class :inherit modus-themes-key-binding))) + `(which-key-local-map-description-face ((,class :foreground ,fg-main))) + `(which-key-note-face ((,class :foreground ,fg-special-warm))) + `(which-key-separator-face ((,class :inherit shadow))) + `(which-key-special-key-face ((,class :inherit bold :foreground ,red-alt))) +;;;;; whitespace-mode + `(whitespace-big-indent ((,class :inherit modus-themes-subtle-red))) + `(whitespace-empty ((,class :inherit modus-themes-intense-magenta))) + `(whitespace-hspace ((,class :background ,bg-whitespace :foreground ,fg-whitespace))) + `(whitespace-indentation ((,class :background ,bg-whitespace :foreground ,fg-whitespace))) + `(whitespace-line ((,class :inherit modus-themes-subtle-yellow))) + `(whitespace-newline ((,class :background ,bg-whitespace :foreground ,fg-whitespace))) + `(whitespace-space ((,class :background ,bg-whitespace :foreground ,fg-whitespace))) + `(whitespace-space-after-tab ((,class :inherit modus-themes-subtle-magenta))) + `(whitespace-space-before-tab ((,class :inherit modus-themes-subtle-cyan))) + `(whitespace-tab ((,class :background ,bg-whitespace :foreground ,fg-whitespace))) + `(whitespace-trailing ((,class :inherit modus-themes-intense-red))) +;;;;; window-divider-mode + `(window-divider ((,class :foreground ,fg-window-divider-inner))) + `(window-divider-first-pixel ((,class :foreground ,fg-window-divider-outer))) + `(window-divider-last-pixel ((,class :foreground ,fg-window-divider-outer))) +;;;;; winum + `(winum-face ((,class :inherit modus-themes-bold :foreground ,cyan-active))) +;;;;; writegood-mode + `(writegood-duplicates-face ((,class :background ,bg-alt :foreground ,red-alt :underline t))) + `(writegood-passive-voice-face ((,class :inherit modus-themes-lang-warning))) + `(writegood-weasels-face ((,class :inherit modus-themes-lang-error))) +;;;;; woman + `(woman-addition ((,class :foreground ,magenta-alt-other))) + `(woman-bold ((,class :inherit bold :foreground ,magenta-alt))) + `(woman-italic ((,class :inherit italic :foreground ,cyan))) + `(woman-unknown ((,class :foreground ,green-alt))) +;;;;; xah-elisp-mode + `(xah-elisp-at-symbol ((,class :inherit font-lock-warning-face))) + `(xah-elisp-cap-variable ((,class :inherit font-lock-preprocessor-face))) + `(xah-elisp-command-face ((,class :inherit font-lock-type-face))) + `(xah-elisp-dollar-symbol ((,class :inherit font-lock-variable-name-face))) +;;;;; xref + `(xref-file-header ((,class :inherit bold :foreground ,fg-special-cold))) + `(xref-line-number ((,class :inherit shadow))) + `(xref-match ((,class :inherit match))) +;;;;; yaml-mode + `(yaml-tab-face ((,class :inherit modus-themes-intense-red))) +;;;;; yasnippet + `(yas-field-highlight-face ((,class :background ,bg-hl-alt-intense))) +;;;;; ztree + `(ztreep-arrow-face ((,class :foreground ,fg-inactive))) + `(ztreep-diff-header-face ((,class :inherit bold :height 1.2 :foreground ,fg-special-cold))) + `(ztreep-diff-header-small-face ((,class :foreground ,fg-main))) + `(ztreep-diff-model-add-face ((,class :inherit modus-themes-grue))) + `(ztreep-diff-model-diff-face ((,class :foreground ,red))) + `(ztreep-diff-model-ignored-face ((,class :inherit shadow :strike-through t))) + `(ztreep-diff-model-normal-face ((,class :inherit shadow))) + `(ztreep-expand-sign-face ((,class :inherit ztreep-arrow-face))) + `(ztreep-header-face ((,class :inherit bold :height 1.2 :foreground ,fg-special-cold))) + `(ztreep-leaf-face ((,class :foreground ,cyan))) + `(ztreep-node-count-children-face ((,class :foreground ,fg-special-warm))) + `(ztreep-node-face ((,class :foreground ,fg-main)))) + "Face specs for use with `modus-themes-theme'.") + +(defconst modus-themes-custom-variables + '( +;;;; ansi-colors + `(ansi-color-faces-vector [default bold shadow italic underline success warning error]) + `(ansi-color-names-vector ["gray35" ,red ,green ,yellow ,blue ,magenta ,cyan "gray65"]) +;;;; awesome-tray + `(awesome-tray-mode-line-active-color ,blue) + `(awesome-tray-mode-line-inactive-color ,bg-active) +;;;; chart + `(chart-face-color-list + '( ,red-graph-0-bg ,green-graph-0-bg ,yellow-graph-0-bg ,blue-graph-0-bg ,magenta-graph-0-bg ,cyan-graph-0-bg + ,red-graph-1-bg ,green-graph-1-bg ,yellow-graph-1-bg ,blue-graph-1-bg ,magenta-graph-1-bg ,cyan-graph-1-bg)) +;;;; exwm + `(exwm-floating-border-color ,fg-window-divider-inner) +;;;; flymake fringe indicators + `(flymake-error-bitmap '(flymake-double-exclamation-mark modus-themes-fringe-red)) + `(flymake-warning-bitmap '(exclamation-mark modus-themes-fringe-yellow)) + `(flymake-note-bitmap '(exclamation-mark modus-themes-fringe-cyan)) +;;;; highlight-changes + `(highlight-changes-colors nil) + `(highlight-changes-face-list '(success warning error bold bold-italic)) +;;;; ibuffer + `(ibuffer-deletion-face 'modus-themes-mark-del) + `(ibuffer-filter-group-name-face 'modus-themes-pseudo-header) + `(ibuffer-marked-face 'modus-themes-mark-sel) + `(ibuffer-title-face 'default) +;;;; hl-todo + `(hl-todo-keyword-faces + '(("HOLD" . ,yellow-alt) + ("TODO" . ,magenta) + ("NEXT" . ,magenta-alt-other) + ("THEM" . ,magenta-alt) + ("PROG" . ,cyan) + ("OKAY" . ,cyan-alt) + ("DONT" . ,green-alt) + ("FAIL" . ,red) + ("BUG" . ,red) + ("DONE" . ,green) + ("NOTE" . ,yellow-alt-other) + ("KLUDGE" . ,yellow) + ("HACK" . ,yellow) + ("TEMP" . ,red-nuanced-fg) + ("FIXME" . ,red-alt-other) + ("XXX+" . ,red-alt) + ("REVIEW" . ,cyan-alt-other) + ("DEPRECATED" . ,blue-nuanced-fg))) +;;;; mini-modeline + `(mini-modeline-face-attr '(:background unspecified)) +;;;; pdf-tools + `(pdf-view-midnight-colors + '(,fg-main . ,bg-dim)) +;;;; wid-edit + `(widget-link-prefix ,(if (memq 'all-buttons modus-themes-box-buttons) + " " + "[")) + `(widget-link-suffix ,(if (memq 'all-buttons modus-themes-box-buttons) + " " + "]")) + `(widget-mouse-face '(highlight widget-button)) + `(widget-push-button-prefix ,(if (memq 'all-buttons modus-themes-box-buttons) + " " + "[")) + `(widget-push-button-suffix ,(if (memq 'all-buttons modus-themes-box-buttons) + " " + "]")) +;;;; xterm-color + `(xterm-color-names ["black" ,red ,green ,yellow ,blue ,magenta ,cyan "gray65"]) + `(xterm-color-names-bright ["gray35" ,red-alt ,green-alt ,yellow-alt ,blue-alt ,magenta-alt ,cyan-alt "white"]) + (if (or (eq modus-themes-org-blocks 'tinted-background) + (eq modus-themes-org-blocks 'rainbow)) + `(org-src-block-faces ; TODO this list should be expanded + `(("emacs-lisp" modus-themes-nuanced-magenta) + ("elisp" modus-themes-nuanced-magenta) + ("clojure" modus-themes-nuanced-magenta) + ("clojurescript" modus-themes-nuanced-magenta) + ("c" modus-themes-nuanced-blue) + ("c++" modus-themes-nuanced-blue) + ("sh" modus-themes-nuanced-green) + ("shell" modus-themes-nuanced-green) + ("html" modus-themes-nuanced-yellow) + ("xml" modus-themes-nuanced-yellow) + ("css" modus-themes-nuanced-red) + ("scss" modus-themes-nuanced-red) + ("python" modus-themes-nuanced-green) + ("ipython" modus-themes-nuanced-magenta) + ("r" modus-themes-nuanced-cyan) + ("yaml" modus-themes-nuanced-cyan) + ("conf" modus-themes-nuanced-cyan) + ("docker" modus-themes-nuanced-cyan))) + `(org-src-block-faces '()))) + "Custom variables for `modus-themes-theme'.") + +;;;###autoload +(when load-file-name + (let ((dir (file-name-directory load-file-name))) + (unless (equal dir (expand-file-name "themes/" data-directory)) + (add-to-list 'custom-theme-load-path dir)))) + +(provide 'modus-themes) +;;; modus-themes.el ends here diff --git a/code/elpa/modus-themes-20220823.1919/modus-themes.info b/code/elpa/modus-themes-20220823.1919/modus-themes.info new file mode 100644 index 0000000..b24dda2 --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/modus-themes.info @@ -0,0 +1,7017 @@ +This is modus-themes.info, produced by makeinfo version 6.8 from +modus-themes.texi. + +Copyright (C) 2020-2022 Free Software Foundation, Inc. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with no Invariant Sections, with the Front-Cover Texts + being “A GNU Manual,” and with the Back-Cover Texts as in (a) + below. A copy of the license is included in the section entitled + “GNU Free Documentation License.” + + (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and + modify this GNU manual.” + +INFO-DIR-SECTION Emacs misc features +START-INFO-DIR-ENTRY +* Modus Themes: (modus-themes). Elegant, highly legible and customizable themes. +END-INFO-DIR-ENTRY + + +File: modus-themes.info, Node: Top, Next: Overview, Up: (dir) + +Modus themes for GNU Emacs +************************** + +Copyright (C) 2020-2022 Free Software Foundation, Inc. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with no Invariant Sections, with the Front-Cover Texts + being “A GNU Manual,” and with the Back-Cover Texts as in (a) + below. A copy of the license is included in the section entitled + “GNU Free Documentation License.” + + (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and + modify this GNU manual.” + + This manual, written by Protesilaos Stavrou, describes the +customization options for the ‘modus-operandi’ and ‘modus-vivendi’ +themes, and provides every other piece of information pertinent to them. + + The documentation furnished herein corresponds to stable version +2.6.0, released on 2022-08-19. Any reference to a newer feature which +does not yet form part of the latest tagged commit, is explicitly marked +as such. + + Current development target is 2.7.0-dev. + + • Homepage: . + • Git repository: . + • Mailing list: . + +* Menu: + +* Overview:: +* Installation:: +* Enable and load:: +* Customization Options:: +* Advanced customization:: +* Face coverage:: +* Notes on individual packages:: +* Frequently Asked Questions:: +* Contributing:: +* Acknowledgements:: +* Other notes about the project:: +* GNU Free Documentation License:: +* Indices:: + +— The Detailed Node Listing — + +Overview + +* How do the themes look like:: +* Learn about the latest changes:: + +Installation + +* Install manually from source:: +* Install from the archives:: +* Install on GNU/Linux:: +* Dealing with byte compilation errors:: + +Install on GNU/Linux + +* Debian 11 Bullseye:: +* GNU Guix:: + +Enable and load + +* Sample configuration with and without use-package:: +* Differences between loading and enabling:: + +Customization Options + +* Custom reload theme:: Toggle auto-reload of the theme when setting custom variables +* Deuteranopia style:: Toggle red/blue color-coding instead of red/green +* Bold constructs:: Toggle bold constructs in code +* Italic constructs:: Toggle italic font constructs in code +* Syntax styles:: Choose the overall aesthetic of code syntax +* Mixed fonts:: Toggle mixing of font families +* Link styles:: Choose among several styles, with or without underline +* Box buttons:: Choose among several styles for buttons +* Command prompts:: Choose among plain, subtle, or intense prompts +* Mode line:: Choose among several styles, with or without borders +* Tab style:: Toggle accented background for tabs +* Completion UIs:: Choose among several styles for completion UIs +* Mail citations:: Choose among colorful, desaturated, monochrome citations +* Fringes:: Choose among invisible, subtle, or intense fringe styles +* Language checkers:: Control the style of language checkers/linters +* Line highlighting:: Choose style of current line (hl-line-mode) +* Line numbers:: Toggle subtle style for line numbers +* Mouse hover effects:: Toggle intense style for mouseover highlights +* Markup:: Choose style for markup in Org and others +* Matching parentheses:: Choose between various styles for matching delimiters/parentheses +* Active region:: Choose between various styles for the active region +* Diffs:: Choose among intense, desaturated, or background-only diffs +* Org mode blocks:: Choose among plain, gray, or tinted backgrounds +* Org agenda:: Control each element in the presentation of the agenda +* Heading styles:: Choose among several styles, also per heading level +* UI typeface:: Toggle the use of variable-pitch across the User Interface + +Advanced customization + +* More accurate colors in terminal emulators:: +* Range of color with terminal emulators:: +* Visualize the active Modus theme's palette:: +* Per-theme customization settings:: +* Case-by-case face specs using the themes' palette:: +* Face specs at scale using the themes' palette:: +* Remap face with local value:: +* Cycle through arbitrary colors:: +* Override colors:: +* Override color saturation:: +* Override colors through blending:: +* Override colors completely:: +* Font configurations for Org and others:: +* Configure bold and italic faces:: +* Custom Org todo keyword and priority faces:: +* Custom Org emphasis faces:: +* Update Org block delimiter fontification:: +* Measure color contrast:: +* Load theme depending on time of day:: +* Backdrop for pdf-tools:: +* Decrease mode line height:: +* Toggle themes without reloading them:: +* A theme-agnostic hook for theme loading:: +* Diffs with only the foreground:: +* Ediff without diff color-coding:: +* Near-monochrome syntax highlighting:: +* Custom hl-todo colors:: +* Add support for solaire-mode:: + +Face coverage + +* Supported packages:: Full list of covered face groups +* Indirectly covered packages:: + +Notes on individual packages + +* Note on calendar.el weekday and weekend colors: Note on calendarel weekday and weekend colors. +* Note on git-gutter in Doom Emacs:: +* Note on php-mode multiline comments:: +* Note on underlines in compilation buffers:: +* Note on inline Latex in Org buffers:: +* Note on dimmer.el: Note on dimmerel. +* Note on display-fill-column-indicator-mode:: +* Note on highlight-parentheses.el: Note on highlight-parenthesesel. +* Note on mmm-mode.el background colors: Note on mmm-modeel background colors. +* Note for prism:: +* Note for god-mode:: +* Note on company-mode overlay pop-up:: +* Note on ERC escaped color sequences:: +* Note on powerline or spaceline:: +* Note on SHR colors:: +* Note on SHR fonts:: +* Note on Ement colors and fonts:: +* Note on Helm grep:: +* Note on pdf-tools link hints:: +* Note on the Notmuch logo:: + +Frequently Asked Questions + +* Is the contrast ratio about adjacent colors?:: +* What does it mean to avoid exaggerations?:: +* Why are colors mostly variants of blue, magenta, cyan?: Why are colors mostly variants of blue magenta cyan?. +* What is the best setup for legibility?:: +* Are these color schemes?:: +* Port the Modus themes to other platforms?:: + +Contributing + +* Sources of the themes:: +* Issues you can help with:: +* Patches require copyright assignment to the FSF:: + +Indices + +* Function index:: +* Variable index:: +* Concept index:: + + + +File: modus-themes.info, Node: Overview, Next: Installation, Prev: Top, Up: Top + +1 Overview +********** + +The Modus themes are designed for accessible readability. They conform +with the highest standard for color contrast between any given +combination of background and foreground values. This corresponds to +the WCAG AAA standard, which specifies a minimum rate of distance in +relative luminance of 7:1. + + Modus Operandi (‘modus-operandi’) is a light theme, while Modus +Vivendi (‘modus-vivendi’) is dark. Each theme’s color palette is +designed to meet the needs of the numerous interfaces that are possible +in the Emacs computing environment. + + The overarching objective of this project is to always offer +accessible color combinations. There shall never be a compromise on +this principle. If there arises an inescapable trade-off between +readability and stylistic considerations, we will always opt for the +former. + + To ensure that users have a consistently accessible experience, the +themes strive to achieve as close to full face coverage as possible +(*note Face coverage::). + + Furthermore, the themes are designed to empower users with red-green +color deficiency (deuteranopia). This is achieved in three ways: + + 1. The conformance with the highest legibility standard means that + text is always readable no matter the perception of its hue. + + 2. Most contexts use colors on the blue-cyan-magenta-purple side of + the spectrum. Put differently, green and/or red are seldom used, + thus minimizing the potential for confusion. + + *note Why are colors mostly variants of blue, magenta, cyan?: Why + are colors mostly variants of blue magenta cyan?. + + 3. In contexts where a red/green color-coding is unavoidable, we + provide a universal toggle to customize the themes so that a + red/blue scheme is used instead. + + *note Option for red-green color deficiency or deuteranopia: + Deuteranopia style. + + Starting with version 0.12.0 and onwards, the themes are built into +GNU Emacs. + +* Menu: + +* How do the themes look like:: +* Learn about the latest changes:: + + +File: modus-themes.info, Node: How do the themes look like, Next: Learn about the latest changes, Up: Overview + +1.1 How do the themes look like +=============================== + +Check the web page with the screen shots +(https://protesilaos.com/emacs/modus-themes-pictures/). There are lots +of scenarios on display that draw attention to details and important +aspects in the design of the themes. They also showcase the numerous +customization options. + + *note Customization options: Customization Options. + + +File: modus-themes.info, Node: Learn about the latest changes, Prev: How do the themes look like, Up: Overview + +1.2 Learn about the latest changes +================================== + +Please refer to the web page with the change log +(https://protesilaos.com/emacs/modus-themes-changelog). It is +comprehensive and covers everything that goes into every tagged release +of the themes. + + +File: modus-themes.info, Node: Installation, Next: Enable and load, Prev: Overview, Up: Top + +2 Installation +************** + +The Modus themes are distributed with Emacs starting with version 28.1. +On older versions of Emacs, they can be installed using Emacs’ package +manager or manually from their code repository. There also exist +packages for distributions of GNU/Linux. + +* Menu: + +* Install manually from source:: +* Install from the archives:: +* Install on GNU/Linux:: +* Dealing with byte compilation errors:: + + +File: modus-themes.info, Node: Install manually from source, Next: Install from the archives, Up: Installation + +2.1 Install manually from source +================================ + +In the following example, we are assuming that your Emacs files are +stored in ‘~/.emacs.d’ and that you want to place the Modus themes in +‘~/.emacs.d/modus-themes’. + + 1. Get the source and store it in the desired path by running the + following in the command line shell: + + $ git clone https://gitlab.com/protesilaos/modus-themes.git ~/.emacs.d/modus-themes + + 1. Add that path to your known Elisp libraries’ list, by placing this + snippet of Emacs Lisp in your init file (e.g. ‘init.el’): + + (add-to-list 'load-path "~/.emacs.d/modus-themes") + + The themes are now ready to be used: *note Enable and load::. + + +File: modus-themes.info, Node: Install from the archives, Next: Install on GNU/Linux, Prev: Install manually from source, Up: Installation + +2.2 Install from the archives +============================= + +The ‘modus-themes’ package is available from the GNU ELPA archive, which +is configured by default. + + Prior to querying any package archive, make sure to update the index, +with ‘M-x package-refresh-contents’. Then all you need to do is type +‘M-x package-install’ and specify the ‘modus-themes’. + + Once installed, the themes are ready to be used: *note Enable and +load::. + + +File: modus-themes.info, Node: Install on GNU/Linux, Next: Dealing with byte compilation errors, Prev: Install from the archives, Up: Installation + +2.3 Install on GNU/Linux +======================== + +The themes are also available from the archives of some distributions of +GNU/Linux. These should correspond to a tagged release rather than +building directly from the latest Git commit. It all depends on the +distro’s packaging policies. + +* Menu: + +* Debian 11 Bullseye:: +* GNU Guix:: + + +File: modus-themes.info, Node: Debian 11 Bullseye, Next: GNU Guix, Up: Install on GNU/Linux + +2.3.1 Debian 11 Bullseye +------------------------ + +The themes are part of Debian 11 Bullseye. Get them with: + + sudo apt install elpa-modus-themes + + They are now ready to be used: *note Enable and load::. + + NOTE that Debian’s package is severely out-of-date as of this writing +2022-07-24 09:57 +0300. + + +File: modus-themes.info, Node: GNU Guix, Prev: Debian 11 Bullseye, Up: Install on GNU/Linux + +2.3.2 GNU Guix +-------------- + +Users of Guix can get the themes with this command: + + guix package -i emacs-modus-themes + + They are now ready to be used: *note Enable and load::. + + +File: modus-themes.info, Node: Dealing with byte compilation errors, Prev: Install on GNU/Linux, Up: Installation + +2.4 Dealing with byte compilation errors +======================================== + +From time to time, we receive bug reports pertaining to errors with byte +compilation. These seldom have to do with faulty code in the themes: it +might be a shortcoming of ‘package.el’, some regression in the current +development target of Emacs, a misconfiguration in an otherwise exotic +setup, and the like. + + The common solution with a stable version of Emacs is to: + + 1. Delete the ‘modus-themes’ package. + 2. Close the current Emacs session. + 3. Install the ‘modus-themes’ again. + + For those building Emacs directly from source, the solution may +involve reverting to an earlier commit in emacs.git. + + At any rate, if you encounter such an issue please report it: we will +either fix the bug on our end if it is truly ours, or help forward it to +the relevant upstream maintainer. Whatever you do, please understand +that a build failure does not mean we are necessarily doing something +wrong. + + *note Issues you can help with::. + + +File: modus-themes.info, Node: Enable and load, Next: Customization Options, Prev: Installation, Up: Top + +3 Enable and load +***************** + +Users of the built-in themes cannot ‘require’ the package as usual +because there is no package to speak of. Instead, things are simpler as +all one needs is to load the theme of their preference by adding either +form to their init file: + + (load-theme 'modus-operandi) ; Light theme + (load-theme 'modus-vivendi) ; Dark theme + + Users of packaged variants of the themes must add a few more lines to +ensure that everything works as intended. First, one has to require the +main library before loading either theme: + + (require 'modus-themes) + + Then it is recommended to load the individual theme files with the +helper function ‘modus-themes-load-themes’: + + ;; Load the theme files before enabling a theme (else you get an error). + (modus-themes-load-themes) + + Once the libraries that define the themes are enabled, one can +activate a theme with either of the following expressions: + + (modus-themes-load-operandi) ; Light theme + ;; OR + (modus-themes-load-vivendi) ; Dark theme + + Changes to the available customization options must always be +evaluated before loading a theme (*note Customization Options::). An +exception to this norm is when using the various Custom interfaces or +with commands like ‘M-x customize-set-variable’, which can optionally +automatically reload the theme (*note Option for inhibiting theme +reload: Custom reload theme.). + + This is how a basic setup could look like: + + ;;; For the built-in themes which cannot use `require': + ;; Add all your customizations prior to loading the themes + (setq modus-themes-italic-constructs t + modus-themes-bold-constructs nil + modus-themes-region '(bg-only no-extend)) + + ;; Load the theme of your choice: + (load-theme 'modus-operandi) ;; OR (load-theme 'modus-vivendi) + + (define-key global-map (kbd "") #'modus-themes-toggle) + + + + ;;; For packaged versions which must use `require': + (require 'modus-themes) + + ;; Add all your customizations prior to loading the themes + (setq modus-themes-italic-constructs t + modus-themes-bold-constructs nil + modus-themes-region '(bg-only no-extend)) + + ;; Load the theme files before enabling a theme + (modus-themes-load-themes) + + ;; Load the theme of your choice: + (modus-themes-load-operandi) ;; OR (modus-themes-load-vivendi) + + (define-key global-map (kbd "") #'modus-themes-toggle) + + *note Sample configuration with and without use-package::. + + With those granted, bear in mind a couple of technical points on +‘modus-themes-load-operandi’ and ‘modus-themes-load-vivendi’, as well as +‘modus-themes-toggle’ which relies on them: + + 1. Those functions call ‘load-theme’. Some users prefer to opt for + ‘enable-theme’ instead (*note Differences between loading and + enabling::). + + 2. The functions will run the ‘modus-themes-after-load-theme-hook’ as + their final step. This can be employed for bespoke configurations + (*note Advanced customization::). Experienced users may not wish + to rely on such a hook and the functions that run it: they may + prefer a custom solution (*note A theme-agnostic hook for theme + loading::). + +* Menu: + +* Sample configuration with and without use-package:: +* Differences between loading and enabling:: + + +File: modus-themes.info, Node: Sample configuration with and without use-package, Next: Differences between loading and enabling, Up: Enable and load + +3.1 Sample configuration with and without use-package +===================================================== + +It is common for Emacs users to rely on ‘use-package’ for declaring +package configurations in their setup. We use this as an example: + + ;;; For the built-in themes which cannot use `require': + (use-package emacs + :init + ;; Add all your customizations prior to loading the themes + (setq modus-themes-italic-constructs t + modus-themes-bold-constructs nil + modus-themes-region '(bg-only no-extend)) + :config + ;; Load the theme of your choice: + (load-theme 'modus-operandi) ;; OR (load-theme 'modus-vivendi) + :bind ("" . modus-themes-toggle)) + + + + ;;; For packaged versions which must use `require': + (use-package modus-themes + :ensure + :init + ;; Add all your customizations prior to loading the themes + (setq modus-themes-italic-constructs t + modus-themes-bold-constructs nil + modus-themes-region '(bg-only no-extend)) + + ;; Load the theme files before enabling a theme + (modus-themes-load-themes) + :config + ;; Load the theme of your choice: + (modus-themes-load-operandi) ;; OR (modus-themes-load-vivendi) + :bind ("" . modus-themes-toggle)) + + The same without ‘use-package’: + + ;;; For the built-in themes which cannot use `require': + ;; Add all your customizations prior to loading the themes + (setq modus-themes-italic-constructs t + modus-themes-bold-constructs nil + modus-themes-region '(bg-only no-extend)) + + ;; Load the theme of your choice: + (load-theme 'modus-operandi) ;; OR (load-theme 'modus-vivendi) + + (define-key global-map (kbd "") #'modus-themes-toggle) + + + + ;;; For packaged versions which must use `require': + (require 'modus-themes) + + ;; Add all your customizations prior to loading the themes + (setq modus-themes-italic-constructs t + modus-themes-bold-constructs nil + modus-themes-region '(bg-only no-extend)) + + ;; Load the theme files before enabling a theme + (modus-themes-load-themes) + + ;; Load the theme of your choice: + (modus-themes-load-operandi) ;; OR (modus-themes-load-vivendi) + + (define-key global-map (kbd "") #'modus-themes-toggle) + + *note Differences between loading and enabling::. + + Note: make sure not to customize the variable +‘custom-theme-load-path’ or ‘custom-theme-directory’ after the themes’ +package declaration. That will lead to failures in loading the files. +If either or both of those variables need to be changed, their values +should be defined before the package declaration of the themes. + + +File: modus-themes.info, Node: Differences between loading and enabling, Prev: Sample configuration with and without use-package, Up: Enable and load + +3.2 Differences between loading and enabling +============================================ + +The reason we recommend ‘load-theme’ instead of the other option of +‘enable-theme’ is that the former does a kind of “reset” on the face +specs. It quite literally loads (or reloads) the theme. Whereas the +latter simply puts an already loaded theme at the top of the list of +enabled items, re-using whatever state was last loaded. + + As such, ‘load-theme’ reads all customizations that may happen during +any given Emacs session: even after the initial setup of a theme. +Examples are calls to ‘custom-set-faces’, as well as new values assigned +to the options the Modus themes provide (*note Customization Options::). + + Our tests show that ‘enable-theme’ does not read such variables anew, +so it might appear to the unsuspecting user that the themes are somehow +broken whenever they try to assign a new value to a customization option +or some face. + + This “reset” that ‘load-theme’ brings about does, however, come at +the cost of being somewhat slower than ‘enable-theme’. Users who have a +stable setup and who seldom update their variables during a given Emacs +session, are better off using something like this: + + (require 'modus-themes) + (load-theme 'modus-operandi t t) + (load-theme 'modus-vivendi t t) + + (enable-theme 'modus-operandi) ;; OR (enable-theme 'modus-vivendi) + + *note Toggle themes without reloading them::. + + *note Sample configuration with and without use-package::. + + With the above granted, other sections of the manual discuss how to +configure custom faces, where ‘load-theme’ is expected, though +‘enable-theme’ could still apply in stable setups: + + *note Case-by-case face specs using the themes' palette::. + + *note Face specs at scale using the themes' palette::. + + +File: modus-themes.info, Node: Customization Options, Next: Advanced customization, Prev: Enable and load, Up: Top + +4 Customization Options +*********************** + +The Modus themes are highly configurable, though they should work well +without any further tweaks. By default, all customization options are +set to nil, unless otherwise noted in this manual. + + Remember that all customization options must be evaluated before +loading a theme (*note Enable and load::). If the theme is already +active, it must be reloaded for changes in user options to come into +force. + + Below is a summary of what you will learn in the subsequent sections +of this manual. + + (setq modus-themes-italic-constructs t + modus-themes-bold-constructs nil + modus-themes-mixed-fonts nil + modus-themes-subtle-line-numbers nil + modus-themes-intense-mouseovers nil + modus-themes-deuteranopia t + modus-themes-tabs-accented t + modus-themes-variable-pitch-ui nil + modus-themes-inhibit-reload t ; only applies to `customize-set-variable' and related + + modus-themes-fringes nil ; {nil,'subtle,'intense} + + ;; Options for `modus-themes-lang-checkers' are either nil (the + ;; default), or a list of properties that may include any of those + ;; symbols: `straight-underline', `text-also', `background', + ;; `intense' OR `faint'. + modus-themes-lang-checkers nil + + ;; Options for `modus-themes-mode-line' are either nil, or a list + ;; that can combine any of `3d' OR `moody', `borderless', + ;; `accented', a natural number for extra padding (or a cons cell + ;; of padding and NATNUM), and a floating point for the height of + ;; the text relative to the base font size (or a cons cell of + ;; height and FLOAT) + modus-themes-mode-line '(accented borderless (padding . 4) (height . 0.9)) + + ;; Same as above: + ;; modus-themes-mode-line '(accented borderless 4 0.9) + + ;; Options for `modus-themes-markup' are either nil, or a list + ;; that can combine any of `bold', `italic', `background', + ;; `intense'. + modus-themes-markup '(background italic) + + ;; Options for `modus-themes-syntax' are either nil (the default), + ;; or a list of properties that may include any of those symbols: + ;; `faint', `yellow-comments', `green-strings', `alt-syntax' + modus-themes-syntax nil + + ;; Options for `modus-themes-hl-line' are either nil (the default), + ;; or a list of properties that may include any of those symbols: + ;; `accented', `underline', `intense' + modus-themes-hl-line '(underline accented) + + ;; Options for `modus-themes-paren-match' are either nil (the + ;; default), or a list of properties that may include any of those + ;; symbols: `bold', `intense', `underline' + modus-themes-paren-match '(bold intense) + + ;; Options for `modus-themes-links' are either nil (the default), + ;; or a list of properties that may include any of those symbols: + ;; `neutral-underline' OR `no-underline', `faint' OR `no-color', + ;; `bold', `italic', `background' + modus-themes-links '(neutral-underline background) + + ;; Options for `modus-themes-box-buttons' are either nil (the + ;; default), or a list that can combine any of `flat', `accented', + ;; `faint', `variable-pitch', `underline', `all-buttons', the + ;; symbol of any font weight as listed in `modus-themes-weights', + ;; and a floating point number (e.g. 0.9) for the height of the + ;; button's text. + modus-themes-box-buttons '(variable-pitch flat faint 0.9) + + ;; Options for `modus-themes-prompts' are either nil (the + ;; default), or a list of properties that may include any of those + ;; symbols: `background', `bold', `gray', `intense', `italic' + modus-themes-prompts '(intense bold) + + ;; The `modus-themes-completions' is an alist that reads three + ;; keys: `matches', `selection', `popup'. Each accepts a nil + ;; value (or empty list) or a list of properties that can include + ;; any of the following (for WEIGHT read further below): + ;; + ;; `matches' - `background', `intense', `underline', `italic', WEIGHT + ;; `selection' - `accented', `intense', `underline', `italic', `text-also' WEIGHT + ;; `popup' - same as `selected' + ;; `t' - applies to any key not explicitly referenced (check docs) + ;; + ;; WEIGHT is a symbol such as `semibold', `light', or anything + ;; covered in `modus-themes-weights'. Bold is used in the absence + ;; of an explicit WEIGHT. + modus-themes-completions '((matches . (extrabold)) + (selection . (semibold accented)) + (popup . (accented intense))) + + modus-themes-mail-citations nil ; {nil,'intense,'faint,'monochrome} + + ;; Options for `modus-themes-region' are either nil (the default), + ;; or a list of properties that may include any of those symbols: + ;; `no-extend', `bg-only', `accented' + modus-themes-region '(bg-only no-extend) + + ;; Options for `modus-themes-diffs': nil, 'desaturated, 'bg-only + modus-themes-diffs 'desaturated + + modus-themes-org-blocks 'gray-background ; {nil,'gray-background,'tinted-background} + + modus-themes-org-agenda ; this is an alist: read the manual or its doc string + '((header-block . (variable-pitch 1.3)) + (header-date . (grayscale workaholic bold-today 1.1)) + (event . (accented varied)) + (scheduled . uniform) + (habit . traffic-light)) + + modus-themes-headings ; this is an alist: read the manual or its doc string + '((1 . (overline background variable-pitch 1.3)) + (2 . (rainbow overline 1.1)) + (t . (semibold)))) + +* Menu: + +* Custom reload theme:: Toggle auto-reload of the theme when setting custom variables +* Deuteranopia style:: Toggle red/blue color-coding instead of red/green +* Bold constructs:: Toggle bold constructs in code +* Italic constructs:: Toggle italic font constructs in code +* Syntax styles:: Choose the overall aesthetic of code syntax +* Mixed fonts:: Toggle mixing of font families +* Link styles:: Choose among several styles, with or without underline +* Box buttons:: Choose among several styles for buttons +* Command prompts:: Choose among plain, subtle, or intense prompts +* Mode line:: Choose among several styles, with or without borders +* Tab style:: Toggle accented background for tabs +* Completion UIs:: Choose among several styles for completion UIs +* Mail citations:: Choose among colorful, desaturated, monochrome citations +* Fringes:: Choose among invisible, subtle, or intense fringe styles +* Language checkers:: Control the style of language checkers/linters +* Line highlighting:: Choose style of current line (hl-line-mode) +* Line numbers:: Toggle subtle style for line numbers +* Mouse hover effects:: Toggle intense style for mouseover highlights +* Markup:: Choose style for markup in Org and others +* Matching parentheses:: Choose between various styles for matching delimiters/parentheses +* Active region:: Choose between various styles for the active region +* Diffs:: Choose among intense, desaturated, or background-only diffs +* Org mode blocks:: Choose among plain, gray, or tinted backgrounds +* Org agenda:: Control each element in the presentation of the agenda +* Heading styles:: Choose among several styles, also per heading level +* UI typeface:: Toggle the use of variable-pitch across the User Interface + + +File: modus-themes.info, Node: Custom reload theme, Next: Deuteranopia style, Up: Customization Options + +4.1 Option for inhibiting theme reload +====================================== + +Brief: Toggle reloading of the active theme when an option is changed +through the Customize UI. + + Symbol: ‘modus-themes-inhibit-reload’ (‘boolean’ type) + + Possible values: + + 1. ‘nil’ + 2. ‘t’ (default) + + By default, customizing a theme-related user option through the +Custom interfaces or with ‘M-x customize-set-variable’ will not reload +the currently active Modus theme. + + Enable this behavior by setting this variable to ‘nil’. + + Regardless of this option, the active theme must be reloaded for +changes to user options to take effect (*note Enable and load::). + + +File: modus-themes.info, Node: Deuteranopia style, Next: Bold constructs, Prev: Custom reload theme, Up: Customization Options + +4.2 Option for red-green color deficiency or deuteranopia +========================================================= + +Brief: When non-nil use red/blue color-coding instead of red/green, +where appropriate. + + Symbol: ‘modus-themes-deuteranopia’ (‘boolean’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘t’ + + This is to account for red-green color deficiency, also know as +deuteranopia and variants. It applies to all contexts where there can +be a color-coded distinction between failure or success, a to-do or done +state, a mark for deletion versus a mark for selection (e.g. in Dired), +current and lazily highlighted search matches, removed lines in diffs as +opposed to added ones, and so on. + + Note that this does not change all colors throughout the active +theme, but only applies to cases that have color-coding significance. +For example, regular code syntax highlighting is not affected. There is +no such need because of the themes’ overarching commitment to the +highest legibility standard, which ensures that text is readable +regardless of hue, as well as the predominance of colors on the +blue-cyan-magenta-purple side of the spectrum. + + *note Why are colors mostly variants of blue, magenta, cyan?: Why are +colors mostly variants of blue magenta cyan?. + + +File: modus-themes.info, Node: Bold constructs, Next: Italic constructs, Prev: Deuteranopia style, Up: Customization Options + +4.3 Option for more bold constructs +=================================== + +Brief: Use bold for code syntax highlighting and related. + + Symbol: ‘modus-themes-bold-constructs’ (‘boolean’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘t’ + + The default is to use a bold typographic weight only when it is +required. + + With a non-nil value (‘t’) display several syntactic constructs in +bold weight. This concerns keywords and other important aspects of code +syntax. It also affects certain mode line indicators and command-line +prompts. + + Advanced users may also want to configure the exact attributes of the +‘bold’ face. + + *note Configure bold and italic faces::. + + +File: modus-themes.info, Node: Italic constructs, Next: Syntax styles, Prev: Bold constructs, Up: Customization Options + +4.4 Option for more italic constructs +===================================== + +Brief: Use italics for code syntax highlighting and related. + + Symbol: ‘modus-themes-italic-constructs’ (‘boolean’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘t’ + + The default is to not use slanted text forms (italics) unless it is +absolutely necessary. + + With a non-nil value (‘t’) choose to render more faces in italics. +This typically affects documentation strings and code comments. + + Advanced users may also want to configure the exact attributes of the +‘italic’ face. + + *note Configure bold and italic faces::. + + +File: modus-themes.info, Node: Syntax styles, Next: Mixed fonts, Prev: Italic constructs, Up: Customization Options + +4.5 Option for syntax highlighting +================================== + +Brief: Set the overall style of code syntax highlighting. + + Symbol: ‘modus-themes-syntax’ (‘choice’ type, list of properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • ‘faint’ + • ‘yellow-comments’ + • ‘green-strings’ + • ‘alt-syntax’ + + The default (a ‘nil’ value or an empty list) is to use a balanced +combination of colors on the cyan-blue-magenta side of the spectrum. +There is little to no use of greens, yellows, and reds. Comments are +gray, strings are blue colored, doc strings are a shade of cyan, while +color combinations are designed to avoid exaggerations. + + The property ‘faint’ fades the saturation of all applicable colors, +where that is possible or appropriate. + + The property ‘yellow-comments’ applies a yellow color to comments. + + The property ‘green-strings’ applies a green color to strings and a +green tint to doc strings. + + The property ‘alt-syntax’ changes the combination of colors beyond +strings and comments, so that the effective palette is broadened to +provide greater variety relative to the default. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (faint) + (green-strings yellow-comments) + (alt-syntax green-strings yellow-comments) + (faint alt-syntax green-strings yellow-comments) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-syntax '(faint alt-syntax)) + + Independent of this variable, users may also control the use of a +bold weight or italic text: ‘modus-themes-bold-constructs’ and +‘modus-themes-italic-constructs’. + + *note Option for more bold constructs: Bold constructs. + + *note Option for more italic constructs: Italic constructs. + + +File: modus-themes.info, Node: Mixed fonts, Next: Link styles, Prev: Syntax styles, Up: Customization Options + +4.6 Option for font mixing +========================== + +Brief: Toggle the use of monospaced fonts for spacing-sensitive +constructs (affects font families). + + Symbol: ‘modus-themes-mixed-fonts’ (‘boolean’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘t’ + + When set to non-nil (‘t’), configure some spacing-sensitive faces +like Org tables and code blocks to always inherit from the ‘fixed-pitch’ +face. This is to ensure that certain constructs like code blocks and +tables remain monospaced even when users opt for a mode that remaps +typeface families, such as the built-in ‘M-x variable-pitch-mode’. +Otherwise the layout would appear broken, due to how spacing is done. + + For a consistent experience, user may need to specify the font family +of the ‘fixed-pitch’ face. + + *note Font configurations for Org and others::. + + Furthermore, users may prefer to use another package for handling +mixed typeface configurations, rather than letting the theme do it, +perhaps because a purpose-specific package has extra functionality. Two +possible options are ‘org-variable-pitch’ and ‘mixed-pitch’. + + +File: modus-themes.info, Node: Link styles, Next: Box buttons, Prev: Mixed fonts, Up: Customization Options + +4.7 Option for links +==================== + +Brief: Control the style of links to web pages, files, buffers... + + Symbol: ‘modus-themes-links’ (‘choice’ type, list of properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • Underline style: + • ‘neutral-underline’ + • ‘no-underline’ + • Text coloration: + • ‘faint’ + • ‘no-color’ + • ‘bold’ + • ‘italic’ + • ‘background’ + + The default (a ‘nil’ value or an empty list) is a prominent text +color, typically blue, with an underline of the same color. + + For the style of the underline, a ‘neutral-underline’ property turns +the color of the line into a subtle gray, while the ‘no-underline’ +property removes the line altogether. If both of those are set, the +latter takes precedence. + + For text coloration, a ‘faint’ property desaturates the color of the +text and the underline, unless the underline is affected by the +aforementioned properties. While a ‘no-color’ property removes the +color from the text. If both of those are set, the latter takes +precedence. + + A ‘bold’ property applies a heavy typographic weight to the text of +the link. + + An ‘italic’ property adds a slant to the link’s text (italic or +oblique forms, depending on the typeface). + + A ‘background’ property applies a subtle tinted background color. + + In case both ‘no-underline’ and ‘no-color’ are set, then a subtle +gray background is applied to all links. This can still be combined +with the ‘bold’ and ‘italic’ properties. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (faint) + (no-underline faint) + (no-color no-underline bold) + (italic bold background no-color no-underline) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-links '(neutral-underline background)) + + The placement of the underline, meaning its proximity to the text, is +controlled by ‘x-use-underline-position-properties’, +‘x-underline-at-descent-line’, ‘underline-minimum-offset’. Please refer +to their documentation strings. + + +File: modus-themes.info, Node: Box buttons, Next: Command prompts, Prev: Link styles, Up: Customization Options + +4.8 Option for box buttons +========================== + +Brief: Control the style of buttons in the Custom UI and related. + + Symbol: ‘modus-themes-box-buttons’ (‘choice’ type, list of +properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • ‘flat’ + • ‘accented’ + • ‘faint’ + • ‘variable-pitch’ + • ‘underline’ + • A font weight, which must be supported by the underlying typeface: + • ‘thin’ + • ‘ultralight’ + • ‘extralight’ + • ‘light’ + • ‘semilight’ + • ‘regular’ + • ‘medium’ + • ‘semibold’ + • ‘bold’ + • ‘heavy’ + • ‘extrabold’ + • ‘ultrabold’ + • A floating point as a height multiple of the default or a cons cell + in the form of ‘(height . FLOAT)’ + • ‘all-buttons’ + + The default (a nil value or an empty list) is a gray background +combined with a pseudo three-dimensional effect. + + The ‘flat’ property makes the button two dimensional. + + The ‘accented’ property changes the background from gray to an accent +color. + + The ‘faint’ property reduces the overall coloration. + + The ‘variable-pitch’ property applies a proportionately spaced +typeface to the button~s text. + + *note Font configurations for Org and others::. + + The ‘underline’ property draws a line below the affected text and +removes whatever box effect. This is optimal when Emacs runs inside a +terminal emulator (*note More accurate colors in terminal emulators::). +If ‘flat’ and ‘underline’ are defined together, the latter takes +precedence. + + The symbol of a weight attribute adjusts the font of the button +accordingly, such as ‘light’, ‘semibold’, etc. Valid symbols are +defined in the variable ‘modus-themes-weights’. + + *note Configure bold and italic faces::. + + A number, expressed as a floating point (e.g. ‘0.9’), adjusts the +height of the button’s text to that many times the base font size. The +default height is the same as ‘1.0’, though it need not be explicitly +stated. Instead of a floating point, an acceptable value can be in the +form of a cons cell like ‘(height . FLOAT)’ or ‘(height FLOAT)’, where +FLOAT is the given number. + + The ‘all-buttons’ property extends the box button effect (or the +aforementioned properties) to the faces of the generic widget library. +By default, those do not look like the buttons of the Custom UI as they +are ordinary text wrapped in square brackets. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (flat) + (variable-pitch flat) + (variable-pitch flat semibold 0.9) + (variable-pitch flat semibold (height 0.9)) ; same as above + (variable-pitch flat semibold (height . 0.9)) ; same as above + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-box-buttons '(variable-pitch flat 0.9)) + + +File: modus-themes.info, Node: Command prompts, Next: Mode line, Prev: Box buttons, Up: Customization Options + +4.9 Option for command prompt styles +==================================== + +Brief: Control the style of command prompts (e.g. minibuffer, shell, +IRC clients). + + Symbol: ‘modus-themes-prompts’ (‘choice’ type, list of properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • ‘background’ + • ‘bold’ + • ‘gray’ + • ‘intense’ + • ‘italic’ + + The default (a ‘nil’ value or an empty list) means to only use a +subtle accented foreground color. + + The property ‘background’ applies a background color to the prompt’s +text. By default, this is a subtle accented value. + + The property ‘intense’ makes the foreground color more prominent. If +the ‘background’ property is also set, it amplifies the value of the +background as well. + + The property ‘gray’ changes the prompt’s colors to grayscale. This +affects the foreground and, if the ‘background’ property is also set, +the background. Its effect is subtle, unless it is combined with the +‘intense’ property. + + The property ‘bold’ makes the text use a bold typographic weight. +Similarly, ‘italic’ adds a slant to the font’s forms (italic or oblique +forms, depending on the typeface). + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (intense) + (bold intense) + (intense bold gray) + (intense background gray bold) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-prompts '(background gray)) + + +File: modus-themes.info, Node: Mode line, Next: Tab style, Prev: Command prompts, Up: Customization Options + +4.10 Option for mode line presentation +====================================== + +Brief: Control the style of the mode lines. + + Symbol: ‘modus-themes-mode-line’ (‘choice’ type, list of properties) + + Possible values, which can be expressed as a list of combinations of +box effect, color, and border visibility: + + • Overall style: + • ‘3d’ + • ‘moody’ + • ‘accented’ + • ‘borderless’ + • A natural number > 1 for extra padding or a cons cell in the form + of ‘(padding . NATNUM)’. + • A floating point to set the height of the mode line’s text. It can + also be a cons cell in the form of ‘(height . FLOAT)’. + + The default (a nil value or an empty list) is a two-dimensional +rectangle with a border around it. The active and the inactive mode +lines use different shades of grayscale values for the background, +foreground, border. + + The ‘3d’ property applies a three-dimensional effect to the active +mode line. The inactive mode lines remain two-dimensional and are toned +down a bit, relative to the default style. + + The ‘moody’ property optimizes the mode line for use with the library +of the same name (hereinafter referred to as ’Moody’). In practice, it +removes the box effect and replaces it with underline and overline +properties. It also tones down the inactive mode lines. Despite its +intended purpose, this option can also be used without the Moody library +(please consult the themes’ manual on this point for more details). If +both ‘3d’ and ‘moody’ properties are set, the latter takes precedence. + + The ‘borderless’ property removes the color of the borders. It does +not actually remove the borders, but only makes their color the same as +the background, effectively creating some padding. + + The ‘accented’ property ensures that the active mode line uses a +colored background instead of the standard shade of gray. + + A positive integer (natural number or natnum) applies a padding +effect of NATNUM pixels at the boundaries of the mode lines. The +default value is 1 and does not need to be specified explicitly. The +padding has no effect when the ‘moody’ property is also used, because +Moody already applies its own tweaks. To ensure that the underline is +placed at the bottom of the mode line, set ‘x-underline-at-descent-line’ +to non-nil (this is not needed when the ‘borderless’ property is also +set). For users on Emacs 29, the ‘x-use-underline-position-properties’ +variable must also be set to nil. + + The padding can also be expressed as a cons cell in the form of +‘(padding . NATNUM)’ or ‘(padding NATNUM)’ where the key is constant and +NATNUM is the desired natural number. + + A floating point applies an adjusted height to the mode line’s text +as a multiple of the main font size. The default rate is 1.0 and does +not need to be specified. Apart from a floating point, the height may +also be expressed as a cons cell in the form of ‘(height . FLOAT)’ or +‘(height FLOAT)’ where the key is constant and the FLOAT is the desired +number. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (accented) + (borderless 3d) + (moody accented borderless) + + Same as above, using the padding and height as an example (these all +yield the same result): + + (accented borderless 4 0.9) + (accented borderless (padding . 4) (height . 0.9)) + (accented borderless (padding 4) (height 0.9)) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-mode-line '(borderless accented)) + + Note that Moody does not expose any faces that the themes could style +directly. Instead it re-purposes existing ones to render its tabs and +ribbons. As such, there may be cases where the contrast ratio falls +below the 7:1 target that the themes conform with (WCAG AAA). To hedge +against this, we configure a fallback foreground for the ‘moody’ +property, which will come into effect when the background of the mode +line changes to something less accessible, such as Moody ribbons (read +the doc string of ‘set-face-attribute’, specifically +‘:distant-foreground’). This fallback is activated when Emacs +determines that the background and foreground of the given construct are +too close to each other in terms of color distance. In practice, users +will need to experiment with the variable +‘face-near-same-color-threshold’ to trigger the effect. We find that a +value of ‘45000’ shall suffice, contrary to the default ‘30000’. Though +for the combinations that involve the ‘accented’ and ‘moody’ properties, +as mentioned above, that should be raised up to ‘70000’. Do not set it +too high, because it has the adverse effect of always overriding the +default colors (which have been carefully designed to be highly +accessible). + + Furthermore, because Moody expects an underline and overline instead +of a box style, it is strongly advised to set +‘x-underline-at-descent-line’ to a non-nil value. + + Finally, note that various packages which heavily modify the mode +line, such as ‘doom-modeline’, ‘nano-modeline’, ‘powerline’, ‘spaceline’ +may not look as intended with all possible combinations of this user +option. + + +File: modus-themes.info, Node: Tab style, Next: Completion UIs, Prev: Mode line, Up: Customization Options + +4.11 Option for accented background in tab interfaces +===================================================== + +Brief: Toggle accent colors for tabbed interfaces. + + Symbol: ‘modus-themes-tabs-accented’ (‘boolean’ type) + + Possible values: + + • ‘nil’ (default) + • ‘t’ + + By default, all tab interfaces use backgrounds which are shades of +gray. When this option is set to non-nil, the backgrounds become +colorful. + + This affects the built-in ‘tab-bar-mode’ and ‘tab-line-mode’, as well +as the Centaur tabs package. + + +File: modus-themes.info, Node: Completion UIs, Next: Mail citations, Prev: Tab style, Up: Customization Options + +4.12 Option for completion framework aesthetics +=============================================== + +Brief: Set the overall style of completion framework interfaces. + + Symbol: ‘modus-themes-completions’ (‘alist’ type properties) + + This affects Company, Corfu, Flx, Helm, Icomplete/Fido, Ido, Ivy, +Orderless, Selectrum, Vertico. The value is an alist that takes the +form of a ‘(key . properties)’ combination. Here is a sample, followed +by a description of the particularities: + + (setq modus-themes-completions + '((matches . (extrabold background intense)) + (selection . (semibold accented intense)) + (popup . (accented)))) + + The ‘matches’ key refers to the highlighted characters that +correspond to the user’s input. By default (nil or an empty list), they +have a bold weight and a colored foreground. The list of properties may +include any of the following symbols regardless of the order they may +appear in: + + • ‘background’ to add a background color; + + • ‘intense’ to increase the overall coloration (also amplifies the + ‘background’, if present); + + • ‘underline’ to draw a line below the characters; + + • ‘italic’ to use a slanted font (italic or oblique forms); + + • The symbol of a font weight attribute such as ‘light’, ‘semibold’, + et cetera. Valid symbols are defined in the ‘modus-themes-weights’ + variable. The absence of a weight means that bold will be used. + + The ‘selection’ key applies to the current line or currently matched +candidate, depending on the specifics of the User Interface. By default +(nil or an empty list), it has a subtle gray background, a bold weight, +and the base foreground value for the text. The list of properties it +accepts is as follows (order is not significant): + + • ‘accented’ to make the background colorful instead of gray; + + • ‘text-also’ to apply extra color to the text of the selected line; + + • ‘intense’ to increase the overall coloration; + + • ‘underline’ to draw a line below the characters; + + • ‘italic’ to use a slanted font (italic or oblique forms); + + • The symbol of a font weight attribute such as ‘light’, ‘semibold’, + et cetera. Valid symbols are defined in the ‘modus-themes-weights’ + variable. The absence of a weight means that bold will be used. + + The ‘popup’ key takes the same values as ‘selection’. + + Apart from specifying each key separately, a fallback list is +accepted. This is only useful when the desired aesthetic is the same +across all keys that are not explicitly referenced. For example, this: + + (setq modus-themes-completions + '((t . (extrabold intense)))) + + Is the same as: + + (setq modus-themes-completions + '((matches . (extrabold intense)) + (selection . (extrabold intense)) + (popup . (extrabold intense)))) + + In the case of the fallback, any property that does not apply to the +corresponding key is simply ignored (‘matches’ does not have ‘accented’ +and ‘text-also’, while ‘selection’ and ‘popup’ do not have +‘background’). + + A concise expression of those associations can be written as follows, +where the ‘car’ is always the key and the ‘cdr’ is the list of +properties (whatever order they may appear in): + + (setq modus-themes-completions + '((matches extrabold background intense) + (selection semibold accented intense) + (popup accented))) + + *note Configure bold and italic faces::. + + Also refer to the Orderless documentation for its intersection with +Company (if you choose to use those in tandem). + + +File: modus-themes.info, Node: Mail citations, Next: Fringes, Prev: Completion UIs, Up: Customization Options + +4.13 Option for mail citations +============================== + +Brief: Set the overall style of citations/quotes when composing emails. + + Symbol: ‘modus-themes-mail-citations’ (‘choice’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘intense’ + 3. ‘faint’ + 4. ‘monochrome’ + + By default (a nil value) citations are styled with contrasting hues +to denote their depth. Colors are easy to tell apart because they +complement each other, but they otherwise are not very prominent. + + Option ‘intense’ is similar to the default in terms of using +contrasting and complementary hues, but applies more saturated colors. + + Option ‘faint’ maintains the same color-based distinction between +citation levels though the colors it uses have subtle differences +between them. + + Option ‘monochrome’ turns all quotes into a shade of gray. + + Whatever the value assigned to this variable, citations in emails are +controlled by typographic elements or indentation, which the themes do +not touch. + + +File: modus-themes.info, Node: Fringes, Next: Language checkers, Prev: Mail citations, Up: Customization Options + +4.14 Option for fringe visibility +================================= + +Brief: Control the overall coloration of the fringes. + + Symbol: ‘modus-themes-fringes’ (‘choice’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘subtle’ + 3. ‘intense’ + + The default is to use the same color as that of the main background, +meaning that the fringes are not obvious though they still occupy the +space given to them by ‘fringe-mode’. + + Options ‘subtle’ and ‘intense’ apply a gray background, making the +fringes visible. The difference between the two is one of degree, as +their names imply. + + +File: modus-themes.info, Node: Language checkers, Next: Line highlighting, Prev: Fringes, Up: Customization Options + +4.15 Option for language checkers +================================= + +Brief: Control the style of in-buffer warnings and errors produced by +spell checkers, code linters, and the like. + + Symbol: ‘modus-themes-lang-checkers’ (‘choice’ type, list of +properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • ‘straight-underline’ + • ‘text-also’ + • ‘background’ + • Overall coloration: + • ‘intense’ + • ‘faint’ + + The default (a ‘nil’ value or an empty list) applies a color-coded +underline to the affected text, while it leaves the original foreground +intact. If the display spec of Emacs has support for it, the +underline’s style is that of a wave, otherwise it is a straight line. + + The property ‘straight-underline’ ensures that the underline under +the affected text is always drawn as a straight line. + + The property ‘text-also’ applies the same color of the underline to +the affected text. + + The property ‘background’ adds a color-coded background. + + The property ‘intense’ amplifies the applicable colors if +‘background’ and/or ‘text-also’ are set. If ‘intense’ is set on its +own, then it implies ‘text-also’. + + The property ‘faint’ uses nuanced colors for the underline and for +the foreground when ‘text-also’ is included. If both ‘faint’ and +‘intense’ are specified, the former takes precedence. + + Combinations of any of those properties can be expressed in a list, +as in those examples: + + (background) + (straight-underline intense) + (background text-also straight-underline) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-lang-checkers '(text-also background)) + + NOTE: The placement of the straight underline, though not the wave +style, is controlled by the built-in variables +‘underline-minimum-offset’, ‘x-underline-at-descent-line’, +‘x-use-underline-position-properties’. + + To disable fringe indicators for Flymake or Flycheck, refer to +variables ‘flymake-fringe-indicator-position’ and +‘flycheck-indication-mode’, respectively. + + +File: modus-themes.info, Node: Line highlighting, Next: Line numbers, Prev: Language checkers, Up: Customization Options + +4.16 Option for line highlighting +================================= + +Brief: Control the style of the current line of ‘hl-line-mode’. + + Symbol: ‘modus-themes-hl-line’ (‘choice’ type, list of properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • ‘accented’ + • ‘intense’ + • ‘underline’ + + The default (a ‘nil’ value or an empty list) is a subtle gray +background color. + + The property ‘accented’ changes the background to a colored variant. + + An ‘underline’ property draws a line below the highlighted area. Its +color is similar to the background, so gray by default or an accent +color when ‘accented’ is also set. + + An ‘intense’ property amplifies the colors in use, which may be both +the background and the underline. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (intense) + (underline intense) + (accented intense underline) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-hl-line '(underline accented)) + + Set ‘x-underline-at-descent-line’ to a non-nil value for better +results with underlines. + + This style affects several packages that enable ‘hl-line-mode’, such +as ‘elfeed’, ‘notmuch’, and ‘mu4e’. + + [ Also check the ‘lin’ package on GNU ELPA (by the author of the +modus-themes) for a stylistic enhancement to ‘hl-line-mode’. ] + + +File: modus-themes.info, Node: Line numbers, Next: Mouse hover effects, Prev: Line highlighting, Up: Customization Options + +4.17 Option for line numbers +============================ + +Brief: Toggle subtle line numbers. + + Symbol: ‘modus-themes-subtle-line-numbers’ (‘boolean’ type) + + Possible value: + + 1. ‘nil’ (default) + 2. ‘t’ + + The default style for ‘display-line-numbers-mode’ and its global +variant is to apply a subtle gray background to the line numbers. The +current line has a more pronounced background and foreground combination +to bring more attention to itself. + + Similarly, the faces for ‘display-line-numbers-major-tick’ and its +counterpart ‘display-line-numbers-minor-tick’ use appropriate styles +that involve a bespoke background and foreground combination. + + With a non-nil value (‘t’), line numbers have no background of their +own. Instead they retain the primary background of the theme, blending +with the rest of the buffer. Foreground values for all relevant faces +are updated to accommodate this aesthetic. + + +File: modus-themes.info, Node: Mouse hover effects, Next: Markup, Prev: Line numbers, Up: Customization Options + +4.18 Option for mouseover effects +================================= + +Brief: Toggle intense mouse hover effects. + + Symbol: ‘modus-themes-intense-mouseovers’ (‘boolean’ type) + + Possible value: + + 1. ‘nil’ (default) + 2. ‘t’ + + By default all mouseover effects apply a highlight with a subtle +colored background. When non-nil, these have a more pronounced effect. + + Note that this affects the generic ‘highlight’ which, strictly +speaking, is not limited to mouse usage. + + +File: modus-themes.info, Node: Markup, Next: Matching parentheses, Prev: Mouse hover effects, Up: Customization Options + +4.19 Option for markup style in Org and others +============================================== + +Brief: Choose style of markup in Org, Markdown, and others (affects +constructs such as Org’s ‘=verbatim=’ and ‘~code~’). + + Symbol: ‘modus-themes-markup’ (‘boolean’ type) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + 1. ‘bold’ + 2. ‘italic’ + 3. ‘background’ + 4. ‘intense’ + + The ‘italic’ property applies a typographic slant (italics). + + The ‘bold’ property applies a heavier typographic weight. + + *note Configure bold and italic faces::. + + The ‘background’ property adds a background color. The background is +a shade of gray, unless the ‘intense’ property is also set. + + The ‘intense’ property amplifies the existing coloration. When +‘background’ is used, the background color is enhanced as well and +becomes tinted instead of being gray. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (bold) + (bold italic) + (bold italic intense) + (bold italic intense background) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-markup '(bold italic)) + + Also check the variables ‘org-hide-emphasis-markers’, +‘org-hide-macro-markers’. + + +File: modus-themes.info, Node: Matching parentheses, Next: Active region, Prev: Markup, Up: Customization Options + +4.20 Option for parenthesis matching +==================================== + +Brief: Control the style of matching delimiters produced by +‘show-paren-mode’. + + Symbol: ‘modus-themes-paren-match’ (‘choice’ type, list of +properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • ‘bold’ + • ‘intense’ + • ‘underline’ + + The default (a ‘nil’ value or an empty list) is a subtle background +color. + + The ‘bold’ property adds a bold weight to the characters of the +matching delimiters. + + The ‘intense’ property applies a more prominent background color to +the delimiters. + + The ‘underline’ property draws a straight line under the affected +text. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (bold) + (underline intense) + (bold intense underline) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-paren-match '(bold intense)) + + This customization variable affects the built-in ‘show-paren-mode’ +and the ‘smartparens’ package. + + +File: modus-themes.info, Node: Active region, Next: Diffs, Prev: Matching parentheses, Up: Customization Options + +4.21 Option for active region +============================= + +Brief: Control the style of the region. + + Symbol: ‘modus-themes-region’ (‘choice’ type, list of properties) + + Possible values are expressed as a list of properties (default is +‘nil’ or an empty list). The list can include any of the following +symbols: + + • ‘no-extend’ + • ‘bg-only’ + • ‘accented’ + + The default (a ‘nil’ value or an empty list) is a prominent gray +background that overrides all foreground colors in the area it +encompasses. Its reach extends to the edge of the window. + + The ‘no-extend’ property limits the region to the end of the line, so +that it does not reach the edge of the window. + + The ‘bg-only’ property makes the region’s background color more +subtle to allow the underlying text to retain its foreground colors. + + The ‘accented’ property applies a more colorful background to the +region. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (no-extend) + (bg-only accented) + (accented bg-only no-extend) + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-region '(bg-only no-extend)) + + +File: modus-themes.info, Node: Diffs, Next: Org mode blocks, Prev: Active region, Up: Customization Options + +4.22 Option for diff buffer looks +================================= + +Brief: Set the overall style of diffs. + + Symbol: ‘modus-themes-diffs’ (‘choice’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘desaturated’ + 3. ‘bg-only’ + + The default (‘nil’) uses fairly intense color combinations for diffs, +by applying prominently colored backgrounds, with appropriately tinted +foregrounds. + + Option ‘desaturated’ follows the same principles as with the default +(‘nil’), though it tones down all relevant colors. + + Option ‘bg-only’ applies a background but does not override the +text’s foreground. This makes it suitable for a non-nil value passed to +‘diff-font-lock-syntax’ (note: Magit does not support syntax +highlighting in diffs—last checked on 2021-12-02). + + When the user option ‘modus-themes-deuteranopia’ is non-nil, all +diffs will use a red/blue color-coding system instead of the standard +red/green. Other stylistic changes are made in the interest of +optimizing for such a use-case. + + *note Option for red-green color deficiency or deuteranopia: +Deuteranopia style. + + In versions before ‘2.0.0’ there was an option for foreground-only +diffs. This is no longer supported at the theme level because there are +cases where the perceived contrast and overall contextuality were not +good enough although the applied colors were technically above the 7:1 +contrast threshold. + + *note Diffs with only the foreground::. + + *note Ediff without diff color-coding::. + + +File: modus-themes.info, Node: Org mode blocks, Next: Org agenda, Prev: Diffs, Up: Customization Options + +4.23 Option for org-mode block styles +===================================== + +Brief: Set the overall style of Org code blocks, quotes, and the like. + + Symbol: ‘modus-themes-org-blocks’ (‘choice’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘gray-background’ (value ‘grayscale’ exists for backward + compatibility) + 3. ‘tinted-background’ (value ‘rainbow’ exists for backward + compatibility) + + Nil (the default) means that the block has no background of its own: +it uses the one that applies to the rest of the buffer. In this case, +the delimiter lines have a gray color for their text, making them look +exactly like all other Org properties. + + Option ‘gray-background’ applies a subtle gray background to the +block’s contents. It also affects the begin and end lines of the block +as they get another shade of gray as their background, which +differentiates them from the contents of the block. All background +colors extend to the edge of the window, giving the area a rectangular, +“blocky” presentation. + + Option ‘tinted-background’ uses a slightly colored background for the +contents of the block. The exact color will depend on the programming +language and is controlled by the variable ‘org-src-block-faces’ (refer +to the theme’s source code for the current association list). For this +to take effect, the Org buffer needs to be restarted with +‘org-mode-restart’. In this scenario, it may be better to inhibit the +extension of the delimiter lines’ background to the edge of the window +because Org does not provide a mechanism to update their colors +depending on the contents of the block. Disable the extension of such +backgrounds by setting ‘org-fontify-whole-block-delimiter-line’ to nil. + + Code blocks use their major mode’s colors only when the variable +‘org-src-fontify-natively’ is non-nil. While quote/verse blocks require +setting ‘org-fontify-quote-and-verse-blocks’ to a non-nil value. + + *note Update Org block delimiter fontification::. + + Older versions of the themes provided options ‘grayscale’ (or +‘greyscale’) and ‘rainbow’. Those will continue to work as they are +aliases for ‘gray-background’ and ‘tinted-background’, respectively. + + +File: modus-themes.info, Node: Org agenda, Next: Heading styles, Prev: Org mode blocks, Up: Customization Options + +4.24 Option for Org agenda constructs +===================================== + +Brief: Control the style of the Org agenda. Multiple parameters are +available, each with its own options. + + Symbol: ‘modus-themes-org-agenda’ (‘alist’ type, multiple styles) + + This is an alist that accepts a ‘(key . value)’ combination. Some +values are specified as a list. Here is a sample, followed by a +description of all possible combinations: + + (setq modus-themes-org-agenda + '((header-block . (variable-pitch 1.5)) + (header-date . (grayscale workaholic bold-today 1.2)) + (event . (accented italic varied)) + (scheduled . uniform) + (habit . traffic-light))) + + A ‘header-block’ key applies to elements that concern the headings +which demarcate blocks in the structure of the agenda. By default (a +‘nil’ value) those are rendered in a bold typographic weight, plus a +height that is slightly taller than the default font size. Acceptable +values come in the form of a list that can include either or both of +those properties: + + • ‘variable-pitch’ to use a proportionately spaced typeface; + + • A number as a floating point (e.g. 1.5) to set the height of the + text to that many times the default font height. A float of 1.0 or + the symbol ‘no-scale’ have the same effect of making the font the + same height as the rest of the buffer. When neither a number nor + ‘no-scale’ are present, the default is a small increase in height + (a value of 1.15). + + Instead of a floating point, an acceptable value can be in the form + of a cons cell like ‘(height . FLOAT)’ or ‘(height FLOAT)’, where + FLOAT is the given number. + + • The symbol of a weight attribute adjusts the font of the heading + accordingly, such as ‘light’, ‘semibold’, etc. Valid symbols are + defined in the variable ‘modus-themes-weights’. The absence of a + weight means that bold will be used by virtue of inheriting the + ‘bold’ face. + + *note Configure bold and italic faces::. + + In case both a number and ‘no-scale’ are in the list, the latter +takes precedence. If two numbers are specified, the first one is +applied. + + Example usage: + + (header-block . nil) + (header-block . (1.5)) + (header-block . (no-scale)) + (header-block . (variable-pitch 1.5)) + (header-block . (variable-pitch 1.5 semibold)) + + A ‘header-date’ key covers date headings. Dates use only a +foreground color by default (a ‘nil’ value), with weekdays and weekends +having a slight difference in hueness. The current date has an added +gray background. This key accepts a list of values that can include any +of the following properties: + + • ‘grayscale’ to make weekdays use the main foreground color and + weekends a more subtle gray; + + • ‘workaholic’ to make weekdays and weekends look the same in terms + of color; + + • ‘bold-today’ to apply a bold typographic weight to the current + date; + + • ‘bold-all’ to render all date headings in a bold weight; + + • ‘underline-today’ applies an underline to the current date while + removing the background it has by default; + + • A number as a floating point (e.g. 1.2) to set the height of the + text to that many times the default font height. The default is + the same as the base font height (the equivalent of 1.0). Instead + of a floating point, an acceptable value can be in the form of a + cons cell like ‘(height . FLOAT)’ or ‘(height FLOAT)’, where FLOAT + is the given number. + + For example: + + (header-date . nil) + (header-date . (workaholic)) + (header-date . (grayscale bold-all)) + (header-date . (grayscale workaholic)) + (header-date . (grayscale workaholic bold-today)) + (header-date . (grayscale workaholic bold-today scale-heading)) + + An ‘event’ key covers (i) headings with a plain time stamp that are +shown on the agenda, also known as events, (ii) entries imported from +the diary, and (iii) other items that derive from a symbolic expression +or sexp (phases of the moon, holidays, etc.). By default all those look +the same and have a subtle foreground color (the default is a nil value +or an empty list). This key accepts a list of properties. Those are: + + • ‘accented’ applies an accent value to the event’s foreground, + replacing the original gray. It makes all entries stand out more. + • ‘italic’ adds a slant to the font’s forms (italic or oblique forms, + depending on the typeface). + • ‘varied’ differentiates between events with a plain time stamp and + entries that are generated from either the diary or a symbolic + expression. It generally puts more emphasis on events. When + ‘varied’ is combined with ‘accented’, it makes only events use an + accent color, while diary/sexp entries retain their original subtle + foreground. When ‘varied’ is used in tandem with ‘italic’, it + applies a slant only to diary and sexp entries, not events. And + when ‘varied’ is the sole property passed to the ‘event’ key, it + has the same meaning as the list (italic varied). The combination + of ‘varied’, ‘accented’, ‘italic’ covers all of the aforementioned + cases. + + For example: + + (event . nil) + (event . (italic)) + (event . (accented italic)) + (event . (accented italic varied)) + + A ‘scheduled’ key applies to tasks with a scheduled date. By default +(a ‘nil’ value), those use varying shades of yellow to denote (i) a past +or current date and (ii) a future date. Valid values are symbols: + + • nil (default); + • ‘uniform’ to make all scheduled dates the same color; + • ‘rainbow’ to use contrasting colors for past, present, future + scheduled dates. + + For example: + + (scheduled . nil) + (scheduled . uniform) + (scheduled . rainbow) + + A ‘habit’ key applies to the ‘org-habit’ graph. All possible value +are passed as a symbol. Those are: + + • The default (‘nil’) is meant to conform with the original aesthetic + of ‘org-habit’. It employs all four color codes that correspond to + the org-habit states—clear, ready, alert, and overdue—while + distinguishing between their present and future variants. This + results in a total of eight colors in use: red, yellow, green, + blue, in tinted and shaded versions. They cover the full set of + information provided by the ‘org-habit’ consistency graph. + • ‘simplified’ is like the default except that it removes the + dichotomy between current and future variants by applying uniform + color-coded values. It applies a total of four colors: red, + yellow, green, blue. They produce a simplified consistency graph + that is more legible (or less busy) than the default. The intent + is to shift focus towards the distinction between the four states + of a habit task, rather than each state’s present/future outlook. + • ‘traffic-light’ further reduces the available colors to red, + yellow, and green. As in ‘simplified’, present and future variants + appear uniformly, but differently from it, the ‘clear’ state is + rendered in a green hue, instead of the original blue. This is + meant to capture the use-case where a habit task being too early is + less important than it being too late. The difference between + ready and clear states is attenuated by painting both of them using + shades of green. This option thus highlights the alert and overdue + states. + • When ‘modus-themes-deuteranopia’ is non-nil the exact style of the + habit graph adapts to the needs of users with red-green color + deficiency by substituting every instance of green with blue or + cyan (depending on the specifics). + + *note Option for red-green color deficiency or deuteranopia: +Deuteranopia style. + + For example: + + (habit . nil) + (habit . simplified) + (habit . traffic-light) + + Putting it all together, the alist can look like this: + + '((header-block . (1.5 variable-pitch)) + (header-date . (grayscale workaholic bold-today)) + (event . (accented varied)) + (scheduled . uniform) + (habit . traffic-light)) + + ;; Or else: + (setq modus-themes-org-agenda + '((header-block . (1.5 variable-pitch)) + (header-date . (grayscale workaholic bold-today)) + (event . (accented varied)) + (scheduled . uniform) + (habit . traffic-light))) + + +File: modus-themes.info, Node: Heading styles, Next: UI typeface, Prev: Org agenda, Up: Customization Options + +4.25 Option for the headings’ overall style +=========================================== + +Brief: Heading styles with optional list of values for levels 0-8. + + Symbol: ‘modus-themes-headings’ (‘alist’ type, multiple properties) + + This is an alist that accepts a ‘(key . list-of-values)’ combination. +The key is either a number, representing the heading’s level (0-8) or t, +which pertains to the fallback style. + + Level 0 is a special heading: it is used for what counts as a +document title or equivalent, such as the ‘#+title’ construct we find in +Org files. Levels 1-8 are regular headings. + + The list of values covers symbols that refer to properties, as +described below. Here is a complete sample, followed by a presentation +of all available properties: + + (setq modus-themes-headings + '((1 . (background overline variable-pitch 1.5)) + (2 . (overline rainbow 1.3)) + (3 . (overline 1.1)) + (t . (monochrome)))) + + Properties: + + • ‘rainbow’ + • ‘overline’ + • ‘background’ + • ‘monochrome’ + • A font weight, which must be supported by the underlying typeface: + • ‘thin’ + • ‘ultralight’ + • ‘extralight’ + • ‘light’ + • ‘semilight’ + • ‘regular’ + • ‘medium’ + • ‘semibold’ + • ‘bold’ + • ‘heavy’ + • ‘extrabold’ + • ‘ultrabold’ + • ‘no-bold’ (deprecated alias of a ‘regular’ weight) + • A floating point as a height multiple of the default or a cons cell + in the form of ‘(height . FLOAT)’. + + By default (a ‘nil’ value for this variable), all headings have a +bold typographic weight and use a desaturated text color. + + A ‘rainbow’ property makes the text color more saturated. + + An ‘overline’ property draws a line above the area of the heading. + + A ‘background’ property adds a subtle tinted color to the background +of the heading. + + A ‘monochrome’ property makes the heading the same as the base color, +which is that of the ‘default’ face’s foreground. When ‘background’ is +also set, ‘monochrome’ changes its color to gray. If both ‘monochrome’ +and ‘rainbow’ are set, the former takes precedence. + + A ‘variable-pitch’ property changes the font family of the heading to +that of the ‘variable-pitch’ face (normally a proportionately spaced +typeface). + + The symbol of a weight attribute adjusts the font of the heading +accordingly, such as ‘light’, ‘semibold’, etc. Valid symbols are +defined in the variable ‘modus-themes-weights’. The absence of a weight +means that bold will be used by virtue of inheriting the ‘bold’ face. +For backward compatibility, the ‘no-bold’ value is accepted, though +users are encouraged to specify a ‘regular’ weight instead. + + *note Configure bold and italic faces::. + + A number, expressed as a floating point (e.g. 1.5), adjusts the +height of the heading to that many times the base font size. The +default height is the same as 1.0, though it need not be explicitly +stated. Instead of a floating point, an acceptable value can be in the +form of a cons cell like ‘(height . FLOAT)’ or ‘(height FLOAT)’, where +FLOAT is the given number. + + Combinations of any of those properties are expressed as a list, like +in these examples: + + (semibold) + (rainbow background) + (overline monochrome semibold 1.3) + (overline monochrome semibold (height 1.3)) ; same as above + (overline monochrome semibold (height . 1.3)) ; same as above + + The order in which the properties are set is not significant. + + In user configuration files the form may look like this: + + (setq modus-themes-headings + '((1 . (background overline rainbow 1.5)) + (2 . (background overline 1.3)) + (t . (overline semibold)))) + + When defining the styles per heading level, it is possible to pass a +non-nil value (‘t’) instead of a list of properties. This will retain +the original aesthetic for that level. For example: + + (setq modus-themes-headings + '((1 . t) ; keep the default style + (2 . (background overline)) + (t . (rainbow)))) ; style for all other headings + + (setq modus-themes-headings + '((1 . (background overline)) + (2 . (rainbow semibold)) + (t . t))) ; default style for all other levels + + For Org users, the extent of the heading depends on the variable +‘org-fontify-whole-heading-line’. This affects the ‘overline’ and +‘background’ properties. Depending on the version of Org, there may be +others, such as ‘org-fontify-done-headline’. + + +File: modus-themes.info, Node: UI typeface, Prev: Heading styles, Up: Customization Options + +4.26 Option for variable-pitch font in UI elements +================================================== + +Brief: Toggle the use of proportionately spaced (‘variable-pitch’) fonts +in the User Interface. + + Symbol: ‘modus-themes-variable-pitch-ui’ (‘boolean’ type) + + Possible values: + + 1. ‘nil’ (default) + 2. ‘t’ + + This option concerns User Interface elements that are under the +direct control of Emacs. In particular: the mode line, header line, tab +bar, and tab line. + + The default is to use the same font as the rest of Emacs, which +usually is a monospaced family. + + With a non-nil value (‘t’) apply a proportionately spaced typeface. +This is done by assigning the ‘variable-pitch’ face to the relevant +items. + + *note Font configurations for Org and others::. + + +File: modus-themes.info, Node: Advanced customization, Next: Face coverage, Prev: Customization Options, Up: Top + +5 Advanced customization +************************ + +Unlike the predefined customization options which follow a clear pattern +of allowing the user to quickly specify their preference, the themes +also provide a more flexible, albeit difficult, mechanism to control +things with precision (*note Customization Options::). + + This section is of interest only to users who are prepared to +maintain their own local tweaks and who are willing to deal with any +possible incompatibilities between versioned releases of the themes. As +such, they are labeled as “do-it-yourself” or “DIY”. + +* Menu: + +* More accurate colors in terminal emulators:: +* Range of color with terminal emulators:: +* Visualize the active Modus theme's palette:: +* Per-theme customization settings:: +* Case-by-case face specs using the themes' palette:: +* Face specs at scale using the themes' palette:: +* Remap face with local value:: +* Cycle through arbitrary colors:: +* Override colors:: +* Override color saturation:: +* Override colors through blending:: +* Override colors completely:: +* Font configurations for Org and others:: +* Configure bold and italic faces:: +* Custom Org todo keyword and priority faces:: +* Custom Org emphasis faces:: +* Update Org block delimiter fontification:: +* Measure color contrast:: +* Load theme depending on time of day:: +* Backdrop for pdf-tools:: +* Decrease mode line height:: +* Toggle themes without reloading them:: +* A theme-agnostic hook for theme loading:: +* Diffs with only the foreground:: +* Ediff without diff color-coding:: +* Near-monochrome syntax highlighting:: +* Custom hl-todo colors:: +* Add support for solaire-mode:: + + +File: modus-themes.info, Node: More accurate colors in terminal emulators, Next: Range of color with terminal emulators, Up: Advanced customization + +5.1 More accurate colors in terminal emulators +============================================== + +[ This is based on partial information. Please help verify and/or +expand these findings. ] + + The graphical version of Emacs can reproduce color values accurately. +Whereas things get more tricky when Emacs is used in a terminal +emulator, because the terminals’ own capabilities determine the number +of colors that may be displayed: the Modus themes don’t look as good in +that case. + + There is, however, a way to instruct supported terminal emulators to +use more accurate colors. In a shell prompt type ‘toe -a | grep direct’ +to get a list of relevant terminfo entries. There should be items such +as ‘xterm-direct’, ‘alacritty-direct’, ‘kitty-direct’. Once you find +the one that corresponds to your terminal, call Emacs with an +environment variable like ‘TERM=xterm-direct’. Example that can be +adapted to shell aliases: + + TERM=xterm-direct emacsclient -nw + + Another example that can be bound to a key: + + TERM=xterm-direct uxterm -e emacsclient -nw + + +File: modus-themes.info, Node: Range of color with terminal emulators, Next: Visualize the active Modus theme's palette, Prev: More accurate colors in terminal emulators, Up: Advanced customization + +5.2 Range of color with terminal emulators +========================================== + +[ This is based on partial information. Please help verify and/or +expand these findings. ] + + When Emacs runs in a non-windowed session its color reproduction +capacity is framed or determined by the underlying terminal emulator +(*note More accurate colors in terminal emulators::). Emacs cannot +produce a color that lies outside the range of what the terminal’s color +palette renders possible. + + This is immediately noticeable when the terminal’s first 16 codes do +not include a pure black value for the ‘termcol0’ entry and a pure white +for ‘termcol15’. Emacs cannot set the correct background (white for +‘modus-operandi’; black for ‘modus-vivendi’) or foreground (inverse of +the background). It thus falls back to the closest approximation, which +seldom is appropriate for the purposes of the Modus themes. + + In such a case, the user is expected to update their terminal’s color +palette such as by adapting these resources: + + ! Theme: modus-operandi + ! Description: XTerm port of modus-operandi (Modus themes for GNU Emacs) + ! Author: Protesilaos Stavrou, + xterm*background: #ffffff + xterm*foreground: #000000 + xterm*color0: #000000 + xterm*color1: #a60000 + xterm*color2: #005e00 + xterm*color3: #813e00 + xterm*color4: #0031a9 + xterm*color5: #721045 + xterm*color6: #00538b + xterm*color7: #bfbfbf + xterm*color8: #595959 + xterm*color9: #972500 + xterm*color10: #315b00 + xterm*color11: #70480f + xterm*color12: #2544bb + xterm*color13: #5317ac + xterm*color14: #005a5f + xterm*color15: #ffffff + + ! Theme: modus-vivendi + ! Description: XTerm port of modus-vivendi (Modus themes for GNU Emacs) + ! Author: Protesilaos Stavrou, + xterm*background: #000000 + xterm*foreground: #ffffff + xterm*color0: #000000 + xterm*color1: #ff8059 + xterm*color2: #44bc44 + xterm*color3: #d0bc00 + xterm*color4: #2fafff + xterm*color5: #feacd0 + xterm*color6: #00d3d0 + xterm*color7: #bfbfbf + xterm*color8: #595959 + xterm*color9: #ef8b50 + xterm*color10: #70b900 + xterm*color11: #c0c530 + xterm*color12: #79a8ff + xterm*color13: #b6a0ff + xterm*color14: #6ae4b9 + xterm*color15: #ffffff + + +File: modus-themes.info, Node: Visualize the active Modus theme's palette, Next: Per-theme customization settings, Prev: Range of color with terminal emulators, Up: Advanced customization + +5.3 Visualize the active Modus theme’s palette +============================================== + +The command ‘modus-themes-list-colors’ prompts for a choice between +‘modus-operandi’ and ‘modus-vivendi’ to produce a help buffer that shows +a preview of each variable in the given theme’s color palette. The +command ‘modus-themes-list-colors-current’ skips the prompt, using the +current Modus theme. + + Each row shows a foreground and background coloration using the +underlying value it references. For example a line with ‘#a60000’ (a +shade of red) will show red text followed by a stripe with that same +color as a backdrop. + + The name of the buffer describes the given Modus theme. It is thus +called ‘*modus-operandi-list-colors*’ or ‘*modus-vivendi-list-colors*’. + + +File: modus-themes.info, Node: Per-theme customization settings, Next: Case-by-case face specs using the themes' palette, Prev: Visualize the active Modus theme's palette, Up: Advanced customization + +5.4 Per-theme customization settings +==================================== + +If you prefer to maintain different customization options between the +two themes, it is best you write your own functions that first set those +options and then load the relevant theme. The following code does +exactly that by simply differentiating the two themes on the choice of +bold constructs in code syntax (enabled for one, disabled for the +other). + + (defun my-demo-modus-operandi () + (interactive) + (setq modus-themes-bold-constructs t) ; ENABLE bold + (modus-themes-load-operandi)) + + (defun my-demo-modus-vivendi () + (interactive) + (setq modus-themes-bold-constructs nil) ; DISABLE bold + (modus-themes-load-vivendi)) + + (defun my-demo-modus-themes-toggle () + (if (eq (car custom-enabled-themes) 'modus-operandi) + (my-demo-modus-vivendi) + (my-demo-modus-operandi))) + + Then assign ‘my-demo-modus-themes-toggle’ to a key instead of the +equivalent the themes provide. + + For a more elaborate design, it is better to inspect the source code +of ‘modus-themes-toggle’ and relevant functions. + + +File: modus-themes.info, Node: Case-by-case face specs using the themes' palette, Next: Face specs at scale using the themes' palette, Prev: Per-theme customization settings, Up: Advanced customization + +5.5 Case-by-case face specs using the themes’ palette +===================================================== + +This section is about tweaking individual faces. If you plan to do +things at scale, consult the next section: *note Set multiple faces: +Face specs at scale using the themes' palette. + + We already covered in previous sections how to toggle between the +themes and how to configure options prior to loading. We also explained +that some of the functions made available to users will fire up a hook +that can be used to pass tweaks in the post-theme-load phase. + + Now assume you wish to change a single face, say, the ‘cursor’. And +you would like to get the standard “blue” color value of the active +Modus theme, whether it is Modus Operandi or Modus Vivendi. To do that, +you can use the ‘modus-themes-color’ function. It accepts a symbol that +is associated with a color in ‘modus-themes-operandi-colors’ and +‘modus-themes-vivendi-colors’. Like this: + + (modus-themes-color 'blue) + + The function always extracts the color value of the active Modus +theme. + + (progn + (load-theme 'modus-operandi t) + (modus-themes-color 'blue)) ; "#0031a9" for `modus-operandi' + + (progn + (load-theme 'modus-vivendi t) + (modus-themes-color 'blue)) ; "#2fafff" for `modus-vivendi' + + Do ‘C-h v’ on the aforementioned variables to check all the available +symbols that can be passed to this function. Or simply invoke the +command ‘modus-themes-list-colors’ to produce a buffer with a preview of +each entry in the palette. + + *note Visualize the active Modus theme's palette::. + + With that granted, let us expand the example to actually change the +‘cursor’ face’s background property. We employ the built-in function of +‘set-face-attribute’: + + (set-face-attribute 'cursor nil :background (modus-themes-color 'blue)) + + If you evaluate this form, your cursor will become blue. But if you +change themes, such as with ‘modus-themes-toggle’, your edits will be +lost, because the newly loaded theme will override the ‘:background’ +attribute you had assigned to that face. + + For such changes to persist, we need to make them after loading the +theme. So we rely on ‘modus-themes-after-load-theme-hook’, which gets +called from ‘modus-themes-load-operandi’, ‘modus-themes-load-vivendi’, +as well as the command ‘modus-themes-toggle’. Here is a sample function +that tweaks two faces and then gets added to the hook: + + (defun my-modus-themes-custom-faces () + (set-face-attribute 'cursor nil :background (modus-themes-color 'blue)) + (set-face-attribute 'font-lock-type-face nil :foreground (modus-themes-color 'magenta-alt))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + *note A theme-agnostic hook for theme loading::. + + Using this principle, it is possible to override the styles of faces +without having to find color values for each case. + + Another application is to control the precise weight for bold +constructs. This is particularly useful if your typeface has several +variants such as “heavy”, “extrabold”, “semibold”. All you have to do +is edit the ‘bold’ face. For example: + + (set-face-attribute 'bold nil :weight 'semibold) + + Remember to use the custom function and hook combo we demonstrated +above. Because the themes do not hard-wire a specific weight, this +simple form is enough to change the weight of all bold constructs +throughout the interface. + + Finally, there are cases where you want to tweak colors though wish +to apply different ones to each theme, say, a blue hue for Modus +Operandi and a shade of red for Modus Vivendi. To this end, we provide +‘modus-themes-color-alts’ as a convenience function to save you from the +trouble of writing separate wrappers for each theme. It still returns a +single value by querying either of ‘modus-themes-operandi-colors’ and +‘modus-themes-vivendi-colors’, only here you pass the two keys you want, +first for ‘modus-operandi’ then ‘modus-vivendi’. + + Take the previous example with the ‘cursor’ face: + + ;; Blue for `modus-operandi' and red for `modus-vivendi' + (set-face-attribute 'cursor nil :background (modus-themes-color-alts 'blue 'red)) + + +File: modus-themes.info, Node: Face specs at scale using the themes' palette, Next: Remap face with local value, Prev: Case-by-case face specs using the themes' palette, Up: Advanced customization + +5.6 Face specs at scale using the themes’ palette +================================================= + +The examples here are for large scale operations. For simple, one-off +tweaks, you may prefer the approach documented in the previous section +(*note Case-by-case face specs using the themes' palette::). + + The ‘modus-themes-with-colors’ macro lets you retrieve multiple color +values by employing the backquote/backtick and comma notation. The +values are stored in the alists ‘modus-themes-operandi-colors’ and +‘modus-themes-vivendi-colors’, while the macro always queries that of +the active Modus theme (preview the current palette with the command +‘modus-themes-list-colors’). + + *note Visualize the active Modus theme's palette::. + + Here is an abstract example that just returns a list of color values +while ‘modus-operandi’ is enabled: + + (modus-themes-with-colors + (list fg-main + blue-faint + magenta + magenta-alt-other + cyan-alt-other + fg-special-cold + blue-alt + magenta-faint + cyan + fg-main + green-faint + red-alt-faint + blue-alt-faint + fg-special-warm + cyan-alt + blue)) + ;; => + ;; ("#000000" "#002f88" "#721045" "#5317ac" + ;; "#005a5f" "#093060" "#2544bb" "#752f50" + ;; "#00538b" "#000000" "#104410" "#702f00" + ;; "#003f78" "#5d3026" "#30517f" "#0031a9") + + Getting a list of colors may have its applications, though what you +are most likely interested in is how to use those variables to configure +several faces at once. To do so we can rely on the built-in +‘custom-set-faces’ function, which sets face specifications for the +special ‘user’ theme. That “theme” gets applied on top of regular +themes like ‘modus-operandi’ and ‘modus-vivendi’. + + This is how it works: + + (modus-themes-with-colors + (custom-set-faces + `(cursor ((,class :background ,blue))) + `(mode-line ((,class :background ,yellow-nuanced-bg + :foreground ,yellow-nuanced-fg))) + `(mode-line-inactive ((,class :background ,blue-nuanced-bg + :foreground ,blue-nuanced-fg))))) + + The above snippet will immediately refashion the faces it names once +it is evaluated. However, if you switch between the Modus themes, say, +from ‘modus-operandi’ to ‘modus-vivendi’, the colors will not get +updated to match those of the new theme. To make things work across the +themes, we need to employ the same technique we discussed in the +previous section, namely, to pass our changes at the post-theme-load +phase via a hook. + + The themes provide the ‘modus-themes-after-load-theme-hook’, which +gets called from ‘modus-themes-load-operandi’, +‘modus-themes-load-vivendi’, as well as the command +‘modus-themes-toggle’. With this knowledge, you can wrap the macro in a +function and then assign that function to the hook. Thus: + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + `(cursor ((,class :background ,blue))) + `(mode-line ((,class :background ,yellow-nuanced-bg + :foreground ,yellow-nuanced-fg))) + `(mode-line-inactive ((,class :background ,blue-nuanced-bg + :foreground ,blue-nuanced-fg)))))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + *note A theme-agnostic hook for theme loading::. + + To discover the faces defined by all loaded libraries, you may do +‘M-x list-faces-display’. Be warned that when you ‘:inherit’ a face you +are introducing an implicit dependency, so try to avoid doing so for +libraries other than the built-in ‘faces.el’ (or at least understand +that things may break if you inherit from a yet-to-be-loaded face). + + Also bear in mind that these examples are meant to work with the +Modus themes. If you are cycling between multiple themes you may +encounter unforeseen issues, such as the colors of the Modus themes +being applied to a non-Modus item. + + Finally, note that you can still use other functions where those make +sense. For example, the ‘modus-themes-color-alts’ that was discussed in +the previous section. Adapt the above example like this: + + ... + (modus-themes-with-colors + (custom-set-faces + `(cursor ((,class :background ,(modus-themes-color-alts 'blue 'green)))) + ...)) + + +File: modus-themes.info, Node: Remap face with local value, Next: Cycle through arbitrary colors, Prev: Face specs at scale using the themes' palette, Up: Advanced customization + +5.7 Remap face with local value +=============================== + +There are cases where we need to change the buffer-local attributes of a +face. This might be because we have our own minor mode that re-uses a +face for a particular purpose, such as a line selection tool that +activates ‘hl-line-mode’, but we wish to keep it distinct from other +buffers. This is where ‘face-remap-add-relative’ can be applied and may +be combined with ‘modus-themes-with-colors’ to deliver consistent +results. + + *note Face specs at scale using the themes' palette::. + + In this example we will write a simple interactive function that +adjusts the background color of the ‘region’ face. This is the sample +code: + + (defvar my-rainbow-region-colors + (modus-themes-with-colors + `((red . ,red-subtle-bg) + (green . ,green-subtle-bg) + (yellow . ,yellow-subtle-bg) + (blue . ,blue-subtle-bg) + (magenta . ,magenta-subtle-bg) + (cyan . ,cyan-subtle-bg))) + "Sample list of color values for `my-rainbow-region'.") + + (defun my-rainbow-region (color) + "Remap buffer-local attribute of `region' using COLOR." + (interactive + (list + (completing-read "Pick a color: " my-rainbow-region-colors))) + (face-remap-add-relative + 'region + `( :background ,(alist-get (intern color) my-rainbow-region-colors) + :foreground ,(face-attribute 'default :foreground)))) + + When ‘my-rainbow-region’ is called interactively, it prompts for a +color to use. The list of candidates is drawn from the car of each +association in ‘my-rainbow-region-colors’ (so “red”, “green”, etc.). + + To extend this principle, we may write wrapper functions that pass a +color directly. Those can be useful in tandem with hooks. Consider +this example: + + (defun my-rainbow-region-magenta () + (my-rainbow-region 'magenta)) + + (add-hook 'diff-mode-hook #'my-rainbow-region-magenta) + + Whenever we enter a ‘diff-mode’ buffer, we now get a magenta-colored +region. + + Perhaps you may wish to generalize those findings in to a set of +functions that also accept an arbitrary face. We shall leave the +experimentation up to you. + + +File: modus-themes.info, Node: Cycle through arbitrary colors, Next: Override colors, Prev: Remap face with local value, Up: Advanced customization + +5.8 Cycle through arbitrary colors +================================== + +Users may opt to customize individual faces of the themes to accommodate +their particular needs. One such case is with the color intensity of +comments, specifically the foreground of ‘font-lock-comment-face’. The +Modus themes set that to a readable value, in accordance with their +accessibility objective, though users may prefer to lower the overall +contrast on an on-demand basis. + + One way to achieve this is to design a command that cycles through +three distinct levels of intensity, though the following can be adapted +to any kind of cyclic behavior, such as to switch between red, green, +and blue. + + In the following example, we employ the ‘modus-themes-color’ function +which reads a symbol that represents an entry in the active theme’s +color palette (*note Case-by-case face specs using the themes' +palette::). Those are stored in ‘my-modus-themes-comment-colors’. + + (defvar my-modus-themes-comment-colors + ;; We are abusing the palette here, as those colors have their own + ;; purpose in the palette, so please ignore the semantics of their + ;; names. + '((low . bg-region) + (medium . bg-tab-inactive-alt) + (high . fg-alt)) + "Alist of levels of intensity mapped to color palette entries. + The entries are found in `modus-themes-operandi-colors' or + `modus-themes-vivendi-colors'.") + + (defvar my-modus-themes--adjust-comment-color-state nil + "The cyclic state of `my-modus-themes-adjust-comment-color'. + For internal use.") + + (defun my-modus-themes--comment-foreground (degree state) + "Set `font-lock-comment-face' foreground. + Use `my-modus-themes-comment-colors' to extract the color value + for each level of intensity. + + This is complementary to `my-modus-themes-adjust-comment-color'." + (let ((palette-colors my-modus-themes-comment-colors)) + (set-face-foreground + 'font-lock-comment-face + (modus-themes-color (alist-get degree palette-colors))) + (setq my-modus-themes--adjust-comment-color-state state) + (message "Comments are set to %s contrast" degree))) + + (defun my-modus-themes-adjust-comment-color () + "Cycle through levels of intensity for comments. + The levels are determined by `my-modus-themes-comment-colors'." + (interactive) + (pcase my-modus-themes--adjust-comment-color-state + ('nil + (my-modus-themes--comment-foreground 'low 1)) + (1 + (my-modus-themes--comment-foreground 'medium 2)) + (_ + (my-modus-themes--comment-foreground 'high nil)))) + + With the above, ‘M-x my-modus-themes-adjust-comment-color’ will cycle +through the three levels of intensity that have been specified. + + Another approach is to not read from the active theme’s color palette +and instead provide explicit color values, either in hexadecimal RGB +notation (like ‘#123456’) or as the names that are displayed in the +output of ‘M-x list-colors-display’. In this case, the alist with the +colors will have to account for the active theme, so as to set the +appropriate colors. While this introduces a bit more complexity, it +ultimately offers greater flexibility on the choice of colors for such a +niche functionality (so there is no need to abuse the palette of the +active Modus theme): + + (defvar my-modus-themes-comment-colors + '((light . ((low . "gray75") + (medium . "gray50") + (high . "#505050"))) ; the default for `modus-operandi' + + (dark . ((low . "gray25") + (medium . "gray50") + (high . "#a8a8a8")))) ; the default for `modus-vivendi' + "Alist of levels of intensity mapped to color values. + For such colors, consult the command `list-colors-display'. Pass + the name of a color or its hex value.") + + (defvar my-modus-themes--adjust-comment-color-state nil + "The cyclic state of `my-modus-themes-adjust-comment-color'. + For internal use.") + + (defun my-modus-themes--comment-foreground (degree state) + "Set `font-lock-comment-face' foreground. + Use `my-modus-themes-comment-colors' to extract the color value + for each level of intensity. + + This is complementary to `my-modus-themes-adjust-comment-color'." + (let* ((colors my-modus-themes-comment-colors) + (levels (pcase (car custom-enabled-themes) + ('modus-operandi (alist-get 'light colors)) + ('modus-vivendi (alist-get 'dark colors))))) + (set-face-foreground + 'font-lock-comment-face + (alist-get degree levels)) + (setq my-modus-themes--adjust-comment-color-state state) + (message "Comments are set to %s contrast" degree))) + + (defun my-modus-themes-adjust-comment-color () + "Cycle through levels of intensity for comments. + The levels are determined by `my-modus-themes-comment-colors'." + (interactive) + (pcase my-modus-themes--adjust-comment-color-state + ('nil + (my-modus-themes--comment-foreground 'low 1)) + (1 + (my-modus-themes--comment-foreground 'medium 2)) + (_ + (my-modus-themes--comment-foreground 'high nil)))) + + The effect of the above configurations on ‘font-lock-comment-face’ is +global. To make it buffer-local, one must tweak the code to employ the +function ‘face-remap-add-relative’ (*note Remap face with local +value::). + + So this form in ‘my-modus-themes--comment-foreground’: + + ;; example 1 + (... + (set-face-foreground + 'font-lock-comment-face + (modus-themes-color (alist-get degree palette-colors))) + ...) + + ;; example 2 + (... + (set-face-foreground + 'font-lock-comment-face + (alist-get degree levels)) + ...) + + Must become this: + + ;; example 1 + (... + (face-remap-add-relative + 'font-lock-comment-face + `(:foreground ,(modus-themes-color (alist-get degree palette-colors)))) + ...) + + ;; example 2 + (... + (face-remap-add-relative + 'font-lock-comment-face + `(:foreground ,(alist-get degree levels))) + ...) + + +File: modus-themes.info, Node: Override colors, Next: Override color saturation, Prev: Cycle through arbitrary colors, Up: Advanced customization + +5.9 Override colors +=================== + +The themes provide a mechanism for overriding their color values. This +is controlled by the variables ‘modus-themes-operandi-color-overrides’ +and ‘modus-themes-vivendi-color-overrides’, which are alists that should +mirror a subset of the associations in ‘modus-themes-operandi-colors’ +and ‘modus-themes-vivendi-colors’ respectively. As with all +customizations, overriding must be done before loading the affected +theme. + + *note Visualize the active Modus theme's palette::. + + Let us approach the present topic one step at a time. Here is a +simplified excerpt of the default palette for Modus Operandi with some +basic background values that apply to buffers and the mode line +(remember to inspect the actual value to find out all the associations +that can be overridden): + + (defconst modus-themes-operandi-colors + '((bg-main . "#ffffff") + (bg-dim . "#f8f8f8") + (bg-alt . "#f0f0f0") + (bg-active . "#d7d7d7") + (bg-inactive . "#efefef"))) + + As one can tell, we bind a key to a hexadecimal RGB color value. Now +say we wish to override those specific values and have our changes +propagate to all faces that use those keys. We could write something +like this, which adds a subtle ochre tint: + + (setq modus-themes-operandi-color-overrides + '((bg-main . "#fefcf4") + (bg-dim . "#faf6ef") + (bg-alt . "#f7efe5") + (bg-active . "#e8dfd1") + (bg-inactive . "#f6ece5"))) + + Once this is evaluated, any subsequent loading of ‘modus-operandi’ +will use those values instead of the defaults. No further intervention +is required. + + To reset the changes, we apply this and reload the theme: + + (setq modus-themes-operandi-color-overrides nil) + + Users who wish to leverage such a mechanism can opt to implement it +on-demand by means of a global minor mode. The following snippet covers +both themes and expands to some more assosiations in the palette: + + (define-minor-mode my-modus-themes-tinted + "Tweak some Modus themes colors." + :init-value nil + :global t + (if my-modus-themes-tinted + (setq modus-themes-operandi-color-overrides + '((bg-main . "#fefcf4") + (bg-dim . "#faf6ef") + (bg-alt . "#f7efe5") + (bg-hl-line . "#f4f0e3") + (bg-active . "#e8dfd1") + (bg-inactive . "#f6ece5") + (bg-region . "#c6bab1") + (bg-header . "#ede3e0") + (bg-tab-active . "#fdf6eb") + (bg-tab-inactive . "#c8bab8")) + modus-themes-vivendi-color-overrides + '((bg-main . "#100b17") + (bg-dim . "#161129") + (bg-alt . "#181732") + (bg-hl-line . "#191628") + (bg-active . "#282e46") + (bg-inactive . "#1a1e39") + (bg-region . "#393a53") + (bg-header . "#202037") + (bg-tab-active . "#120f18") + (bg-tab-inactive . "#3a3a5a"))) + (setq modus-themes-operandi-color-overrides nil + modus-themes-vivendi-color-overrides nil))) + + A more neutral style for ‘modus-themes-operandi-color-overrides’ can +look like this: + + '((bg-main . "#f7f7f7") + (bg-dim . "#f2f2f2") + (bg-alt . "#e8e8e8") + (bg-hl-line . "#eaeaef") + (bg-active . "#e0e0e0") + (bg-inactive . "#e6e6e6") + (bg-region . "#b5b5b5") + (bg-header . "#e4e4e4") + (bg-tab-active . "#f5f5f5") + (bg-tab-inactive . "#c0c0c0")) + + With those in place, one can use ‘M-x my-modus-themes-tinted’ and +then load the Modus theme of their choice. The new palette subset will +come into effect: subtle ochre tints (or shades of gray) for Modus +Operandi and night sky blue shades for Modus Vivendi. Switching between +the two themes, such as with ‘M-x modus-themes-toggle’ will also use the +overrides. + + Given that this is a user-level customization, one is free to +implement whatever color values they desire, even if the possible +combinations fall below the minimum 7:1 contrast ratio that governs the +design of the themes (the WCAG AAA legibility standard). Alternatively, +this can also be done programmatically (*note Override color +saturation::). + + The above are expanded into a fully fledged derivative elsewhere in +this document (*note Override colors completely::). + + For manual interventions it is advised to inspect the source code of +‘modus-themes-operandi-colors’ and ‘modus-themes-vivendi-colors’ for the +inline commentary: it explains what the intended use of each palette +subset is. + + Furthermore, users may benefit from the ‘modus-themes-contrast’ +function that we provide: *note test color combinations: Measure color +contrast. It measures the contrast ratio between two color values, so +it can help in overriding the palette (or a subset thereof) without +making the end result inaccessible. + + +File: modus-themes.info, Node: Override color saturation, Next: Override colors through blending, Prev: Override colors, Up: Advanced customization + +5.10 Override color saturation +============================== + +In the previous section we documented how one can override color values +manually (*note Override colors::). Here we use a programmatic approach +which leverages the built-in ‘color-saturate-name’ function to adjust +the saturation of all color values used by the active Modus theme. Our +goal is to prepare a counterpart of the active theme’s palette that +holds modified color values, adjusted for a percent change in +saturation. A positive number amplifies the effect, while a negative +one will move towards a grayscale spectrum. + + We start with a function that can be either called from Lisp or +invoked interactively. In the former scenario, we pass to it the rate +of change we want. While in the latter, a minibuffer prompt asks for a +number to apply the desired effect. In either case, we intend to assign +anew the value of ‘modus-themes-operandi-color-overrides’ (light theme) +and the same for ‘modus-themes-vivendi-color-overrides’ (dark theme). + + (defun my-modus-themes-saturate (percent) + "Saturate current Modus theme palette overrides by PERCENT." + (interactive + (list (read-number "Saturation by percent: "))) + (let* ((theme (modus-themes--current-theme)) + (palette (pcase theme + ('modus-operandi modus-themes-operandi-colors) + ('modus-vivendi modus-themes-vivendi-colors) + (_ (error "No Modus theme is active")))) + (overrides (pcase theme + ('modus-operandi 'modus-themes-operandi-color-overrides) + ('modus-vivendi 'modus-themes-vivendi-color-overrides) + (_ (error "No Modus theme is active"))))) + (let (name cons colors) + (dolist (cons palette) + (setq name (color-saturate-name (cdr cons) percent)) + (setq name (format "%s" name)) + (setq cons `(,(car cons) . ,name)) + (push cons colors)) + (set overrides colors)) + (pcase theme + ('modus-operandi (modus-themes-load-operandi)) + ('modus-vivendi (modus-themes-load-vivendi))))) + + ;; sample Elisp calls (or call `my-modus-themes-saturate' interactively) + (my-modus-themes-saturate 50) + (my-modus-themes-saturate -75) + + Using the above has an immediate effect, as it reloads the active +Modus theme. + + The ‘my-modus-themes-saturate’ function stores new color values in +the variables ‘modus-themes-operandi-color-overrides’ and +‘modus-themes-vivendi-color-overrides’, meaning that it undoes changes +implemented by the user on individual colors. To have both automatic +saturation adjustment across the board and retain per-case edits to the +palette, some tweaks to the above function are required. For example: + + (defvar my-modus-themes-vivendi-extra-color-overrides + '((fg-main . "#ead0c0") + (bg-main . "#050515")) + "My bespoke colors for `modus-vivendi'.") + + (defvar my-modus-themes-operandi-extra-color-overrides + '((fg-main . "#1a1a1a") + (bg-main . "#fefcf4")) + "My bespoke colors for `modus-operandi'.") + + (defun my-modus-themes-saturate (percent) + "Saturate current Modus theme palette overrides by PERCENT. + Preserve the color values stored in + `my-modus-themes-operandi-extra-color-overrides', + `my-modus-themes-vivendi-extra-color-overrides'." + (interactive + (list (read-number "Saturation by percent: "))) + (let* ((theme (modus-themes--current-theme)) + (palette (pcase theme + ('modus-operandi modus-themes-operandi-colors) + ('modus-vivendi modus-themes-vivendi-colors) + (_ (error "No Modus theme is active")))) + (overrides (pcase theme + ('modus-operandi 'modus-themes-operandi-color-overrides) + ('modus-vivendi 'modus-themes-vivendi-color-overrides) + (_ (error "No Modus theme is active")))) + (extra-overrides (pcase theme + ('modus-operandi my-modus-themes-operandi-extra-color-overrides) + ('modus-vivendi my-modus-themes-vivendi-extra-color-overrides) + (_ (error "No Modus theme is active"))))) + (let (name cons colors) + (dolist (cons palette) + (setq name (color-saturate-name (cdr cons) percent)) + (setq name (format "%s" name)) + (setq cons `(,(car cons) . ,name)) + (push cons colors)) + (set overrides (append extra-overrides colors))) + (pcase theme + ('modus-operandi (modus-themes-load-operandi)) + ('modus-vivendi (modus-themes-load-vivendi))))) + + To disable the effect, one must reset the aforementioned variables of +the themes to ‘nil’. Or specify a command for it, such as by taking +inspiration from the ‘modus-themes-toggle’ we already provide: + + (defun my-modus-themes-revert-overrides () + "Reset palette overrides and reload active Modus theme." + (interactive) + (setq modus-themes-operandi-color-overrides nil + modus-themes-vivendi-color-overrides nil) + (pcase (modus-themes--current-theme) + ('modus-operandi (modus-themes-load-operandi)) + ('modus-vivendi (modus-themes-load-vivendi)))) + + +File: modus-themes.info, Node: Override colors through blending, Next: Override colors completely, Prev: Override color saturation, Up: Advanced customization + +5.11 Override colors through blending +===================================== + +This is yet another method of overriding color values. + + *note Override colors::. + + *note Override color saturation::. + + Building on ideas and concepts from the previous sections, this +method blends the entire palette at once with the chosen colors. The +function ‘my-modus-themes-interpolate’ blends two colors, taking a value +from the themes and mixing it with a user-defined color to arrive at a +midpoint. This scales to all background and foreground colors with the +help of the ‘my-modus-themes-tint-palette’ function. + + (setq my-modus-operandi-bg-blend "#fbf1c7" + my-modus-operandi-fg-blend "#3a6084" + my-modus-vivendi-bg-blend "#3a4042" + my-modus-vivendi-fg-blend "#d7b765") + + ;; Adapted from the `kurecolor-interpolate' function of kurecolor.el + (defun my-modus-themes-interpolate (color1 color2) + (cl-destructuring-bind (r g b) + (mapcar #'(lambda (n) (* (/ n 2) 255.0)) + (cl-mapcar '+ (color-name-to-rgb color1) (color-name-to-rgb color2))) + (format "#%02X%02X%02X" r g b))) + + (defun my-modus-themes-tint-palette (palette bg-blend fg-blend) + "Modify Modus PALETTE programmatically and return a new palette. + Blend background colors with BG-BLEND and foreground colors with FG-BLEND." + (let (name cons colors) + (dolist (cons palette) + (let ((blend (if (string-match "bg" (symbol-name (car cons))) + bg-blend + fg-blend))) + (setq name (my-modus-themes-interpolate (cdr cons) blend))) + (setq name (format "%s" name)) + (setq cons `(,(car cons) . ,name)) + (push cons colors)) + colors)) + + (define-minor-mode modus-themes-tinted-mode + "Tweak some Modus themes colors." + :init-value nil + :global t + (if modus-themes-tinted-mode + (setq modus-themes-operandi-color-overrides + (my-modus-themes-tint-palette modus-themes-operandi-colors + my-modus-operandi-bg-blend + my-modus-operandi-fg-blend) + modus-themes-vivendi-color-overrides + (my-modus-themes-tint-palette modus-themes-vivendi-colors + my-modus-vivendi-bg-blend + my-modus-vivendi-fg-blend)) + (setq modus-themes-operandi-color-overrides nil + modus-themes-vivendi-color-overrides nil))) + + (modus-themes-tinted-mode 1) + + +File: modus-themes.info, Node: Override colors completely, Next: Font configurations for Org and others, Prev: Override colors through blending, Up: Advanced customization + +5.12 Override colors completely +=============================== + +Based on the ideas we have already covered in these sections, the +following code block provides a complete, bespoke pair of color palettes +which override the defaults. They are implemented as a minor mode, as +explained before (*note Override colors::). We call them “Summertime” +for convenience. + + ;; Read the relevant blog post: + ;; + (define-minor-mode modus-themes-summertime + "Refashion the Modus themes by overriding their colors. + + This is a complete technology demonstration to show how to + manually override the colors of the Modus themes. I have taken + good care of those overrides to make them work as a fully fledged + color scheme that is compatible with all user options of the + Modus themes. + + These overrides are usable by those who (i) like something more + fancy than the comparatively austere looks of the Modus themes, + and (ii) can cope with a lower contrast ratio. + + The overrides are set up as a minor mode, so that the user can + activate the effect on demand. Those who want to load the + overrides at all times can either add them directly to their + configuration or enable `modus-themes-summertime' BEFORE loading + either of the Modus themes (if the overrides are evaluated after + the theme, the theme must be reloaded). + + Remember that all changes to theme-related variables require a + reload of the theme to take effect (the Modus themes have lots of + user options, apart from those overrides). + + The `modus-themes-summertime' IS NOT an official extension to the + Modus themes and DOES NOT comply with its lofty accessibility + standards. It is included in the official manual as guidance for + those who want to make use of the color overriding facility we + provide." + :init-value nil + :global t + (if modus-themes-summertime + (setq modus-themes-operandi-color-overrides + '((bg-main . "#fff0f2") + (bg-dim . "#fbe6ef") + (bg-alt . "#f5dae6") + (bg-hl-line . "#fad8e3") + (bg-active . "#efcadf") + (bg-inactive . "#f3ddef") + (bg-active-accent . "#ffbbef") + (bg-region . "#dfc5d1") + (bg-region-accent . "#efbfef") + (bg-region-accent-subtle . "#ffd6ef") + (bg-header . "#edd3e0") + (bg-tab-active . "#ffeff2") + (bg-tab-inactive . "#f8d3ef") + (bg-tab-inactive-accent . "#ffd9f5") + (bg-tab-inactive-alt . "#e5c0d5") + (bg-tab-inactive-alt-accent . "#f3cce0") + (fg-main . "#543f78") + (fg-dim . "#5f476f") + (fg-alt . "#7f6f99") + (fg-unfocused . "#8f6f9f") + (fg-active . "#563068") + (fg-inactive . "#8a5698") + (fg-docstring . "#5f5fa7") + (fg-comment-yellow . "#a9534f") + (fg-escape-char-construct . "#8b207f") + (fg-escape-char-backslash . "#a06d00") + (bg-special-cold . "#d3e0f4") + (bg-special-faint-cold . "#e0efff") + (bg-special-mild . "#c4ede0") + (bg-special-faint-mild . "#e0f0ea") + (bg-special-warm . "#efd0c4") + (bg-special-faint-warm . "#ffe4da") + (bg-special-calm . "#f0d3ea") + (bg-special-faint-calm . "#fadff9") + (fg-special-cold . "#405fb8") + (fg-special-mild . "#407f74") + (fg-special-warm . "#9d6f4f") + (fg-special-calm . "#af509f") + (bg-completion . "#ffc5e5") + (bg-completion-subtle . "#f7cfef") + (red . "#ed2f44") + (red-alt . "#e0403d") + (red-alt-other . "#e04059") + (red-faint . "#ed4f44") + (red-alt-faint . "#e0603d") + (red-alt-other-faint . "#e06059") + (green . "#217a3c") + (green-alt . "#417a1c") + (green-alt-other . "#006f3c") + (green-faint . "#318a4c") + (green-alt-faint . "#518a2c") + (green-alt-other-faint . "#20885c") + (yellow . "#b06202") + (yellow-alt . "#a95642") + (yellow-alt-other . "#a06f42") + (yellow-faint . "#b07232") + (yellow-alt-faint . "#a96642") + (yellow-alt-other-faint . "#a08042") + (blue . "#275ccf") + (blue-alt . "#475cc0") + (blue-alt-other . "#3340ef") + (blue-faint . "#476ce0") + (blue-alt-faint . "#575ccf") + (blue-alt-other-faint . "#3f60d7") + (magenta . "#bf317f") + (magenta-alt . "#d033c0") + (magenta-alt-other . "#844fe4") + (magenta-faint . "#bf517f") + (magenta-alt-faint . "#d053c0") + (magenta-alt-other-faint . "#846fe4") + (cyan . "#007a9f") + (cyan-alt . "#3f709f") + (cyan-alt-other . "#107f7f") + (cyan-faint . "#108aaf") + (cyan-alt-faint . "#3f80af") + (cyan-alt-other-faint . "#3088af") + (red-active . "#cd2f44") + (green-active . "#116a6c") + (yellow-active . "#993602") + (blue-active . "#475ccf") + (magenta-active . "#7f2ccf") + (cyan-active . "#007a8f") + (red-nuanced-bg . "#ffdbd0") + (red-nuanced-fg . "#ed6f74") + (green-nuanced-bg . "#dcf0dd") + (green-nuanced-fg . "#3f9a4c") + (yellow-nuanced-bg . "#fff3aa") + (yellow-nuanced-fg . "#b47232") + (blue-nuanced-bg . "#e3e3ff") + (blue-nuanced-fg . "#201f6f") + (magenta-nuanced-bg . "#fdd0ff") + (magenta-nuanced-fg . "#c0527f") + (cyan-nuanced-bg . "#dbefff") + (cyan-nuanced-fg . "#0f3f60") + (bg-diff-heading . "#b7cfe0") + (fg-diff-heading . "#041645") + (bg-diff-added . "#d6f0d6") + (fg-diff-added . "#004520") + (bg-diff-changed . "#fcefcf") + (fg-diff-changed . "#524200") + (bg-diff-removed . "#ffe0ef") + (fg-diff-removed . "#891626") + (bg-diff-refine-added . "#84cfa4") + (fg-diff-refine-added . "#002a00") + (bg-diff-refine-changed . "#cccf8f") + (fg-diff-refine-changed . "#302010") + (bg-diff-refine-removed . "#da92b0") + (fg-diff-refine-removed . "#500010") + (bg-diff-focus-added . "#a6e5c6") + (fg-diff-focus-added . "#002c00") + (bg-diff-focus-changed . "#ecdfbf") + (fg-diff-focus-changed . "#392900") + (bg-diff-focus-removed . "#efbbcf") + (fg-diff-focus-removed . "#5a0010")) + modus-themes-vivendi-color-overrides + '((bg-main . "#25152a") + (bg-dim . "#2a1930") + (bg-alt . "#382443") + (bg-hl-line . "#332650") + (bg-active . "#463358") + (bg-inactive . "#2d1f3a") + (bg-active-accent . "#50308f") + (bg-region . "#5d4a67") + (bg-region-accent . "#60509f") + (bg-region-accent-subtle . "#3f285f") + (bg-header . "#3a2543") + (bg-tab-active . "#26162f") + (bg-tab-inactive . "#362647") + (bg-tab-inactive-accent . "#36265a") + (bg-tab-inactive-alt . "#3e2f5a") + (bg-tab-inactive-alt-accent . "#3e2f6f") + (fg-main . "#debfe0") + (fg-dim . "#d0b0da") + (fg-alt . "#ae85af") + (fg-unfocused . "#8e7f9f") + (fg-active . "#cfbfef") + (fg-inactive . "#b0a0c0") + (fg-docstring . "#c8d9f7") + (fg-comment-yellow . "#cf9a70") + (fg-escape-char-construct . "#ff75aa") + (fg-escape-char-backslash . "#dbab40") + (bg-special-cold . "#2a3f58") + (bg-special-faint-cold . "#1e283f") + (bg-special-mild . "#0f3f31") + (bg-special-faint-mild . "#0f281f") + (bg-special-warm . "#44331f") + (bg-special-faint-warm . "#372213") + (bg-special-calm . "#4a314f") + (bg-special-faint-calm . "#3a223f") + (fg-special-cold . "#c0b0ff") + (fg-special-mild . "#bfe0cf") + (fg-special-warm . "#edc0a6") + (fg-special-calm . "#ff9fdf") + (bg-completion . "#502d70") + (bg-completion-subtle . "#451d65") + (red . "#ff5f6f") + (red-alt . "#ff8f6d") + (red-alt-other . "#ff6f9d") + (red-faint . "#ffa0a0") + (red-alt-faint . "#f5aa80") + (red-alt-other-faint . "#ff9fbf") + (green . "#51ca5c") + (green-alt . "#71ca3c") + (green-alt-other . "#51ca9c") + (green-faint . "#78bf78") + (green-alt-faint . "#99b56f") + (green-alt-other-faint . "#88bf99") + (yellow . "#f0b262") + (yellow-alt . "#f0e242") + (yellow-alt-other . "#d0a272") + (yellow-faint . "#d2b580") + (yellow-alt-faint . "#cabf77") + (yellow-alt-other-faint . "#d0ba95") + (blue . "#778cff") + (blue-alt . "#8f90ff") + (blue-alt-other . "#8380ff") + (blue-faint . "#82b0ec") + (blue-alt-faint . "#a0acef") + (blue-alt-other-faint . "#80b2f0") + (magenta . "#ff70cf") + (magenta-alt . "#ff77f0") + (magenta-alt-other . "#ca7fff") + (magenta-faint . "#e0b2d6") + (magenta-alt-faint . "#ef9fe4") + (magenta-alt-other-faint . "#cfa6ff") + (cyan . "#30cacf") + (cyan-alt . "#60caff") + (cyan-alt-other . "#40b79f") + (cyan-faint . "#90c4ed") + (cyan-alt-faint . "#a0bfdf") + (cyan-alt-other-faint . "#a4d0bb") + (red-active . "#ff6059") + (green-active . "#64dc64") + (yellow-active . "#ffac80") + (blue-active . "#4fafff") + (magenta-active . "#cf88ff") + (cyan-active . "#50d3d0") + (red-nuanced-bg . "#440a1f") + (red-nuanced-fg . "#ffcccc") + (green-nuanced-bg . "#002904") + (green-nuanced-fg . "#b8e2b8") + (yellow-nuanced-bg . "#422000") + (yellow-nuanced-fg . "#dfdfb0") + (blue-nuanced-bg . "#1f1f5f") + (blue-nuanced-fg . "#bfd9ff") + (magenta-nuanced-bg . "#431641") + (magenta-nuanced-fg . "#e5cfef") + (cyan-nuanced-bg . "#042f49") + (cyan-nuanced-fg . "#a8e5e5") + (bg-diff-heading . "#304466") + (fg-diff-heading . "#dae7ff") + (bg-diff-added . "#0a383a") + (fg-diff-added . "#94ba94") + (bg-diff-changed . "#2a2000") + (fg-diff-changed . "#b0ba9f") + (bg-diff-removed . "#50163f") + (fg-diff-removed . "#c6adaa") + (bg-diff-refine-added . "#006a46") + (fg-diff-refine-added . "#e0f6e0") + (bg-diff-refine-changed . "#585800") + (fg-diff-refine-changed . "#ffffcc") + (bg-diff-refine-removed . "#952838") + (fg-diff-refine-removed . "#ffd9eb") + (bg-diff-focus-added . "#1d4c3f") + (fg-diff-focus-added . "#b4dfb4") + (bg-diff-focus-changed . "#424200") + (fg-diff-focus-changed . "#d0daaf") + (bg-diff-focus-removed . "#6f0f39") + (fg-diff-focus-removed . "#eebdba"))) + (setq modus-themes-operandi-color-overrides nil + modus-themes-vivendi-color-overrides nil))) + + +File: modus-themes.info, Node: Font configurations for Org and others, Next: Configure bold and italic faces, Prev: Override colors completely, Up: Advanced customization + +5.13 Font configurations for Org and others +=========================================== + +The themes are designed to optionally cope well with mixed font +configurations. This mostly concerns ‘org-mode’ and ‘markdown-mode’, +though expect to find it elsewhere like in ‘Info-mode’. + + *note Option for font mixing: Mixed fonts. + + In practice it means that the user can safely opt for a more +prose-friendly proportionately spaced typeface as their default, while +spacing-sensitive elements like tables and inline code always use a +monospaced font, by inheriting from the ‘fixed-pitch’ face. + + Users can try the built-in ‘M-x variable-pitch-mode’ to see the +effect in action. + + To make everything use your desired font families, you need to +configure the ‘variable-pitch’ (proportional spacing) and ‘fixed-pitch’ +(monospaced) faces respectively. It may also be convenient to set your +main typeface by configuring the ‘default’ face the same way. + + [ The ‘fontaine’ package on GNU ELPA (by the author of the +modus-themes) is designed to handle this case. ] + + Put something like this in your initialization file (also consider +reading the doc string of ‘set-face-attribute’): + + ;; Main typeface + (set-face-attribute 'default nil :family "DejaVu Sans Mono" :height 110) + + ;; Proportionately spaced typeface + (set-face-attribute 'variable-pitch nil :family "DejaVu Serif" :height 1.0) + + ;; Monospaced typeface + (set-face-attribute 'fixed-pitch nil :family "DejaVu Sans Mono" :height 1.5) + + Or employ the ‘face-attribute’ function to read an existing value, +such as if you want to make ‘fixed-pitch’ use the font family of the +‘default’ face: + + (set-face-attribute 'fixed-pitch nil :family (face-attribute 'default :family)) + + The next section shows how to make those work in a more elaborate +setup that is robust to changes between the Modus themes. + + *note Configure bold and italic faces::. + + Note the differences in the ‘:height’ property. The ‘default’ face +must specify an absolute value, which is the point size × 10. So if you +want to use a font at point size ‘11’, you set the height to ‘110’.(1) +Whereas every other face must either not specify a height or have a +value that is relative to the default, represented as a floating point. +If you use an integer, then that means an absolute height. This is of +paramount importance: it ensures that all fonts can scale gracefully +when using something like the ‘text-scale-adjust’ command which only +operates on the base font size (i.e. the ‘default’ face’s absolute +height). + + *note Note for EWW and Elfeed fonts: Note on SHR fonts. + + ---------- Footnotes ---------- + + (1) ‘:height’ values do not need to be rounded to multiples of ten: +the likes of ‘115’ are perfectly valid—some typefaces will change to +account for those finer increments. + + +File: modus-themes.info, Node: Configure bold and italic faces, Next: Custom Org todo keyword and priority faces, Prev: Font configurations for Org and others, Up: Advanced customization + +5.14 Configure bold and italic faces +==================================== + +The Modus themes do not hardcode a ‘:weight’ or ‘:slant’ attribute in +the thousands of faces they cover. Instead, they configure the generic +faces called ‘bold’ and ‘italic’ to use the appropriate styles and then +instruct all relevant faces that require emphasis to inherit from them. + + This practically means that users can change the particularities of +what it means for a construct to be bold/italic, by tweaking the ‘bold’ +and ‘italic’ faces. Cases where that can be useful include: + + • The default typeface does not have a variant with slanted glyphs + (e.g. Fira Mono/Code as of this writing on 2021-07-07), so the + user wants to add another family for the italics, such as Hack. + + • The typeface of choice provides a multitude of weights and the user + prefers the light one by default. To prevent the bold weight from + being too heavy compared to the light one, they opt to make ‘bold’ + use a semibold weight. + + • The typeface distinguishes between oblique and italic forms by + providing different font variants (the former are just slanted + versions of the upright forms, while the latter have distinguishing + features as well). In this case, the user wants to specify the + font that applies to the ‘italic’ face. + + To achieve those effects, one must first be sure that the fonts they +use have support for those features. It then is a matter of following +the instructions for all typeface tweaks. + + *note Font configurations for Org and others::. + + In this example, we set the default font family to Fira Code, while +we choose to render italics in the Hack typeface (obviously you need to +pick fonts that work well together): + + (set-face-attribute 'default nil :family "Fira Code" :height 110) + (set-face-attribute 'italic nil :family "Hack") + + And here we play with different weights, using Source Code Pro: + + (set-face-attribute 'default nil :family "Source Code Pro" :height 110 :weight 'light) + (set-face-attribute 'bold nil :weight 'semibold) + + To reset the font family, one can use this: + + (set-face-attribute 'italic nil :family 'unspecified) + + To ensure that the effects persist after switching between the Modus +themes (such as with ‘M-x modus-themes-toggle’), the user needs to write +their configurations to a function and pass it to the +‘modus-themes-after-load-theme-hook’. This is necessary because themes +set the styles of faces upon activation, overriding prior values where +conflicts occur between the previous and the current states (otherwise +changing themes would not be possible). + + *note A theme-agnostic hook for theme loading::. + + This is a minimal setup to preserve font configurations across theme +load phases. For a more permanent setup, it is better to rely on the +‘custom-set-faces’ function: ‘set-face-attribute’ works just fine, +though it probably is better suited for quick previews or for smaller +scale operations (‘custom-set-faces’ follows the format used in the +source code of the themes, which can make it easier to redefine faces in +bulk). + + ;; our generic function + (defun my-modes-themes-bold-italic-faces () + (set-face-attribute 'default nil :family "Source Code Pro" :height 110) + (set-face-attribute 'bold nil :weight 'semibold)) + + ;; or use this if you configure a lot of face and attributes and + ;; especially if you plan to use `modus-themes-with-colors', as shown + ;; elsewhere in the manual + (defun my-modes-themes-bold-italic-faces () + (custom-set-faces + '(default ((t :family "Source Code Pro" :height 110))) + '(bold ((t :weight semibold))))) + + ;; and here is the hook + (add-hook 'modus-themes-after-load-theme-hook #'my-modes-themes-bold-italic-faces) + + *note Face specs at scale using the themes' palette::. + + +File: modus-themes.info, Node: Custom Org todo keyword and priority faces, Next: Custom Org emphasis faces, Prev: Configure bold and italic faces, Up: Advanced customization + +5.15 Custom Org todo keyword and priority faces +=============================================== + +Users of ‘org-mode’ have the option to configure various keywords and +priority cookies to better match their workflow. User options are +‘org-todo-keyword-faces’ and ‘org-priority-faces’. + + As those are meant to be custom faces, it is futile to have the +themes guess what each user wants to use, which keywords to target, and +so on. Instead, we can provide guidelines on how to customize things to +one’s liking with the intent of retaining the overall aesthetic of the +themes. + + Please bear in mind that the end result of those is not controlled by +the active Modus theme but by how Org maps faces to its constructs. +Editing those while ‘org-mode’ is active requires re-initialization of +the mode with ‘M-x org-mode-restart’ for changes to take effect. + + Let us assume you wish to visually differentiate your keywords. You +have something like this: + + (setq org-todo-keywords + '((sequence "TODO(t)" "|" "DONE(D)" "CANCEL(C)") + (sequence "MEET(m)" "|" "MET(M)") + (sequence "STUDY(s)" "|" "STUDIED(S)") + (sequence "WRITE(w)" "|" "WROTE(W)"))) + + You could then use a variant of the following to inherit from a face +that uses the styles you want and also to preserve the properties +applied by the ‘org-todo’ face (in case there is a difference between +the two): + + (setq org-todo-keyword-faces + '(("MEET" . '(bold org-todo)) + ("STUDY" . '(warning org-todo)) + ("WRITE" . '(shadow org-todo)))) + + This will refashion the keywords you specify, while letting the other +items in ‘org-todo-keywords’ use their original styles (which are +defined in the ‘org-todo’ and ‘org-done’ faces). + + If you want back the defaults, try specifying just the ‘org-todo’ +face: + + (setq org-todo-keyword-faces + '(("MEET" . org-todo) + ("STUDY" . org-todo) + ("WRITE" . org-todo))) + + When you inherit from multiple faces, you need to quote the list as +shown further above. The order is significant: the first entry is +applied on top of the second, overriding any properties that are +explicitly set for both of them: any property that is not specified is +not overridden, so, for example, if ‘org-todo’ has a background and a +foreground, while ‘font-lock-type-face’ only has a foreground, the +merged face will include the background of the former and the foreground +of the latter. If you do not want to blend multiple faces, you do not +need a quoted list. A pattern of ‘keyword . face’ will suffice. + + Both approaches can be used simultaneously, as illustrated in this +configuration of the priority cookies: + + (setq org-priority-faces + '((?A . '(bold org-priority)) + (?B . org-priority) + (?C . '(shadow org-priority)))) + + To find all the faces that are loaded in your current Emacs session, +use ‘M-x list-faces-display’. Try ‘M-x describe-variable’ as well and +then specify the name of each of those Org variables demonstrated above. +Their documentation strings will offer you further guidance. + + Recall that the themes let you retrieve a color from their palette. +Do it if you plan to control face attributes. + + *note Custom face specs using the themes’ palette: Case-by-case face +specs using the themes' palette. + + *note Check color combinations: Measure color contrast. + + +File: modus-themes.info, Node: Custom Org emphasis faces, Next: Update Org block delimiter fontification, Prev: Custom Org todo keyword and priority faces, Up: Advanced customization + +5.16 Custom Org emphasis faces +============================== + +Org provides the user option ‘org-emphasis-alist’ which associates a +character with a face, list of faces, or face attributes. The default +specification of that variable looks like this: + + (setq org-emphasis-alist + '(("*" bold) + ("/" italic) + ("_" underline) + ("=" org-verbatim verbatim) + ("~" org-code verbatim) + ("+" (:strike-through t)))) + + With the exception of ‘org-verbatim’ and ‘org-code’ faces, everything +else uses the corresponding type of emphasis: a bold typographic weight, +or italicised, underlined, and struck through text. + + The best way for users to add some extra attributes, such as a +foreground color, is to define their own faces and assign them to the +given emphasis marker/character. + + This is a custom face that extends the standard ‘bold’ face with a +red foreground value (so it colorises the text in addition to the bold +weight): + + (defface my-org-emphasis-bold + '((default :inherit bold) + (((class color) (min-colors 88) (background light)) + :foreground "#a60000") + (((class color) (min-colors 88) (background dark)) + :foreground "#ff8059")) + "My bold emphasis for Org.") + + This face definition reads as follows: + + • Always inherit the ‘bold’ face (*note Configure bold and italic + faces::). + • For versions of Emacs that support at least 88 colors (graphical + Emacs, for example) and use a light background, apply the ‘#a60000’ + value. + • For the same kind of Emacs that has a dark background use the + ‘#ff8059’ color instead. + + Same principle for how to extend ‘italic’ and ‘underline’ with, for +example, green and yellow hues, respectively: + + (defface my-org-emphasis-italic + '((default :inherit italic) + (((class color) (min-colors 88) (background light)) + :foreground "#005e00") + (((class color) (min-colors 88) (background dark)) + :foreground "#44bc44")) + "My italic emphasis for Org.") + + (defface my-org-emphasis-underline + '((default :inherit underline) + (((class color) (min-colors 88) (background light)) + :foreground "#813e00") + (((class color) (min-colors 88) (background dark)) + :foreground "#d0bc00")) + "My underline emphasis for Org.") + + In the case of a strike-through effect, we have no generic face to +inherit from, so we can write it as follows to also change the +foreground to a more subtle gray: + + (defface my-org-emphasis-strike-through + '((default :strike-through t) + (((class color) (min-colors 88) (background light)) + :foreground "#505050") + (((class color) (min-colors 88) (background dark)) + :foreground "#a8a8a8")) + "My strike-through emphasis for Org.") + + Or we can just change the color of the line that strikes through the +text to, for example, a shade of red: + + (defface my-org-emphasis-strike-through + '((((class color) (min-colors 88) (background light)) + :strike-through "#972500") + (((class color) (min-colors 88) (background dark)) + :strike-through "#ef8b50")) + "My strike-through emphasis for Org.") + + It is possible to combine those effects: + + (defface my-org-emphasis-strike-through + '((((class color) (min-colors 88) (background light)) + :strike-through "#972500" :foreground "#505050") + (((class color) (min-colors 88) (background dark)) + :strike-through "#ef8b50" :foreground "#a8a8a8")) + "My strike-through emphasis for Org.") + + One may inspect the variables ‘modus-themes-operandi-colors’ and +‘modus-themes-vivendi-colors’ for possible color values. Or call the +command ‘modus-themes-list-colors’ to show a buffer that previews each +entry in the palette. + + *note Visualize the active Modus theme's palette::. + + Once we have defined the faces we need, we must update the +‘org-emphasis-alist’. Given that ‘org-verbatim’ and ‘org-code’ are +already styled by the themes, it probably is best not to edit them: + + (setq org-emphasis-alist + '(("*" my-org-emphasis-bold) + ("/" my-org-emphasis-italic) + ("_" my-org-emphasis-underline) + ("=" org-verbatim verbatim) + ("~" org-code verbatim) + ("+" my-org-emphasis-strike-through))) + + That’s it! For changes to take effect in already visited Org files, +invoke ‘M-x org-mode-restart’. + + +File: modus-themes.info, Node: Update Org block delimiter fontification, Next: Measure color contrast, Prev: Custom Org emphasis faces, Up: Advanced customization + +5.17 Update Org block delimiter fontification +============================================= + +As noted in the section about ‘modus-themes-org-blocks’, Org contains a +variable that determines whether the block’s begin and end lines are +extended to the edge of the window (*note Option for org-mode block +styles: Org mode blocks.). The variable is +‘org-fontify-whole-block-delimiter-line’. + + Users who change the style of Org blocks from time to time may prefer +to automatically update delimiter line fontification, such as with the +following setup: + + (defun my-modus-themes-org-fontify-block-delimiter-lines () + "Match `org-fontify-whole-block-delimiter-line' to theme style. + Run this function at the post theme load phase, such as with the + `modus-themes-after-load-theme-hook'." + (if (eq modus-themes-org-blocks 'gray-background) + (setq org-fontify-whole-block-delimiter-line t) + (setq org-fontify-whole-block-delimiter-line nil))) + + (add-hook 'modus-themes-after-load-theme-hook + #'my-modus-themes-org-fontify-block-delimiter-lines) + + Then ‘M-x org-mode-restart’ for changes to take effect, though manual +intervention can be circumvented by tweaking the function thus: + + (defun my-modus-themes-org-fontify-block-delimiter-lines () + "Match `org-fontify-whole-block-delimiter-line' to theme style. + Run this function at the post theme load phase, such as with the + `modus-themes-after-load-theme-hook'." + (if (eq modus-themes-org-blocks 'gray-background) + (setq org-fontify-whole-block-delimiter-line t) + (setq org-fontify-whole-block-delimiter-line nil)) + (when (derived-mode-p 'org-mode) + (font-lock-flush))) + + +File: modus-themes.info, Node: Measure color contrast, Next: Load theme depending on time of day, Prev: Update Org block delimiter fontification, Up: Advanced customization + +5.18 Measure color contrast +=========================== + +The themes provide the functions ‘modus-themes-wcag-formula’ and +‘modus-themes-contrast’. The former is a direct implementation of the +WCAG formula: . It +calculates the relative luminance of a color value that is expressed in +hexadecimal RGB notation. While the latter function is just a +convenient wrapper for comparing the relative luminance between two +colors. + + In practice, one needs to work only with ‘modus-themes-contrast’. It +accepts two color values and returns their contrast ratio. Values range +from 1 to 21 (lowest to highest). The themes are designed to always be +equal or higher than 7 for each combination of background and foreground +that they use (this is the WCAG AAA standard—the most demanding of its +kind). + + A couple of examples (rounded numbers): + + ;; Pure white with pure green + (modus-themes-contrast "#ffffff" "#00ff00") + ;; => 1.37 + ;; That is an outright inaccessible combo + + ;; Pure black with pure green + (modus-themes-contrast "#000000" "#00ff00") + ;; => 15.3 + ;; That is a highly accessible combo + + It does not matter which color value comes first. The ratio is +always the same. + + If one does not wish to read all the decimal points, it is possible +to try something like this: + + (format "%0.2f" (modus-themes-contrast "#000000" "#00ff00")) + + While it is fine to perform such calculations on a case-by-case +basis, it is preferable to implement formulas and tables for more +demanding tasks. Such instruments are provided by ‘org-mode’ or +‘orgtbl-mode’, both of which are built into Emacs. Below is such a +table that derives the contrast ratio of all colors in the first column +(pure red, green, blue) relative to the color specified in the first row +of the second column (pure white) and rounds the results: + + | | #ffffff | + |---------+---------| + | #ff0000 | 4.00 | + | #00ff00 | 1.37 | + | #0000ff | 8.59 | + #+tblfm: $2='(modus-themes-contrast $1 @1$2);%0.2f + + To measure color contrast one needs to start from a known value. +This typically is the background. The Modus themes define an expanded +palette in large part because certain colors are only meant to be used +in combination with some others. Consult the source code for the +minutia and relevant commentary. + + Such knowledge may prove valuable while attempting to override some +of the themes’ colors: *note Override colors::. + + +File: modus-themes.info, Node: Load theme depending on time of day, Next: Backdrop for pdf-tools, Prev: Measure color contrast, Up: Advanced customization + +5.19 Load theme depending on time of day +======================================== + +While we do provide ‘modus-themes-toggle’ to manually switch between the +themes, users may also set up their system to perform such a task +automatically at sunrise and sunset. + + This can be accomplished by specifying the coordinates of one’s +location using the built-in ‘solar.el’ and then configuring the +‘circadian’ package: + + (use-package solar ; built-in + :config + (setq calendar-latitude 35.17 + calendar-longitude 33.36)) + + (use-package circadian ; you need to install this + :ensure + :after solar + (setq circadian-themes '((:sunrise . modus-operandi) + (:sunset . modus-vivendi))) + (circadian-setup)) + + +File: modus-themes.info, Node: Backdrop for pdf-tools, Next: Decrease mode line height, Prev: Load theme depending on time of day, Up: Advanced customization + +5.20 Backdrop for pdf-tools +=========================== + +Most PDF files use a white background for their page, making it +impossible to discern the file’s boundaries in the buffer while using +the Modus Operandi theme. To introduce a distinction between the +buffer’s backdrop and the PDF page’s background, the former must be +rendered as some shade of gray. Ideally, ‘pdf-tools’ would provide a +face that the themes could support directly, though this does not seem +to be the case for the time being. We must thus employ the face +remapping technique that is documented elsewhere in this document to +change the buffer-local value of the ‘default’ face. + + *note Remap face with local value::. + + To remap the buffer’s backdrop, we start with a function like this +one: + + (defun my-pdf-tools-backdrop () + (face-remap-add-relative + 'default + `(:background ,(modus-themes-color 'bg-alt)))) + + (add-hook 'pdf-tools-enabled-hook #'my-pdf-tools-backdrop) + + The idea is to assign that function to a hook that gets called when +‘pdf-tools’ renders the document: ‘pdf-tools-enabled-hook’. This is +enough when you only use one theme. However it has the downside of +setting the background color value only at render time. In other words, +the face remapping function does not get evaluated anew whenever the +theme changes, such as upon invoking ‘M-x modus-themes-toggle’. + + To have our face remapping adapt gracefully while switching between +the Modus themes, we need to also account for the current theme and +control the activation of ‘pdf-view-midnight-minor-mode’. To which end +we arrive at something like the following, which builds on the above +example: + + (defun my-pdf-tools-backdrop () + (face-remap-add-relative + 'default + `(:background ,(modus-themes-color 'bg-alt)))) + + (defun my-pdf-tools-midnight-mode-toggle () + (when (derived-mode-p 'pdf-view-mode) + (if (eq (car custom-enabled-themes) 'modus-vivendi) + (pdf-view-midnight-minor-mode 1) + (pdf-view-midnight-minor-mode -1)) + (my-pdf-tools-backdrop))) + + (defun my-pdf-tools-themes-toggle () + (mapc + (lambda (buf) + (with-current-buffer buf + (my-pdf-tools-midnight-mode-toggle))) + (buffer-list))) + + (add-hook 'pdf-tools-enabled-hook #'my-pdf-tools-midnight-mode-toggle) + (add-hook 'modus-themes-after-load-theme-hook #'my-pdf-tools-themes-toggle) + + With those in place, PDFs have a distinct backdrop for their page, +while buffers with major-mode as ‘pdf-view-mode’ automatically switches +to dark mode when ‘modus-themes-toggle’ is called. + + +File: modus-themes.info, Node: Decrease mode line height, Next: Toggle themes without reloading them, Prev: Backdrop for pdf-tools, Up: Advanced customization + +5.21 Decrease mode line height +============================== + +By default, the mode line of the Modus themes is set to 1 pixel width +for its ‘:box’ attribute. In contrast, the mode line of stock Emacs is +-1 pixel. This small difference is considered necessary for the +purposes of accessibility as our out-of-the-box design has a prominent +color around the mode line (a border) to make its boundaries clear. +With a negative width the border and the text on the mode line can feel +a bit more difficult to read under certain scenaria. + + Furthermore, the user option ‘modus-themes-mode-line’ (*note Mode +line::) does not allow for such a negative value because there are many +edge cases that simply make for a counter-intuitive set of +possibilities, such as a ‘0’ value not being acceptable by the +underlying face infrastructure, and negative values greater than ‘-2’ +not being particularly usable. + + For these reasons, users who wish to decrease the overall height of +the mode line must handle things on their own by implementing the +methods for face customization documented herein. + + *note Basic face customization: Case-by-case face specs using the +themes' palette. + + One such method is to create a function that configures the desired +faces and hook it to ‘modus-themes-after-load-theme-hook’ so that it +persists while switching between the Modus themes with the command +‘modus-themes-toggle’. + + This one simply disables the box altogether, which will reduce the +height of the mode lines, but also remove their border: + + (defun my-modus-themes-custom-faces () + (set-face-attribute 'mode-line nil :box nil) + (set-face-attribute 'mode-line-inactive nil :box nil)) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + The above relies on the ‘set-face-attribute’ function, though users +who plan to re-use colors from the theme and do so at scale are better +off with the more streamlined combination of the +‘modus-themes-with-colors’ macro and ‘custom-set-faces’. + + *note Face customization at scale: Face specs at scale using the +themes' palette. + + As explained before in this document, this approach has a syntax that +is consistent with the source code of the themes, so it probably is +easier to re-use parts of the design. + + The following emulates the stock Emacs style, while still using the +colors of the Modus themes (whichever attribute is not explicitly stated +is inherited from the underlying theme): + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + `(mode-line ((,class :box (:line-width -1 :style released-button)))) + `(mode-line-inactive ((,class :box (:line-width -1 :color ,bg-region))))))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + And this one is like the out-of-the-box style of the Modus themes, +but with the -1 height instead of 1: + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + `(mode-line ((,class :box (:line-width -1 :color ,fg-alt)))) + `(mode-line-inactive ((,class :box (:line-width -1 :color ,bg-region))))))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + Finally, to also change the background color of the active mode line, +such as that it looks like the “accented” variant which is possible via +the user option ‘modus-themes-mode-line’, the ‘:background’ attribute +needs to be specified as well: + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + `(mode-line ((,class :box (:line-width -1 :color ,fg-alt) :background ,bg-active-accent))) + `(mode-line-inactive ((,class :box (:line-width -1 :color ,bg-region))))))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + +File: modus-themes.info, Node: Toggle themes without reloading them, Next: A theme-agnostic hook for theme loading, Prev: Decrease mode line height, Up: Advanced customization + +5.22 Toggle themes without reloading them +========================================= + +Users who have a stable setup and who only ever need to toggle between +the themes without triggering a full reload, are better off defining +their own command which calls ‘enable-theme’ instead of ‘load-theme’: + + (defun my-modus-themes-toggle () + "Toggle between `modus-operandi' and `modus-vivendi' themes. + This uses `enable-theme' instead of the standard method of + `load-theme'. The technicalities are covered in the Modus themes + manual." + (interactive) + (pcase (modus-themes--current-theme) + ('modus-operandi (progn (enable-theme 'modus-vivendi) + (disable-theme 'modus-operandi))) + ('modus-vivendi (progn (enable-theme 'modus-operandi) + (disable-theme 'modus-vivendi))) + (_ (error "No Modus theme is loaded; evaluate `modus-themes-load-themes' first")))) + + *note Differences between loading and enabling::. + + Recall that ‘modus-themes-toggle’ uses ‘load-theme’. + + +File: modus-themes.info, Node: A theme-agnostic hook for theme loading, Next: Diffs with only the foreground, Prev: Toggle themes without reloading them, Up: Advanced customization + +5.23 A theme-agnostic hook for theme loading +============================================ + +The themes are designed with the intent to be useful to Emacs users of +varying skill levels, from beginners to experts. This means that we try +to make things easier by not expecting anyone reading this document to +be proficient in Emacs Lisp or programming in general. + + Such a case is with the use of the +‘modus-themes-after-load-theme-hook’, which runs after +‘modus-themes-toggle’, ‘modus-themes-load-operandi’, or +‘modus-themes-load-vivendi’ is evaluated. We recommend using that hook +for advanced customizations, because (1) we know for sure that it is +available once the themes are loaded, and (2) anyone consulting this +manual, especially the sections on enabling and loading the themes, will +be in a good position to benefit from that hook. + + Advanced users who have a need to switch between the Modus themes and +other items will find that such a hook does not meet their requirements: +it only works with the Modus themes and only with the aforementioned +functions. + + A theme-agnostic setup can be configured thus: + + (defvar after-enable-theme-hook nil + "Normal hook run after enabling a theme.") + + (defun run-after-enable-theme-hook (&rest _args) + "Run `after-enable-theme-hook'." + (run-hooks 'after-enable-theme-hook)) + + (advice-add 'enable-theme :after #'run-after-enable-theme-hook) + + This creates the ‘after-enable-theme-hook’ and makes it run after +each call to ‘enable-theme’, which means that it will work for all +themes and also has the benefit that it does not depend on functions +such as ‘modus-themes-toggle’ and the others mentioned above. +‘enable-theme’ is called internally by ‘load-theme’, so the hook works +everywhere. + + Now this specific piece of Elisp may be simple for experienced users, +but it is not easy to read for newcomers, including the author of the +Modus themes for the first several months of their time as an Emacs +user. Hence our hesitation to recommend it as part of the standard +setup of the Modus themes (it is generally a good idea to understand +what the implications are of advising a function). + + +File: modus-themes.info, Node: Diffs with only the foreground, Next: Ediff without diff color-coding, Prev: A theme-agnostic hook for theme loading, Up: Advanced customization + +5.24 Diffs with only the foreground +=================================== + +Buffers that show differences between versions of a file or buffer, such +as in ‘diff-mode’ and ‘ediff’ always use color-coded background and +foreground combinations. + + *note Option for diff buffer looks: Diffs. + + User may, however, prefer a style that removes the color-coded +backgrounds from regular changes while keeping them for word-wise (aka +“refined”) changes—backgrounds for word-wise diffs are helpful in +context. To make this happen, one can use the +‘modus-themes-with-colors’ macro (*note Face specs at scale using the +themes' palette::): + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + `(modus-themes-diff-added ((,class :background unspecified :foreground ,green))) ; OR ,blue for deuteranopia + `(modus-themes-diff-changed ((,class :background unspecified :foreground ,yellow))) + `(modus-themes-diff-removed ((,class :background unspecified :foreground ,red))) + + `(modus-themes-diff-refine-added ((,class :background ,bg-diff-added :foreground ,fg-diff-added))) + ;; `(modus-themes-diff-refine-added ((,class :background ,bg-diff-added-deuteran :foreground ,fg-diff-added-deuteran))) + `(modus-themes-diff-refine-changed ((,class :background ,bg-diff-changed :foreground ,fg-diff-changed))) + `(modus-themes-diff-refine-removed ((,class :background ,bg-diff-removed :foreground ,fg-diff-removed))) + + `(modus-themes-diff-focus-added ((,class :background ,bg-dim :foreground ,green))) ; OR ,blue for deuteranopia + `(modus-themes-diff-focus-changed ((,class :background ,bg-dim :foreground ,yellow))) + `(modus-themes-diff-focus-removed ((,class :background ,bg-dim :foreground ,red))) + + `(modus-themes-diff-heading ((,class :background ,bg-alt :foreground ,fg-main))) + + `(diff-indicator-added ((,class :foreground ,green))) ; OR ,blue for deuteranopia + `(diff-indicator-changed ((,class :foreground ,yellow))) + `(diff-indicator-removed ((,class :foreground ,red))) + + `(magit-diff-added ((,class :background unspecified :foreground ,green-faint))) + `(magit-diff-changed ((,class :background unspecified :foreground ,yellow-faint))) + `(magit-diff-removed ((,class :background unspecified :foreground ,red-faint))) + `(magit-diff-context-highlight ((,class :background ,bg-dim :foreground ,fg-dim)))))) + + ;; This is so that the changes persist when switching between + ;; `modus-operandi' and `modus-vivendi'. + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + This used to be an optional style of ‘modus-themes-diffs’, but has +been removed since version ‘2.0.0’ to ensure that the accessibility +standard and aesthetic quality of the themes is not compromised. + + +File: modus-themes.info, Node: Ediff without diff color-coding, Next: Near-monochrome syntax highlighting, Prev: Diffs with only the foreground, Up: Advanced customization + +5.25 Ediff without diff color-coding +==================================== + +Ediff uses the same color-coding as ordinary diffs in ‘diff-mode’, +Magit, etc. (*note Option for diff buffer looks: Diffs.). This is +consistent with the principle of least surprise. + + Users may, however, prefer to treat Ediff differently on the premise +that it does not need any particular color-coding to show added or +removed lines/words: it does not use the ‘+’ or ‘-’ markers, after all. + + This can be achieved by customizing the Ediff faces with color +combinations that do not carry the same connotations as those of diffs. +Consider this example, which leverages the ‘modus-themes-with-colors’ +macro (*note Face specs at scale using the themes' palette::): + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + `(ediff-current-diff-A ((,class :inherit unspecified :background ,bg-special-faint-cold :foreground ,fg-special-cold))) + `(ediff-current-diff-B ((,class :inherit unspecified :background ,bg-special-faint-warm :foreground ,fg-special-warm))) + `(ediff-current-diff-C ((,class :inherit unspecified :background ,bg-special-faint-calm :foreground ,fg-special-calm))) + `(ediff-fine-diff-A ((,class :inherit unspecified :background ,bg-special-cold :foreground ,fg-special-cold))) + `(ediff-fine-diff-B ((,class :inherit unspecified :background ,bg-special-warm :foreground ,fg-special-warm))) + `(ediff-fine-diff-C ((,class :inherit unspecified :background ,bg-special-calm :foreground ,fg-special-calm)))))) + + ;; This is so that the changes persist when switching between + ;; `modus-operandi' and `modus-vivendi'. + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + Remove the ‘:foreground’ and its value to preserve the underlying +coloration. + + *note Visualize the active Modus theme's palette::. + + +File: modus-themes.info, Node: Near-monochrome syntax highlighting, Next: Custom hl-todo colors, Prev: Ediff without diff color-coding, Up: Advanced customization + +5.26 Near-monochrome syntax highlighting +======================================== + +While the Modus themes do provide a user option to control the overall +style of syntax highlighting in programming major modes, they do not +cover the possibility of a monochromatic or near-monochromatic design +(*note Option for syntax highlighting: Syntax styles.). This is due to +the multitude of preferences involved: one may like comments to be +styled with an accent value, another may want certain constructs to be +bold, a third may apply italics to doc strings but not comments... The +possibilities are virtually endless. As such, this sort of design is +best handled at the user level in accordance with the information +furnished elsewhere in this manual. + + *note Case-by-case face specs using the themes' palette::. + + *note Face specs at scale using the themes' palette::. + + The gist is that we want to override the font-lock faces. For our +changes to persist while switching between ‘modus-operandi’ and +‘modus-vivendi’ we wrap our face overrides in a function that we hook to +‘modus-themes-after-load-theme-hook’. + + Users who want to replicate the structure of the themes’ source code +are advised to use the examples with ‘custom-set-faces’. Those who +prefer a different approach can use the snippets which call +‘set-face-attribute’. Below are the code blocks. + + The following uses a yellow accent value for comments and green hues +for strings. Regexp grouping constructs have color values that work in +the context of a green string. All other elements use the main +foreground color, except warnings such as the ‘user-error’ function in +Elisp buffers which gets a subtle red tint (not to be confused with the +‘warning’ face which is used for genuine warnings). Furthermore, notice +the ‘modus-themes-bold’ and ‘modus-themes-slant’ which apply the +preference set in the user options ‘modus-themes-bold-constructs’ and +‘modus-themes-italic-constructs’, respectively. Users who do not want +this conditionally must replace these faces with ‘bold’ and ‘italic’ +respectively (or ‘unspecified’ to disable the effect altogether). + + ;; This is the hook. It will not be replicated across all code samples. + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-subtle-syntax) + + (defun my-modus-themes-subtle-syntax () + (modus-themes-with-colors + (custom-set-faces + `(font-lock-builtin-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-comment-delimiter-face ((,class :inherit font-lock-comment-face))) + `(font-lock-comment-face ((,class :inherit unspecified :foreground ,fg-comment-yellow))) + `(font-lock-constant-face ((,class :foreground unspecified))) + `(font-lock-doc-face ((,class :inherit modus-themes-slant :foreground ,fg-special-mild))) + `(font-lock-function-name-face ((,class :foreground unspecified))) + `(font-lock-keyword-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-negation-char-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-preprocessor-face ((,class :foreground unspecified))) + `(font-lock-regexp-grouping-backslash ((,class :inherit bold :foreground ,yellow))) + `(font-lock-regexp-grouping-construct ((,class :inherit bold :foreground ,blue-alt-other))) + `(font-lock-string-face ((,class :foreground ,green-alt-other))) + `(font-lock-type-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-variable-name-face ((,class :foreground unspecified))) + `(font-lock-warning-face ((,class :inherit modus-themes-bold :foreground ,red-nuanced-fg)))))) + + ;; Same as above with `set-face-attribute' instead of `custom-set-faces' + (defun my-modus-themes-subtle-syntax () + (modus-themes-with-colors + (set-face-attribute 'font-lock-builtin-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-comment-delimiter-face nil :inherit 'font-lock-comment-face) + (set-face-attribute 'font-lock-comment-face nil :inherit 'unspecified :foreground fg-comment-yellow) + (set-face-attribute 'font-lock-constant-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-doc-face nil :inherit 'modus-themes-slant :foreground fg-special-mild) + (set-face-attribute 'font-lock-function-name-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-keyword-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-negation-char-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-preprocessor-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-regexp-grouping-backslash nil :inherit 'bold :foreground yellow) + (set-face-attribute 'font-lock-regexp-grouping-construct nil :inherit 'bold :foreground blue-alt-other) + (set-face-attribute 'font-lock-string-face nil :foreground green-alt-other) + (set-face-attribute 'font-lock-type-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-variable-name-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-warning-face nil :inherit 'modus-themes-bold :foreground red-nuanced-fg))) + + The following sample is the same as above, except strings are blue +and comments are gray. Regexp constructs are adapted accordingly. + + (defun my-modus-themes-subtle-syntax () + (modus-themes-with-colors + (custom-set-faces + `(font-lock-builtin-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-comment-delimiter-face ((,class :inherit font-lock-comment-face))) + `(font-lock-comment-face ((,class :inherit unspecified :foreground ,fg-alt))) + `(font-lock-constant-face ((,class :foreground unspecified))) + `(font-lock-doc-face ((,class :inherit modus-themes-slant :foreground ,fg-docstring))) + `(font-lock-function-name-face ((,class :foreground unspecified))) + `(font-lock-keyword-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-negation-char-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-preprocessor-face ((,class :foreground unspecified))) + `(font-lock-regexp-grouping-backslash ((,class :inherit bold :foreground ,fg-escape-char-backslash))) + `(font-lock-regexp-grouping-construct ((,class :inherit bold :foreground ,fg-escape-char-construct))) + `(font-lock-string-face ((,class :foreground ,blue-alt))) + `(font-lock-type-face ((,class :inherit modus-themes-bold :foreground unspecified))) + `(font-lock-variable-name-face ((,class :foreground unspecified))) + `(font-lock-warning-face ((,class :inherit modus-themes-bold :foreground ,red-nuanced-fg)))))) + + ;; Same as above with `set-face-attribute' instead of `custom-set-faces' + (defun my-modus-themes-subtle-syntax () + (modus-themes-with-colors + (set-face-attribute 'font-lock-builtin-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-comment-delimiter-face nil :inherit 'font-lock-comment-face) + (set-face-attribute 'font-lock-comment-face nil :inherit 'unspecified :foreground fg-alt) + (set-face-attribute 'font-lock-constant-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-doc-face nil :inherit 'modus-themes-slant :foreground fg-docstring) + (set-face-attribute 'font-lock-function-name-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-keyword-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-negation-char-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-preprocessor-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-regexp-grouping-backslash nil :inherit 'bold :foreground fg-escape-char-backslash) + (set-face-attribute 'font-lock-regexp-grouping-construct nil :inherit 'bold :foreground fg-escape-char-construct) + (set-face-attribute 'font-lock-string-face nil :foreground blue-alt) + (set-face-attribute 'font-lock-type-face nil :inherit 'modus-themes-bold :foreground 'unspecified) + (set-face-attribute 'font-lock-variable-name-face nil :foreground 'unspecified) + (set-face-attribute 'font-lock-warning-face nil :inherit 'modus-themes-bold :foreground red-nuanced-fg))) + + +File: modus-themes.info, Node: Custom hl-todo colors, Next: Add support for solaire-mode, Prev: Near-monochrome syntax highlighting, Up: Advanced customization + +5.27 Custom hl-todo colors +========================== + +The ‘hl-todo’ package provides the user option ‘hl-todo-keyword-faces’: +it specifies a pair of keyword and corresponding color value. The Modus +themes configure that option in the interest of legibility. While this +works for our purposes, users may still prefer to apply their custom +values, in which case the following approach is necessary: + + (defun my-modus-themes-hl-todo-faces () + (setq hl-todo-keyword-faces '(("TODO" . "#ff0000") + ("HACK" . "#ffff00") + ("XXX" . "#00ffff") + ("NOTE" . "#ff00ff")))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-hl-todo-faces) + + Or include a ‘let’ form, if needed: + + (defun my-modus-themes-hl-todo-faces () + (let ((red "#ff0000") + (blue "#0000ff")) + (setq hl-todo-keyword-faces `(("TODO" . ,blue) + ("HACK" . ,red) + ("XXX" . ,red) + ("NOTE" . ,blue))))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-hl-todo-faces) + + Normally, we do not touch user options, though this is an exception: +otherwise the defaults are not always legible. + + +File: modus-themes.info, Node: Add support for solaire-mode, Prev: Custom hl-todo colors, Up: Advanced customization + +5.28 Add support for solaire-mode +================================= + +The ‘solaire-mode’ package dims the background of what it considers +ancillary “UI” buffers, such as the minibuffer and Dired buffers. The +Modus themes used to support Solaire on the premise that the user was +(i) opting in to it, (ii) understood why certain buffers were more gray, +and (iii) knew what other adjustments had to be made to prevent broken +visuals (e.g. the default style of the ‘modus-themes-completions’ uses +a subtle gray background for the selection, which with Solaire becomes +practically invisible). + + However, the assumption that users opt in to this feature does not +always hold true. There are cases where it is enabled by defaultsuch as +in the popular Doom Emacs configuration. Thus, the unsuspecting user +who loads ‘modus-operandi’ or ‘modus-vivendi’ without the requisite +customizations is getting a sub-par experience; an experience that we +did not intend and cannot genuinely fix. + + Because the Modus themes are meant to work everywhere, we cannot make +an exception for Doom Emacs and/or Solaire users. Furthermore, we shall +not introduce hacks, such as by adding a check in all relevant faces to +be adjusted based on Solaire or whatever other package. Hacks of this +sort are unsustainable and penalize the entire userbase. Besides, the +themes are built into Emacs and we must keep their standard high. + + The fundamental constraint with Solaire is that Emacs does not have a +real distinction between “content” and “UI” buffers. For themes to work +with Solaire, they need to be designed around that package. Such is an +arrangement that compromises on our accessibility standards and/or +hinders our efforts to provide the best possible experience while using +the Modus themes. + + As such, ‘solaire-mode’ is not—and will not be—supported by the Modus +themes (or any other of my themes, for that matter). Users who want it +must style the faces manually. Below is some sample code, based on what +we cover at length elsewhere in this manual: + + *note Advanced customization::. + + *note Face specs at scale using the themes' palette::. + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + `(solaire-default-face ((,class :inherit default :background ,bg-alt :foreground ,fg-dim))) + `(solaire-line-number-face ((,class :inherit solaire-default-face :foreground ,fg-unfocused))) + `(solaire-hl-line-face ((,class :background ,bg-active))) + `(solaire-org-hide-face ((,class :background ,bg-alt :foreground ,bg-alt)))))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + As always, re-load the theme for changes to take effect. + + +File: modus-themes.info, Node: Face coverage, Next: Notes on individual packages, Prev: Advanced customization, Up: Top + +6 Face coverage +*************** + +The Modus themes try to provide as close to full face coverage as +possible. This is necessary to ensure a consistently accessible reading +experience across all available interfaces. + +* Menu: + +* Supported packages:: Full list of covered face groups +* Indirectly covered packages:: + + +File: modus-themes.info, Node: Supported packages, Next: Indirectly covered packages, Up: Face coverage + +6.1 Full support for packages or face groups +============================================ + +This list will always be updated to reflect the current state of the +project. The idea is to offer an overview of the known status of all +affected face groups. The items with an appended asterisk ‘*’ tend to +have lots of extensions, so the “full support” may not be 100% true… + + • ace-window + • alert + • all-the-icons + • all-the-icons-dired + • all-the-icons-ibuffer + • annotate + • ansi-color + • anzu + • apropos + • artbollocks-mode + • auctex and TeX + • auto-dim-other-buffers + • avy + • awesome-tray + • bbdb + • binder + • bm + • bongo + • boon + • bookmark + • breakpoint (provided by the built-in ‘gdb-mi.el’ library) + • calendar and diary + • calfw + • calibredb + • cfrs + • change-log and log-view (such as ‘vc-print-log’, + ‘vc-print-root-log’) + • chart + • cider + • circe + • citar + • color-rg + • column-enforce-mode + • company-mode* + • company-posframe + • compilation-mode + • completions + • consult + • corfu + • corfu-quick + • counsel* + • counsel-css + • cov + • cperl-mode + • css-mode + • csv-mode + • ctrlf + • custom (what you get with ‘M-x customize’) + • dap-mode + • deadgrep + • debbugs + • deft + • denote + • devdocs + • dictionary + • diff-hl + • diff-mode + • dim-autoload + • dir-treeview + • dired + • dired-async + • dired-git + • dired-git-info + • dired-narrow + • dired-subtree + • diredfl + • diredp (dired+) + • display-fill-column-indicator-mode + • doom-modeline + • easy-jekyll + • ebdb + • ediff + • ein (Emacs IPython Notebook) + • eglot + • el-search + • eldoc-box + • elfeed + • elfeed-score + • elpher + • embark + • ement + • emms + • enh-ruby-mode (enhanced-ruby-mode) + • epa + • equake + • erc + • eros + • ert + • eshell + • eshell-fringe-status + • eshell-git-prompt + • eshell-prompt-extras (epe) + • eshell-syntax-highlighting + • evil* (evil-mode) + • evil-goggles + • evil-snipe + • evil-visual-mark-mode + • eww + • exwm + • eyebrowse + • fancy-dabbrev + • flycheck + • flycheck-color-mode-line + • flycheck-indicator + • flycheck-posframe + • flymake + • flyspell + • flx + • freeze-it + • focus + • fold-this + • font-lock (generic syntax highlighting) + • forge + • fountain (fountain-mode) + • geiser + • git-commit + • git-gutter (and variants) + • git-rebase + • git-timemachine + • gnus + • gotest + • golden-ratio-scroll-screen + • helm* + • helm-ls-git + • helm-switch-shell + • helm-xref + • helpful + • highlight-indentation + • highlight-numbers + • highlight-parentheses (*note Note on highlight-parentheses.el: Note + on highlight-parenthesesel.) + • highlight-thing + • hl-defined + • hl-fill-column + • hl-line-mode + • hl-todo + • hydra + • ibuffer + • icomplete + • icomplete-vertical + • ido-mode + • iedit + • iflipb + • image-dired + • imenu-list + • indium + • info + • info+ (info-plus) + • info-colors + • interaction-log + • ioccur + • isearch, occur, etc. + • ivy* + • ivy-posframe + • jira (org-jira) + • journalctl-mode + • js2-mode + • julia + • jupyter + • kaocha-runner + • keycast + • ledger-mode + • leerzeichen + • line numbers (‘display-line-numbers-mode’ and global variant) + • lsp-mode + • lsp-ui + • macrostep + • magit + • magit-imerge + • make-mode + • man + • marginalia + • markdown-mode + • markup-faces (‘adoc-mode’) + • mentor + • messages + • mini-modeline + • minimap + • mmm-mode + • mode-line + • mood-line + • moody + • mpdel + • mu4e + • multiple-cursors + • nano-modeline + • neotree + • notmuch + • num3-mode + • nxml-mode + • orderless + • org* + • org-journal + • org-noter + • org-pomodoro + • org-recur + • org-roam + • org-superstar + • org-table-sticky-header + • org-tree-slide + • origami + • outline-mode + • outline-minor-faces + • package (what you get with ‘M-x list-packages’) + • page-break-lines + • pandoc-mode + • paren-face + • pass + • pdf-tools + • persp-mode + • perspective + • phi-grep + • pomidor + • popup + • powerline + • powerline-evil + • prism (*note Note for prism.el: Note for prism.) + • proced + • prodigy + • pulse + • pyim + • quick-peek + • racket-mode + • rainbow-blocks + • rainbow-delimiters + • rcirc + • recursion-indicator + • regexp-builder (also known as ‘re-builder’) + • rg (rg.el) + • ripgrep + • rmail + • ruler-mode + • selectrum + • selectrum-prescient + • semantic + • sesman + • shell-script-mode + • shortdoc + • show-paren-mode + • shr + • side-notes + • sieve-mode + • skewer-mode + • slime (slbd) + • sly + • smart-mode-line + • smartparens + • smerge + • spaceline + • speedbar + • spell-fu [ part of 2.7.0-dev ] + • stripes + • suggest + • switch-window + • swiper + • sx + • symbol-overlay + • syslog-mode + • tab-bar-groups + • tab-bar-mode + • tab-line-mode + • table (built-in table.el) + • telega + • telephone-line + • terraform-mode + • term + • textsec + • tomatinho + • transient (pop-up windows such as Magit’s) + • trashed + • tree-sitter + • treemacs + • tty-menu + • tuareg + • typescript + • undo-tree + • vc (vc-dir.el, vc-hooks.el) + • vertico + • vertico-quick + • vimish-fold + • visible-mark + • visual-regexp + • vterm + • vundo + • wcheck-mode + • web-mode + • wgrep + • which-function-mode + • which-key + • whitespace-mode + • window-divider-mode + • winum + • writegood-mode + • woman + • xah-elisp-mode + • xref + • xterm-color (and ansi-colors) + • yaml-mode + • yasnippet + • ztree + + Plus many other miscellaneous faces that are provided by the upstream +GNU Emacs distribution. + + +File: modus-themes.info, Node: Indirectly covered packages, Prev: Supported packages, Up: Face coverage + +6.2 Indirectly covered packages +=============================== + +These do not require any extra styles because they are configured to +inherit from some basic faces or their dependencies which are directly +supported by the themes. + + • ag + • apt-sources-list + • buffer-expose + • bufler + • counsel-notmuch + • counsel-org-capture-string + • dashboard (emacs-dashboard) + • define-word + • disk-usage + • dtache + • dynamic-ruler + • easy-kill + • edit-indirect + • egerrit + • elfeed-summary + • evil-owl + • flyspell-correct + • fortran-mode + • git-walktree + • goggles + • highlight-defined + • highlight-escape-sequences (‘hes-mode’) + • i3wm-config-mode + • minibuffer-line + • no-emoji + • org-remark + • parrot + • perl-mode + • php-mode + • rjsx-mode + • side-hustle + • spell-fu + • swift-mode + • tab-bar-echo-area + • tide + • undo-hl + • vdiff + • vertico-indexed + • vertico-mouse + + +File: modus-themes.info, Node: Notes on individual packages, Next: Frequently Asked Questions, Prev: Face coverage, Up: Top + +7 Notes on individual packages +****************************** + +This section covers information that may be of interest to users of +individual packages. + +* Menu: + +* Note on calendar.el weekday and weekend colors: Note on calendarel weekday and weekend colors. +* Note on git-gutter in Doom Emacs:: +* Note on php-mode multiline comments:: +* Note on underlines in compilation buffers:: +* Note on inline Latex in Org buffers:: +* Note on dimmer.el: Note on dimmerel. +* Note on display-fill-column-indicator-mode:: +* Note on highlight-parentheses.el: Note on highlight-parenthesesel. +* Note on mmm-mode.el background colors: Note on mmm-modeel background colors. +* Note for prism:: +* Note for god-mode:: +* Note on company-mode overlay pop-up:: +* Note on ERC escaped color sequences:: +* Note on powerline or spaceline:: +* Note on SHR colors:: +* Note on SHR fonts:: +* Note on Ement colors and fonts:: +* Note on Helm grep:: +* Note on pdf-tools link hints:: +* Note on the Notmuch logo:: + + +File: modus-themes.info, Node: Note on calendarel weekday and weekend colors, Next: Note on git-gutter in Doom Emacs, Up: Notes on individual packages + +7.1 Note on calendar.el weekday and weekend colors +================================================== + +By default, the ‘M-x calendar’ interface differentiates weekdays from +weekends by applying a gray color to the former and a faint red to the +latter. The idea for this approach is that the weekend should serve as +a subtle warning that no work is supposed to be done on that day, per +the design of traditional calendars. + + Users who prefer all days to look the same can configure the variable +‘calendar-weekend-days’ to either use gray of weekdays or the faint red +of weekends uniformly. + + ;; All are treated like weekdays (gray color) + (setq calendar-weekend-days nil) + + ;; All are treated like weekends (red-faint color) + (setq calendar-weekend-days (number-sequence 0 6)) + + ;; The default marks the Saturday and Sunday as the weekend + (setq calendar-weekend-days '(0 6)) + + For changes to take effect, the Calendar buffer needs to be generated +anew. + + +File: modus-themes.info, Node: Note on git-gutter in Doom Emacs, Next: Note on php-mode multiline comments, Prev: Note on calendarel weekday and weekend colors, Up: Notes on individual packages + +7.2 Note on git-gutter in Doom Emacs +==================================== + +The ‘git-gutter’ and ‘git-gutter-fr’ packages default to drawing bitmaps +for the indicators they display (e.g. bitmap of a plus sign for added +lines). In Doom Emacs, these bitmaps are replaced with contiguous lines +which may look nicer, but require a change to the foreground of the +relevant faces to yield the desired color combinations. + + Since this is Doom-specific, we urge users to apply changes in their +local setup. Below is some sample code, based on what we cover at +length elsewhere in this manual: + + *note Advanced customization::. + + *note Face specs at scale using the themes' palette::. + + (defun my-modus-themes-custom-faces () + (modus-themes-with-colors + (custom-set-faces + ;; Replace green with blue if you use `modus-themes-deuteranopia'. + `(git-gutter-fr:added ((,class :foreground ,green-fringe-bg))) + `(git-gutter-fr:deleted ((,class :foreground ,red-fringe-bg))) + `(git-gutter-fr:modified ((,class :foreground ,yellow-fringe-bg)))))) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-custom-faces) + + As always, re-load the theme for changes to take effect. + + If the above does not work, try this instead: + + (after! modus-themes + (modus-themes-with-colors + (custom-set-faces + ;; Replace green with blue if you use `modus-themes-deuteranopia'. + `(git-gutter-fr:added ((,class :foreground ,green-fringe-bg))) + `(git-gutter-fr:deleted ((,class :foreground ,red-fringe-bg))) + `(git-gutter-fr:modified ((,class :foreground ,yellow-fringe-bg)))))) + + Replace ‘green-fringe-bg’ with ‘blue-fringe-bg’ if you want to +optimize for red-green color deficiency. + + *note Option for red-green color deficiency or deuteranopia: +Deuteranopia style. + + +File: modus-themes.info, Node: Note on php-mode multiline comments, Next: Note on underlines in compilation buffers, Prev: Note on git-gutter in Doom Emacs, Up: Notes on individual packages + +7.3 Note on php-mode multiline comments +======================================= + +Depending on your build of Emacs and/or the environment it runs in, +multiline comments in PHP with the ‘php-mode’ package use the +‘font-lock-doc-face’ instead of ‘font-lock-comment-face’. + + This seems to make all comments use the appropriate face: + + (defun my-multine-comments () + (setq-local c-doc-face-name 'font-lock-comment-face)) + + (add-hook 'php-mode-hook #'my-multine-comments) + + As always, re-load the theme for changes to take effect. + + +File: modus-themes.info, Node: Note on underlines in compilation buffers, Next: Note on inline Latex in Org buffers, Prev: Note on php-mode multiline comments, Up: Notes on individual packages + +7.4 Note on underlines in compilation buffers +============================================= + +Various buffers that produce compilation results or run tests on code +apply an underline to the file names they reference or to relevant +messages. Users may consider this unnecessary or excessive. + + To outright disable the effect, use this (buffers need to be +generated anew): + + (setq compilation-message-face nil) + + If some element of differentiation is still desired, a good option is +to render the affected text with the ‘italic’ face: + + (setq compilation-message-face 'italic) + + *note Configure bold and italic faces::. + + +File: modus-themes.info, Node: Note on inline Latex in Org buffers, Next: Note on dimmerel, Prev: Note on underlines in compilation buffers, Up: Notes on individual packages + +7.5 Note on inline Latex in Org buffers +======================================= + +Org can work with inline latex and related syntax. To actually fontify +those constructs, set the variable ‘org-highlight-latex-and-related’ to +the desired list of values (per its doc string). For example: + + (setq org-highlight-latex-and-related '(latex script)) + + Remember to use ‘M-x org-mode-restart’ for changes to take effect. + + +File: modus-themes.info, Node: Note on dimmerel, Next: Note on display-fill-column-indicator-mode, Prev: Note on inline Latex in Org buffers, Up: Notes on individual packages + +7.6 Note on dimmer.el +===================== + +The ‘dimmer.el’ library by Neil Okamoto can be configured to +automatically dim the colors of inactive Emacs windows. To guarantee +consistent results with the Modus themes, we suggest some tweaks to the +default styles, such as in this minimal setup: + + (use-package dimmer + :config + (setq dimmer-fraction 0.3) + (setq dimmer-adjustment-mode :foreground) + (setq dimmer-use-colorspace :rgb) + + (dimmer-mode 1)) + + Of the above, we strongly recommend the RGB color space because it is +the one that remains faithful to the hueness of the colors used by the +themes. Whereas the default CIELAB space has a tendency to distort +colors in addition to applying the dim effect, which can be somewhat +disorienting. + + The value of the ‘dimmer-fraction’ has been selected empirically. +Users might prefer to tweak it further (increasing it makes the dim +effect more pronounced). + + Changing the ‘dimmer-adjustment-mode’ is a matter of preference. +Though because the Modus themes use black and white as their base +colors, any other value for that variable will turn the main background +gray. This inadvertently leads to the opposite of the intended utility +of this package: it draws too much attention to unfocused windows. + + +File: modus-themes.info, Node: Note on display-fill-column-indicator-mode, Next: Note on highlight-parenthesesel, Prev: Note on dimmerel, Up: Notes on individual packages + +7.7 Note on display-fill-column-indicator-mode +============================================== + +The ‘display-fill-column-indicator-mode’ uses a typographic character to +draw its line. This has the downside of creating a dashed line. The +dashes are further apart depending on how tall the font’s glyph height +is and what integer the ‘line-spacing’ is set to. + + At the theme level we eliminate this effect by making the character +one pixel tall: the line is contiguous. Users who prefer the dashed +line are advised to change the ‘fill-column-indicator’ face, as +explained elsewhere in this document. For example: + + (modus-themes-with-colors + (custom-set-faces + `(fill-column-indicator ((,class :foreground ,bg-active))))) + + *note Face specs at scale using the themes' palette::. + + To make the line thicker, set the height to be equal to the base font +size instead of the one pixel we use. This is done by specifying a rate +instead of an absolute number, as in ‘:height 1.0’ versus ‘:height 1’. +For example: + + (modus-themes-with-colors + (custom-set-faces + `(fill-column-indicator ((,class :height 1.0 :background ,bg-inactive :foreground ,bg-inactive))))) + + +File: modus-themes.info, Node: Note on highlight-parenthesesel, Next: Note on mmm-modeel background colors, Prev: Note on display-fill-column-indicator-mode, Up: Notes on individual packages + +7.8 Note on highlight-parentheses.el +==================================== + +The ‘highlight-parentheses’ package provides contextual coloration of +surrounding parentheses, highlighting only those which are around the +point. The package expects users to customize the applicable colors on +their own by configuring certain variables. + + To make the Modus themes work as expected with this, we need to use +some of the techniques that are discussed at length in the various +“Do-It-Yourself” (DIY) sections, which provide insight into the more +advanced customization options of the themes. + + *note Advanced customization::. + + In the following example, we are assuming that the user wants to (i) +re-use color variables provided by the themes, (ii) be able to retain +their tweaks while switching between ‘modus-operandi’ and +‘modus-vivendi’, and (iii) have the option to highlight either the +foreground of the parentheses or the background as well. + + We start by defining our own variable, which will serve as a toggle +between foreground and background coloration styles: + + (defvar my-highlight-parentheses-use-background t + "Prefer `highlight-parentheses-background-colors'.") + + Then we can update our preference with this: + + ;; Set to nil to disable backgrounds. + (setq my-highlight-parentheses-use-background nil) + + To re-use colors from the themes, we must wrap our code in the +‘modus-themes-with-colors’ macro. Our implementation must interface +with the variables ‘highlight-parentheses-background-colors’ and/or +‘highlight-parentheses-colors’. + + So we can have something like this (the doc string of +‘modus-themes-with-colors’ explains where the names of the colors can be +found): + + (modus-themes-with-colors + ;; Our preference for setting either background or foreground + ;; styles, depending on `my-highlight-parentheses-use-background'. + (if my-highlight-parentheses-use-background + + ;; Here we set color combinations that involve both a background + ;; and a foreground value. + (setq highlight-parentheses-background-colors (list cyan-refine-bg + magenta-refine-bg + green-refine-bg + yellow-refine-bg) + highlight-parentheses-colors (list cyan-refine-fg + magenta-refine-fg + green-refine-fg + yellow-refine-fg)) + + ;; And here we pass only foreground colors while disabling any + ;; backgrounds. + (setq highlight-parentheses-colors (list green-intense + magenta-intense + blue-intense + red-intense) + highlight-parentheses-background-colors nil))) + + ;; Include this if you also want to make the parentheses bold: + (set-face-attribute 'highlight-parentheses-highlight nil :inherit 'bold) + + ;; Our changes must be evaluated before enabling the relevant mode, so + ;; this comes last. + (global-highlight-parentheses-mode 1) + + For our changes to persist while switching between the Modus themes, +we need to include them in a function which can then get passed to +‘modus-themes-after-load-theme-hook’. This is the complete +implementation: + + ;; Configurations for `highlight-parentheses': + (require 'highlight-parentheses) + + (defvar my-highlight-parentheses-use-background t + "Prefer `highlight-parentheses-background-colors'.") + + (setq my-highlight-parentheses-use-background nil) ; Set to nil to disable backgrounds + + (defun my-modus-themes-highlight-parentheses () + (modus-themes-with-colors + ;; Our preference for setting either background or foreground + ;; styles, depending on `my-highlight-parentheses-use-background'. + (if my-highlight-parentheses-use-background + + ;; Here we set color combinations that involve both a background + ;; and a foreground value. + (setq highlight-parentheses-background-colors (list cyan-refine-bg + magenta-refine-bg + green-refine-bg + yellow-refine-bg) + highlight-parentheses-colors (list cyan-refine-fg + magenta-refine-fg + green-refine-fg + yellow-refine-fg)) + + ;; And here we pass only foreground colors while disabling any + ;; backgrounds. + (setq highlight-parentheses-colors (list green-intense + magenta-intense + blue-intense + red-intense) + highlight-parentheses-background-colors nil))) + + ;; Include this if you also want to make the parentheses bold: + (set-face-attribute 'highlight-parentheses-highlight nil :inherit 'bold) + + ;; Our changes must be evaluated before enabling the relevant mode, so + ;; this comes last. + (global-highlight-parentheses-mode 1)) + + (add-hook 'modus-themes-after-load-theme-hook #'my-modus-themes-highlight-parentheses) + + As always, re-load the theme for changes to take effect. + + +File: modus-themes.info, Node: Note on mmm-modeel background colors, Next: Note for prism, Prev: Note on highlight-parenthesesel, Up: Notes on individual packages + +7.9 Note on mmm-mode.el background colors +========================================= + +The faces used by ‘mmm-mode.el’ are expected to have a colorful +background, while they should not touch any foreground value. The idea +is that they must not interfere with existing fontification. Those +background colors need to be distinct from each other, such as an +unambiguous red juxtaposed with a clear blue. + + While this design may be internally consistent with the raison d’être +of that library, it inevitably produces inaccessible color combinations. + + There are two competing goals at play: + + 1. Legibility of the text, understood as the contrast ratio between + the background and the foreground. + + 2. Semantic precision of each face which entails faithfulness to + color-coding of the underlying background. + + As the Modus themes are designed with the express purpose of +conforming with the first point, we have to forgo the apparent +color-coding of the background elements. Instead we use subtle colors +that do not undermine the legibility of the affected text while they +still offer a sense of added context. + + Users who might prefer to fall below the minimum 7:1 contrast ratio +in relative luminance (the accessibility target we conform with), can +opt to configure the relevant faces on their own. + + *note Face specs at scale using the themes' palette::. + + This example uses more vivid background colors, though it comes at +the very high cost of degraded legibility. + + (modus-themes-with-colors + (custom-set-faces + `(mmm-cleanup-submode-face ((,class :background ,yellow-refine-bg))) + `(mmm-code-submode-face ((,class :background ,bg-active))) + `(mmm-comment-submode-face ((,class :background ,blue-refine-bg))) + `(mmm-declaration-submode-face ((,class :background ,cyan-refine-bg))) + `(mmm-default-submode-face ((,class :background ,bg-alt))) + `(mmm-init-submode-face ((,class :background ,magenta-refine-bg))) + `(mmm-output-submode-face ((,class :background ,red-refine-bg))) + `(mmm-special-submode-face ((,class :background ,green-refine-bg))))) + + +File: modus-themes.info, Node: Note for prism, Next: Note for god-mode, Prev: Note on mmm-modeel background colors, Up: Notes on individual packages + +7.10 Note on prism.el +===================== + +This package by Adam Porter, aka “alphapapa” or “github-alphapapa”, +implements an alternative to the typical coloration of code. Instead of +highlighting the syntactic constructs, it applies color to different +levels of depth in the code structure. + + As ‘prism.el’ offers a broad range of customizations, we cannot style +it directly at the theme level: that would run contrary to the spirit of +the package. Instead, we may offer preset color schemes. Those should +offer a starting point for users to adapt to their needs. + + In the following code snippets, we employ the +‘modus-themes-with-colors’ macro: *note Face specs at scale using the +themes' palette::. + + These are the minimum recommended settings with 16 colors: + + (setq prism-num-faces 16) + + (prism-set-colors + :desaturations '(0) ; do not change---may lower the contrast ratio + :lightens '(0) ; same + :colors (modus-themes-with-colors + (list fg-main + magenta + cyan-alt-other + magenta-alt-other + blue + magenta-alt + cyan-alt + red-alt-other + green + fg-main + cyan + yellow + blue-alt + red-alt + green-alt-other + fg-special-warm))) + + With 8 colors: + + (setq prism-num-faces 8) + + (prism-set-colors + :desaturations '(0) ; do not change---may lower the contrast ratio + :lightens '(0) ; same + :colors (modus-themes-with-colors + (list blue + magenta + magenta-alt-other + cyan-alt-other + fg-main + blue-alt + red-alt-other + cyan))) + + And this is with 4 colors, which produces results that are the +closest to the themes’ default aesthetic: + + (setq prism-num-faces 4) + + (prism-set-colors + :desaturations '(0) ; do not change---may lower the contrast ratio + :lightens '(0) ; same + :colors (modus-themes-with-colors + (list blue + magenta + magenta-alt-other + green-alt))) + + If you need to apply desaturation and lightening, you can use what +the ‘prism.el’ documentation recommends, like this (adapting to the +examples with the 4, 8, 16 colors): + + (prism-set-colors + :desaturations (cl-loop for i from 0 below 16 collect (* i 2.5)) + :lightens (cl-loop for i from 0 below 16 collect (* i 2.5)) + :colors (modus-themes-with-colors + (list fg-main + cyan-alt-other + magenta-alt-other + magenta))) + + +File: modus-themes.info, Node: Note for god-mode, Next: Note on company-mode overlay pop-up, Prev: Note for prism, Up: Notes on individual packages + +7.11 Note on god-mode.el +======================== + +The ‘god-mode’ library does not provide faces that could be configured +by the Modus themes. Users who would like to get some visual feedback +on the status of ‘M-x god-mode’ are instead encouraged by upstream to +set up their own configurations, such as by changing the ‘mode-line’ +face (*note Advanced customization::). This is an adaptation of the +approach followed in the upstream README: + + (defun my-god-mode-update-mode-line () + "Make `mode-line' blue if God local mode is active." + (modus-themes-with-colors + (if god-local-mode + (set-face-attribute 'mode-line nil + :foreground blue-active + :background bg-active-accent + :box blue) + (set-face-attribute 'mode-line nil + :foreground fg-active + :background bg-active + :box fg-alt)))) + + (add-hook 'post-command-hook 'my-god-mode-update-mode-line) + + We employ the ‘modus-themes-with-colors’ which provides access to +color variables defined by the active theme. Its use is covered +elsewhere in this manual (*note Face specs at scale using the themes' +palette::). As for the attributes that can be passed to each face, +start by consulting the documentation string of ‘set-face-attribute’. + + +File: modus-themes.info, Node: Note on company-mode overlay pop-up, Next: Note on ERC escaped color sequences, Prev: Note for god-mode, Up: Notes on individual packages + +7.12 Note on company-mode overlay pop-up +======================================== + +By default, the ‘company-mode’ pop-up that lists completion candidates +is drawn using an overlay. This creates alignment issues every time it +is placed above a piece of text that has a different height than the +default. + + The solution recommended by the project’s maintainer is to use an +alternative front-end for drawing the pop-up which draws child frames +instead of overlays.(1)(2) + + ---------- Footnotes ---------- + + (1) + + (2) + + +File: modus-themes.info, Node: Note on ERC escaped color sequences, Next: Note on powerline or spaceline, Prev: Note on company-mode overlay pop-up, Up: Notes on individual packages + +7.13 Note on ERC escaped color sequences +======================================== + +The built-in IRC client ‘erc’ has the ability to colorise any text using +escape sequences that start with ‘^C’ (inserted with ‘C-q C-c’) and are +followed by a number for the foreground and background.(1) Possible +numbers are 0-15, with the first entry being the foreground and the +second the background, separated by a comma. Like this ‘^C1,6’. The +minimum setup is this: + + (add-to-list 'erc-modules 'irccontrols) + (setq erc-interpret-controls-p t + erc-interpret-mirc-color t) + + As this allows users the chance to make arbitrary combinations, it is +impossible to guarantee a consistently high contrast ratio. All we can +we do is provide guidance on the combinations that satisfy the +accessibility standard of the themes: + +Modus Operandi + Use foreground color 1 for all backgrounds from 2-15. Like so: + ‘C-q C-c1’ where ‘N’ is the background. + +Modus Vivendi + Use foreground color 0 for all backgrounds from 2-13. Use + foreground ‘1’ for backgrounds 14, 15. + + Colors 0 and 1 are white and black respectively. So combine them +together, if you must. + + ---------- Footnotes ---------- + + (1) This page explains the basics, though it is not specific to +Emacs: + + +File: modus-themes.info, Node: Note on powerline or spaceline, Next: Note on SHR colors, Prev: Note on ERC escaped color sequences, Up: Notes on individual packages + +7.14 Note on powerline or spaceline +=================================== + +Both Powerline and Spaceline package users will likely need to use the +command ‘powerline-reset’ whenever they make changes to their themes +and/or mode line setup. + + +File: modus-themes.info, Node: Note on SHR colors, Next: Note on SHR fonts, Prev: Note on powerline or spaceline, Up: Notes on individual packages + +7.15 Note on SHR colors +======================= + +Emacs’ HTML rendering library (‘shr.el’) may need explicit configuration +to respect the theme’s colors instead of whatever specifications the +webpage provides. + + Consult the doc string of ‘shr-use-colors’. + + +File: modus-themes.info, Node: Note on SHR fonts, Next: Note on Ement colors and fonts, Prev: Note on SHR colors, Up: Notes on individual packages + +7.16 Note on SHR fonts +====================== + +By default, packages that build on top of the Simple HTML Remember +(‘shr’) use proportionately spaced fonts. This is controlled by the +user option ‘shr-use-fonts’, which is set to non-nil by default. To use +the standard font instead, set that variable to nil. + + *note Font configurations for Org and others::. + + Packages affected by this are: + + • elfeed + • ement + • eww + + This is a non-exhaustive list. + + +File: modus-themes.info, Node: Note on Ement colors and fonts, Next: Note on Helm grep, Prev: Note on SHR fonts, Up: Notes on individual packages + +7.17 Note on Ement colors and fonts +=================================== + +The ‘ement.el’ library by Adam Porter (also known as “alphapapa”) +defaults to a method of colorizing usernames in a rainbow style. This +is controlled by the user option ‘ement-room-prism’ and can be disabled +with: + + (setq ement-room-prism nil) + + The contrast ratio of these colors is governed by another user +option: ‘ement-room-prism-minimum-contrast’. By default, it is set to 6 +which is slightly below our nominal target. Try this instead: + + (setq ement-room-prism-minimum-contrast 7) + + With regard to fonts, Ement depends on ‘shr’ (*note Note on SHR +fonts::). + + Since we are here, here is an excerpt from Ement’s source code: + + (defcustom ement-room-prism-minimum-contrast 6 + "Attempt to enforce this minimum contrast ratio for user faces. + This should be a reasonable number from, e.g. 0-7 or so." + ;; Prot would almost approve of this default. :) I would go all the way + ;; to 7, but 6 already significantly dilutes the colors in some cases. + :type 'number) + + Yes, I do approve of that default. Even a 4.5 (the WCAG AA rating) +would be a good baseline for many themes and/or user configurations. +Our target is the highest of the sort, though we do not demand that +everyone conforms with it. + + +File: modus-themes.info, Node: Note on Helm grep, Next: Note on pdf-tools link hints, Prev: Note on Ement colors and fonts, Up: Notes on individual packages + +7.18 Note on Helm grep +====================== + +There is one face from the Helm package that is meant to highlight the +matches of a grep or grep-like command (‘ag’ or ‘ripgrep’). It is +‘helm-grep-match’. However, this face can only apply when the user does +not pass ‘--color=always’ as a command-line option for their command. + + Here is the docstring for that face, which is defined in the +‘helm-grep.el’ library (you can always visit the source code with ‘M-x +find-library’). + + Face used to highlight grep matches. Have no effect when grep + backend use “–color=” + + The user must either remove ‘--color’ from the flags passed to the +grep function, or explicitly use ‘--color=never’ (or equivalent). Helm +provides user-facing customization options for controlling the grep +function’s parameters, such as ‘helm-grep-default-command’ and +‘helm-grep-git-grep-command’. + + When ‘--color=always’ is in effect, the grep output will use red text +in bold letter forms to present the matching part in the list of +candidates. That style still meets the contrast ratio target of >= 7:1 +(accessibility standard WCAG AAA), because it draws the reference to +ANSI color number 1 (red) from the already-supported array of +‘ansi-color-names-vector’. + + +File: modus-themes.info, Node: Note on pdf-tools link hints, Next: Note on the Notmuch logo, Prev: Note on Helm grep, Up: Notes on individual packages + +7.19 Note on pdf-tools link hints +================================= + +Hints are drawn by ImageMagick (https://imagemagick.org/), not Emacs, +i.e., ImageMagick doesn’t know about the hint face unless you tell +ImageMagick about it. By default, only the foreground and background +color attributes are passed. The below snippet adds to those the +various font attributes. As it queries various faces, specifically +‘pdf-links-read-link’ and the faces it inherits, it needs to be added to +your initialization file after you’ve customized any faces. + + (use-package pdf-links + :config + (let ((spec + (apply #'append + (mapcar + (lambda (name) + (list name + (face-attribute 'pdf-links-read-link + name nil 'default))) + '(:family :width :weight :slant))))) + (setq pdf-links-read-link-convert-commands + `("-density" "96" + "-family" ,(plist-get spec :family) + "-stretch" ,(let* ((width (plist-get spec :width)) + (name (symbol-name width))) + (replace-regexp-in-string "-" "" + (capitalize name))) + "-weight" ,(pcase (plist-get spec :weight) + ('ultra-light "Thin") + ('extra-light "ExtraLight") + ('light "Light") + ('semi-bold "SemiBold") + ('bold "Bold") + ('extra-bold "ExtraBold") + ('ultra-bold "Black") + (_weight "Normal")) + "-style" ,(pcase (plist-get spec :slant) + ('italic "Italic") + ('oblique "Oblique") + (_slant "Normal")) + "-pointsize" "%P" + "-undercolor" "%f" + "-fill" "%b" + "-draw" "text %X,%Y '%c'")))) + + +File: modus-themes.info, Node: Note on the Notmuch logo, Prev: Note on pdf-tools link hints, Up: Notes on individual packages + +7.20 Note on the Notmuch logo +============================= + +By default, the “hello” buffer of Notmuch includes a header with the +programs’ logo and a couple of buttons. The logo has the effect of +enlarging the height of the line, which negatively impacts the shape of +those buttons. Disabling the logo fixes the problem: + + (setq notmuch-show-logo nil) + + +File: modus-themes.info, Node: Frequently Asked Questions, Next: Contributing, Prev: Notes on individual packages, Up: Top + +8 Frequently Asked Questions +**************************** + +In this section we provide answers related to some aspects of the Modus +themes’ design and application. + +* Menu: + +* Is the contrast ratio about adjacent colors?:: +* What does it mean to avoid exaggerations?:: +* Why are colors mostly variants of blue, magenta, cyan?: Why are colors mostly variants of blue magenta cyan?. +* What is the best setup for legibility?:: +* Are these color schemes?:: +* Port the Modus themes to other platforms?:: + + +File: modus-themes.info, Node: Is the contrast ratio about adjacent colors?, Next: What does it mean to avoid exaggerations?, Up: Frequently Asked Questions + +8.1 Is the contrast ratio about adjacent colors? +================================================ + +The minimum contrast ratio in relative luminance that the themes conform +with always refers to any given combination of background and foreground +colors. If we have some blue colored text next to a magenta one, both +against a white background, we do not mean to imply that blue:magenta is +7:1 in terms of relative luminance. Rather, we state that blue:white +and magenta:white each are 7:1 or higher. + + The point of reference is always the background. Because colors have +about the same minimum distance in luminance from their backdrop, they +necessarily are fairly close to each other in this measure. A possible +blue:magenta combination would naturally be around 1:1 in contrast of +the sort here considered. + + To differentiate between sequential colors, we rely on hueness by +mapping contrasting hues to adjacent constructs, while avoiding +exaggerations. A blue next to a magenta can be told apart regardless of +their respective contrast ratio against their common background. +Exceptions would be tiny characters in arguably not so realistic cases, +such as two dots drawn side-by-side which for some reason would need to +be colored differently. They would still be legible though, which is +the primary objective of the Modus themes. + + +File: modus-themes.info, Node: What does it mean to avoid exaggerations?, Next: Why are colors mostly variants of blue magenta cyan?, Prev: Is the contrast ratio about adjacent colors?, Up: Frequently Asked Questions + +8.2 What does it mean to avoid exaggerations? +============================================= + +The Modus themes are designed with restraint, so that their default +looks do not overdo it with the application of color. + + *note Customization Options::. + + This is the non-quantifiable aspect of the themes’ design: the +artistic part, if you will. There are a lot of cases where color can be +used inconsiderately, without accounting for layout, typographic, or +other properties of the presentation. For example, two headings with +distinct markers, such as leading asterisks in Org buffers, do not have +to have highly contrasting hues between them in order to be told apart: +the added element of contrast in hueness does not contribute +significantly more to the distinction between the headings than colors +whose hues are relatively closer to each other in the color space. + + Exaggerations can be hard to anticipate or identify. Multiple shades +of blue and magenta in the same context may not seem optimal: one might +think that it would be better to use highly contrasting hues to ensure +that all colors stand out, such as by placing blue next to yellow, next +to magenta, and green. That would, however, be a case of design for its +own sake; a case where color is being applied without consideration of +its end results in the given context. Too many contrasting hues in +close proximity force an erratic rate to how the eye jumps from one +piece of text to the next. Whereas multiple shades of, say, blue and +magenta can suffice to tell things apart and avoid excess coloration: a +harmonious rhythm. + + +File: modus-themes.info, Node: Why are colors mostly variants of blue magenta cyan?, Next: What is the best setup for legibility?, Prev: What does it mean to avoid exaggerations?, Up: Frequently Asked Questions + +8.3 Why are colors mostly variants of blue, magenta, cyan? +========================================================== + +Due to the innate properties of color, some options are better than +others for the accessibility purposes of the themes, the stylistic +consistency between ‘modus-operandi’ and ‘modus-vivendi’, and the +avoidance of exaggerations in design. + + *note What does it mean to avoid exaggerations?:: + + What we describe as color is a function of three distinct channels of +light: red, green, blue. In hexadecimal RGB notation, a color value is +read as three pairs of red, green, and blue light: ‘#RRGGBB’. Of those +three, the most luminant is green, while the least luminant is blue. + + The three basic colors represent each of the channels of light. They +can be intermixed to give us six colors: red and green derive yellow, +green and blue make cyan, red and blue turn into magenta. + + We can test the luminance of each of those against white and black to +get a sense of how not all colors are equally good for accessibility +(white is ‘#ffffff’, which means that all three light channels are fully +luminated, while black is ‘#000000’ meaning that no light is present +(notwithstanding display technology)). + + | Name | | #ffffff | #000000 | + |---------+---------+---------+---------| + | red | #ff0000 | 4.00 | 5.25 | + | yellow | #ffff00 | 1.07 | 19.56 | + | green | #00ff00 | 1.37 | 15.30 | + | cyan | #00ffff | 1.25 | 16.75 | + | blue | #0000ff | 8.59 | 2.44 | + | magenta | #ff00ff | 3.14 | 6.70 | + + *note Measure color contrast::. + + By reading this table we learn that every color that has a high level +of green light (green, yellow, cyan) is virtually unreadable against a +white background and, conversely, can be easily read against black. + + We can then infer that red and blue, in different combinations, with +green acting as calibrator for luminance, will give us fairly moderate +colors that pass the 7:1 target. Blue with a bit of green produce +appropriate variants of cyan. Similarly, blue combined with some red +and hints of green give us suitable shades of purple. + + Due to the need of maintaining some difference in hueness between +adjacent colors, it is not possible to make red, green, and yellow the +main colors, because blue cannot be used to control their luminance and, +thus the relevant space will shrink considerably. + + *note Is the contrast ratio about adjacent colors?:: + + This phenomenon is best illustrated by the following table that +measures the relative luminance of shades of red, yellow, magenta +against white: + + | | #ffffff | + |---------+---------| + | #990000 | 8.92 | + | #995500 | 5.75 | + | #990099 | 7.46 | + + We notice that equal values of red and blue light in ‘#990099’ +(magenta shade) do not lead to a considerable change in luminance +compared with ‘#990000’ (red variant). Whereas less amount of green +light in ‘#995500’ leads to a major drop in luminance relative to white. +It follows that using the green channel of light to calibrate the +luminance of colors is more effective than trying to do the same with +either red or blue (the latter is the least effective in that regard). + + When we need to work with several colors, it is always better to have +sufficient manoeuvring space, especially since we cannot pick arbitrary +colors but only those that satisfy the accessibility objectives of the +themes. + + As for why we do not mostly use green, yellow, cyan for the dark +theme, it is because those colors are far more luminant than their +counterparts on the other side of the spectrum, so to ensure that they +all have about the same contrast ratios we would have to alter their +hueness considerably. In short, the effect would not be optimal as it +would lead to exaggerations. Plus, it would make ‘modus-vivendi’ look +completely different than ‘modus-operandi’, to the effect that the two +could not be properly considered part of the same project. + + +File: modus-themes.info, Node: What is the best setup for legibility?, Next: Are these color schemes?, Prev: Why are colors mostly variants of blue magenta cyan?, Up: Frequently Asked Questions + +8.4 What is the best setup for legibility? +========================================== + +The Modus themes can be conceptually simplified as combinations of color +values that account for relative luminance and inner harmony. Those +qualities do not guarantee that every end-user will have the same +experience, due to differences between people, but also because of +variances in hardware capabilities and configurations. For the purposes +of this document, we may only provide suggestions pertaining to the +latter case. + + ‘modus-operandi’ is best used outdoors or in a room that either gets +direct sunlight or has plenty of light. Whereas ‘modus-vivendi’ works +better when there is not a lot of sunshine or the room has a source of +light that is preferably a faint and/or warm one. It is possible to use +‘modus-operandi’ at night and ‘modus-vivendi’ during the day, though +that will depend on several variables, such as one’s overall perception +of color, the paint on the walls and how that contributes to the +impression of lightness in the room, the sense of space within the eye’s +peripheral vision, hardware specifications, and environmental factors. + + In general, an additional source of light other than that of the +monitor can help reduce eye strain: the eyes are more relaxed when they +do not have to focus on one point to gather light. + + The monitor’s display settings must be accounted for. Gamma values, +in particular, need to be calibrated to neither amplify nor distort the +perception of black. Same principle for sharpness, brightness, and +contrast as determined by the hardware, which all have an effect on how +text is read on the screen. + + There are software level methods on offer, such as the XrandR utility +for the X Window System (X.org), which can make gamma corrections for +each of the three channels of light (red, green, blue). For example: + + xrandr --output LVDS1 --brightness 1.0 --gamma 0.76:0.75:0.68 + + Typography is another variable. Some font families are blurry at +small point sizes. Others may have a regular weight that is lighter +(thiner) than that of their peers which may, under certain +circumstances, cause a halo effect around each glyph. + + The gist is that legibility cannot be fully solved at the theme +level. The color combinations may have been optimized for +accessibility, though the remaining contributing factors in each case +need to be considered in full. + + +File: modus-themes.info, Node: Are these color schemes?, Next: Port the Modus themes to other platforms?, Prev: What is the best setup for legibility?, Up: Frequently Asked Questions + +8.5 Are these color schemes? +============================ + +No, the Modus themes are not color schemes. + + A color scheme is a collection of colors. A good color scheme is a +combination of colors with an inner logic or abstract structure. + + A theme is a set of patterns that are applied across different +contexts. A good theme is one that does so with consistency, though not +uniformity. + + In practical terms, a color scheme is what one uses when, for +example, they edit the first sixteen escape sequences of a terminal +emulator to the hues of their preference. The terminal offers the +option to choose, say, the exact value of what counts as “red”, but does +not provide the means to control where that is mapped to and whether it +should also have other qualities such as a bold weight for the +underlying text or an added background color. In contradistinction, +Emacs uses constructs known as “faces” which allow the user/developer to +specify where a given color will be used and whether it should be +accompanied by other typographic or stylistic attributes. + + By configuring the multitude of faces on offer we thus control both +which colors are applied and how they appear in their context. When a +package wants to render each instance of “foo” with the “bar” face, it +is not requesting a specific color, which makes things considerably more +flexible as we can treat “bar” in its own right without necessarily +having to use some color value that we hardcoded somewhere. + + Which brings us to the distinction between consistency and uniformity +where our goal is always the former: we want things to look similar +across all interfaces, but we must never force a visual identity where +that runs contrary to the functionality of the given interface. For +instance, all links are underlined by default yet there are cases such +as when viewing listings of emails in Gnus (and Mu4e, Notmuch) where (i) +it is already understood that one must follow the indicator or headline +to view its contents and (ii) underlining everything would make the +interface virtually unusable. + + *note Option for links: Link styles. + + Again, one must exercise judgement in order to avoid discrimination, +where “discrimination” refers to: + + • The treatment of substantially different magnitudes as if they were + of the same class. + • Or the treatment of the same class of magnitudes as if they were of + a different class. + + (To treat similar things differently; to treat dissimilar things +alike.) + + If, in other words, one was to enforce uniformity without accounting +for the particular requirements of each case—the contextual demands for +usability beyond matters of color—they would be making a not-so-obvious +error of treating different cases as if they were the same. + + The Modus themes prioritise “thematic consistency” over abstract +harmony or regularity among their applicable colors. In concrete terms, +we do not claim that, say, our yellows are the best complements for our +blues because we generally avoid using complementary colors +side-by-side, so it is wrong to optimise for a decontextualised +blue+yellow combination. Not to imply that our colors do not work well +together because they do, just to clarify that consistency of context is +what themes must strive for, and that requires widening the scope of the +design beyond the particularities of a color scheme. + + Long story short: color schemes and themes have different +requirements. Please do not conflate the two. + + +File: modus-themes.info, Node: Port the Modus themes to other platforms?, Prev: Are these color schemes?, Up: Frequently Asked Questions + +8.6 Port the Modus themes to other platforms? +============================================= + +There is no plan to port the themes to other platforms or text editors. +I (Protesilaos) only use GNU Emacs and thus cannot maintain code that +targets software I am either not familiar with or am not using on a +daily basis. + + While it is possible to produce a simulacrum based on a given +template, doing so would run contrary to how this project is maintained +where details matter greatly. + + Each program has its own requirements so it won’t always be +possible—or indeed desirable—to have 1:1 correspondence between what +applies to Emacs and what should be done elsewhere. No port should ever +strive to be a faithful copy of the Emacs implementation, as no other +program is an Emacs equivalent, but instead try to follow the spirit of +the design. For example, some of the customization options accept a +list as their value, or an alist, which may not be possible to reproduce +on other platforms. + + *note Customization options: Customization Options. + + In other words, if something must be done differently on a certain +editor then that is acceptable so long as (i) the accessibility +standards are not compromised and (ii) the overall character of the +themes remains consistent. + + The former criterion should be crystal clear as it pertains to the +scientific foundations of the themes: high legibility and taking care of +the needs of users with red-green color deficiency (deuteranopia) by +avoiding red+green color coding paradigms and/or by providing red+blue +variants. + + The latter criterion is the “je ne sais quoi” of the artistic aspect +of the themes, which is partially fleshed out in this manual. + + *note Frequently Asked Questions::. + + With regard to the artistic aspect (where “art” qua skill may amount +to an imprecise science), there is no hard-and-fast rule in effect as it +requires one to exercise discretion and make decisions based on +context-dependent information or constraints. As is true with most +things in life, when in doubt, do not cling on to the letter of the law +but try to understand its spirit. + + For a trivial example: the curly underline that Emacs draws for +spelling errors is thinner than, e.g., what a graphical web browser has, +so if I was to design for an editor than has a thicker curly underline I +would make the applicable colors less intense to counterbalance the +typographic intensity of the added thickness. + + With those granted, if anyone is willing to develop a port of the +themes, they are welcome to contact me and I will do my best to help +them in their efforts. + + +File: modus-themes.info, Node: Contributing, Next: Acknowledgements, Prev: Frequently Asked Questions, Up: Top + +9 Contributing +************** + +This section documents the canonical sources of the themes and the ways +in which you can contribute to their ongoing development. + +* Menu: + +* Sources of the themes:: +* Issues you can help with:: +* Patches require copyright assignment to the FSF:: + + +File: modus-themes.info, Node: Sources of the themes, Next: Issues you can help with, Up: Contributing + +9.1 Sources of the themes +========================= + +The ‘modus-operandi’ and ‘modus-vivendi’ themes are built into Emacs 28. + + The source code of the themes is available on SourceHut +(https://git.sr.ht/~protesilaos/modus-themes). Or check the GitLab +mirror (former main source) +(https://gitlab.com/protesilaos/modus-themes/) and the GitHub mirror +(https://github.com/protesilaos/modus-themes/). + + An HTML version of this manual is provided as an extension of the +author’s personal website (https://protesilaos.com/emacs/modus-themes/) +(does not rely on any non-free code). + + +File: modus-themes.info, Node: Issues you can help with, Next: Patches require copyright assignment to the FSF, Prev: Sources of the themes, Up: Contributing + +9.2 Issues you can help with +============================ + +A few tasks you can help with by sending an email to the general +modus-themes public mailing list +(https://lists.sr.ht/~protesilaos/modus-themes) (or use the command +‘modus-themes-report-bug’). + + • Suggest refinements to packages that are covered. + • Report packages not covered thus far. + • Report bugs, inconsistencies, shortcomings. + • Help expand the documentation of covered-but-not-styled packages. + • Suggest refinements to the color palette. + • Help expand this document or any other piece of documentation. + • Send patches for code refinements (if you need, ask me for help + with Git—we all start out as beginners). + + *note Patches require copyright assignment to the FSF::. + + It is preferable that your feedback includes some screenshots, GIFs, +or short videos, as well as further instructions to reproduce a given +setup. Though this is not a requirement. + + Also consider mentioning the version of the themes you are using, +such as by invoking the command ‘modus-themes-version’. + + Whatever you do, bear in mind the overarching objective of the Modus +themes: to keep a contrast ratio that is greater or equal to 7:1 between +background and foreground colors. If a compromise is ever necessary +between aesthetics and accessibility, it shall always be made in the +interest of the latter. + + +File: modus-themes.info, Node: Patches require copyright assignment to the FSF, Prev: Issues you can help with, Up: Contributing + +9.3 Patches require copyright assignment to the FSF +=================================================== + +Code contributions are most welcome. For any major edit (more than 15 +lines, or so, in aggregate per person), you need to make a copyright +assignment to the Free Software Foundation. This is necessary because +the themes are part of the upstream Emacs distribution: the FSF must at +all times be in a position to enforce the GNU General Public License. + + Copyright assignment is a simple process. Check the request form +below (please adapt it accordingly). You must write an email to the +address mentioned in the form and then wait for the FSF to send you a +legal agreement. Sign the document and file it back to them. This +could all happen via email and take about a week. You are encouraged to +go through this process. You only need to do it once. It will allow +you to make contributions to Emacs in general. + + Please email the following information to assign@gnu.org, and we + will send you the assignment form for your past and future changes. + + Please use your full legal name (in ASCII characters) as the subject + line of the message. + + REQUEST: SEND FORM FOR PAST AND FUTURE CHANGES + + [What is the name of the program or package you're contributing to?] + + GNU Emacs + + [Did you copy any files or text written by someone else in these changes? + Even if that material is free software, we need to know about it.] + + Copied a few snippets from the same files I edited. Their author, + Protesilaos Stavrou, has already assigned copyright to the Free Software + Foundation. + + [Do you have an employer who might have a basis to claim to own + your changes? Do you attend a school which might make such a claim?] + + + [For the copyright registration, what country are you a citizen of?] + + + [What year were you born?] + + + [Please write your email address here.] + + + [Please write your postal address here.] + + + + + + [Which files have you changed so far, and which new files have you written + so far?] + + + +File: modus-themes.info, Node: Acknowledgements, Next: Other notes about the project, Prev: Contributing, Up: Top + +10 Acknowledgements +******************* + +The Modus themes are a collective effort. Every bit of work matters. + +Author/maintainer + Protesilaos Stavrou. + +Contributions to code or documentation + Alex Griffin, Anders Johansson, Antonio Ruiz, Basil L. + Contovounesios, Björn Lindström, Carlo Zancanaro, Christian Tietze, + Daniel Mendler, Eli Zaretskii, Fritz Grabo, Illia Ostapyshyn, Kévin + Le Gouguec, Kostadin Ninev, Madhavan Krishnan, Manuel Giraud, + Markus Beppler, Matthew Stevenson, Mauro Aranda, Nicolas De + Jaeghere, Paul David, Philip Kaludercic, Pierre Téchoueyres, Rudolf + Adamkovič, Stephen Gildea, Shreyas Ragavan, Stefan Kangas, Utkarsh + Singh, Vincent Murphy, Xinglu Chen, Yuanchen Xie. + +Ideas and user feedback + Aaron Jensen, Adam Porter, Adam Spiers, Adrian Manea, Alex Griffin, + Alex Koen, Alex Peitsinis, Alexey Shmalko, Alok Singh, Anders + Johansson, André Alexandre Gomes, Andrew Tropin, Antonio Hernández + Blas, Arif Rezai, Augusto Stoffel, Basil L. Contovounesios, Burgess + Chang, Christian Tietze, Christopher Dimech, Christopher League, + Damien Cassou, Daniel Mendler, Dario Gjorgjevski, David Edmondson, + Davor Rotim, Divan Santana, Eliraz Kedmi, Emanuele Michele Alberto + Monterosso, Farasha Euker, Feng Shu, Gautier Ponsinet, Gerry + Agbobada, Gianluca Recchia, Gonçalo Marrafa, Guilherme Semente, + Gustavo Barros, Hörmetjan Yiltiz, Ilja Kocken, Iris Garcia, Ivan + Popovych, Jeremy Friesen, Jerry Zhang, Johannes Grødem, John Haman, + Jonas Collberg, Jorge Morais, Joshua O’Connor, Julio C. + Villasante, Kenta Usami, Kevin Fleming, Kévin Le Gouguec, Kostadin + Ninev, Len Trigg, Lennart C. Karssen, Magne Hov, Manuel Uberti, + Mark Bestley, Mark Burton, Markus Beppler, Matt Armstrong, Mauro + Aranda, Maxime Tréca, Michael Goldenberg, Morgan Smith, Morgan + Willcock, Murilo Pereira, Nicky van Foreest, Nicolas De Jaeghere, + Paul Poloskov, Pengji Zhang, Pete Kazmier, Peter Wu, Philip + Kaludercic, Pierre Téchoueyres, Przemysław Kryger, Robert Hepple, + Roman Rudakov, Ryan Phillips, Rytis Paškauskas, Rudolf Adamkovič, + Sam Kleinman, Samuel Culpepper, Saša Janiška, Shreyas Ragavan, + Simon Pugnet, Tassilo Horn, Thibaut Verron, Thomas Heartman, Togan + Muftuoglu, Tony Zorman, Trey Merkley, Tomasz Hołubowicz, Toon + Claes, Uri Sharf, Utkarsh Singh, Vincent Foley. As well as users: + Ben, CsBigDataHub1, Emacs Contrib, Eugene, Fourchaux, Fredrik, + Moesasji, Nick, Summer Emacs, TheBlob42, Trey, bepolymathe, + bit9tream, derek-upham, doolio, fleimgruber, gitrj95, iSeeU, + jixiuf, okamsn, pRot0ta1p. + +Packaging + Basil L. Contovounesios, Eli Zaretskii, Glenn Morris, Mauro Aranda, + Richard Stallman, Stefan Kangas (core Emacs), Stefan Monnier (GNU + Elpa), André Alexandre Gomes, Andrew Tropin, Dimakakos Dimos, + Morgan Smith, Nicolas Goaziou (Guix), Dhavan Vaidya (Debian). + +Inspiration for certain features + Bozhidar Batsov (zenburn-theme), Fabrice Niessen (leuven-theme). + + Special thanks (from A-Z) to Daniel Mendler, Gustavo Barros, Manuel +Uberti, Nicolas De Jaeghere, and Omar Antolín Camarena for their long +time contributions and insightful commentary on key aspects of the +themes’ design and/or aspects of their functionality. + + All errors are my own. + + +File: modus-themes.info, Node: Other notes about the project, Next: GNU Free Documentation License, Prev: Acknowledgements, Up: Top + +11 Other notes about the project +******************************** + +If you are curious about the principles that govern the development of +this project read the essay On the design of the Modus themes +(https://protesilaos.com/codelog/2020-03-17-design-modus-themes-emacs/) +(2020-03-17). + + Here are some more publications for those interested in the kind of +work that goes into this project (sometimes the commits also include +details of this sort): + + • Modus Operandi theme subtle palette review + (https://protesilaos.com/codelog/2020-05-10-modus-operandi-palette-review/) + (2020-05-10) + • Modus Vivendi theme subtle palette review + (https://protesilaos.com/codelog/2020-06-13-modus-vivendi-palette-review/) + (2020-06-13) + • Modus themes: new “faint syntax” option + (https://protesilaos.com/codelog/2020-07-04-modus-themes-faint-colours/) + (2020-07-04) + • Modus themes: major review of “nuanced” colours + (https://protesilaos.com/codelog/2020-07-08-modus-themes-nuanced-colours/) + (2020-07-08) + • Modus themes: review of blue colours + (https://protesilaos.com/codelog/2020-09-14-modus-themes-review-blues/) + (2020-09-14) + • Modus themes: review rainbow-delimiters faces + (https://protesilaos.com/codelog/2020-12-27-modus-themes-review-rainbow-delimiters/) + (2020-12-27) + • Modus themes: review of select “faint” colours + (https://protesilaos.com/codelog/2021-01-11-modus-themes-review-select-faint-colours/) + (2021-01-11) + • The Modus themes now cover deuteranopia in diffs + (https://protesilaos.com/codelog/2021-02-25-modus-themes-diffs-deuteranopia/) + (2021-02-25) + • Introducing the variable modus-themes-org-agenda + (https://protesilaos.com/codelog/2021-06-02-modus-themes-org-agenda/) + (2021-06-02) + • Modus themes: review of the org-habit graph colours + (https://protesilaos.com/codelog/2022-01-02-review-modus-themes-org-habit-colours/) + (2022-01-02) + • Re: VSCode or Vim ports of the Emacs modus-themes? + (https://protesilaos.com/codelog/2022-01-03-modus-themes-port-faq/) + (2022-01-03) + • Modus themes: case study on Avy faces and colour combinations + (https://protesilaos.com/codelog/2022-04-20-modus-themes-case-study-avy/) + (2022-04-20) + • Emacs: colour theory and techniques used in the Modus themes + (https://protesilaos.com/codelog/2022-04-21-modus-themes-colour-theory/) + (2022-04-21) + + And here are the canonical sources of this project: + +Manual + +Change Log + +Screenshots + +Git repository + +Mailing list + + + +File: modus-themes.info, Node: GNU Free Documentation License, Next: Indices, Prev: Other notes about the project, Up: Top + +Appendix A GNU Free Documentation License +***************************************** + + Version 1.3, 3 November 2008 + + Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document “free” in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of “copyleft”, which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. We + recommend this License principally for works whose purpose is + instruction or reference. + + 1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it can + be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + “Document”, below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as “you”. You accept + the license if you copy, modify or distribute the work in a way + requiring permission under copyright law. + + A “Modified Version” of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A “Secondary Section” is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document’s overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The “Invariant Sections” are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in the + notice that says that the Document is released under this License. + If a section does not fit the above definition of Secondary then it + is not allowed to be designated as Invariant. The Document may + contain zero Invariant Sections. If the Document does not identify + any Invariant Sections then there are none. + + The “Cover Texts” are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A “Transparent” copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images composed + of pixels) generic paint programs or (for drawings) some widely + available drawing editor, and that is suitable for input to text + formatters or for automatic translation to a variety of formats + suitable for input to text formatters. A copy made in an otherwise + Transparent file format whose markup, or absence of markup, has + been arranged to thwart or discourage subsequent modification by + readers is not Transparent. An image format is not Transparent if + used for any substantial amount of text. A copy that is not + “Transparent” is called “Opaque”. + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and standard-conforming + simple HTML, PostScript or PDF designed for human modification. + Examples of transparent image formats include PNG, XCF and JPG. + Opaque formats include proprietary formats that can be read and + edited only by proprietary word processors, SGML or XML for which + the DTD and/or processing tools are not generally available, and + the machine-generated HTML, PostScript or PDF produced by some word + processors for output purposes only. + + The “Title Page” means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, “Title + Page” means the text near the most prominent appearance of the + work’s title, preceding the beginning of the body of the text. + + The “publisher” means any person or entity that distributes copies + of the Document to the public. + + A section “Entitled XYZ” means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) + To “Preserve the Title” of such a section when you modify the + Document means that it remains a section “Entitled XYZ” according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + + 2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow the + conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + + 3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document’s license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the title + equally prominent and visible. You may add other material on the + covers in addition. Copying with changes limited to the covers, as + long as they preserve the title of the Document and satisfy these + conditions, can be treated as verbatim copying in other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a machine-readable + Transparent copy along with each Opaque copy, or state in or with + each Opaque copy a computer-network location from which the general + network-using public has access to download using public-standard + network protocols a complete Transparent copy of the Document, free + of added material. If you use the latter option, you must take + reasonably prudent steps, when you begin distribution of Opaque + copies in quantity, to ensure that this Transparent copy will + remain thus accessible at the stated location until at least one + year after the last time you distribute an Opaque copy (directly or + through your agents or retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of copies, + to give them a chance to provide you with an updated version of the + Document. + + 4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with the + Modified Version filling the role of the Document, thus licensing + distribution and modification of the Modified Version to whoever + possesses a copy of it. In addition, you must do these things in + the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of previous + versions (which should, if there were any, be listed in the + History section of the Document). You may use the same title + as a previous version if the original publisher of that + version gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has fewer than five), unless they release you + from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified + Version under the terms of this License, in the form shown in + the Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document’s + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled “History”, Preserve its Title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on the + Title Page. If there is no section Entitled “History” in the + Document, create one stating the title, year, authors, and + publisher of the Document as given on its Title Page, then add + an item describing the Modified Version as stated in the + previous sentence. + + J. Preserve the network location, if any, given in the Document + for public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in the + “History” section. You may omit a network location for a work + that was published at least four years before the Document + itself, or if the original publisher of the version it refers + to gives permission. + + K. For any section Entitled “Acknowledgements” or “Dedications”, + Preserve the Title of the section, and preserve in the section + all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, unaltered + in their text and in their titles. Section numbers or the + equivalent are not considered part of the section titles. + + M. Delete any section Entitled “Endorsements”. Such a section + may not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + “Endorsements” or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option designate + some or all of these sections as invariant. To do this, add their + titles to the list of Invariant Sections in the Modified Version’s + license notice. These titles must be distinct from any other + section titles. + + You may add a section Entitled “Endorsements”, provided it contains + nothing but endorsements of your Modified Version by various + parties—for example, statements of peer review or that the text has + been approved by an organization as the authoritative definition of + a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end of + the list of Cover Texts in the Modified Version. Only one passage + of Front-Cover Text and one of Back-Cover Text may be added by (or + through arrangements made by) any one entity. If the Document + already includes a cover text for the same cover, previously added + by you or by arrangement made by the same entity you are acting on + behalf of, you may not add another; but you may replace the old + one, on explicit permission from the previous publisher that added + the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + + 5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination all + of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + “History” in the various original documents, forming one section + Entitled “History”; likewise combine any sections Entitled + “Acknowledgements”, and any sections Entitled “Dedications”. You + must delete all sections Entitled “Endorsements.” + + 6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the documents + in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow this + License in all other respects regarding verbatim copying of that + document. + + 7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of a + storage or distribution medium, is called an “aggregate” if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation’s users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document’s Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + + 8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of section + 4. Replacing Invariant Sections with translations requires special + permission from their copyright holders, but you may include + translations of some or all Invariant Sections in addition to the + original versions of these Invariant Sections. You may include a + translation of this License, and all the license notices in the + Document, and any Warranty Disclaimers, provided that you also + include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled “Acknowledgements”, + “Dedications”, or “History”, the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + + 9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, or distribute it is void, + and will automatically terminate your rights under this License. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, receipt of a copy of some or all of the + same material does not give you any rights to use it. + + 10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. See + . + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License “or any later version” applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If the + Document does not specify a version number of this License, you may + choose any version ever published (not as a draft) by the Free + Software Foundation. If the Document specifies that a proxy can + decide which future versions of this License can be used, that + proxy’s public statement of acceptance of a version permanently + authorizes you to choose that version for the Document. + + 11. RELICENSING + + “Massive Multiauthor Collaboration Site” (or “MMC Site”) means any + World Wide Web server that publishes copyrightable works and also + provides prominent facilities for anybody to edit those works. A + public wiki that anybody can edit is an example of such a server. + A “Massive Multiauthor Collaboration” (or “MMC”) contained in the + site means any set of copyrightable works thus published on the MMC + site. + + “CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 + license published by Creative Commons Corporation, a not-for-profit + corporation with a principal place of business in San Francisco, + California, as well as future copyleft versions of that license + published by that same organization. + + “Incorporate” means to publish or republish a Document, in whole or + in part, as part of another Document. + + An MMC is “eligible for relicensing” if it is licensed under this + License, and if all works that were first published under this + License somewhere other than this MMC, and subsequently + incorporated in whole or in part into the MMC, (1) had no cover + texts or invariant sections, and (2) were thus incorporated prior + to November 1, 2008. + + The operator of an MMC Site may republish an MMC contained in the + site under CC-BY-SA on the same site at any time before August 1, + 2009, provided the MMC is eligible for relicensing. + +ADDENDUM: How to use this License for your documents +==================================================== + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and license +notices just after the title page: + + Copyright (C) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + + If you have Invariant Sections, Front-Cover Texts and Back-Cover +Texts, replace the “with...Texts.” line with this: + + with the Invariant Sections being LIST THEIR TITLES, with + the Front-Cover Texts being LIST, and with the Back-Cover Texts + being LIST. + + If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + + If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of free +software license, such as the GNU General Public License, to permit +their use in free software. + + +File: modus-themes.info, Node: Indices, Prev: GNU Free Documentation License, Up: Top + +B Indices +********* + +* Menu: + +* Function index:: +* Variable index:: +* Concept index:: + + +File: modus-themes.info, Node: Function index, Next: Variable index, Up: Indices + +B.1 Function index +================== + +[index] +* Menu: + +* modus-themes-color: Case-by-case face specs using the themes' palette. + (line 6) +* modus-themes-color-alts: Case-by-case face specs using the themes' palette. + (line 6) +* modus-themes-contrast: Measure color contrast. + (line 6) +* modus-themes-list-colors: Visualize the active Modus theme's palette. + (line 6) +* modus-themes-list-colors-current: Visualize the active Modus theme's palette. + (line 6) +* modus-themes-load-operandi: Enable and load. (line 6) +* modus-themes-load-themes: Enable and load. (line 6) +* modus-themes-load-vivendi: Enable and load. (line 6) +* modus-themes-report-bug: Issues you can help with. + (line 6) +* modus-themes-toggle: Enable and load. (line 6) +* modus-themes-version: Issues you can help with. + (line 26) +* modus-themes-wcag-formula: Measure color contrast. + (line 6) +* modus-themes-with-colors: Face specs at scale using the themes' palette. + (line 6) + + +File: modus-themes.info, Node: Variable index, Next: Concept index, Prev: Function index, Up: Indices + +B.2 Variable index +================== + +[index] +* Menu: + +* modus-themes-after-load-theme-hook: Enable and load. (line 6) +* modus-themes-bold-constructs: Bold constructs. (line 6) +* modus-themes-box-buttons: Box buttons. (line 6) +* modus-themes-completions: Completion UIs. (line 6) +* modus-themes-deuteranopia: Deuteranopia style. (line 6) +* modus-themes-diffs: Diffs. (line 6) +* modus-themes-fringes: Fringes. (line 6) +* modus-themes-headings: Heading styles. (line 6) +* modus-themes-hl-line: Line highlighting. (line 6) +* modus-themes-inhibit-reload: Custom reload theme. (line 6) +* modus-themes-intense-mouseovers: Mouse hover effects. (line 6) +* modus-themes-italic-constructs: Italic constructs. (line 6) +* modus-themes-lang-checkers: Language checkers. (line 6) +* modus-themes-links: Link styles. (line 6) +* modus-themes-mail-citations: Mail citations. (line 6) +* modus-themes-markup: Markup. (line 6) +* modus-themes-mixed-fonts: Mixed fonts. (line 6) +* modus-themes-mode-line: Mode line. (line 6) +* modus-themes-operandi-color-overrides: Override colors. (line 6) +* modus-themes-org-agenda: Org agenda. (line 6) +* modus-themes-org-blocks: Org mode blocks. (line 6) +* modus-themes-paren-match: Matching parentheses. (line 6) +* modus-themes-prompts: Command prompts. (line 6) +* modus-themes-region: Active region. (line 6) +* modus-themes-subtle-line-numbers: Line numbers. (line 6) +* modus-themes-syntax: Syntax styles. (line 6) +* modus-themes-tabs-accented: Tab style. (line 6) +* modus-themes-variable-pitch-ui: UI typeface. (line 6) +* modus-themes-vivendi-color-overrides: Override colors. (line 6) + + +File: modus-themes.info, Node: Concept index, Prev: Variable index, Up: Indices + +B.3 Concept index +================= + +[index] +* Menu: + +* Avoiding exaggerations in design: What does it mean to avoid exaggerations?. + (line 6) +* Bold and italic fonts: Configure bold and italic faces. + (line 6) +* Change a theme’s color saturation: Override color saturation. + (line 6) +* Change a theme’s colors: Override colors. (line 6) +* Change theme colors through blending: Override colors through blending. + (line 6) +* Changelog: Learn about the latest changes. + (line 6) +* Color accuracy of terminal emulators: More accurate colors in terminal emulators. + (line 6) +* Color contrast: Measure color contrast. + (line 6) +* Contrast between adjacent colors: Is the contrast ratio about adjacent colors?. + (line 6) +* Contributing: Issues you can help with. + (line 6) +* Contributors: Acknowledgements. (line 6) +* Cycle colors: Cycle through arbitrary colors. + (line 6) +* Decrease mode line height: Decrease mode line height. + (line 6) +* Development notes: Other notes about the project. + (line 6) +* Essential configuration: Enable and load. (line 6) +* Explicitly supported packages: Supported packages. (line 6) +* Extracting colors en masse: Face specs at scale using the themes' palette. + (line 6) +* Extracting individual colors: Case-by-case face specs using the themes' palette. + (line 6) +* Font configurations: Font configurations for Org and others. + (line 6) +* Fonts in EWW, Elfeed, Ement, and SHR: Note on SHR fonts. (line 6) +* Foreground-only diffs: Diffs with only the foreground. + (line 6) +* Frequently Asked Questions: Frequently Asked Questions. + (line 6) +* General setup for readability: What is the best setup for legibility?. + (line 6) +* Implicitly supported packages: Indirectly covered packages. + (line 6) +* Innate color qualities of the palette: Why are colors mostly variants of blue magenta cyan?. + (line 6) +* load-theme VS enable-theme: Differences between loading and enabling. + (line 6) +* Monochrome code syntax: Near-monochrome syntax highlighting. + (line 6) +* Org custom emphasis faces: Custom Org emphasis faces. + (line 6) +* Org custom todo faces: Custom Org todo keyword and priority faces. + (line 6) +* Porting the themes to other editors: Port the Modus themes to other platforms?. + (line 6) +* Preview color values: Visualize the active Modus theme's palette. + (line 6) +* Pure white and pure black in terminal emulators: Range of color with terminal emulators. + (line 6) +* Remapping faces: Remap face with local value. + (line 6) +* Remapping pdf-tools backdrop: Backdrop for pdf-tools. + (line 6) +* sample configuration: Sample configuration with and without use-package. + (line 6) +* Screenshots: How do the themes look like. + (line 6) +* Sources of the themes: Sources of the themes. (line 6) +* Switch themes without load-theme: Toggle themes without reloading them. + (line 6) +* Themes, not color schemes: Are these color schemes?. + (line 6) +* use-package configuration: Sample configuration with and without use-package. + (line 6) + + + +Tag Table: +Node: Top872 +Node: Overview7760 +Node: How do the themes look like9884 +Node: Learn about the latest changes10398 +Node: Installation10786 +Node: Install manually from source11308 +Node: Install from the archives12133 +Node: Install on GNU/Linux12732 +Node: Debian 11 Bullseye13225 +Node: GNU Guix13635 +Node: Dealing with byte compilation errors13918 +Node: Enable and load15076 +Node: Sample configuration with and without use-package18615 +Node: Differences between loading and enabling21520 +Node: Customization Options23529 +Node: Custom reload theme31909 +Node: Deuteranopia style32698 +Node: Bold constructs34129 +Node: Italic constructs34963 +Node: Syntax styles35730 +Node: Mixed fonts37862 +Node: Link styles39126 +Node: Box buttons41600 +Node: Command prompts44897 +Node: Mode line46718 +Node: Tab style52213 +Node: Completion UIs52876 +Node: Mail citations56726 +Node: Fringes57872 +Node: Language checkers58612 +Node: Line highlighting61054 +Node: Line numbers62792 +Node: Mouse hover effects63873 +Node: Markup64488 +Node: Matching parentheses66088 +Node: Active region67465 +Node: Diffs68878 +Node: Org mode blocks70529 +Node: Org agenda72919 +Node: Heading styles81735 +Node: UI typeface86647 +Node: Advanced customization87546 +Node: More accurate colors in terminal emulators89306 +Node: Range of color with terminal emulators90547 +Node: Visualize the active Modus theme's palette93252 +Node: Per-theme customization settings94248 +Node: Case-by-case face specs using the themes' palette95604 +Node: Face specs at scale using the themes' palette100152 +Node: Remap face with local value104953 +Node: Cycle through arbitrary colors107378 +Node: Override colors113805 +Node: Override color saturation119047 +Node: Override colors through blending124728 +Node: Override colors completely127573 +Node: Font configurations for Org and others141075 +Ref: Font configurations for Org and others-Footnote-1144002 +Node: Configure bold and italic faces144189 +Node: Custom Org todo keyword and priority faces148337 +Node: Custom Org emphasis faces152010 +Node: Update Org block delimiter fontification156811 +Node: Measure color contrast158728 +Node: Load theme depending on time of day161451 +Node: Backdrop for pdf-tools162444 +Node: Decrease mode line height165296 +Node: Toggle themes without reloading them169418 +Node: A theme-agnostic hook for theme loading170697 +Node: Diffs with only the foreground173099 +Node: Ediff without diff color-coding176213 +Node: Near-monochrome syntax highlighting178352 +Node: Custom hl-todo colors187349 +Node: Add support for solaire-mode188870 +Node: Face coverage191788 +Node: Supported packages192240 +Node: Indirectly covered packages198781 +Node: Notes on individual packages199909 +Node: Note on calendarel weekday and weekend colors201017 +Node: Note on git-gutter in Doom Emacs202165 +Node: Note on php-mode multiline comments204253 +Node: Note on underlines in compilation buffers205006 +Node: Note on inline Latex in Org buffers205843 +Node: Note on dimmerel206453 +Node: Note on display-fill-column-indicator-mode207938 +Node: Note on highlight-parenthesesel209336 +Node: Note on mmm-modeel background colors215386 +Node: Note for prism217701 +Node: Note for god-mode220857 +Node: Note on company-mode overlay pop-up222461 +Ref: Note on company-mode overlay pop-up-Footnote-1223151 +Ref: Note on company-mode overlay pop-up-Footnote-2223218 +Node: Note on ERC escaped color sequences223273 +Ref: Note on ERC escaped color sequences-Footnote-1224701 +Node: Note on powerline or spaceline224811 +Node: Note on SHR colors225225 +Node: Note on SHR fonts225649 +Node: Note on Ement colors and fonts226282 +Node: Note on Helm grep227781 +Node: Note on pdf-tools link hints229251 +Node: Note on the Notmuch logo231698 +Node: Frequently Asked Questions232196 +Node: Is the contrast ratio about adjacent colors?232827 +Node: What does it mean to avoid exaggerations?234334 +Node: Why are colors mostly variants of blue magenta cyan?236163 +Node: What is the best setup for legibility?240469 +Node: Are these color schemes?243114 +Node: Port the Modus themes to other platforms?246834 +Node: Contributing249615 +Node: Sources of the themes250012 +Node: Issues you can help with250711 +Node: Patches require copyright assignment to the FSF252283 +Node: Acknowledgements254503 +Node: Other notes about the project257998 +Node: GNU Free Documentation License260996 +Node: Indices286373 +Node: Function index286552 +Node: Variable index288355 +Node: Concept index290642 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/modus-themes-20220823.1919/modus-vivendi-theme.el b/code/elpa/modus-themes-20220823.1919/modus-vivendi-theme.el new file mode 100644 index 0000000..ba75a25 --- /dev/null +++ b/code/elpa/modus-themes-20220823.1919/modus-vivendi-theme.el @@ -0,0 +1,74 @@ +;;; modus-vivendi-theme.el --- Elegant, highly legible and customizable light theme -*- lexical-binding:t -*- + +;; Copyright (C) 2019-2022 Free Software Foundation, Inc. + +;; Author: Protesilaos Stavrou +;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht> +;; URL: https://git.sr.ht/~protesilaos/modus-themes +;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes +;; Version: 2.6.0 +;; Package-Requires: ((emacs "27.1")) +;; Keywords: faces, theme, accessibility + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: +;; +;; Modus Vivendi is the dark variant of the Modus themes (Modus Operandi +;; is the light one). The themes are designed for color-contrast +;; accessibility. More specifically: +;; +;; 1. Provide a consistent minimum contrast ratio between background +;; and foreground values of 7:1 or higher. This meets the highest +;; such accessibility criterion per the guidelines of the Worldwide +;; Web Consortium's Working Group on Accessibility (WCAG AAA +;; standard). +;; +;; 2. Offer as close to full face coverage as possible. The list is +;; already quite long, with more additions to follow as part of the +;; ongoing development process. +;; +;; For a complete view of the project, also refer to the following files +;; (should be distributed in the same repository/directory as the +;; current item): +;; +;; - modus-themes.el (Main code shared between the themes) +;; - modus-operandi-theme.el (Light theme) + +;;; Code: + + + +(eval-and-compile + (unless (and (fboundp 'require-theme) + load-file-name + (equal (file-name-directory load-file-name) + (expand-file-name "themes/" data-directory)) + (require-theme 'modus-themes t)) + (require 'modus-themes)) + + (deftheme modus-vivendi + "Elegant, highly legible and customizable dark theme. +Conforms with the highest legibility standard for color contrast +between background and foreground in any given piece of text, +which corresponds to a minimum contrast in relative luminance of +7:1 (WCAG AAA standard).") + + (modus-themes-theme modus-vivendi) + + (provide-theme 'modus-vivendi)) + +;;; modus-vivendi-theme.el ends here diff --git a/code/elpa/powershell-20220805.1712/powershell-autoloads.el b/code/elpa/powershell-20220805.1712/powershell-autoloads.el new file mode 100644 index 0000000..e455176 --- /dev/null +++ b/code/elpa/powershell-20220805.1712/powershell-autoloads.el @@ -0,0 +1,53 @@ +;;; powershell-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "powershell" "powershell.el" (0 0 0 0)) +;;; Generated autoloads from powershell.el + +(add-to-list 'auto-mode-alist '("\\.ps[dm]?1\\'" . powershell-mode)) + +(autoload 'powershell-mode "powershell" "\ +Major mode for editing PowerShell scripts. + +\\{powershell-mode-map} +Entry to this mode calls the value of `powershell-mode-hook' if +that value is non-nil. + +\(fn)" t nil) + +(autoload 'powershell "powershell" "\ +Run an inferior PowerShell. +If BUFFER is non-nil, use it to hold the powershell +process. Defaults to *PowerShell*. + +Interactively, a prefix arg means to prompt for BUFFER. + +If BUFFER exists but the shell process is not running, it makes a +new shell. + +If BUFFER exists and the shell process is running, just switch to +BUFFER. + +If PROMPT-STRING is non-nil, sets the prompt to the given value. + +See the help for `shell' for more details. (Type +\\[describe-mode] in the shell buffer for a list of commands.) + +\(fn &optional BUFFER PROMPT-STRING)" t nil) + +(register-definition-prefixes "powershell" '("explicit-powershell.exe-args" "powershell-")) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; powershell-autoloads.el ends here diff --git a/code/elpa/powershell-20220805.1712/powershell-pkg.el b/code/elpa/powershell-20220805.1712/powershell-pkg.el new file mode 100644 index 0000000..92154d6 --- /dev/null +++ b/code/elpa/powershell-20220805.1712/powershell-pkg.el @@ -0,0 +1,2 @@ +;;; Generated package description from powershell.el -*- no-byte-compile: t -*- +(define-package "powershell" "20220805.1712" "Mode for editing PowerShell scripts" '((emacs "24")) :commit "f2da15857e430206e215a3c65289b4058ae3c976" :authors '(("Frédéric Perrin ")) :maintainer '("Frédéric Perrin ") :keywords '("powershell" "languages") :url "http://github.com/jschaf/powershell.el") diff --git a/code/elpa/powershell-20220805.1712/powershell.el b/code/elpa/powershell-20220805.1712/powershell.el new file mode 100644 index 0000000..b482deb --- /dev/null +++ b/code/elpa/powershell-20220805.1712/powershell.el @@ -0,0 +1,1394 @@ +;;; powershell.el --- Mode for editing PowerShell scripts -*- lexical-binding: t; -*- + +;; Copyright (C) 2009, 2010 Frédéric Perrin +;; Copyright (C) 2012 Richard Bielawski rbielaws-at-i1-dot-net +;; http://www.emacswiki.org/emacs/Rick_Bielawski + +;; Author: Frédéric Perrin +;; URL: http://github.com/jschaf/powershell.el +;; Package-Version: 20220805.1712 +;; Package-Commit: f2da15857e430206e215a3c65289b4058ae3c976 +;; Version: 0.3 +;; Package-Requires: ((emacs "24")) +;; Keywords: powershell, languages + +;; This file is NOT part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Installation: + +;; Place powershell.el on your `load-path' by adding the following +;; code to your `user-init-file', which is usually ~/.emacs.d/init.el +;; or ~/.emacs. +;; +;; (add-to-list 'load-path "~/path/to/powershell") +;; + +;;; Commentary: + +;; powershell.el is a combination of powershell.el by Dino Chiesa +;; and powershell-mode.el by Frédéric Perrin +;; and Richard Bielawski. Joe Schafer combined the work into a single +;; file. + +;;; Frédéric Perrin Comments: +;; +;; The original powershell-mode.el was written from scratch, without +;; using Vivek Sharma's code: it had issues I wanted to correct, but +;; unfortunately there were no licence indication, and Vivek didn't +;; answered my mails. +;; +;;; Rick Bielawski Comments 2012/09/28: +;; +;; On March 31, 2012 Frédéric gave me permission to take over support +;; for powershell-mode.el. I've added support for multi-line comments +;; and here-strings as well as enhancement/features such as: Functions +;; to quote, unquote and escape a selection, and one to wrap a +;; selection in $(). Meanwhile I hope I didn't break anything. +;; +;; Joe Schafer Comments 2013-06-06: +;; +;; I combined powershell.el and powershell-mode.el. Since +;; powershell.el was licensed with the new BSD license I combined the +;; two files using the more restrictive license, the GPL. I also +;; cleaned up the documentation and reorganized some of the code. + +;;; Updates: + +;; 2012/10/01 Fixed several bugs in highlighting variables and types. +;; Renamed some variables to be more descriptive. +;; 2012/10/02 Enhanced PowerShell-mode indenting & syntax table. +;; Fixed dangling parens and re-indented the elisp itself. +;; 2012/10/05 Added eldoc support. Fixed bug where indent could loop. +;; See comment below on how to generate powershell-eldoc.el +;; 2013/06/06 Merged powershell.el and powershell-mode.el + +;;; Code: + +(eval-when-compile (require 'thingatpt)) +(require 'shell) +(require 'compile) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.ps[dm]?1\\'" . powershell-mode)) + + +;; User Variables + +(defgroup powershell nil + "Customization of PowerShell mode." + :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces) + :group 'languages) + +(defcustom powershell-indent 4 + "Amount of horizontal space to indent. +After, for instance, an opening brace" + :type 'integer + :group 'powershell) + +(defcustom powershell-continuation-indent 2 + "Amount of horizontal space to indent a continuation line." + :type 'integer + :group 'powershell) + +(defcustom powershell-continued-regexp ".*\\(|[\\t ]*\\|`\\)$" + "Regexp matching a continued line. +Ending either with an explicit backtick, or with a pipe." + :type 'integer + :group 'powershell) + +;; Note: There are no explicit references to the variable +;; `explicit-powershell.exe-args'. It is used implicitly by M-x shell +;; when the shell is `powershell.exe'. See +;; http://blogs.msdn.com/b/dotnetinterop/archive/2008/04/10/run-powershell-as-a-shell-within-emacs.aspx +;; for details. +(defcustom explicit-powershell.exe-args '("-Command" "-" ) + "Args passed to inferior shell by \\[shell], if the shell is powershell.exe. +Value is a list of strings, which may be nil." + :type '(repeat (string :tag "Argument")) + :group 'powershell) + +(defun powershell-continuation-line-p () + "Return t is the current line is a continuation line. +The current line is a continued line when the previous line ends +with a backtick or a pipe" + (interactive) + (save-excursion + (forward-line -1) + (looking-at powershell-continued-regexp))) + +;; Rick added significant complexity to Frédéric's original version +(defun powershell-indent-line-amount () + "Return the column to which the current line ought to be indented." + (interactive) + (save-excursion + (beginning-of-line) + (if (powershell-continuation-line-p) + ;; on a continuation line (i.e. prior line ends with backtick + ;; or pipe), indent relative to the continued line. + (progn + (while (and (not (bobp))(powershell-continuation-line-p)) + (forward-line -1)) + (+ (current-indentation) powershell-continuation-indent)) + ;; otherwise, indent relative to the block's opening char ([{ + ;; \\s- includes newline, which make the line right before closing paren not indented + (let ((closing-paren (looking-at "[ \t]*\\s)")) + new-indent + block-open-line) + (condition-case nil + (progn + (backward-up-list) ;when at top level, throw to no-indent + (setq block-open-line (line-number-at-pos)) + ;; We're in a block, calculate/return indent amount. + (if (not (looking-at "\\s(\\s-*\\(#.*\\)?$")) + ;; code (not comments) follow the block open so + ;; vertically align the block with the code. + (if closing-paren + ;; closing indent = open + (setq new-indent (current-column)) + ;; block indent = first line of code + (forward-char) + (skip-syntax-forward " ") + (setq new-indent (current-column))) + ;; otherwise block open is at eol so indent is relative to + ;; bol or another block open on the same line. + (if closing-paren ; this sets the default indent + (setq new-indent (current-indentation)) + (setq new-indent (+ powershell-indent (current-indentation)))) + ;; now see if the block is nested on the same line + (when (condition-case nil + (progn + (backward-up-list) + (= block-open-line (line-number-at-pos))) + (scan-error nil)) + (forward-char) + (skip-syntax-forward " ") + (if closing-paren + (setq new-indent (current-column)) + (setq new-indent (+ powershell-indent (current-column)))))) + new-indent) + (scan-error ;; most likely, we are at the top-level + 0)))))) + +(defun powershell-indent-line () + "Indent the current line of powershell mode. +Leave the point in place if it is inside the meat of the line" + (interactive) + (let ((savep (> (current-column) (current-indentation))) + (amount (powershell-indent-line-amount))) + (if savep + (save-excursion (indent-line-to amount)) + (indent-line-to amount)))) + +(defun powershell-quote-selection (beg end) + "Quotes the selection between BEG and END. +Quotes with single quotes and doubles embedded single quotes." + (interactive `(,(region-beginning) ,(region-end))) + (if (not mark-active) + (error "Command requires a marked region")) + (goto-char beg) + (while (re-search-forward "'" end t) + (replace-match "''")(setq end (1+ end))) + (goto-char beg) + (insert "'") + (setq end (1+ end)) + (goto-char end) + (insert "'")) + +(defun powershell-unquote-selection (beg end) + "Unquotes the selected text between BEG and END. +Remove doubled single quotes as we go." + (interactive `(,(region-beginning) ,(region-end))) + (if (not mark-active) + (error "Command requires a marked region")) + (goto-char beg) + (cond ((looking-at "'") + (goto-char end) + (when (looking-back "'" nil) + (delete-char -1) + (setq end (1- end)) + (goto-char beg) + (delete-char 1) + (setq end (1- end)) + (while (search-forward "'" end t) + (delete-char -1) + (forward-char) + (setq end (1- end))))) + ((looking-at "\"") + (goto-char end) + (when (looking-back "\"" nil) + (delete-char -1) + (setq end (1- end)) + (goto-char beg) + (delete-char 1) + (setq end (1- end)) + (while (search-forward "\"" end t) + (delete-char -1) + (forward-char) + (setq end (1- end))) + (while (search-forward "`" end t) + (delete-char -1) + (forward-char) + (setq end (1- end))))) + (t (error "Must select quoted text exactly")))) + +(defun powershell-escape-selection (beg end) + "Escape variables between BEG and END. +Also extend existing escapes." + (interactive `(,(region-beginning) ,(region-end))) + (if (not mark-active) + (error "Command requires a marked region")) + (goto-char beg) + (while (re-search-forward "`" end t) + (replace-match "```")(setq end (+ end 2))) + (goto-char beg) + (while (re-search-forward "\\(?:\\=\\|[^`]\\)[$]" end t) + (goto-char (car (cdr (match-data)))) + (backward-char) + (insert "`") + (forward-char) + (setq end (1+ end)))) + +(defun powershell-doublequote-selection (beg end) + "Quotes the text between BEG and END with double quotes. +Embedded quotes are doubled." + (interactive `(,(region-beginning) ,(region-end))) + (if (not mark-active) + (error "Command requires a marked region")) + (goto-char beg) + (while (re-search-forward "\"" end t) + (replace-match "\"\"")(setq end (1+ end))) + (goto-char beg) + (while (re-search-forward "`'" end t) + (replace-match "```")(setq end (+ 2 end))) + (goto-char beg) + (insert "\"") + (setq end (1+ end)) + (goto-char end) + (insert "\"")) + +(defun powershell-dollarparen-selection (beg end) + "Wraps the text between BEG and END with $(). +The point is moved to the closing paren." + (interactive `(,(region-beginning) ,(region-end))) + (if (not mark-active) + (error "Command requires a marked region")) + (save-excursion + (goto-char end) + (insert ")") + (goto-char beg) + (insert "$(")) + (forward-char)) + +(defun powershell-regexp-to-regex (beg end) + "Turn the text between BEG and END into a regex. +The text is assumed to be `regexp-opt' output." + (interactive `(,(region-beginning) ,(region-end))) + (if (not mark-active) + (error "Command requires a marked region")) + (save-restriction + (narrow-to-region beg end) + (goto-char (point-min)) + (while (re-search-forward "\\\\(" nil t) + (replace-match "(")) + (goto-char (point-min)) + (while (re-search-forward "\\\\)" nil t) + (replace-match ")")) + (goto-char (point-min)) + (while (re-search-forward "\\\\|" nil t) + (replace-match "|")))) + + +;; Taken from About_Keywords +(defvar powershell-keywords + (concat "\\_<" + (regexp-opt + '("begin" "break" "catch" "class" "continue" "data" "define" "do" "default" + "dynamicparam" "else" "elseif" "end" "enum" "exit" "filter" "finally" + "for" "foreach" "from" "function" "hidden" "if" "in" "param" "process" + "return" "static" "switch" "throw" "trap" "try" "until" "using" "var" "where" "while" + ;; Questionable, specific to workflow sessions + "inlinescript") + t) + "\\_>") + "PowerShell keywords.") + +;; Taken from About_Comparison_Operators and some questionable sources :-) +(defvar powershell-operators + (concat "\\_<" + (regexp-opt + '("-eq" "-ne" "-gt" "-ge" "-lt" "-le" + ;; case sensitive versions + "-ceq" "-cne" "-cgt" "-cge" "-clt" "-cle" + ;; explicitly case insensitive + "-ieq" "-ine" "-igt" "-ige" "-ilt" "-ile" + "-band" "-bor" "-bxor" "-bnot" + "-and" "-or" "-xor" "-not" "!" + "-like" "-notlike" "-clike" "-cnotlike" "-ilike" "-inotlike" + "-match" "-notmatch" "-cmatch" "-cnotmatch" "-imatch" "-inotmatch" + "-contains" "-notcontains" "-ccontains" "-cnotcontains" + "-icontains" "-inotcontains" + "-replace" "-creplace" "-ireplace" + "-is" "-isnot" "-as" "-f" + "-in" "-cin" "-iin" "-notin" "-cnotin" "-inotin" + "-split" "-csplit" "-isplit" + "-join" + "-shl" "-shr" + ;; Questionable --> specific to certain contexts + "-casesensitive" "-wildcard" "-regex" "-exact" ;specific to case + "-begin" "-process" "-end" ;specific to scriptblock + ) t) + "\\_>") + "PowerShell operators.") + +(defvar powershell-scope-names + '("global" "local" "private" "script" ) + "Names of scopes in PowerShell mode.") + +(defvar powershell-variable-drive-names + (append '("env" "function" "variable" "alias" "hklm" "hkcu" "wsman") powershell-scope-names) + "Names of scopes in PowerShell mode.") + +(defconst powershell-variables-regexp + ;; There are 2 syntaxes detected: ${[scope:]name} and $[scope:]name + ;; Match 0 is the entire variable name. + ;; Match 1 is scope when the former syntax is found. + ;; Match 2 is scope when the latter syntax is found. + (concat + "\\_<$\\(?:{\\(?:" (regexp-opt powershell-variable-drive-names t) + ":\\)?[^}]+}\\|" + "\\(?:" (regexp-opt powershell-variable-drive-names t) + ":\\)?[a-zA-Z0-9_]+\\_>\\)") + "Identifies legal powershell variable names.") + +(defconst powershell-function-names-regex + ;; Syntax detected is [scope:]verb-noun + ;; Match 0 is the entire name. + ;; Match 1 is the scope if any. + ;; Match 2 is the function name (which must exist) + (concat + "\\_<\\(?:" (regexp-opt powershell-scope-names t) ":\\)?" + "\\([A-Z][a-zA-Z0-9]*-[A-Z0-9][a-zA-Z0-9]*\\)\\_>") + "Identifies legal function & filter names.") + +(defconst powershell-object-types-regexp + ;; Syntax is \[name[.name]\] (where the escaped []s are literal) + ;; Only Match 0 is returned. + "\\[\\(?:[a-zA-Z_][a-zA-Z0-9]*\\)\\(?:\\.[a-zA-Z_][a-zA-Z0-9]*\\)*\\]" + "Identifies object type references. I.E. [object.data.type] syntax.") + +(defconst powershell-function-switch-names-regexp + ;; Only Match 0 is returned. + "\\_<-[a-zA-Z][a-zA-Z0-9]*\\_>" + "Identifies function parameter names of the form -xxxx.") + +;; Taken from Get-Variable on a fresh shell, merged with man +;; about_automatic_variables +(defvar powershell-builtin-variables-regexp + (regexp-opt + '("$" "?" + "^" "_" + "args" "ConsoleFileName" + "Error" "Event" + "EventArgs" + "EventSubscriber" "ExecutionContext" + "false" "Foreach" + "HOME" "Host" + "input" "lsCoreCLR" + "lsLinux" "lsMacOS" + "lsWindows" "LASTEXITCODE" + "Matches" "MyInvocation" + "NestedPromptLevel" "null" + "PID" "PROFILE" + "PSBoundParameters" "PSCmdlet" + "PSCommandPath" + "PSCulture" "PSDebugContext" + "PSHOME" "PSITEM" + "PSScriptRoot" "PSSenderInfo" + "PSUICulture" "PSVersionTable" + "PWD" "ReportErrorShowExceptionClass" + "ReportErrorShowInnerException" "ReportErrorShowSource" + "ReportErrorShowStackTrace" "Sender" + "ShellId" "SourceArgs" + "SourceEventArgs" "StackTrace" + "this" "true" ) t) + "The names of the built-in PowerShell variables. +They are highlighted differently from the other variables.") + +(defvar powershell-config-variables-regexp + (regexp-opt + '("ConfirmPreference" "DebugPreference" + "ErrorActionPreference" "ErrorView" + "FormatEnumerationLimit" "InformationPreference" + "LogCommandHealthEvent" + "LogCommandLifecycleEvent" "LogEngineHealthEvent" + "LogEngineLifecycleEvent" "LogProviderHealthEvent" + "LogProviderLifecycleEvent" "MaximumAliasCount" + "MaximumDriveCount" "MaximumErrorCount" + "MaximumFunctionCount" "MaximumHistoryCount" + "MaximumVariableCount" "OFS" + "OutputEncoding" "ProgressPreference" + "PSDefaultParameterValues" "PSEmailServer" + "PSModuleAutoLoadingPreference" "PSSessionApplicationName" + "PSSessionConfigurationName" "PSSessionOption" + "VerbosePreference" "WarningPreference" + "WhatIfPreference" ) t) + "Names of variables that configure powershell features.") + + +(defun powershell-find-syntactic-comments (limit) + "Find PowerShell comment begin and comment end characters. +Returns match 1 and match 2 for <# #> comment sequences respectively. +Returns match 3 and optionally match 4 for #/eol comments. +Match 4 is returned only if eol is found before LIMIT" + (when (search-forward "#" limit t) + (cond + ((looking-back "<#" nil) + (set-match-data (list (match-beginning 0) (1+ (match-beginning 0)) + (match-beginning 0) (1+ (match-beginning 0))))) + ((looking-at ">") + (set-match-data (list (match-beginning 0) (match-end 0) + nil nil + (match-beginning 0) (match-end 0))) + (forward-char)) + (t + (let ((start (point))) + (if (search-forward "\n" limit t) + (set-match-data (list (1- start) (match-end 0) + nil nil nil nil + (1- start) start + (match-beginning 0) (match-end 0))) + (set-match-data (list start (match-end 0) + nil nil nil nil + (1- start) start)))))) + t)) + +(defun powershell-find-syntactic-quotes (limit) + "Find PowerShell hear string begin and end sequences upto LIMIT. +Returns match 1 and match 2 for @' '@ sequences respectively. +Returns match 3 and match 4 for @\" \"@ sequences respectively." + (when (search-forward "@" limit t) + (cond + ((looking-at "'$") + (set-match-data (list (match-beginning 0) (1+ (match-beginning 0)) + (match-beginning 0) (1+ (match-beginning 0)))) + (forward-char)) + ((looking-back "^'@" nil) + (set-match-data (list (1- (match-end 0)) (match-end 0) + nil nil + (1- (match-end 0)) (match-end 0)))) + ((looking-at "\"$") + (set-match-data (list (match-beginning 0) (1+ (match-beginning 0)) + nil nil + nil nil + (match-beginning 0) (1+ (match-beginning 0)))) + (forward-char)) + ((looking-back "^\"@" nil) + (set-match-data (list (1- (match-end 0)) (match-end 0) + nil nil + nil nil + nil nil + (1- (match-end 0)) (match-end 0))))) + t)) +(defvar powershell-font-lock-syntactic-keywords + `((powershell-find-syntactic-comments (1 "!" t t) (2 "!" t t) + (3 "<" t t) (4 ">" t t)) + (powershell-find-syntactic-quotes (1 "|" t t) (2 "|" t t) + (3 "|" t t) (4 "|" t t))) + "A list of regexp's or functions. +Used to add `syntax-table' properties to +characters that can't be set by the `syntax-table' alone.") + + +(defvar powershell-font-lock-keywords-1 + `( ;; Type annotations + (,powershell-object-types-regexp . font-lock-type-face) + ;; syntaxic keywords + (,powershell-keywords . font-lock-keyword-face) + ;; operators + (,powershell-operators . font-lock-builtin-face) + ;; the REQUIRES mark + ("^#\\(REQUIRES\\)" 1 font-lock-warning-face t)) + "Keywords for the first level of font-locking in PowerShell mode.") + +(defvar powershell-font-lock-keywords-2 + (append + powershell-font-lock-keywords-1 + `( ;; Built-in variables + (,(concat "\\$\\(" powershell-builtin-variables-regexp "\\)\\>") + 0 font-lock-builtin-face t) + (,(concat "\\$\\(" powershell-config-variables-regexp "\\)\\>") + 0 font-lock-builtin-face t))) + "Keywords for the second level of font-locking in PowerShell mode.") + +(defvar powershell-font-lock-keywords-3 + (append + powershell-font-lock-keywords-2 + `( ;; user variables + (,powershell-variables-regexp + (0 font-lock-variable-name-face) + (1 (cons font-lock-type-face '(underline)) t t) + (2 (cons font-lock-type-face '(underline)) t t)) + ;; function argument names + (,powershell-function-switch-names-regexp + (0 font-lock-constant-face) + (1 (cons font-lock-type-face '(underline)) t t) + (2 (cons font-lock-type-face '(underline)) t t)) + ;; function names + (,powershell-function-names-regex + (0 font-lock-function-name-face) + (1 (cons font-lock-type-face '(underline)) t t)))) + "Keywords for the maximum level of font-locking in PowerShell mode.") + + +(defun powershell-setup-font-lock () + "Set up the buffer local value for `font-lock-defaults'." + ;; I use font-lock-syntactic-keywords to set some properties and I + ;; don't want them ignored. + (set (make-local-variable 'parse-sexp-lookup-properties) t) + ;; This is where all the font-lock stuff actually gets set up. Once + ;; font-lock-defaults has its value, setting font-lock-mode true should + ;; cause all your syntax highlighting dreams to come true. + (setq font-lock-defaults + ;; The first value is all the keyword expressions. + '((powershell-font-lock-keywords-1 + powershell-font-lock-keywords-2 + powershell-font-lock-keywords-3) + ;; keywords-only means no strings or comments get fontified + nil + ;; case-fold (t ignores case) + t + ;; syntax-alist nothing special here + nil + ;; syntax-begin - no function defined to move outside syntactic block + nil + ;; font-lock-syntactic-keywords + ;; takes (matcher (match syntax override lexmatch) ...)... + (font-lock-syntactic-keywords + . powershell-font-lock-syntactic-keywords)))) + +(defvar powershell-mode-syntax-table + (let ((powershell-mode-syntax-table (make-syntax-table))) + (modify-syntax-entry ?$ "_" powershell-mode-syntax-table) + (modify-syntax-entry ?: "_" powershell-mode-syntax-table) + (modify-syntax-entry ?- "_" powershell-mode-syntax-table) + (modify-syntax-entry ?^ "_" powershell-mode-syntax-table) + (modify-syntax-entry ?\\ "_" powershell-mode-syntax-table) + (modify-syntax-entry ?\{ "(}" powershell-mode-syntax-table) + (modify-syntax-entry ?\} "){" powershell-mode-syntax-table) + (modify-syntax-entry ?\[ "(]" powershell-mode-syntax-table) + (modify-syntax-entry ?\] ")[" powershell-mode-syntax-table) + (modify-syntax-entry ?\( "()" powershell-mode-syntax-table) + (modify-syntax-entry ?\) ")(" powershell-mode-syntax-table) + (modify-syntax-entry ?` "\\" powershell-mode-syntax-table) + (modify-syntax-entry ?_ "w" powershell-mode-syntax-table) + (modify-syntax-entry ?= "." powershell-mode-syntax-table) + (modify-syntax-entry ?| "." powershell-mode-syntax-table) + (modify-syntax-entry ?+ "." powershell-mode-syntax-table) + (modify-syntax-entry ?* "." powershell-mode-syntax-table) + (modify-syntax-entry ?/ "." powershell-mode-syntax-table) + (modify-syntax-entry ?' "\"" powershell-mode-syntax-table) + (modify-syntax-entry ?# "<" powershell-mode-syntax-table) + powershell-mode-syntax-table) + "Syntax for PowerShell major mode.") + +(defvar powershell-mode-map + (let ((powershell-mode-map (make-keymap))) + ;; (define-key powershell-mode-map "\r" 'powershell-indent-line) + (define-key powershell-mode-map (kbd "M-\"") + 'powershell-doublequote-selection) + (define-key powershell-mode-map (kbd "M-'") 'powershell-quote-selection) + (define-key powershell-mode-map (kbd "C-'") 'powershell-unquote-selection) + (define-key powershell-mode-map (kbd "C-\"") 'powershell-unquote-selection) + (define-key powershell-mode-map (kbd "M-`") 'powershell-escape-selection) + (define-key powershell-mode-map (kbd "C-$") + 'powershell-dollarparen-selection) + powershell-mode-map) + "Keymap for PS major mode.") + +(defun powershell-setup-menu () + "Add a menu of PowerShell specific functions to the menu bar." + (define-key (current-local-map) [menu-bar powershell-menu] + (cons "PowerShell" (make-sparse-keymap "PowerShell"))) + (define-key (current-local-map) [menu-bar powershell-menu doublequote] + '(menu-item "DoubleQuote Selection" powershell-doublequote-selection + :key-sequence(kbd "M-\"") + :help + "DoubleQuotes the selection escaping embedded double quotes")) + (define-key (current-local-map) [menu-bar powershell-menu quote] + '(menu-item "SingleQuote Selection" powershell-quote-selection + :key-sequence (kbd "M-'") + :help + "SingleQuotes the selection escaping embedded single quotes")) + (define-key (current-local-map) [menu-bar powershell-menu unquote] + '(menu-item "UnQuote Selection" powershell-unquote-selection + :key-sequence (kbd "C-'") + :help "Un-Quotes the selection un-escaping any escaped quotes")) + (define-key (current-local-map) [menu-bar powershell-menu escape] + '(menu-item "Escape Selection" powershell-escape-selection + :key-sequence (kbd "M-`") + :help (concat "Escapes variables in the selection" + " and extends existing escapes."))) + (define-key (current-local-map) [menu-bar powershell-menu dollarparen] + '(menu-item "DollarParen Selection" powershell-dollarparen-selection + :key-sequence (kbd "C-$") + :help "Wraps the selection in $()"))) + + +;;; Eldoc support + +(defcustom powershell-eldoc-def-files nil + "List of files containing function help strings used by function `eldoc-mode'. +These are the strings function `eldoc-mode' displays as help for +functions near point. The format of the file must be exactly as +follows or who knows what happens. + + (set (intern \"\" powershell-eldoc-obarray) \"\") + (set (intern \"\" powershell-eldoc-obarray) \"\") +... + +Where is the name of the function to which applies. + is the string to display when point is near ." + :type '(repeat string) + :group 'powershell) + +(defvar powershell-eldoc-obarray () + "Array for file entries by the function `eldoc'. +`powershell-eldoc-def-files' entries are added into this array.") + +(defun powershell-eldoc-function () + "Return a documentation string appropriate for the current context or nil." + (let ((word (thing-at-point 'symbol))) + (if word + (eval (intern-soft word powershell-eldoc-obarray))))) + +(defun powershell-setup-eldoc () + "Load the function documentation for use with eldoc." + (when (not (null powershell-eldoc-def-files)) + (set (make-local-variable 'eldoc-documentation-function) + 'powershell-eldoc-function) + (unless (vectorp powershell-eldoc-obarray) + (setq powershell-eldoc-obarray (make-vector 41 0)) + (condition-case var (mapc 'load powershell-eldoc-def-files) + (error (message "*** powershell-setup-eldoc ERROR *** %s" var)))))) +;;; Note: You can create quite a bit of help with these commands: +;; +;; function Get-Signature ($Cmd) { +;; if ($Cmd -is [Management.Automation.PSMethod]) { +;; $List = @($Cmd)} +;; elseif ($Cmd -isnot [string]) { +;; throw ("Get-Signature {|}`n" + +;; "'$Cmd' is not a method or command")} +;; else {$List = @(Get-Command $Cmd -ErrorAction SilentlyContinue)} +;; if (!$List[0] ) { +;; throw "Command '$Cmd' not found"} +;; foreach ($O in $List) { +;; switch -regex ($O.GetType().Name) { +;; 'AliasInfo' { +;; Get-Signature ($O.Definition)} +;; '(Cmdlet|ExternalScript)Info' { +;; $O.Definition} # not sure what to do with ExternalScript +;; 'F(unction|ilter)Info'{ +;; if ($O.Definition -match '^param *\(') { +;; $t = [Management.Automation.PSParser]::tokenize($O.Definition, +;; [ref]$null) +;; $c = 1;$i = 1 +;; while($c -and $i++ -lt $t.count) { +;; switch ($t[$i].Type.ToString()) { +;; GroupStart {$c++} +;; GroupEnd {$c--}}} +;; $O.Definition.substring(0,$t[$i].start + 1)} #needs parsing +;; else {$O.Name}} +;; 'PSMethod' { +;; foreach ($t in @($O.OverloadDefinitions)) { +;; while (($b=$t.IndexOf('`1[[')) -ge 0) { +;; $t=$t.remove($b,$t.IndexOf(']]')-$b+2)} +;; $t}}}}} +;; get-command| +;; ?{$_.CommandType -ne 'Alias' -and $_.Name -notlike '*:'}| +;; %{$_.Name}| +;; sort| +;; %{("(set (intern ""$($_.Replace('\','\\'))"" powershell-eldoc-obarray)" + +;; " ""$(Get-Signature $_|%{$_.Replace('\','\\').Replace('"','\"')})"")" +;; ).Replace("`r`n"")",""")")} > .\powershell-eldoc.el + + +(defvar powershell-imenu-expression + `(("Functions" ,(concat "function " powershell-function-names-regex) 2) + ("Filters" ,(concat "filter " powershell-function-names-regex) 2) + ("Top variables" + , (concat "^\\(" powershell-object-types-regexp "\\)?\\(" + powershell-variables-regexp "\\)\\s-*=") + 2)) + "List of regexps matching important expressions, for speebar & imenu.") + +(defun powershell-setup-imenu () + "Install `powershell-imenu-expression'." + (when (require 'imenu nil t) + ;; imenu doc says these are buffer-local by default + (setq imenu-generic-expression powershell-imenu-expression) + (setq imenu-case-fold-search nil) + (imenu-add-menubar-index))) + +(defun powershell-setup-speedbar () + "Install `speedbar-add-supported-extension'." + (when (require 'speedbar nil t) + (speedbar-add-supported-extension ".ps1?"))) + +;; A better command would be something like "powershell.exe -NoLogo +;; -NonInteractive -Command & (buffer-file-name)". But it will just +;; sit there waiting... The following will only work when .ps1 files +;; are associated with powershell.exe. And if they don't contain spaces. +(defvar powershell-compile-command + '(buffer-file-name) + "Default command used to invoke a powershell script.") + +;; The column number will be off whenever tabs are used. Since this is +;; the default in this mode, we will not capture the column number. +(setq compilation-error-regexp-alist + (cons '("At \\(.*\\):\\([0-9]+\\) char:\\([0-9]+\\)" 1 2) + compilation-error-regexp-alist)) + + +(add-hook 'powershell-mode-hook #'imenu-add-menubar-index) + +;;;###autoload +(define-derived-mode powershell-mode prog-mode "PS" + "Major mode for editing PowerShell scripts. + +\\{powershell-mode-map} +Entry to this mode calls the value of `powershell-mode-hook' if +that value is non-nil." + (powershell-setup-font-lock) + (setq-local indent-line-function 'powershell-indent-line) + (setq-local compile-command powershell-compile-command) + (setq-local comment-start "#") + (setq-local comment-start-skip "#+\\s*") + (setq-local parse-sexp-ignore-comments t) + ;; Support electric-pair-mode + (setq-local electric-indent-chars + (append "{}():;," electric-indent-chars)) + (powershell-setup-imenu) + (powershell-setup-speedbar) + (powershell-setup-menu) + (powershell-setup-eldoc)) + +;;; PowerShell inferior mode + +;;; Code: +(defcustom powershell-location-of-exe + (or (executable-find "pwsh") (executable-find "powershell")) + "A string providing the location of the powershell executable. Since +the newer PowerShell Core (pwsh.exe) does not replace the older Windows +PowerShell (powershell.exe) when installed, this attempts to find the +former first, and only if it doesn't exist, falls back to the latter." + :group 'powershell + :type 'string) + +(defcustom powershell-log-level 3 + "The current log level for powershell internal operations. +0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG." + :group 'powershell + :type 'integer) + +(defcustom powershell-squish-results-of-silent-commands t + "The function `powershell-invoke-command-silently' returns the results +of a command in a string. PowerShell by default, inserts newlines when +the output exceeds the configured width of the powershell virtual +window. In some cases callers might want to get the results with the +newlines and formatting removed. Set this to true, to do that." + :group 'powershell + :type 'boolean) + +(defvar powershell-prompt-regex "PS [^#$%>]+> " + "Regexp to match the powershell prompt. +powershell.el uses this regex to determine when a command has +completed. Therefore, you need to set this appropriately if you +explicitly change the prompt function in powershell. Any value +should include a trailing space, if the powershell prompt uses a +trailing space, but should not include a trailing newline. + +The default value will match the default PowerShell prompt.") + +(defvar powershell-command-reply nil + "The reply of powershell commands. +This is retained for housekeeping purposes.") + +(defvar powershell--max-window-width 0 + "The maximum width of a powershell window. +You shouldn't need to ever set this. It gets set automatically, +once, when the powershell starts up.") + +(defvar powershell-command-timeout-seconds 12 + "The timeout for a powershell command. +powershell.el will wait this long before giving up.") + +(defvar powershell--need-rawui-resize t + "No need to fuss with this. It's intended for internal use +only. It gets set when powershell needs to be informed that +emacs has resized its window.") + +(defconst powershell--find-max-window-width-command + (concat + "function _Emacs_GetMaxPhsWindowSize" + " {" + " $rawui = (Get-Host).UI.RawUI;" + " $mpws_exists = ($rawui | Get-Member | Where-Object" + " {$_.Name -eq \"MaxPhysicalWindowSize\"});" + " if ($mpws_exists -eq $null) {" + " 210" + " } else {" + " $rawui.MaxPhysicalWindowSize.Width" + " }" + " };" + " _Emacs_GetMaxPhsWindowSize") + "The powershell logic to determine the max physical window width.") + +(defconst powershell--set-window-width-fn-name "_Emacs_SetWindowWidth" + "The name of the function this mode defines in PowerShell to +set the window width. Intended for internal use only.") + +(defconst powershell--text-of-set-window-width-ps-function + ;; see + ;; http://blogs.msdn.com/lior/archive/2009/05/27/ResizePowerShellConsoleWindow.aspx + ;; + ;; When making the console window narrower, you mus set the window + ;; size first. When making the console window wider, you must set the + ;; buffer size first. + + (concat "function " powershell--set-window-width-fn-name + "([string] $pswidth)" + " {" + " $rawui = (Get-Host).UI.RawUI;" + " $bufsize = $rawui.BufferSize;" + " $winsize = $rawui.WindowSize;" + " $cwidth = $winsize.Width;" + " $winsize.Width = $pswidth;" + " $bufsize.Width = $pswidth;" + " if ($cwidth -lt $pswidth) {" + " $rawui.BufferSize = $bufsize;" + " $rawui.WindowSize = $winsize;" + " }" + " elseif ($cwidth -gt $pswidth) {" + " $rawui.WindowSize = $winsize;" + " $rawui.BufferSize = $bufsize;" + " };" + " Set-Variable -name rawui -value $null;" + " Set-Variable -name winsize -value $null;" + " Set-Variable -name bufsize -value $null;" + " Set-Variable -name cwidth -value $null;" + " }") + + "The text of the powershell function that will be used at runtime to +set the width of the virtual Window in PowerShell, as the Emacs window +gets resized.") + +(defun powershell-log (level text &rest args) + "Log a message at level LEVEL. +If LEVEL is higher than `powershell-log-level', the message is +ignored. Otherwise, it is printed using `message'. +TEXT is a format control string, and the remaining arguments ARGS +are the string substitutions (see `format')." + (if (<= level powershell-log-level) + (let* ((msg (apply 'format text args))) + (message "%s" msg)))) + +;; (defun dino-powershell-complete (arg) +;; "do powershell completion on the given STRING. Pop up a buffer +;; with the completion list." +;; (interactive +;; (list (read-no-blanks-input "\ +;; Stub to complete: "))) + +;; (let ((proc +;; (get-buffer-process (current-buffer)))) +;; (comint-proc-query proc (concat "Get-Command " arg "*\n")) +;; ) +;; ) + +;; (defun dino-powershell-cmd-complete () +;; "try to get powershell completion to work." +;; (interactive) +;; (let ((proc +;; (get-buffer-process (current-buffer)))) +;; ;; (comint-proc-query proc "Get-a\t") +;; ;; (comint-simple-send proc "Get-a\t") +;; (comint-send-string proc "Get-a\t\n") +;; ;; (process-send-eof) +;; ) +;; ) + +(defun powershell--define-set-window-width-function (proc) + "Sends a function definition to the PowerShell instance +identified by PROC. The function sets the window width of the +PowerShell virtual window. Later, the function will be called +when the width of the emacs window changes." + (if proc + (progn + ;;process-send-string + (comint-simple-send + proc + powershell--text-of-set-window-width-ps-function)))) + +(defun powershell--get-max-window-width (buffer-name) + "Gets the maximum width of the virtual window for PowerShell running +in the buffer with name BUFFER-NAME. + +In PowerShell 1.0, the maximum WindowSize.Width for +PowerShell is 210, hardcoded, I believe. In PowerShell 2.0, the max +windowsize.Width is provided in the RawUI.MaxPhysicalWindowSize +property. + +This function does the right thing, and sets the buffer-local +`powershell--max-window-width' variable with the correct value." + (let ((proc (get-buffer-process buffer-name))) + + (if proc + (with-current-buffer buffer-name + (powershell-invoke-command-silently + proc + powershell--find-max-window-width-command + 0.90) + + ;; store the retrieved width + (setq powershell--max-window-width + (if (and (not (null powershell-command-reply)) + (string-match + "\\([1-9][0-9]*\\)[ \t\f\v\n]+" + powershell-command-reply)) + (string-to-number (match-string 1 powershell-command-reply)) + 200)))))) ;; could go to 210, but let's use 200 to be safe + +(defun powershell--set-window-width (proc) + "Run the PowerShell function that sets the RawUI width +appropriately for a PowerShell shell. + +This is necessary to get powershell to do the right thing, as far +as text formatting, when the emacs window gets resized. + +The function gets defined in powershell upon powershell startup." + (let ((ps-width + (number-to-string (min powershell--max-window-width (window-width))))) + (progn + ;;(process-send-string + (comint-simple-send + proc + (concat powershell--set-window-width-fn-name + "('" ps-width "')"))))) + +;;;###autoload +(defun powershell (&optional buffer prompt-string) + "Run an inferior PowerShell. +If BUFFER is non-nil, use it to hold the powershell +process. Defaults to *PowerShell*. + +Interactively, a prefix arg means to prompt for BUFFER. + +If BUFFER exists but the shell process is not running, it makes a +new shell. + +If BUFFER exists and the shell process is running, just switch to +BUFFER. + +If PROMPT-STRING is non-nil, sets the prompt to the given value. + +See the help for `shell' for more details. \(Type +\\[describe-mode] in the shell buffer for a list of commands.)" + (interactive + (list + (and current-prefix-arg + (read-buffer "Shell buffer: " + (generate-new-buffer-name "*PowerShell*"))))) + + (setq buffer (get-buffer-create (or buffer "*PowerShell*"))) + (powershell-log 1 "powershell starting up...in buffer %s" (buffer-name buffer)) + (let ((explicit-shell-file-name (if (and (eq system-type 'cygwin) + (fboundp 'cygwin-convert-file-name-from-windows)) + (cygwin-convert-file-name-from-windows powershell-location-of-exe) + powershell-location-of-exe))) + ;; set arguments for the powershell exe. + ;; Does this need to be tunable? + + (shell buffer)) + + ;; (powershell--get-max-window-width "*PowerShell*") + ;; (powershell-invoke-command-silently (get-buffer-process "*csdeshell*") + ;; "[Ionic.Csde.Utilities]::Version()" 2.9) + + ;; (comint-simple-send (get-buffer-process "*csdeshell*") "prompt\n") + + (let ((proc (get-buffer-process buffer))) + + (make-local-variable 'powershell-prompt-regex) + (make-local-variable 'powershell-command-reply) + (make-local-variable 'powershell--max-window-width) + (make-local-variable 'powershell-command-timeout-seconds) + (make-local-variable 'powershell-squish-results-of-silent-commands) + (make-local-variable 'powershell--need-rawui-resize) + (make-local-variable 'comint-prompt-read-only) + + ;; disallow backspace over the prompt: + (setq comint-prompt-read-only t) + + ;; We need to tell powershell how wide the emacs window is, because + ;; powershell pads its output to the width it thinks its window is. + ;; + ;; The way it's done: every time the width of the emacs window changes, we + ;; set a flag. Then, before sending a powershell command that is + ;; typed into the buffer, to the actual powershell process, we check + ;; that flag. If it is set, we resize the powershell window appropriately, + ;; before sending the command. + + ;; If we didn't do this, powershell output would get wrapped at a + ;; column width that would be different than the emacs buffer width, + ;; and everything would look ugly. + + ;; get the maximum width for powershell - can't go beyond this + (powershell--get-max-window-width buffer) + + ;; define the function for use within powershell to resize the window + (powershell--define-set-window-width-function proc) + + ;; add the hook that sets the flag + (add-hook 'window-size-change-functions + #'(lambda (&rest _) + (setq powershell--need-rawui-resize t))) + + ;; set the flag so we resize properly the first time. + (setq powershell--need-rawui-resize t) + + (if prompt-string + (progn + ;; This sets up a prompt for the PowerShell. The prompt is + ;; important because later, after sending a command to the + ;; shell, the scanning logic that grabs the output looks for + ;; the prompt string to determine that the output is complete. + (comint-simple-send + proc + (concat "function prompt { '" prompt-string "' }")) + + (setq powershell-prompt-regex prompt-string))) + + ;; hook the kill-buffer action so we can kill the inferior process? + (add-hook 'kill-buffer-hook 'powershell-delete-process) + + ;; wrap the comint-input-sender with a PS version + ;; must do this after launching the shell! + (make-local-variable 'comint-input-sender) + (setq comint-input-sender 'powershell-simple-send) + + ;; set a preoutput filter for powershell. This will trim newlines + ;; after the prompt. + (add-hook 'comint-preoutput-filter-functions + 'powershell-preoutput-filter-for-prompt) + + ;; send a carriage-return (get the prompt) + (comint-send-input) + (accept-process-output proc)) + + ;; The launch hooks for powershell has not (yet?) been implemented + ;;(run-hooks 'powershell-launch-hook) + + ;; return the buffer created + buffer) + +;; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +;; Using powershell on emacs23, I get an error: +;; +;; ansi-color-process-output: Marker does not point anywhere +;; +;; Here's what's happening. +;; +;; In order to be able to read the output from powershell, this shell +;; starts powershell.exe in "interactive mode", using the -i +;; option. This which has the curious side-effect of turning off the +;; prompt in powershell. Normally powershell will return its results, +;; then emit a prompt to indicate that it is ready for more input. In +;; interactive mode it doesn't emit the prompt. To work around this, +;; this code (powershell.el) sends an explicit `prompt` command after +;; sending any user-entered command to powershell. This tells powershell +;; to explicitly return the prompt, after the results of the prior +;; command. The prompt then shows up in the powershell buffer. Lovely. +;; +;; But, `ansi-color-apply-on-region` gets called after every command +;; gets sent to powershell. It gets called with args `(begin end)`, +;; which are both markers. Turns out the very first time this fn is +;; called, the position for the begin marker is nil. +;; +;; `ansi-color-apply-on-region` calls `(goto-char begin)` (effectively), +;; and when the position on the marker is nil, the call errors with +;; "Marker does not point anywhere." +;; +;; The following advice suppresses the call to +;; `ansi-color-apply-on-region` when the begin marker points +;; nowhere. +(defadvice ansi-color-apply-on-region (around + powershell-throttle-ansi-colorizing + (begin end) + compile) + (progn + (let ((start-pos (marker-position begin))) + (cond + (start-pos + (progn + ad-do-it)))))) + +(defun powershell--silent-cmd-filter (process result) +"A process filter that captures output from a shell and stores it +to `powershell-command-reply', rather than allowing the output to +be displayed in the shell buffer. + +This function is intended for internal use only." + (let ((end-of-result + (string-match (concat ".*\n\\(" powershell-prompt-regex "\\)[ \n]*\\'") + result))) + (if (and end-of-result (numberp end-of-result)) + + (progn + ;; Store everything except the follow-on prompt. + ;; The result probably includes a final newline! + (setq result (substring result 0 (match-beginning 1))) + + (if powershell-squish-results-of-silent-commands + (setq result + (replace-regexp-in-string "\n" "" result))) + + (setq powershell-command-reply + (concat powershell-command-reply result))) + + (progn + (if powershell-squish-results-of-silent-commands + (setq result + (replace-regexp-in-string "\n" "" result))) + + (setq powershell-command-reply + (concat powershell-command-reply result)) + + ;; recurse. For very very long output, the recursion can + ;; cause stack overflow. Careful! + (accept-process-output process powershell-command-timeout-seconds))))) + +(defun powershell-invoke-command-silently (proc command + &optional timeout-seconds) + "In the PowerShell instance PROC, invoke COMMAND silently. +Neither the COMMAND is echoed nor the results to the associated +buffer. Use TIMEOUT-SECONDS as the timeout, waiting for a +response. The COMMAND should be a string, and need not be +terminated with a newline. + +This is helpful when, for example, doing setup work. Or other sneaky +stuff, such as resetting the size of the PowerShell virtual window. + +Returns the result of the command, a string, without the follow-on +command prompt. The result will probably end in a newline. This result +is also stored in the buffer-local variable `powershell-command-reply'. + +In some cases the result can be prepended with the command prompt, as +when, for example, several commands have been send in succession and the +results of the prior command were not fully processed by the application. + +If a PowerShell buffer is not the current buffer, this function +should be invoked within a call to `with-current-buffer' or +similar in order to insure that the buffer-local values of +`powershell-command-reply', `powershell-prompt-regex', and +`powershell-command-timeout-seconds' are used. + +Example: + + (with-current-buffer powershell-buffer-name + (powershell-invoke-command-silently + proc + command-string + 1.90))" + + (let ((old-timeout powershell-command-timeout-seconds) + (original-filter (process-filter proc))) + + (setq powershell-command-reply nil) + + (if timeout-seconds + (setq powershell-command-timeout-seconds timeout-seconds)) + + (set-process-filter proc 'powershell--silent-cmd-filter) + + ;; Send the command plus the "prompt" command. The filter + ;; will know the command is finished when it sees the command + ;; prompt. + ;; + (process-send-string proc (concat command "\nprompt\n")) + + (accept-process-output proc powershell-command-timeout-seconds) + + ;; output of the command is now available in powershell-command-reply + + ;; Trim prompt from the beginning of the output. + ;; this can happen for the first command through + ;; the shell. I think there's a race condition. + (if (and powershell-command-reply + (string-match (concat "^" powershell-prompt-regex "\\(.*\\)\\'") + powershell-command-reply)) + (setq powershell-command-reply + (substring powershell-command-reply + (match-beginning 1) + (match-end 1)))) + + ;; restore the original filter + (set-process-filter proc original-filter) + + ;; restore the original timeout + (if timeout-seconds + (setq powershell-command-timeout-seconds old-timeout)) + + ;; the result: + powershell-command-reply)) + +(defun powershell-delete-process (&optional proc) + "Delete the current buffer process or PROC." + (or proc + (setq proc (get-buffer-process (current-buffer)))) + (and (processp proc) + (delete-process proc))) + +(defun powershell-preoutput-filter-for-prompt (string) + "Trim the newline from STRING, the prompt that we get back from +powershell. This fn is set into the preoutput filters, so the +newline is trimmed before being put into the output buffer." + (if (string-match (concat powershell-prompt-regex "\n\\'") string) + (substring string 0 -1) ;; remove newline + string)) + +(defun powershell-simple-send (proc string) + "Override of the comint-simple-send function, with logic +specifically designed for powershell. This just sends STRING, +plus the prompt command. + +When running as an inferior shell with stdin/stdout redirected, +powershell is in noninteractive mode. This means no prompts get +emitted when a PS command completes. This makes it difficult for +a comint mode to determine when the command has completed. +Therefore, we send an explicit request for the prompt, after +sending the actual (primary) command. When the primary command +completes, PowerShell then responds to the \"prompt\" command, +and emits the prompt. + +This insures we get and display the prompt." + ;; Tell PowerShell to resize its virtual window, if necessary. We do + ;; this by calling a resize function in the PowerShell, before sending + ;; the user-entered command to the shell. + ;; + ;; PowerShell keeps track of its \"console\", and formats its output + ;; according to the width it thinks it is using. This is true even when + ;; powershell is invoked with the - argument, which tells it to use + ;; stdin as input. + + ;; Therefore, if the user has resized the emacs window since the last + ;; PowerShell command, we need to tell PowerShell to change the size + ;; of its virtual window. Calling that function does not change the + ;; size of a window that is visible on screen - it only changes the + ;; size of the virtual window that PowerShell thinks it is using. We + ;; do that by invoking the PowerShell function that this module + ;; defined for that purpose. + ;; + (if powershell--need-rawui-resize + (progn + (powershell--set-window-width proc) + (setq powershell--need-rawui-resize nil))) + (comint-simple-send proc (concat string "\n")) + (comint-simple-send proc "prompt\n")) + +;; Notes on TAB for completion. +;; ------------------------------------------------------- +;; Emacs calls comint-dynamic-complete when the TAB key is pressed in a shell. +;; This is set up in shell-mode-map. +;; +;; comint-dynamic-complete calls the functions in +;; comint-dynamic-complete-functions, until one of them returns +;; non-nil. +;; +;; comint-dynamic-complete-functions is a good thing to set in the mode hook. +;; +;; The default value for that var in a powershell shell is: +;; (comint-replace-by-expanded-history +;; shell-dynamic-complete-environment-variable +;; shell-dynamic-complete-command +;; shell-replace-by-expanded-directory +;; comint-dynamic-complete-filename) + +;; (defun powershell-dynamic-complete-command () +;; "Dynamically complete the command at point. This function is +;; similar to `comint-dynamic-complete-filename', except that it +;; searches the commands from powershell and then the `exec-path' +;; (minus the trailing Emacs library path) for completion candidates. + +;; Completion is dependent on the value of +;; `shell-completion-execonly', plus those that effect file +;; completion. See `powershell-dynamic-complete-as-command'. + +;; Returns t if successful." +;; (interactive) +;; (let ((filename (comint-match-partial-filename))) +;; (if (and filename +;; (save-match-data (not (string-match "[~/]" filename))) +;; (eq (match-beginning 0) +;; (save-excursion (shell-backward-command 1) (point)))) +;; (prog2 (message "Completing command name...") +;; (powershell-dynamic-complete-as-command))))) + +;; (defun powershell-dynamic-complete-as-command () +;; "Dynamically complete at point as a command. +;; See `shell-dynamic-complete-filename'. Returns t if successful." +;; (let* ((filename (or (comint-match-partial-filename) "")) +;; (filenondir (file-name-nondirectory filename)) +;; (path-dirs (cdr (reverse exec-path))) +;; (cwd (file-name-as-directory (expand-file-name default-directory))) +;; (ignored-extensions +;; (and comint-completion-fignore +;; (mapconcat (function (lambda (x) (concat (regexp-quote x) "$"))) +;; comint-completion-fignore "\\|"))) +;; (dir "") (comps-in-dir ()) +;; (file "") (abs-file-name "") (completions ())) + +;; ;; Go thru each cmd in powershell's lexicon, finding completions. + +;; ;; Go thru each dir in the search path, finding completions. +;; (while path-dirs +;; (setq dir (file-name-as-directory (comint-directory (or (car path-dirs) "."))) +;; comps-in-dir (and (file-accessible-directory-p dir) +;; (file-name-all-completions filenondir dir))) +;; ;; Go thru each completion found, to see whether it should be used. +;; (while comps-in-dir +;; (setq file (car comps-in-dir) +;; abs-file-name (concat dir file)) +;; (if (and (not (member file completions)) +;; (not (and ignored-extensions +;; (string-match ignored-extensions file))) +;; (or (string-equal dir cwd) +;; (not (file-directory-p abs-file-name))) +;; (or (null shell-completion-execonly) +;; (file-executable-p abs-file-name))) +;; (setq completions (cons file completions))) +;; (setq comps-in-dir (cdr comps-in-dir))) +;; (setq path-dirs (cdr path-dirs))) +;; ;; OK, we've got a list of completions. +;; (let ((success (let ((comint-completion-addsuffix nil)) +;; (comint-dynamic-simple-complete filenondir completions)))) +;; (if (and (memq success '(sole shortest)) comint-completion-addsuffix +;; (not (file-directory-p (comint-match-partial-filename)))) +;; (insert " ")) +;; success))) + +(provide 'powershell) + +;;; powershell.el ends here diff --git a/code/elpa/python-mode-20220817.2017/completion/pycomplete.el b/code/elpa/python-mode-20220817.2017/completion/pycomplete.el new file mode 100644 index 0000000..8f4354b --- /dev/null +++ b/code/elpa/python-mode-20220817.2017/completion/pycomplete.el @@ -0,0 +1,446 @@ +;;; Complete symbols at point using Pymacs. + +;; Copyright (C) 2007 Skip Montanaro + +;; Original Author: Skip Montanaro +;; Maintainer: Urs Fleisch +;; Created: Oct 2004 +;; Keywords: python pymacs 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 . + +;;; Commentary: + +;; Along with pycomplete.py this file allows programmers to complete Python +;; symbols within the current buffer. See pycomplete.py for the Python side +;; of things and a short description of what to expect. + +;; BAW 2012-09-28: pymacs may not be installed on Debian. + +;; WRT compiler warnings: +;; ‘ac-sources’: is provided by auto-complete, which is required by auto-complete-pycomplete. +;; ‘goto-line’: ‘forward-line’ doesn't look like an appropriate replacement. + + +(require 'python-mode) + +(condition-case nil + (require 'pymacs) + (file-error nil)) + +(eval-when-compile (require 'cl)) + +(pymacs-load "pycomplete") + +(defcustom py-complete-set-keymap-p nil + "If keys shall be defined when calling `py-complete-initialize'. +Default is nil. + +See also `py-complete-set-keymap'" + :type 'boolean + :group 'python-mode) + +(defvar py-complete-variable-index nil + "An alist with mappings of local variable names to types.") +(defvar py-complete-variable-index-position 0 + "The line-beginning-position when py-complete-variable-index was last updated. +This can be used to skip updating the index when still on the same line.") + +(defun py-complete-type-for-value (val) + "Return name of type for variable assignment value. +If the type cannot be deduced, nil is returned." + (let ((firstchar (string-to-char val)) + (case-fold-search nil)) + (cond + ((or (equal 0 firstchar) (string= val "None")) nil) + ((equal ?\[ firstchar) "list") + ((equal ?{ firstchar) "dict") + ((or (equal ?' firstchar) (equal ?\" firstchar)) "str") + ((string-match "^[rR]['\"]" val) "str") + ((string-match "^[uU][rR]?['\"]" val) "unicode") + ((or (string= val "True") (string= val "False")) "bool") + ((string-match "^[+\\-]?[0-9]+$" val) "int") + ((string-match "^[+\\-]?[0-9]+[lL]$" val) "long") + ((string-match "^[+\\-]?[0-9]+\\(?:\\.[0-9]+\\)?" val) "float") + ((string-match "^\\(\\(?:[[:word:]\\.]+\\.\\)?_?[A-Z][A-Za-z0-9]+\\)($" val) + (match-string-no-properties 1 val)) + ((string= "(" (substring-no-properties val -1)) + (concat "_PYCFRT(" (substring-no-properties val 0 -1) ")"))))) + +(defun py-complete-variables-in-def (&optional limit) + "Return an alist with mappings of local variable names to types. +Local variable assignments and parameters of the current function are +parsed. If limit is given, it limits the size of the returned alist." + (let ((pos (point)) + (i 0) + (beg 0) + candidates + (variable-assignment-re (concat "^[ \t]+\\(\\w+\\)[ \t]*\\(?:=\\|+=\\|*=\\|%=\\|&=\\|^=\\|<<=\\|-=\\|/=\\|**=\\||=\\|>>=\\|//=\\)[ \t]*\\([({[]\\|[rRuU]*['\"]\\|[+\\-]?[[:word:].]+(?\\)"))) + (save-excursion + ;; First get the current def and its parameters + (py-backward-def) + (when (looking-at (concat py-def-re " *\\([^( ]+\\) *\\(([^:]+\\) *:")) + (setq beg (match-end 0)) + (let ((params (replace-regexp-in-string + "[ )]+$" "" + (replace-regexp-in-string + "^[ (]+" "" + (match-string-no-properties 3))))) + (dolist (param (split-string params "[ \t\r\n]*,[ \t\r\n]*")) + ;; Transform param into an assignment string + (setq param (concat " " param + (unless (memq ?= (string-to-list param)) + "=None"))) + (if (string-match variable-assignment-re param) + (push `(,(match-string-no-properties 1 param) . + ,(py-complete-type-for-value (match-string-no-properties 2 param))) + candidates))))) + ;; Search backward + (goto-char pos) + (while (and (or (not (integerp limit)) (< i limit)) + (re-search-backward variable-assignment-re nil t) + (> (point) beg)) + (let* ((candidate (match-string-no-properties 1)) + (entry (assoc candidate candidates))) + (cond ((null entry) + (push `(,(match-string-no-properties 1) . + ,(py-complete-type-for-value (match-string-no-properties 2))) + candidates) + (incf i)) + ((not (cdr entry)) + (setcdr entry (py-complete-type-for-value (match-string-no-properties 2))))))) + (nreverse candidates)))) + +(defun py-complete-update-variable-index (&optional limit) + "Update py-complete-variable-index from the local variables of the current +function. An update is only performed if point was on a different line for +the last update. If limit is given, it limits the size of the index." + (unless (local-variable-p 'py-complete-variable-index) + (make-local-variable 'py-complete-variable-index)) + (unless (local-variable-p 'py-complete-variable-index-position) + (make-local-variable 'py-complete-variable-index-position)) + (let ((linebeg (line-beginning-position))) + (unless (eq linebeg py-complete-variable-index-position) + (setq py-complete-variable-index (py-complete-variables-in-def limit)) + (setq py-complete-variable-index-position linebeg)))) + +(defun py-complete-variable-completions-for-symbol (sym) + "Get completions for local variables in current def. +If sym is an empty string, all local variables are returned, +else those starting with sym." + (when (and (stringp sym) (string-match "^\\w*$" sym)) + (py-complete-update-variable-index) + (let ((symlen (length sym))) + (if (zerop symlen) + (mapcar 'car py-complete-variable-index) + (remove-if-not + #'(lambda (s) (and (>= (length s) symlen) (string= sym (substring s 0 symlen)))) + (mapcar 'car py-complete-variable-index)))))) + +(defun py-complete-which-class () + "Return current class name based on point. +If no class name is found, return nil." + (interactive) + (let (classname) + (save-excursion + (save-restriction + (py-backward-class) + (when (looking-at (concat py-class-re " *\\([^( ]+\\)")) + (setq classname (match-string-no-properties 2)) + (if (called-interactively-p 'interactive) + (message "%s" classname))))) + classname)) + +(defun py-complete-type-before-dotexpr (&optional pos) + "Get type for expression before dot expression. +The character after pos (point if omitted) must be a dot. +Returns list, str or dict if such an expression is before +the dot, else nil." + (let ((dotchar (char-after pos))) + (if (and dotchar (char-equal ?. dotchar)) + (save-excursion + (if pos + (goto-char pos)) + (cond + ((looking-back "\\(\\[\\|,[^[]*\\)\\]" "list" (point-min))) + ((looking-back "['\"]" "str" (point-min))) + ((looking-back "}" "dict" (point-min)))))))) + +(defun py-complete-substitute-type-for-var (word) + "Substitute the type for the variable starting the dot-expression word. +Returns the word with replaced variable if known, else the unchanged word." + (let* (type + (firstsym (car (split-string word "\\."))) + (firstlen (length firstsym))) + (if (string= firstsym "self") + (setq type (py-complete-which-class)) + (py-complete-update-variable-index) + (setq type (cdr (assoc firstsym py-complete-variable-index)))) + (if (stringp type) + (concat type (substring word firstlen)) + word))) + +(defun py-complete-python-dotexpr-begin nil + (re-search-backward "[^a-zA-Z_0-9\\.]") + (forward-char)) + +(defun py-complete-python-dotexpr-end nil + (re-search-forward "[a-zA-Z_0-9\\.]*")) + +(put 'python-dotexpr 'beginning-op 'py-complete-python-dotexpr-begin) +(put 'python-dotexpr 'end-op 'py-complete-python-dotexpr-end) + +(defun py-complete-enhanced-dotexpr-at-point () + "Enhanced (thing-at-point 'python-dotexpr). +The returned word starts with a type if an expression is found before the dot +or if the dot-expression starts with a variable for which the type is known." + (require 'thingatpt) + (let ((bounds (bounds-of-thing-at-point 'python-dotexpr))) + (if bounds + (let* ((beg (car bounds)) + (end (cdr bounds)) + (word (buffer-substring-no-properties beg end)) + (prefix (py-complete-type-before-dotexpr beg))) + (if prefix + (concat prefix word) + (py-complete-substitute-type-for-var word)))))) + +(defun py-complete-enhanced-symbol-before-point () + "Return the dotted python symbol before point. +The returned word starts with a type if an expression is found before the dot +or if the dot-expression starts with a variable for which the type is known." + (let* (prefix + (word (buffer-substring-no-properties + (save-excursion + (skip-chars-backward "a-zA-Z0-9_.") + (setq prefix (py-complete-type-before-dotexpr)) + (point)) + (point)))) + (if prefix + (concat prefix word) + (py-complete-substitute-type-for-var word)))) + +;; Not used anymore +(defun py-find-global-imports () + "Find Python import statements in buffer." + (save-excursion + (let (first-class-or-def imports) + (goto-char (point-min)) + (setq first-class-or-def + (re-search-forward "^ *\\(def\\|class\\) " nil t)) + (goto-char (point-min)) + (setq imports nil) + (while (re-search-forward + "^\\(import \\|from \\([A-Za-z_\\.][A-Za-z_0-9\\.]*\\) import \\).*" + first-class-or-def t) + (setq imports (cons (buffer-substring-no-properties + (match-beginning 0) + (match-end 0)) + imports))) + (nreverse imports)))) + +(defun py-complete () + "Complete symbol before point using Pymacs. " + (interactive) + (setq py-last-window-configuration + (current-window-configuration)) + (let ((symbol (py-complete-enhanced-symbol-before-point))) + (if (string= "" symbol) + (tab-to-tab-stop) + (let ((completions + (py-complete-completions-for-symbol symbol))) + (if completions + (let* (completion + (lastsym (car (last (split-string symbol "\\.")))) + (lastlen (length lastsym))) + (cond ((null (cdr completions)) + (setq completion (car completions))) + (t + (setq completion (try-completion lastsym completions)) + (message "Making completion list...") + (with-output-to-temp-buffer "*PythonCompletions*" + (display-completion-list completions)) + (message "Making completion list...%s" "done"))) + (when (and (stringp completion) + (> (length completion) lastlen)) + (insert (substring completion lastlen)))) + (message "Can't find completion for \"%s\"" symbol) + (ding)))))) + +(defun py-complete-completions-for-symbol (sym &optional imports) + "Get possible completions for symbol using statements given in imports." + (let ((pymacs-forget-mutability t)) + (append + (py-complete-variable-completions-for-symbol sym) + (pycomplete-pycompletions + sym (buffer-file-name) + imports)))) + +(defun py-complete-docstring-for-symbol (sym &optional imports) + "Get docstring for symbol using statements given in imports." + (let ((pymacs-forget-mutability t)) + (pycomplete-pydocstring + sym (buffer-file-name) + imports))) + +(defun py-complete-completions () + "Get possible completions for current statement." + (py-complete-completions-for-symbol + (py-complete-enhanced-symbol-before-point))) + +(defun py-complete-completion-at-point () + "Return a (start end collection) list, so that this function +can be used as a hook for completion-at-point-functions." + (setq py-last-window-configuration + (current-window-configuration)) + (let ((symbol (py-complete-enhanced-symbol-before-point))) + (when (not (string= "" symbol)) + (let ((completions (py-complete-completions-for-symbol symbol))) + (when completions + (when (> (length completions) 1) + ;; this-command is changed to avoid the following situation: + ;; This function is invoked via indent-for-tab-command (because + ;; tab-always-indent is complete) and there is a "Complete, but + ;; not unique" case (e.g. "for" is completed and the next TAB key + ;; press shall display a list with "for", "format"). In such a + ;; case, py-indent-line would detect a repeated indentation + ;; request and thus change the indentation. The changed + ;; indentation would then prevent indent-for-tab-command + ;; from calling the completion function. + (setq this-command 'py-complete-completion-at-point)) + (list (- (point) (length (car (last (split-string symbol "\\."))))) + (point) + completions)))))) + +(defun py-complete-show (string) + (display-message-or-buffer string "*PythonHelp*")) + +(defun py-complete-help (string) + "get help on a python expression" + (interactive "sHelp: ") + (let* ((pymacs-forget-mutability t) + (help-string + (pycomplete-pyhelp string (buffer-file-name)))) + (if (and help-string (> (length help-string) 300)) + (with-output-to-temp-buffer "*Python Help*" + (princ help-string)) + (py-complete-show help-string)))) + +(defun py-complete-help-thing-at-point nil + (interactive) + (let ((sym (py-complete-enhanced-dotexpr-at-point))) + (if sym + (py-complete-help sym)))) + +(defvar py-complete-current-signature nil + "Internally used by pycomplete.el") + +(defun py-complete-signature (function) + "get signature of a python function or method" + (let ((pymacs-forget-mutability t)) + (set 'py-complete-current-signature + (pycomplete-pysignature function (buffer-file-name))))) + +(defun py-complete-signature-show nil + (let ((sym (py-complete-enhanced-dotexpr-at-point))) + (if sym + (progn + (py-complete-show (py-complete-signature sym)))))) + +(defun py-complete-signature-expr nil + (interactive) + (let ((dotexpr (read-string "signature on: " + (py-complete-enhanced-dotexpr-at-point)))) + (if dotexpr + (py-complete-show + (py-complete-signature dotexpr))))) + +(defun py-complete-electric-lparen nil + "electricly insert '(', and try to get a signature for the stuff to the left" + (interactive) + (py-complete-signature-show) + (self-insert-command 1)) + +(defun py-complete-electric-comma nil + "electricly insert ',', and redisplay latest signature" + (interactive) + (self-insert-command 1) + (if py-complete-current-signature + (py-complete-show (format "%s" py-complete-current-signature)))) + +(defun py-complete-location (sym) + "Get definition location of sym in cons form (FILE . LINE)." + (let ((location (pycomplete-pylocation sym (buffer-file-name)))) + (when (and location (vectorp location) (= (length location) 2)) + (cons (aref location 0) (aref location 1))))) + +(defun py-complete-goto-definition nil + "Got to definition of Python function." + (interactive) + (let ((sym (py-complete-enhanced-dotexpr-at-point))) + (if sym + (let ((location + (pycomplete-pylocation sym (buffer-file-name)))) + (if (and location (vectorp location) (= (length location) 2)) + (progn + (find-file (aref location 0)) + (goto-line (aref location 1))) + (message "Cannot find the definition!")))))) + +(defun py-complete-parse-source () + "Parse source code of Python file to get imports and completions." + (let ((errstr (pycomplete-pyparse (buffer-file-name) t))) + (if errstr + (message "%s" errstr)))) + +(defun py-complete-set-keymap () + "Define key map with pycomplete functions." + (interactive) + (define-key python-mode-map [C-tab] 'py-complete) + (define-key python-mode-map [f1] 'py-complete-help-thing-at-point) + (define-key python-mode-map "(" 'py-complete-electric-lparen) + (define-key python-mode-map "," 'py-complete-electric-comma) + (define-key python-mode-map [S-f1] 'py-complete-signature-expr) + (define-key python-mode-map [f2] 'py-complete-goto-definition) + (define-key python-mode-map [f3] 'py-complete-help)) + +(defun py-complete-initialize () + "Initialize pycomplete hooks. +Should be called from python-mode-hook. Keys are set when +`py-complete-set-keymap-p' is non-nil." + (interactive) + (when py-set-complete-keymap-p + (py-complete-set-keymap)) + (when py-complete-set-keymap-p + (py-complete-set-keymap)) + ;; Parse source file after it is saved + (add-hook 'after-save-hook 'py-complete-parse-source nil 'local) + ;; Set up auto-complete or company if enabled + (cond + ((fboundp 'auto-complete-mode) + (require 'auto-complete-pycomplete) + (setq ac-sources + (if (boundp 'py-complete-ac-sources) + py-complete-ac-sources + '(ac-source-pycomplete)))) + ((fboundp 'company-mode) + (company-mode t) + (require 'company-pycomplete) + (set (make-local-variable 'company-backends) + '((company-pycomplete)))) + ((or py-set-complete-keymap-p py-complete-set-keymap-p) + (set (make-local-variable 'tab-always-indent) 'complete) + (define-key python-mode-map [tab] 'indent-for-tab-command)))) + +(provide 'pycomplete) diff --git a/code/elpa/python-mode-20220817.2017/completion/pycomplete.py b/code/elpa/python-mode-20220817.2017/completion/pycomplete.py new file mode 100644 index 0000000..be381c7 --- /dev/null +++ b/code/elpa/python-mode-20220817.2017/completion/pycomplete.py @@ -0,0 +1,798 @@ +""" +Python dot expression completion using Pymacs. + +This almost certainly needs work, but if you add + + (require 'pycomplete) + +to your init.el file and have Pymacs installed, when you hit M-TAB it will +try to complete the dot expression before point. For example, given this +import at the top of the file: + + import time + +typing "time.cl" then hitting M-TAB should complete "time.clock". + +This is unlikely to be done the way Emacs completion ought to be done, but +it's a start. Perhaps someone with more Emacs mojo can take this stuff and +do it right. + +See pycomplete.el for the Emacs Lisp side of things. + +Most of the public functions in this module have the signature + +(s, fname=None, imports=None) + +where s is the symbol to complete, fname is the file path and imports +the list of import statements to use. The fname parameter is used as a +key to cache the global and local context and the symbols imported or +evaluated so far. The cache for an fname is cleared when its imports +are changed. When not passing a list of imports (or None), the currently +used imports are preserved. The caching should make subsequent operations +(e.g. another completion or signature lookup after a completion) less +expensive. +""" + +# Original Author: Skip Montanaro +# Maintainer: Urs Fleisch +# Created: Oct 2004 +# Keywords: python pymacs emacs + +# This software is provided as-is, without express or implied warranty. +# Permission to use, copy, modify, distribute or sell this software, without +# fee, for any purpose and by any individual or organization, is hereby +# granted, provided that the above copyright notice and this paragraph +# appear in all copies. + +# Along with pycomplete.el this file allows programmers to complete Python +# symbols within the current buffer. + +import sys +import types +import inspect +import keyword +import os +import pydoc +import ast +import re + +if sys.version_info[0] >= 3: # Python 3 + import io +else: # Python 2 + import StringIO + +try: + x = set +except NameError: + from sets import Set as set +else: + del x + + +class _PyCompleteDocument(object): + """Completion data for Python source file.""" + + if sys.version_info[0] >= 3: # Python 3 + _helpout = io.StringIO + @staticmethod + def is_num_or_str(obj): + return isinstance(obj, (int, float, str)) + @staticmethod + def is_class_type(obj): + return type(obj) == type + @staticmethod + def get_unbound_function(unbound): + return unbound + @staticmethod + def get_method_function(meth): + return meth.__func__ + @staticmethod + def get_function_code(func): + return func.__code__ + @staticmethod + def update_with_builtins(keys): + import builtins + keys.update(dir(builtins)) + else: # Python 2 + _helpout = StringIO.StringIO + @staticmethod + def is_num_or_str(obj): + return isinstance(obj, (int, long, float, basestring)) + @staticmethod + def is_class_type(obj): + return type(obj) in (types.ClassType, types.TypeType) + @staticmethod + def get_unbound_function(unbound): + return unbound.im_func + @staticmethod + def get_method_function(meth): + return meth.im_func + @staticmethod + def get_function_code(func): + return func.func_code + @staticmethod + def update_with_builtins(keys): + import __builtin__ + keys.update(dir(__builtin__)) + + _sre_SRE_Pattern = type(re.compile('')) + _sre_SRE_Match = type(re.match('', '')) + + # Class name in CapCase, as suggested by PEP8 Python style guide + _class_name_re = re.compile(r'(?:^|\.)_?[A-Z][A-Za-z0-9]+$') + + _return_type_of_func = { + 'bin': 'str', + 'bytearray': 'bytearray', + 'bytes': 'bytes', + 'compile': 'types.CodeType', + 'complex': 'complex', + 'dict': 'dict', + 'frozenset': 'frozenset', + 'oct': 'str', + 'open': 'io.BufferedIOBase' if sys.version_info[0] >= 3 else 'file', + 'list': 'list', + 'repr': 'str', + 'set': 'set', + 'sorted': 'list', + 'str': 'str', + 'tuple': 'tuple', + 'vars': 'dict', + 're.compile': '_PyCompleteDocument._sre_SRE_Pattern', + 're.escape': 'str', + 're.findall': 'list', + 're.match': '_PyCompleteDocument._sre_SRE_Match', + 're.search': '_PyCompleteDocument._sre_SRE_Match', + 're.split': 'list', + 're.sub': 'str', + 're.subn': 'tuple', + 'datetime.datetime': 'datetime.datetime', + 'datetime.date': 'datetime.date', + 'datetime.time': 'datetime.time', + 'datetime.timedelta': 'datetime.timedelta', + 'sys.exc_info': 'tuple', + 'os.getcwd': 'str', + 'os.getenv': 'str', + 'os.urandom': 'bytes', + 'os.path.abspath': 'str', + 'os.path.basename': 'str', + 'os.path.commonprefix': 'str', + 'os.path.dirname': 'str', + 'os.path.expanduser': 'str', + 'os.path.expandvars': 'str', + 'os.path.join': 'str', + 'os.path.normcase': 'str', + 'os.path.normpath': 'str', + 'os.path.realpath': 'str', + 'os.path.relpath': 'str', + 'os.path.split': 'tuple', + 'os.path.splitdrive': 'tuple', + 'os.path.splitext': 'tuple', + 'collections.defaultdict': 'collections.defaultdict', + 'collections.deque': 'collections.deque', + 'collections.namedtuple': 'collections.namedtuple', + 'socket.socket': 'socket.socket', + 'csv.excel': 'csv.excel', + 'csv.excel_tab': 'csv.excel_tab' + } + + @staticmethod + def _get_type_for_function(funcname): + typename = _PyCompleteDocument._return_type_of_func.get(funcname) + if not typename and \ + _PyCompleteDocument._class_name_re.search(funcname): + typename = funcname + return typename + + @staticmethod + def _replace_pycfrt_with_typename(s): + """Replace _PYCFRT(..) expressions with function return type. + + The names of variables assigned from function calls can be replaced by + _PYCFRT(name.of.function). This function tries to replace such expressions + with the corresponding function return type (PYCFRT = pycomplete function + return type). If no such expression can be found, s is returned unchanged. + If the type cannot be deduced, None is returned, else the substituted + string.""" + pycfrt_re = re.compile(r'_PYCFRT\(([^()]+)\)') + return pycfrt_re.sub(lambda m: _PyCompleteDocument._get_type_for_function(m.group(1)), s) + + class ImportExtractor(ast.NodeVisitor): + """NodeVisitor to extract the top-level import statements from an AST. + + To generate code containing all imports in try-except statements, + call get_import_code(node), where node is a parsed AST. + """ + def visit_FunctionDef(self, node): + # Ignore imports inside functions or methods. + pass + + def visit_ClassDef(self, node): + # Ignore imports inside classes. + pass + + def generic_visit(self, node): + # Store import statement nodes. + if isinstance(node, (ast.Import, ast.ImportFrom)): + self._import_nodes.append(node) + ast.NodeVisitor.generic_visit(self, node) + + def get_import_code(self, node, fname=''): + """Get compiled code of all top-level import statements found in the + AST of node.""" + self._import_nodes = [] + self.visit(node) + body = [] + for imp_node in self._import_nodes: + if isinstance(imp_node, ast.ImportFrom) and \ + imp_node.module == '__future__': + # 'SyntaxError: from __future__ imports must occur at the + # beginning of the file' is raised if a 'from __future__ import' + # is wrapped in try-except, so use only the import statement. + body.append(imp_node) + else: + if sys.version_info[0] >= 3: # Python 3 + body.append(ast.Try(body=[imp_node], handlers=[ + ast.ExceptHandler(type=None, name=None, body=[ast.Pass()])], + orelse=[], finalbody=[])) + else: + body.append(ast.TryExcept(body=[imp_node], handlers=[ + ast.ExceptHandler(type=None, name=None, body=[ast.Pass()])], + orelse=[])) + node = ast.Module(body=body) + ast.fix_missing_locations(node) + code = compile(node, fname, 'exec') + return code + + + class CodeRemover(ast.NodeTransformer): + """NodeTransformer which replaces function statements with 'pass' + and keeps only safe assignments, so that the resulting code can + be used for code completion. + + To reduce the code from the node of a parsed AST, call + get_transformed_code(node). + """ + def visit_FunctionDef(self, node): + # Replace all function statements except doc string by 'pass'. + if node.body: + if isinstance(node.body[0], ast.Expr) and \ + isinstance(node.body[0].value, ast.Str): + # Keep doc string. + first_stmt = node.body[1] if len(node.body) > 1 else node.body[0] + node.body = [node.body[0]] + else: + first_stmt = node.body[0] + node.body = [] + node.body.append(ast.copy_location(ast.Pass(), first_stmt)) + return node + return None + + def visit_Expr(self, node): + # Remove all expressions except strings to keep doc strings. + if isinstance(node.value, ast.Str): + return node + return None + + @staticmethod + def _get_type_node_for_function_node(node): + # Convert node to dot expression + attrs = [] + while isinstance(node, ast.Attribute): + attrs.insert(0, node.attr) + node = node.value + if isinstance(node, ast.Name): + attrs.insert(0, node.id) + funcname = '.'.join(attrs) + typename = _PyCompleteDocument._get_type_for_function(funcname) + if typename: + # Convert dot expression to node + attrs = typename.split('.') + node = None + if attrs: + node = ast.Name(id=attrs.pop(0), ctx=ast.Load()) + while attrs: + node = ast.Attribute(value=node, attr=attrs.pop(0), + ctx=ast.Load()) + return node + return None + + @staticmethod + def replace_unsafe_value(node, replace_self=None): + """Modify value from assignment if unsafe. + + If replace_self is given, only assignments starting with 'self.' are + processed, the assignment node is returned with 'self.' replaced by + the value of replace_self (typically the class name). + For other assignments, None is returned.""" + for i, target in enumerate(node.targets): + if not isinstance(target, (ast.Name, ast.Attribute)): + # Only process assignments to names and attributes, + # not tuples. + return None + if replace_self: + if isinstance(target, ast.Attribute) and \ + isinstance(target.value, ast.Name) and \ + target.value.id == 'self' and \ + isinstance(target.value.ctx, ast.Load): + node.targets[i].value.id = replace_self + if target.attr == '__name__': + node.value = ast.copy_location(ast.Str(s=''), + node.value) + elif target.attr in ('__dict__', '__class__', '__bases__', + '__doc__'): + return None + else: + return None + elif isinstance(target, ast.Name) and \ + isinstance(target.ctx, ast.Store): + if target.id == '__metaclass__': + # Do not modify __metaclass__ assignments + return node + elif target.id == '__slots__': + node.value = ast.copy_location( + ast.List(elts=[], ctx=ast.Load()), node.value) + if isinstance(node.value, (ast.Str, ast.Num)): + pass + elif isinstance(node.value, (ast.List, ast.Tuple)): + node.value.elts = [] + elif isinstance(node.value, ast.Dict): + node.value.keys = [] + node.value.values = [] + elif isinstance(node.value, ast.ListComp): + node.value = ast.copy_location(ast.List(elts=[], ctx=ast.Load()), node.value) + elif isinstance(node.value, ast.Call): + type_node = _PyCompleteDocument.CodeRemover._get_type_node_for_function_node(node.value.func) + if type_node: + # Wrap class lookup in try-except because it is not fail-safe. + node.value = ast.copy_location(type_node, node.value) + if sys.version_info[0] >= 3: # Python 3 + node = ast.copy_location(ast.Try(body=[node], handlers=[ + ast.ExceptHandler(type=None, name=None, body=[ast.Pass()])], + orelse=[], finalbody=[]), node) + else: + node = ast.copy_location(ast.TryExcept(body=[node], handlers=[ + ast.ExceptHandler(type=None, name=None, body=[ast.Pass()])], + orelse=[]), node) + ast.fix_missing_locations(node) + else: + node.value = ast.copy_location( + ast.Name(id='None', ctx=ast.Load()), node.value) + else: + node.value = ast.copy_location(ast.Name(id='None', ctx=ast.Load()), node.value) + return node + + def visit_Assign(self, node): + # Replace unsafe values of assignements by None. + return self.replace_unsafe_value(node) + + def visit_Name(self, node): + # Pass names for bases in ClassDef. + return node + + def visit_Attribute(self, node): + # Pass attributes for bases in ClassDef. + return node + + def visit_ClassDef(self, node): + # Visit nodes of class. + # Store instance member assignments to be added later to generated code. + self_assignments = {} + for child in ast.walk(node): + if isinstance(child, ast.Assign): + new_child = self.replace_unsafe_value(child, + replace_self=node.name) + if new_child: + new_var = child.targets[0].attr + old_assign = self_assignments.get(new_var) + if not old_assign or ( + isinstance(old_assign, ast.Assign) and + isinstance(old_assign.value, ast.Name) and + old_assign.value.id == 'None'): + self_assignments[new_var] = new_child + self._class_assignments.extend(list(self_assignments.values())) + return ast.NodeTransformer.generic_visit(self, node) + + def visit_Module(self, node): + # Visit nodes of module + return ast.NodeTransformer.generic_visit(self, node) + + def generic_visit(self, node): + # Remove everything which is not handled by the methods above + return None + + def get_transformed_code(self, node, fname=''): + """Get compiled code containing only empty functions and methods + and safe assignments found in the AST of node.""" + self._class_assignments = [] + node = self.visit(node) + # The self members are added as attributes to the class objects + # rather than included as class variables inside the class definition + # so that names starting with '__' are not mangled. + node.body.extend(self._class_assignments) + code = compile(node, fname, 'exec') + return code + + _stdout = sys.stdout + + _instances = {} + + def __init__(self, fname=None): + """Constructor for internal use. + The factory method instance() shall be used instead. + """ + self._fname = fname + self._imports = None + self._globald = globals().copy() + self._symnames = [] + self._symobjs = {} + self._parse_source_called = False + + @classmethod + def instance(cls, fname): + """Get _PyCompleteDocument object for fname. + If no object for this file name exists, a new object is created and + registered. + """ + obj = cls._instances.get(fname) + if obj is None: + obj = _PyCompleteDocument(fname) + cls._instances[fname] = obj + return obj + + def _import_modules(self, imports): + """Import modules using the statements in imports. + If the imports are the same as in the last call, the methods + immediately returns, also if imports is None. + """ + if imports is None and not self._parse_source_called: + self.parse_source() + if imports is None or imports == self._imports: + return + # changes to where the file is + if self._fname: + os.chdir(os.path.dirname(self._fname)) + self._globald = globals().copy() + self._symnames = [] + self._symobjs = {} + for stmt in imports: + try: + exec(stmt, self._globald) + except TypeError: + raise TypeError('invalid type: %s' % stmt) + except Exception: + continue + self._imports = imports + + def _collect_symbol_names(self): + """Collect the global, local, builtin symbols in _symnames. + If _symnames is already set, the method immediately returns. + """ + if not self._symnames: + keys = set(keyword.kwlist) + keys.update(list(self._globald.keys())) + self.update_with_builtins(keys) + self._symnames = list(keys) + self._symnames.sort() + + def _get_symbol_object(self, s): + """Get a symbol by evaluating its name or importing a module + or submodule with the name s. + """ + sym = self._symobjs.get(s) + if sym is not None: + return sym + # changes to where the file is + if self._fname: + os.chdir(os.path.dirname(self._fname)) + try: + sym = eval(s, self._globald) + except NameError: + try: + sym = __import__(s, self._globald) + self._globald[s] = sym + except ImportError: + pass + except AttributeError: + try: + sym = __import__(s, self._globald) + except ImportError: + pass + except SyntaxError: + pass + if sym is not None: + self._symobjs[s] = sym + return sym + + def _load_symbol(self, s, strict=False): + """Get a symbol for a dotted expression. + + Returns the last successfully found symbol object in the + dotted chain. If strict is set True, it returns True as + soon as a symbol is not found. Therefore strict=True can + be used to find exactly the symbol for s, otherwise a + symbol for a parent can be returned, which may be enough + if searching for help on symbol. + """ + s = self._replace_pycfrt_with_typename(s) + sym = self._symobjs.get(s) + if sym is not None: + return sym + dots = s.split('.') + if not s or len(dots) == 1: + sym = self._get_symbol_object(s) + else: + for i in range(1, len(dots) + 1): + s = '.'.join(dots[:i]) + if not s: + continue + sym_i = self._get_symbol_object(s) + if sym_i is not None: + sym = sym_i + elif strict: + return None + return sym + + def _get_help(self, s, imports=None): + """Return string printed by help function.""" + if not s: + return '' + if s == 'pydoc.help': + # Prevent pydoc from going into interactive mode + s = 'pydoc.Helper' + obj = None + if not keyword.iskeyword(s): + try: + self._import_modules(imports) + obj = self._load_symbol(s, strict=False) + except Exception as ex: + return '%s' % ex + if not obj: + obj = str(s) + out = self._helpout() + try: + sys.stdout = out + pydoc.help(obj) + finally: + sys.stdout = self._stdout + return out.getvalue() + + @staticmethod + def _find_constructor(class_ob): + """Given a class object, return a function object used for the + constructor (ie, __init__() ) or None if we can't find one.""" + try: + return _PyCompleteDocument.get_unbound_function(class_ob.__init__) + except AttributeError: + for base in class_ob.__bases__: + rc = _PyCompleteDocument._find_constructor(base) + if rc is not None: + return rc + return None + + def get_all_completions(self, s, imports=None): + """Return contextual completion of s (string of >= zero chars). + + If given, imports is a list of import statements to be executed + first. + """ + self._import_modules(imports) + + s = self._replace_pycfrt_with_typename(s) + last_dot_pos = s.rfind('.') + if last_dot_pos == -1: + self._collect_symbol_names() + if s: + return [k for k in self._symnames if k.startswith(s)] + else: + return self._symnames + + sym = self._load_symbol(s[:last_dot_pos], strict=True) + if sym is not None: + s = s[last_dot_pos + 1:] + return [k for k in dir(sym) if k.startswith(s)] + return [] + + def complete(self, s, imports=None): + """Complete symbol if unique, else return list of completions.""" + if not s: + return '' + + completions = self.get_all_completions(s, imports) + if len(completions) == 0: + return None + else: + dots = s.split(".") + prefix = os.path.commonprefix([k for k in completions]) + if len(completions) == 1 or len(prefix) > len(dots[-1]): + return [prefix[len(dots[-1]):]] + return completions + + def help(self, s, imports=None): + """Return help on object.""" + try: + return self._get_help(s, imports) + except Exception as ex: + return '%s' % ex + + def get_docstring(self, s, imports=None): + """Return docstring for symbol s.""" + if s and not keyword.iskeyword(s): + try: + self._import_modules(imports) + obj = self._load_symbol(s, strict=True) + if obj and not self.is_num_or_str(obj): + doc = inspect.getdoc(obj) + if doc: + return doc + except: + pass + return '' + + def get_signature(self, s, imports=None): + """Return info about function parameters.""" + if not s or keyword.iskeyword(s): + return '' + obj = None + sig = "" + + try: + self._import_modules(imports) + obj = self._load_symbol(s, strict=False) + except Exception as ex: + return '%s' % ex + + if self.is_class_type(obj): + # Look for the highest __init__ in the class chain. + ctr = self._find_constructor(obj) + if ctr is not None and type(ctr) in ( + types.MethodType, types.FunctionType, types.LambdaType): + obj = ctr + elif type(obj) == types.MethodType: + # bit of a hack for methods - turn it into a function + # but we drop the "self" param. + obj = self.get_method_function(obj) + + if type(obj) in [types.FunctionType, types.LambdaType]: + try: + (args, varargs, varkw, defaults) = inspect.getargspec(obj) + sig = ('%s: %s' % (obj.__name__, + inspect.formatargspec(args, varargs, varkw, + defaults))) + except ValueError: + try: + (args, varargs, varkw, defaults, kwonlyargs, + kwonlydefaults, annotations) = inspect.getfullargspec(obj) + sig = ('%s: %s' % ( + obj.__name__, inspect.formatargspec( + args, varargs, varkw, defaults, kwonlyargs, + kwonlydefaults, annotations))) + except AttributeError: + pass + + doc = getattr(obj, '__doc__', '') + if doc and not sig: + doc = doc.lstrip() + pos = doc.find('\n') + if pos < 0 or pos > 70: + pos = 70 + sig = doc[:pos] + return sig + + def get_location(self, s, imports=None): + """Return file path and line number of symbol, None if not found.""" + if not s or keyword.iskeyword(s): + return None + try: + self._import_modules(imports) + obj = self._load_symbol(s, strict=False) + if obj is not None: + if self.is_class_type(obj): + obj = obj.__init__ + if type(obj) == types.MethodType: + obj = self.get_method_function(obj) + if type(obj) in [types.FunctionType, types.LambdaType]: + code = self.get_function_code(obj) + return (os.path.abspath(code.co_filename), code.co_firstlineno) + # If not found, try using inspect. + return (inspect.getsourcefile(obj), inspect.getsourcelines(obj)[1]) + except: + pass + return None + + def parse_source(self, only_reload=False): + """Parse source code to get imports and completions. + + If this method is called, the imports parameter for the other methods + must be omitted (or None), so that the imports are taken from the + parsed source code. If only_reload is True, the source is only parsed + if it has been parsed before. + None is returned if OK, else a string describing the error. + """ + if only_reload and not self._parse_source_called: + return None + self._parse_source_called = True + if not self._fname: + return None + + try: + with open(self._fname) as fh: + src = fh.read() + except IOError as ex: + return '%s' % ex + + # changes to where the file is + os.chdir(os.path.dirname(self._fname)) + + try: + node = ast.parse(src, self._fname) + import_code = self.ImportExtractor().get_import_code(node, self._fname) + except (SyntaxError, TypeError) as ex: + return '%s' % ex + + old_globald = self._globald.copy() + self._globald = globals().copy() + try: + exec(import_code, self._globald) + except Exception as ex: + self._globald = old_globald + return '%s' % ex + + self._symnames = [] + self._symobjs = {} + + reduced_code = self.CodeRemover().get_transformed_code(node, self._fname) + try: + exec(reduced_code, self._globald) + except Exception as ex: + return '%s' % ex + return None + +def pycompletions(s, fname=None, imports=None): + """Get a list of possible completions for s. + + The completions extend the expression s after the last dot. + """ + return _PyCompleteDocument.instance(fname).get_all_completions( + s, imports) + +def pycomplete(s, fname=None, imports=None): + """Complete the Python expression s. + + If multiple completions are found, a list of possible completions + (names after the last dot) is returned. + If one completion is found, a list with a string containing the + remaining characters is returned. + If no completion is found, None is returned. + """ + return _PyCompleteDocument.instance(fname).complete(s, imports) + +def pyhelp(s, fname=None, imports=None): + """Return help on object s.""" + return _PyCompleteDocument.instance(fname).help(s, imports) + +def pydocstring(s, fname=None, imports=None): + """Return docstring of symbol.""" + return _PyCompleteDocument.instance(fname).get_docstring(s, imports) + +def pysignature(s, fname=None, imports=None): + """Return info about function parameters.""" + return _PyCompleteDocument.instance(fname).get_signature(s, imports) + +def pylocation(s, fname=None, imports=None): + """Return file path and line number of symbol, None if not found.""" + return _PyCompleteDocument.instance(fname).get_location(s, imports) + +def pyparse(fname, only_reload=False): + """Parse source code to get imports and completions. + + If this function is called, the imports parameter for the other functions + must be omitted (or None), so that the imports are taken from the + parsed source code. If only_reload is True, the source is only parsed if + it has been parsed before. + """ + return _PyCompleteDocument.instance(fname).parse_source(only_reload) + +# Local Variables : +# pymacs-auto-reload : t +# End : diff --git a/code/elpa/python-mode-20220817.2017/python-mode-autoloads.el b/code/elpa/python-mode-20220817.2017/python-mode-autoloads.el new file mode 100644 index 0000000..2dbedda --- /dev/null +++ b/code/elpa/python-mode-20220817.2017/python-mode-autoloads.el @@ -0,0 +1,41 @@ +;;; python-mode-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "python-mode" "python-mode.el" (0 0 0 0)) +;;; Generated autoloads from python-mode.el + +(autoload 'py-backward-class-bol "python-mode" "\ +Go to beginning of `class', go to BOL. +If already at beginning, go one `class' backward. +Return beginning of `class' if successful, nil otherwise" t nil) + +(autoload 'py-backward-def-bol "python-mode" "\ +Go to beginning of `def', go to BOL. +If already at beginning, go one `def' backward. +Return beginning of `def' if successful, nil otherwise" t nil) + +(autoload 'py-backward-def-or-class-bol "python-mode" "\ +Go to beginning of `def-or-class', go to BOL. +If already at beginning, go one `def-or-class' backward. +Return beginning of `def-or-class' if successful, nil otherwise" t nil) + +(register-definition-prefixes "python-mode" '("all-mode-setting" "autopair-mode" "flake8" "force-py-shell-name-p-o" "highlight-indent-active" "hs-hide-comments-when-hiding-all" "info-lookup-mode" "ipython" "isympy3" "iypthon" "jython" "pdb-track-stack-from-shell-p" "pep8" "pst-here" "stri" "toggle-force-py-shell-name-p" "turn-o" "virtualenv-")) + +;;;*** + +;;;### (autoloads nil nil ("python-mode-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; python-mode-autoloads.el ends here diff --git a/code/elpa/python-mode-20220817.2017/python-mode-pkg.el b/code/elpa/python-mode-20220817.2017/python-mode-pkg.el new file mode 100644 index 0000000..f0cbeca --- /dev/null +++ b/code/elpa/python-mode-20220817.2017/python-mode-pkg.el @@ -0,0 +1,13 @@ +(define-package "python-mode" "20220817.2017" "Python major mode" 'nil :commit "765af4569eaf93c07c6aecdf7f134022677f9620" :authors + '(("2015-2021 https://gitlab.com/groups/python-mode-devs") + ("2003-2014 https://launchpad.net/python-mode") + ("1995-2002 Barry A. Warsaw") + ("1992-1994 Tim Peters")) + :maintainer + '(nil . "python-mode@python.org") + :keywords + '("languages" "processes" "python" "oop") + :url "https://gitlab.com/groups/python-mode-devs") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/python-mode-20220817.2017/python-mode.el b/code/elpa/python-mode-20220817.2017/python-mode.el new file mode 100644 index 0000000..71a89f7 --- /dev/null +++ b/code/elpa/python-mode-20220817.2017/python-mode.el @@ -0,0 +1,26014 @@ +;;; python-mode.el --- Edit, debug, develop, run Python programs. -*- lexical-binding: t; -*- + +;; Version: 6.3.1 + +;; Keywords: languages, processes, python, oop + +;; URL: https://gitlab.com/groups/python-mode-devs + +;; Package-Requires: ((emacs "24")) + +;; Author: 2015-2021 https://gitlab.com/groups/python-mode-devs +;; 2003-2014 https://launchpad.net/python-mode +;; 1995-2002 Barry A. Warsaw +;; 1992-1994 Tim Peters +;; Maintainer: python-mode@python.org +;; Created: Feb 1992 +;; Keywords: python languages oop + +;; 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 . + +;;; Commentary: + +;; Includes a minor mode for handling a Python/IPython shell, and can +;; take advantage of Pymacs when installed. + +;; See documentation in README.org, README.DEVEL.org + +;; Please report bugs at +;; https://gitlab.com/python-mode-devs/python-mode/issues + +;; available commands are documented in directory "doc" as +;; commands-python-mode.org + +;; As for `py-add-abbrev': +;; Similar to `add-mode-abbrev', but uses +;; `py-partial-expression' before point for expansion to +;; store, not `word'. Also provides a proposal for new +;; abbrevs. + +;; Proposal for an abbrev is composed from the downcased +;; initials of expansion - provided they are of char-class +;; [:alpha:] +;; +;; For example code below would be recognised as a +;; `py-expression' composed by three +;; py-partial-expressions. +;; +;; OrderedDict.popitem(last=True) +;; +;; Putting the curser at the EOL, M-3 M-x py-add-abbrev +;; +;; would prompt "op" for an abbrev to store, as first +;; `py-partial-expression' beginns with a "(", which is +;; not taken as proposal. + +;;; Code: + +(require 'ansi-color) +(ignore-errors (require 'subr-x)) +(require 'cc-cmds) +(require 'comint) +(require 'compile) +(require 'custom) +(require 'ert) +(require 'flymake) +(require 'hippie-exp) +(require 'hideshow) +(require 'json) +(require 'shell) +(require 'thingatpt) +(require 'which-func) +(require 'tramp) +(require 'tramp-sh) +(require 'org-loaddefs) +(unless (functionp 'mapcan) + (require 'cl-extra) + ;; mapcan doesn't exist in Emacs 25 + (defalias 'mapcan 'cl-mapcan) + ) + +;; (require 'org) + +(defgroup python-mode nil + "Support for the Python programming language, " + :group 'languages + :prefix "py-") + +(defconst py-version "6.3.1") + +(defvar py-install-directory nil + "Make sure it exists.") + +(defcustom py-install-directory nil + "Directory where python-mode.el and it's subdirectories should be installed. + +Needed for completion and other environment stuff only." + + :type 'string + :tag "py-install-directory" + :group 'python-mode) + +(or + py-install-directory + (and (buffer-live-p (ignore-errors (set-buffer (get-buffer "python--mode.el")))) + (setq py-install-directory (ignore-errors (file-name-directory (buffer-file-name (get-buffer "python-mode.el")))))) + (and (buffer-live-p (ignore-errors (set-buffer (get-buffer "python-components-mode.el")))) + (setq py-install-directory (ignore-errors (file-name-directory (buffer-file-name (get-buffer "python-components-mode.el"))))))) + +(defcustom py-font-lock-defaults-p t + "If fontification is not required, avoiding it might speed up things." + + :type 'boolean + :tag "py-font-lock-defaults-p" + :group 'python-mode + :safe 'booleanp) + +(defcustom py-pythonpath "" + "Define $PYTHONPATH here, if needed. + +Emacs doesn't read .bashrc" + + :type 'string + :tag "py-pythonpath" + :group 'python-mode) + +(defcustom python-mode-modeline-display "Py" + "String to display in Emacs modeline." + + :type 'string + :tag "python-mode-modeline-display" + :group 'python-mode) + +(defcustom py-python2-modeline-display "Py2" + "String to display in Emacs modeline." + + :type 'string + :tag "python2-mode-modeline-display" + :group 'python-mode) + +(defcustom py-python3-modeline-display "Py3" + "String to display in Emacs modeline." + + :type 'string + :tag "python3-mode-modeline-display" + :group 'python-mode) + +(defcustom py-ipython-modeline-display "IPy" + "String to display in Emacs modeline." + + :type 'string + :tag "ipython-modeline-display" + :group 'python-mode) + +(defcustom py-jython-modeline-display "Jy" + "String to display in Emacs modeline." + + :type 'string + :tag "jython-modeline-display" + :group 'python-mode) + +(defcustom py-extensions "py-extensions.el" + "File where extensions to python-mode.el should be installed. + +Used by virtualenv support." + + :type 'string + :tag "py-extensions" + :group 'python-mode) + +(defcustom info-lookup-mode "python" + "Which Python documentation should be queried. + +Make sure it's accessible from Emacs by \\ \\[info] ... +See INSTALL-INFO-FILES for help." + + :type 'string + :tag "info-lookup-mode" + :group 'python-mode) + +(defcustom py-fast-process-p nil + "Use `py-fast-process'. + +Commands prefixed \"py-fast-...\" suitable for large output + +See: large output makes Emacs freeze, lp:1253907 + +Results arrive in output buffer, which is not in comint-mode" + + :type 'boolean + :tag "py-fast-process-p" + :group 'python-mode + :safe 'booleanp) + +;; credits to python.el +(defcustom py-shell-compilation-regexp-alist + `((,(rx line-start (1+ (any " \t")) "File \"" + (group (1+ (not (any "\"<")))) ; avoid `' &c + "\", line " (group (1+ digit))) + 1 2) + (,(rx " in file " (group (1+ not-newline)) " on line " + (group (1+ digit))) + 1 2) + (,(rx line-start "> " (group (1+ (not (any "(\"<")))) + "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") + 1 2)) + "`compilation-error-regexp-alist' for `py-shell'." + :type '(alist string) + :tag "py-shell-compilation-regexp-alist" + :group 'python-mode) + +(defcustom py-shift-require-transient-mark-mode-p t + "If py-shift commands require variable `transient-mark-mode' set to t. + +Default is t" + + :type 'boolean + :tag "py-shift-require-transient-mark-mode-p" + :group 'python-mode + :safe 'booleanp) + +(defvar py-fast-output-buffer "*Python Fast*" + "Internally used. `buffer-name' for fast-processes.") + +(defvar py-this-result nil + "Internally used, store return-value.") + +(defconst py-coding-re + "\\(# *coding[ \t]*=\\|#[ \t]*\-*\-[ \t]*coding:\\|#[ \t]*encoding:\\)[ \t]*\\([[:graph:]+]\\)" + "Fetch the coding cookie maybe.") + +(defcustom py-comment-auto-fill-p nil + "When non-nil, fill comments. + +Defaut is nil" + + :type 'boolean + :tag "py-comment-auto-fill-p" + :group 'python-mode + :safe 'booleanp) + +(defcustom py-sexp-use-expression-p nil + "If non-nil, `forward-sexp' will call `py-forward-expression'. + +Respective `backward-sexp' will call `py-backward-expression' +Default is t" + :type 'boolean + :tag "py-sexp-use-expression-p" + :group 'python-mode + :safe 'booleanp) + +(defcustom py-session-p t + "If commands would use an existing process. + +Default is t" + + :type 'boolean + :tag "py-session-p" + :group 'python-mode + :safe 'booleanp) + +(defvar py-chars-before " \t\n\r\f" + "Used by `py--string-strip'.") + +(defvar py-chars-after " \t\n\r\f" + "Used by `py--string-strip'.") + +(unless (functionp 'file-local-name) + (defun file-local-name (file) + "Return the local name component of FILE. +This function removes from FILE the specification of the remote host +and the method of accessing the host, leaving only the part that +identifies FILE locally on the remote system. +The returned file name can be used directly as argument of +`process-file', `start-file-process', or `shell-command'." + (or (file-remote-p file 'localname) file))) + +(defun py---emacs-version-greater-23 () + "Return `t' if emacs major version is above 23" + (< 23 (string-to-number (car (split-string emacs-version "\\."))))) + +;; (format "execfile(r'%s')\n" file) +(defun py-execute-file-command (filename) + "Return the command using FILENAME." + (format "exec(compile(open(r'%s').read(), r'%s', 'exec')) # PYTHON-MODE\n" filename filename) + ) + +(defun py--beginning-of-buffer-p () + "Returns position, if cursor is at the beginning of buffer. +Return nil otherwise. " + (when (bobp)(point))) + +;; (setq strip-chars-before "[ \t\r\n]*") +(defun py--string-strip (str &optional chars-before chars-after) + "Return a copy of STR, CHARS removed. +`CHARS-BEFORE' and `CHARS-AFTER' default is \"[ \t\r\n]*\", +i.e. spaces, tabs, carriage returns, newlines and newpages." + (let ((s-c-b (or chars-before + py-chars-before)) + (s-c-a (or chars-after + py-chars-after)) + (erg str)) + (setq erg (replace-regexp-in-string s-c-b "" erg)) + (setq erg (replace-regexp-in-string s-c-a "" erg)) + erg)) + +(defun py-toggle-session-p (&optional arg) + "Switch boolean variable `py-session-p'. + +With optional ARG message state switched to" + (interactive "p") + (setq py-session-p (not py-session-p)) + (when arg (message "py-session-p: %s" py-session-p))) + +(defcustom py-max-help-buffer-p nil + "If \"\*Python-Help\*\"-buffer should appear as the only visible. + +Default is nil. In `help-buffer', \"q\" will close it." + + :type 'boolean + :tag "py-max-help-buffer-p" + :group 'python-mode + :safe 'booleanp) + +(defcustom py-highlight-error-source-p nil + "Respective code in source-buffer will be highlighted. + +Default is nil. + +\\ `py-remove-overlays-at-point' removes that highlighting." + :type 'boolean + :tag "py-highlight-error-source-p" + :group 'python-mode) + +(defcustom py-set-pager-cat-p nil + "If the shell environment variable $PAGER should set to `cat'. + +Avoids lp:783828, + \"Terminal not fully functional\", for help('COMMAND') in python-shell + +When non-nil, imports module `os'" + + :type 'boolean + :tag "py-set-pager-cat-p" + :group 'python-mode) + +(defcustom py-empty-line-closes-p nil + "When non-nil, dedent after empty line following block. + +if True: + print(\"Part of the if-statement\") + +print(\"Not part of the if-statement\") + +Default is nil" + + :type 'boolean + :tag "py-empty-line-closes-p" + :group 'python-mode) + +(defcustom py-prompt-on-changed-p t + "Ask for save before a changed buffer is sent to interpreter. + +Default is t" + + :type 'boolean + :tag "py-prompt-on-changed-p" + :group 'python-mode) + +(defcustom py-dedicated-process-p nil + "If commands executing code use a dedicated shell. + +Default is nil + +When non-nil and `py-session-p', an existing +dedicated process is re-used instead of default + - which allows executing stuff in parallel." + :type 'boolean + :tag "py-dedicated-process-p" + :group 'python-mode) + +(defcustom py-store-result-p nil + "Put resulting string of `py-execute-...' into `kill-ring'. + +Default is nil" + + :type 'boolean + :tag "py-dedicated-process-p" + :group 'python-mode) + +(defvar py-shell--font-lock-buffer "*PSFLB*" + "May contain the `py-buffer-name' currently fontified." ) + +(defvar py-return-result-p nil + "Internally used. + +When non-nil, return resulting string of `py-execute-...'. +Imports will use it with nil. +Default is nil") + +(defun py-toggle-py-return-result-p () + "Toggle value of `py-return-result-p'." + (interactive) + (setq py-return-result-p (not py-return-result-p)) + (when (called-interactively-p 'interactive) (message "py-return-result-p: %s" py-return-result-p))) + +(defcustom py--execute-use-temp-file-p nil + "Assume execution at a remote machine. + + where write-access is not given." + + :type 'boolean + :tag "py--execute-use-temp-file-p" + :group 'python-mode) + +(defvar py--match-paren-forward-p nil + "Internally used by `py-match-paren'.") + +(defvar py-new-session-p t + "Internally used. See lp:1393882. + +Restart `py-shell' once with new Emacs/`python-mode'.") + +(defcustom py-electric-close-active-p nil + "Close completion buffer if no longer needed. + +Works around a bug in `choose-completion'. +Default is nil" + :type 'boolean + :tag "py-electric-close-active-p" + :group 'python-mode) + +(defcustom py-hide-show-minor-mode-p nil + "If hide-show minor-mode should be on, default is nil." + + :type 'boolean + :tag "py-hide-show-minor-mode-p" + :group 'python-mode) + +(defcustom py-load-skeletons-p nil + "If skeleton definitions should be loaded, default is nil. + +If non-nil and variable `abbrev-mode' on, block-skeletons will inserted. +Pressing \"if\" for example will prompt for the if-condition." + + :type 'boolean + :tag "py-load-skeletons-p" + :group 'python-mode) + +(defcustom py-if-name-main-permission-p t + "Allow execution of code inside blocks started. + +by \"if __name__== '__main__':\". +Default is non-nil" + + :type 'boolean + :tag "py-if-name-main-permission-p" + :group 'python-mode) + +(defcustom py-use-font-lock-doc-face-p nil + "If documention string inside of def or class get `font-lock-doc-face'. + +`font-lock-doc-face' inherits `font-lock-string-face'. +Call \\ \\[customize-face] in order to have a effect." + + :type 'boolean + :tag "py-use-font-lock-doc-face-p" + :group 'python-mode) + +(defcustom py-empty-comment-line-separates-paragraph-p t + "Consider paragraph start/end lines with nothing inside but comment sign. + +Default is non-nil" + :type 'boolean + :tag "py-empty-comment-line-separates-paragraph-p" + :group 'python-mode) + +(defcustom py-indent-honors-inline-comment nil + "If non-nil, indents to column of inlined comment start. +Default is nil." + :type 'boolean + :tag "py-indent-honors-inline-comment" + :group 'python-mode) + +(defcustom py-auto-fill-mode nil + "If `python-mode' should set `fill-column'. + +according to values +in `py-comment-fill-column' and `py-docstring-fill-column'. +Default is nil" + + :type 'boolean + :tag "py-auto-fill-mode" + :group 'python-mode) + +(defcustom py-error-markup-delay 4 + "Seconds error's are highlighted in exception buffer." + + :type 'integer + :tag "py-error-markup-delay" + :group 'python-mode) + +(defcustom py-fast-completion-delay 0.1 + "Used by `py-fast-send-string'." + + :type 'float + :tag "py-fast-completion-delay" + :group 'python-mode) + +(defcustom py-new-shell-delay + (if (eq system-type 'windows-nt) + 2.0 + 1.0) + + "If a new comint buffer is connected to Python. +Commands like completion might need some delay." + + :type 'float + :tag "py-new-shell-delay" + :group 'python-mode) + +(defcustom py-autofill-timer-delay 1 + "Delay when idle." + :type 'integer + :tag "py-autofill-timer-delay" + :group 'python-mode) + +(defcustom py-docstring-fill-column 72 + "Value of `fill-column' to use when filling a docstring. +Any non-integer value means do not use a different value of +`fill-column' when filling docstrings." + :type '(choice (integer) + (const :tag "Use the current `fill-column'" t)) + :tag "py-docstring-fill-column" + :group 'python-mode) + +(defcustom py-comment-fill-column 79 + "Value of `fill-column' to use when filling a comment. +Any non-integer value means do not use a different value of +`fill-column' when filling docstrings." + :type '(choice (integer) + (const :tag "Use the current `fill-column'" t)) + :tag "py-comment-fill-column" + :group 'python-mode) + +(defcustom py-fontify-shell-buffer-p nil + "If code in Python shell should be highlighted as in script buffer. + +Default is nil. + +If t, related vars like `comment-start' will be set too. +Seems convenient when playing with stuff in IPython shell +Might not be TRT when a lot of output arrives" + + :type 'boolean + :tag "py-fontify-shell-buffer-p" + :group 'python-mode) + +(defvar py-modeline-display "" + "Internally used.") + +(defcustom py-modeline-display-full-path-p nil + "If the full PATH/TO/PYTHON be in modeline. + +Default is nil. Note: when `py-python-command' is +specified with path, it's shown as an acronym in +`buffer-name' already." + + :type 'boolean + :tag "py-modeline-display-full-path-p" + :group 'python-mode) + +(defcustom py-modeline-acronym-display-home-p nil + "If the modeline acronym should contain chars indicating the home-directory. + +Default is nil" + :type 'boolean + :tag "py-modeline-acronym-display-home-p" + :group 'python-mode) + +(defun py-autopair-check () + "Check, if `autopair-mode' is available. + +Give some hints, if not." + (interactive) + (if (featurep 'autopair) + 't + (progn + (message "py-autopair-check: %s" "Don't see autopair.el. Make sure, it's installed. If not, maybe see source: URL: http://autopair.googlecode.com") + nil))) + +(defvar highlight-indent-active nil) +(defvar autopair-mode nil) + +(defvar-local py--editbeg nil + "Internally used by `py-edit-docstring' and others") + +(defvar-local py--editend nil + "Internally used by `py-edit-docstring' and others") + +(defvar py--oldbuf nil + "Internally used by `py-edit-docstring'.") + +(defvar py-edit-buffer "Edit docstring" + "Name of the temporary buffer to use when editing.") + +(defvar py--edit-register nil) + +(defvar py-result nil + "Internally used. May store result from Python process. + +See var `py-return-result-p' and command `py-toggle-py-return-result-p'") + +(defvar py-error nil + "Takes the error-messages from Python process.") + +(defvar py-python-completions "*Python Completions*" + "Buffer name for Python-shell completions, internally used.") + +(defvar py-ipython-completions "*IPython Completions*" + "Buffer name for IPython-shell completions, internally used.") + +(defcustom py-timer-close-completions-p t + "If `py-timer-close-completion-buffer' should run, default is non-nil." + + :type 'boolean + :tag "py-timer-close-completions-p" + :group 'python-mode) + +(defcustom py-autopair-mode nil + "If `python-mode' calls (autopair-mode-on) + +Default is nil +Load `autopair-mode' written by Joao Tavora +URL: http://autopair.googlecode.com" + :type 'boolean + :tag "py-autopair-mode" + :group 'python-mode) + +(defcustom py-indent-no-completion-p nil + "If completion function should insert a TAB when no completion found. + +Default is nil" + :type 'boolean + :tag "py-indent-no-completion-p" + :group 'python-mode) + +(defcustom py-company-pycomplete-p nil + "Load company-pycomplete stuff. Default is nil." + + :type 'boolean + :tag "py-company-pycomplete-p" + :group 'python-mode) + +(defvar py-last-position nil + "Used by `py-help-at-point'. + +Avoid repeated call at identic pos.") + +(defvar py-auto-completion-mode-p nil + "Internally used by `py-auto-completion-mode'.") + +(defvar py-complete-last-modified nil + "Internally used by `py-auto-completion-mode'.") + +(defvar py--auto-complete-timer nil + "Internally used by `py-auto-completion-mode'.") + +(defvar py-auto-completion-buffer nil + "Internally used by `py-auto-completion-mode'.") + +(defvar py--auto-complete-timer-delay 1 + "Seconds Emacs must be idle to trigger auto-completion. + +See `py-auto-completion-mode'") + +(defcustom py-auto-complete-p nil + "Run python-mode's built-in auto-completion via `py-complete-function'. + +Default is nil." + + :type 'boolean + :tag "py-auto-complete-p" + :group 'python-mode) + +(defcustom py-tab-shifts-region-p nil + "If t, TAB will indent/cycle the region, not just the current line. + +Default is nil +See also `py-tab-indents-region-p'" + + :type 'boolean + :tag "py-tab-shifts-region-p" + :group 'python-mode) + +(defcustom py-tab-indents-region-p nil + "When t and first TAB doesn't shift, `indent-region' is called. + +Default is nil +See also `py-tab-shifts-region-p'" + + :type 'boolean + :tag "py-tab-indents-region-p" + :group 'python-mode) + +(defcustom py-block-comment-prefix-p t + "If py-comment inserts `py-block-comment-prefix'. + +Default is t" + + :type 'boolean + :tag "py-block-comment-prefix-p" + :group 'python-mode) + +(defcustom py-org-cycle-p nil + "When non-nil, command `org-cycle' is available at shift-TAB, . + +Default is nil." + :type 'boolean + :tag "py-org-cycle-p" + :group 'python-mode) + +(defcustom py-set-complete-keymap-p nil + "If `py-complete-initialize'. + +Sets up enviroment for Pymacs based py-complete. + Should load it's keys into `python-mode-map' +Default is nil. +See also resp. edit `py-complete-set-keymap'" + + :type 'boolean + :tag "py-set-complete-keymap-p" + :group 'python-mode) + +(defcustom py-outline-minor-mode-p t + "If outline minor-mode should be on, default is t." + :type 'boolean + :tag "py-outline-minor-mode-p" + :group 'python-mode) + +(defvar py-guess-py-install-directory-p nil + "If in cases, `py-install-directory' isn't set, `py-set-load-path' guess it.") + +(defcustom py-guess-py-install-directory-p nil + "If in cases, `py-install-directory' isn't set, `py-set-load-path' guesses it." + :type 'boolean + :tag "py-guess-py-install-directory-p" + :group 'python-mode) + +(defcustom py-load-pymacs-p nil + "If Pymacs related stuff should be loaded. Default is nil. + +Pymacs has been written by François Pinard and many others. +See original source: http://pymacs.progiciels-bpi.ca" + :type 'boolean + :tag "py-load-pymacs-p" + :group 'python-mode) + +(defcustom py-verbose-p nil + "If functions should report results. + +Default is nil." + :type 'boolean + :tag "py-verbose-p" + :group 'python-mode) + +(defcustom py-sexp-function nil + "Called instead of `forward-sexp', `backward-sexp'. + +Default is nil." + + :type '(choice + + (const :tag "default" nil) + (const :tag "py-forward-partial-expression" py-forward-partial-expression) + (const :tag "py-forward-expression" py-forward-expression)) + :tag "py-sexp-function" + :group 'python-mode) + +(defcustom py-close-provides-newline t + "If a newline is inserted, when line after block isn't empty. + +Default is non-nil. +When non-nil, `py-forward-def' and related will work faster" + :type 'boolean + :tag "py-close-provides-newline" + :group 'python-mode) + +(defcustom py-dedent-keep-relative-column t + "If point should follow dedent or kind of electric move to end of line. + +Default is t - keep relative position." + :type 'boolean + :tag "py-dedent-keep-relative-column" + :group 'python-mode) + +(defcustom py-indent-list-style 'line-up-with-first-element + "Sets the basic indentation style of lists. + +The term ‘list’ here is seen from Emacs Lisp editing purpose. +A list symbolic expression means everything delimited by +brackets, parentheses or braces. + +Setting here might be ignored in case of canonical indent. + +`line-up-with-first-element' indents to 1+ column +of opening delimiter + +def foo (a, + b): + +but ‘one-level-to-beginning-of-statement’ in case of EOL at list-start + +def foo ( + a, + b): + +`one-level-to-beginning-of-statement' adds +`py-indent-offset' to beginning + +def long_function_name( + var_one, var_two, var_three, + var_four): + print(var_one) + +`one-level-from-first-element' adds `py-indent-offset' from first element +def foo(): + if (foo && + baz): + bar()" + :type '(choice + (const :tag "line-up-with-first-element" line-up-with-first-element) + (const :tag "one-level-to-beginning-of-statement" one-level-to-beginning-of-statement) + (const :tag "one-level-from-first-element" one-level-from-first-element) + ) + :tag "py-indent-list-style" + :group 'python-mode) +(make-variable-buffer-local 'py-indent-list-style) + +(defcustom py-closing-list-dedents-bos nil + "When non-nil, indent lists closing delimiter like start-column. + +It will be lined up under the first character of + the line that starts the multi-line construct, as in: + +my_list = [ + 1, 2, 3, + 4, 5, 6 +] + +result = some_function_that_takes_arguments( + \\='a\\=', \\='b\\=', \\='c\\=', + \\='d\\=', \\='e\\=', \\='f\\=' +) + +Default is nil, i.e. + +my_list = [ + 1, 2, 3, + 4, 5, 6 + ] + +result = some_function_that_takes_arguments( + \\='a\\=', \\='b\\=', \\='c\\=', + \\='d\\=', \\='e\\=', \\='f\\=' + ) + +Examples from PEP8 +URL: https://www.python.org/dev/peps/pep-0008/#indentation" + :type 'boolean + :tag "py-closing-list-dedents-bos" + :group 'python-mode) + +(defvar py-imenu-max-items 99) +(defcustom py-imenu-max-items 99 + "Python-mode specific `imenu-max-items'." + :type 'number + :tag "py-imenu-max-items" + :group 'python-mode) + +(defcustom py-closing-list-space 1 + "Number of chars, closing parenthesis outdent from opening, default is 1." + :type 'number + :tag "py-closing-list-space" + :group 'python-mode) + +(defcustom py-max-specpdl-size 99 + "Heuristic exit. +e +Limiting number of recursive calls by `py-forward-statement' and related. +Default is `max-specpdl-size'. + +This threshold is just an approximation. It might set far higher maybe. + +See lp:1235375. In case code is not to navigate due to errors, +command `which-function-mode' and others might make Emacs hang. + +Rather exit than." + + :type 'number + :tag "py-max-specpdl-size" + :group 'python-mode) + +(defcustom py-closing-list-keeps-space nil + "If non-nil, closing parenthesis dedents onto column of opening. +Adds `py-closing-list-space'. +Default is nil." + :type 'boolean + :tag "py-closing-list-keeps-space" + :group 'python-mode) + +(defcustom py-electric-colon-active-p nil + "`py-electric-colon' feature. + +Default is nil. See lp:837065 for discussions. +See also `py-electric-colon-bobl-only'" + :type 'boolean + :tag "py-electric-colon-active-p" + :group 'python-mode) + +(defcustom py-electric-colon-bobl-only t + + "When inserting a colon, do not indent lines unless at beginning of block. + +See lp:1207405 resp. `py-electric-colon-active-p'" + + :type 'boolean + :tag "py-electric-colon-bobl-only" + :group 'python-mode) + +(defcustom py-electric-yank-active-p nil + "When non-nil, `yank' will be followed by an `indent-according-to-mode'. + +Default is nil" + :type 'boolean + :tag "py-electric-yank-active-p" + :group 'python-mode) + +(defcustom py-electric-colon-greedy-p nil + "If `py-electric-colon' should indent to the outmost reasonable level. + +If nil, default, it will not move from at any reasonable level." + :type 'boolean + :tag "py-electric-colon-greedy-p" + :group 'python-mode) + +(defcustom py-electric-colon-newline-and-indent-p nil + "If non-nil, `py-electric-colon' will call `newline-and-indent'. + +Default is nil." + :type 'boolean + :tag "py-electric-colon-newline-and-indent-p" + :group 'python-mode) + +(defcustom py-electric-comment-p nil + "If \"#\" should call `py-electric-comment'. Default is nil." + :type 'boolean + :tag "py-electric-comment-p" + :group 'python-mode) + +(defcustom py-electric-comment-add-space-p nil + "If `py-electric-comment' should add a space. Default is nil." + :type 'boolean + :tag "py-electric-comment-add-space-p" + :group 'python-mode) + +(defcustom py-mark-decorators nil + "If decorators should be marked too. + +Default is nil. + +Also used by navigation" + :type 'boolean + :tag "py-mark-decorators" + :group 'python-mode) + +(defcustom py-defun-use-top-level-p nil + "If `beginning-of-defun', `end-of-defun' calls function `top-level' form. + +Default is nil. + +beginning-of defun, `end-of-defun' forms use +commands `py-backward-top-level', `py-forward-top-level' + +`mark-defun' marks function `top-level' form at point etc." + + :type 'boolean + :tag "py-defun-use-top-level-p" + :group 'python-mode) + +(defcustom py-tab-indent t + "Non-nil means TAB in Python mode calls `py-indent-line'." + :type 'boolean + :tag "py-tab-indent" + :group 'python-mode) + +(defcustom py-return-key 'py-newline-and-indent + "Which command should call." + :type '(choice + + (const :tag "default" py-newline-and-indent) + (const :tag "newline" newline) + (const :tag "py-newline-and-indent" py-newline-and-indent) + (const :tag "py-newline-and-dedent" py-newline-and-dedent) + ) + :tag "py-return-key" + :group 'python-mode) + +(defcustom py-complete-function 'py-fast-complete + "When set, enforces function todo completion, default is `py-fast-complete'. + +Might not affect IPython, as `py-shell-complete' is the only known working here. +Normally `python-mode' knows best which function to use." + :type '(choice + + (const :tag "default" nil) + (const :tag "Pymacs and company based py-complete" py-complete) + (const :tag "py-shell-complete" py-shell-complete) + (const :tag "py-indent-or-complete" py-indent-or-complete) + (const :tag "py-fast-complete" py-fast-complete) + ) + :tag "py-complete-function" + :group 'python-mode) + +(defcustom py-encoding-string " # -*- coding: utf-8 -*-" + "Default string specifying encoding of a Python file." + :type 'string + :tag "py-encoding-string" + :group 'python-mode) + +(defcustom py-shebang-startstring "#! /bin/env" + "Detecting the shell in head of file." + :type 'string + :tag "py-shebang-startstring" + :group 'python-mode) + +(defcustom py-flake8-command "" + "Which command to call flake8. + +If empty, `python-mode' will guess some" + :type 'string + :tag "py-flake8-command" + :group 'python-mode) + +(defcustom py-flake8-command-args "" + "Arguments used by flake8. + +Default is the empty string." + :type 'string + :tag "py-flake8-command-args" + :group 'python-mode) + +(defvar py-flake8-history nil + "Used by flake8, resp. `py-flake8-command'. + +Default is nil.") + +(defcustom py-message-executing-temporary-file t + "If execute functions using a temporary file should message it. + +Default is t. +Messaging increments the prompt counter of IPython shell." + :type 'boolean + :tag "py-message-executing-temporary-file" + :group 'python-mode) + +(defcustom py-execute-no-temp-p nil + "Seems Emacs-24.3 provided a way executing stuff without temporary files." + :type 'boolean + :tag "py-execute-no-temp-p" + :group 'python-mode) + +(defcustom py-lhs-inbound-indent 1 + "When line starts a multiline-assignment. + +How many colums indent more than opening bracket, brace or parenthesis." + :type 'integer + :tag "py-lhs-inbound-indent" + :group 'python-mode) + +(defcustom py-continuation-offset 2 + "Additional amount of offset to give for some continuation lines. +Continuation lines are those that immediately follow a backslash +terminated line." + :type 'integer + :tag "py-continuation-offset" + :group 'python-mode) + +(defcustom py-indent-tabs-mode nil + "Python-mode starts `indent-tabs-mode' with the value specified here. + +Default is nil." + :type 'boolean + :tag "py-indent-tabs-mode" + :group 'python-mode) + +(defcustom py-smart-indentation nil + "Guess `py-indent-offset'. Default is nil. + +Setting it to t seems useful only in cases where customizing +`py-indent-offset' is no option - for example because the +indentation step is unknown or differs inside the code. + +When this variable is non-nil, `py-indent-offset' is guessed from existing code. + +Which might slow down the proceeding." + + :type 'boolean + :tag "py-smart-indentation" + :group 'python-mode) + +(defcustom py-block-comment-prefix "##" + "String used by \\[comment-region] to comment out a block of code. +This should follow the convention for non-indenting comment lines so +that the indentation commands won't get confused (i.e., the string +should be of the form `#x...' where `x' is not a blank or a tab, and + `...' is arbitrary). However, this string should not end in whitespace." + :type 'string + :tag "py-block-comment-prefix" + :group 'python-mode) + +(defcustom py-indent-offset 4 + "Amount of offset per level of indentation. +`\\[py-guess-indent-offset]' can usually guess a good value when +you're editing someone else's Python code." + :type 'integer + :tag "py-indent-offset" + :group 'python-mode) +(make-variable-buffer-local 'py-indent-offset) + +(defcustom py-backslashed-lines-indent-offset 5 + "Amount of offset per level of indentation of backslashed. +No semantic indent, which diff to `py-indent-offset' indicates" + :type 'integer + :tag "py-backslashed-lines-indent-offset" + :group 'python-mode) + +(defcustom py-shell-completion-native-output-timeout 5.0 + "Time in seconds to wait for completion output before giving up." + :version "25.1" + :type 'float + :tag "py-shell-completion-native-output-timeout" + :group 'python-mode) + +(defcustom py-shell-completion-native-try-output-timeout 1.0 + "Time in seconds to wait for *trying* native completion output." + :version "25.1" + :type 'float + :tag "py-shell-completion-native-try-output-timeout" + :group 'python-mode) + +(defvar py-shell--first-prompt-received-output-buffer nil) +(defvar py-shell--first-prompt-received nil) + +(defcustom py-shell-first-prompt-hook nil + "Hook run upon first (non-pdb) shell prompt detection. +This is the place for shell setup functions that need to wait for +output. Since the first prompt is ensured, this helps the +current process to not hang while waiting. This is useful to +safely attach setup code for long-running processes that +eventually provide a shell." + :version "25.1" + :type 'hook + :tag "py-shell-first-prompt-hook" + :group 'python-mode) + +(defvar py-shell--parent-buffer nil) + +(defvar py-shell--package-depth 10) + +(defcustom py-indent-comments t + "When t, comment lines are indented." + :type 'boolean + :tag "py-indent-comments" + :group 'python-mode) + +(defcustom py-uncomment-indents-p nil + "When non-nil, after uncomment indent lines." + :type 'boolean + :tag "py-uncomment-indents-p" + :group 'python-mode) + +(defcustom py-separator-char "/" + "The character, which separates the system file-path components. + +Precedes guessing when not empty, returned by function `py-separator-char'." + :type 'string + :tag "py-separator-char" + :group 'python-mode) + +(defvar py-separator-char "/" + "Values set by defcustom only will not be seen in batch-mode.") + +(and + ;; used as a string finally + ;; kept a character not to break existing customizations + (characterp py-separator-char)(setq py-separator-char (char-to-string py-separator-char))) + +(defcustom py-custom-temp-directory "" + "If set, will take precedence over guessed values from `py-temp-directory'. + +Default is the empty string." + :type 'string + :tag "py-custom-temp-directory" + :group 'python-mode) + +(defcustom py-beep-if-tab-change t + "Ring the bell if `tab-width' is changed. +If a comment of the form + + \t# vi:set tabsize=: + +is found before the first code line when the file is entered, and the +current value of (the general Emacs variable) `tab-width' does not +equal , `tab-width' is set to , a message saying so is +displayed in the echo area, and if `py-beep-if-tab-change' is non-nil +the Emacs bell is also rung as a warning." + :type 'boolean + :tag "py-beep-if-tab-change" + :group 'python-mode) + +(defcustom py-jump-on-exception t + "Jump to innermost exception frame in Python output buffer. +When this variable is non-nil and an exception occurs when running +Python code synchronously in a subprocess, jump immediately to the +source code of the innermost traceback frame." + :type 'boolean + :tag "py-jump-on-exception" + :group 'python-mode) + +(defcustom py-ask-about-save t + "If not nil, ask about which buffers to save before executing some code. +Otherwise, all modified buffers are saved without asking." + :type 'boolean + :tag "py-ask-about-save" + :group 'python-mode) + +(defcustom py-delete-function 'delete-char + "Function called by `py-electric-delete' when deleting forwards." + :type 'function + :tag "py-delete-function" + :group 'python-mode) + +(defcustom py-import-check-point-max + 20000 + "Max number of characters to search Java-ish import statement. + +When `python-mode' tries to calculate the shell +-- either a CPython or a Jython shell -- +it looks at the so-called `shebang'. +If that's not available, it looks at some of the +file heading imports to see if they look Java-like." + :type 'integer + :tag "py-import-check-point-max +" + :group 'python-mode) + +;; (setq py-shells +;; (list +;; "" +;; 'ipython +;; 'ipython2.7 +;; 'ipython3 +;; 'jython +;; 'python +;; 'python2 +;; 'python3 +;; 'pypy +;; )) + +(defcustom py-known-shells + (list + "ipython" + "ipython2.7" + "ipython3" + "jython" + "python" + "python2" + "python3" + "pypy" + ) + "A list of available shells instrumented for commands. +Expects its executables installed + +Edit for your needs." + :type '(repeat string) + :tag "py-shells" + :group 'python-mode) + +(defcustom py-known-shells-extended-commands + (list "ipython" + "python" + "python3" + "pypy" + ) + "A list of shells for finer grained commands. +like `py-execute-statement-ipython' +Expects its executables installed + +Edit for your needs." + :type '(repeat string) + :tag "py-shells" + :group 'python-mode) + +(defun py-install-named-shells-fix-doc (ele) + "Internally used by `py-load-named-shells'. + +Argument ELE: a shell name, a string." + (cond ((string-match "^i" ele) + (concat "I" (capitalize (substring ele 1)))) + ((string-match "^pypy" ele) + "PyPy") + (t (capitalize ele)))) + +(defcustom py-jython-packages + '("java" "javax") + "Imported packages that imply `jython-mode'." + :type '(repeat string) + :tag "py-jython-packages +" + :group 'python-mode) + +(defcustom py-current-defun-show t + "If `py-current-defun' should jump to the definition. + +Highlights it while waiting PY-WHICH-FUNC-DELAY seconds. +Afterwards returning to previous position. + +Default is t." + + :type 'boolean + :tag "py-current-defun-show" + :group 'python-mode) + +(defcustom py-current-defun-delay 2 + "`py-current-defun' waits PY-WHICH-FUNC-DELAY seconds. + +Before returning to previous position." + + :type 'number + :tag "py-current-defun-delay" + :group 'python-mode) + +(defcustom py-python-send-delay 1 + "Seconds to wait for output, used by `py--send-...' functions. + +See also `py-ipython-send-delay'" + + :type 'number + :tag "py-python-send-delay" + :group 'python-mode) + +(defcustom py-python3-send-delay 1 + "Seconds to wait for output, used by `py--send-...' functions. + +See also `py-ipython-send-delay'" + + :type 'number + :tag "py-python3-send-delay" + :group 'python-mode) + +(defcustom py-ipython-send-delay 1 + "Seconds to wait for output, used by `py--send-...' functions. + +See also `py-python-send-delay'" + + :type 'number + :tag "py-ipython-send-delay" + :group 'python-mode) + +(defcustom py-master-file nil + "Execute the named master file instead of the buffer's file. + +Default is nil. +With relative path variable `default-directory' is prepended. + +Beside you may set this variable in the file's local +variable section, e.g.: + + # Local Variables: + # py-master-file: \"master.py\" + # End:" + :type 'string + :tag "py-master-file" + :group 'python-mode) +(make-variable-buffer-local 'py-master-file) + +(defcustom py-pychecker-command "pychecker" + "Shell command used to run Pychecker." + :type 'string + :tag "py-pychecker-command" + :group 'python-mode) + +(defcustom py-pychecker-command-args "--stdlib" + "String arguments to be passed to pychecker." + :type 'string + :tag "py-pychecker-command-args" + :group 'python-mode) + +(defcustom py-pyflakes-command "pyflakes" + "Shell command used to run Pyflakes." + :type 'string + :tag "py-pyflakes-command" + :group 'python-mode) + +(defcustom py-pyflakes-command-args "" + "String arguments to be passed to pyflakes. + +Default is \"\"" + :type 'string + :tag "py-pyflakes-command-args" + :group 'python-mode) + +(defcustom py-pep8-command "pep8" + "Shell command used to run pep8." + :type 'string + :tag "py-pep8-command" + :group 'python-mode) + +(defcustom py-pep8-command-args "" + "String arguments to be passed to pylint. + +Default is \"\"" + :type 'string + :tag "py-pep8-command-args" + :group 'python-mode) + +(defcustom py-pyflakespep8-command (concat py-install-directory "/pyflakespep8.py") + "Shell command used to run `pyflakespep8'." + :type 'string + :tag "py-pyflakespep8-command" + :group 'python-mode) + +(defcustom py-pyflakespep8-command-args "" + "String arguments to be passed to pyflakespep8. + +Default is \"\"" + :type 'string + :tag "py-pyflakespep8-command-args" + :group 'python-mode) + +(defcustom py-pylint-command "pylint" + "Shell command used to run Pylint." + :type 'string + :tag "py-pylint-command" + :group 'python-mode) + +(defcustom py-pylint-command-args '("--errors-only") + "String arguments to be passed to pylint. + +Default is \"--errors-only\"" + :type '(repeat string) + :tag "py-pylint-command-args" + :group 'python-mode) + +(defvar py-pdbtrack-input-prompt "^[(<]*[Ii]?[Pp]y?db[>)]+ *" + "Recognize the prompt.") + +(defcustom py-shell-input-prompt-1-regexp ">>> " + "A regular expression to match the input prompt of the shell." + :type 'regexp + :tag "py-shell-input-prompt-1-regexp" + :group 'python-mode) + +(defcustom py-shell-input-prompt-2-regexp "[.][.][.]:? " + "A regular expression to match the input prompt. + +Applies to the shell after the first line of input." + :type 'string + :tag "py-shell-input-prompt-2-regexp" + :group 'python-mode) + +(defvar py-shell-ipython-input-prompt-1-regexp "In \\[[0-9]+\\]: " + "Regular Expression matching input prompt of python shell. +It should not contain a caret (^) at the beginning.") + +(defvar py-shell-ipython-input-prompt-2-regexp " \\.\\.\\.: " + "Regular Expression matching second level input prompt of python shell. +It should not contain a caret (^) at the beginning.") + +(defcustom py-shell-input-prompt-2-regexps + '(">>> " "\\.\\.\\. " ; Python + "In \\[[0-9]+\\]: " ; IPython + " \\.\\.\\.: " ; IPython + ;; Using ipdb outside IPython may fail to cleanup and leave static + ;; IPython prompts activated, this adds some safeguard for that. + "In : " "\\.\\.\\.: ") + "List of regular expressions matching input prompts." + :type '(repeat string) + :version "24.4" + :tag "py-shell-input-prompt-2-regexps" + :group 'python-mode) + +(defcustom py-shell-input-prompt-regexps + '(">>> " "\\.\\.\\. " ; Python + "In \\[[0-9]+\\]: " ; IPython + " \\.\\.\\.: " ; IPython + ;; Using ipdb outside IPython may fail to cleanup and leave static + ;; IPython prompts activated, this adds some safeguard for that. + "In : " "\\.\\.\\.: ") + "List of regular expressions matching input prompts." + :type '(repeat regexp) + :version "24.4" + :tag "py-shell-input-prompt-regexps" + :group 'python-mode) + +(defvar py-ipython-output-prompt-re "^Out\\[[0-9]+\\]: " + "A regular expression to match the output prompt of IPython.") + +(defcustom py-shell-output-prompt-regexps + '("" ; Python + "Out\\[[0-9]+\\]: " ; IPython + "Out :") ; ipdb safeguard + "List of regular expressions matching output prompts." + :type '(repeat string) + :version "24.4" + :tag "py-shell-output-prompt-regexps" + :group 'python-mode) + +(defvar py-pydbtrack-input-prompt "^[(]*ipydb[>)]+ " + "Recognize the pydb-prompt.") +;; (setq py-pdbtrack-input-prompt "^[(< \t]*[Ii]?[Pp]y?db[>)]*.*") + +(defvar py-ipython-input-prompt-re "In \\[?[0-9 ]*\\]?: *\\|^[ ]\\{3\\}[.]\\{3,\\}: *" + "A regular expression to match the IPython input prompt.") + +(defvar py-shell-prompt-regexp + (concat "\\(" + (mapconcat 'identity + (delq nil + (list + py-shell-input-prompt-1-regexp + py-shell-input-prompt-2-regexp + py-ipython-input-prompt-re + py-ipython-output-prompt-re + py-pdbtrack-input-prompt + py-pydbtrack-input-prompt + "[.]\\{3,\\}:? *" + )) + "\\|") + "\\)") + "Internally used by `py-fast-filter'. +`ansi-color-filter-apply' might return +Result: \"\\nIn [10]: ....: ....: ....: 1\\n\\nIn [11]: \"") + +(defvar py-fast-filter-re + (concat "\\(" + (mapconcat 'identity + (delq nil + (list + py-shell-input-prompt-1-regexp + py-shell-input-prompt-2-regexp + py-ipython-input-prompt-re + py-ipython-output-prompt-re + py-pdbtrack-input-prompt + py-pydbtrack-input-prompt + "[.]\\{3,\\}:? *" + )) + "\\|") + "\\)") + "Internally used by `py-fast-filter'. +`ansi-color-filter-apply' might return +Result: \"\\nIn [10]: ....: ....: ....: 1\\n\\nIn [11]: \"") + +(defcustom py-shell-prompt-detect-p nil + "Non-nil enables autodetection of interpreter prompts." + :type 'boolean + :safe 'booleanp + :version "24.4" + :tag "py-shell-prompt-detect-p" + :group 'python-mode) + +(defcustom py-shell-prompt-read-only t + "If non-nil, the python prompt is read only. + +Setting this variable will only effect new shells." + :type 'boolean + :tag "py-shell-prompt-read-only" + :group 'python-mode) + +(setq py-fast-filter-re + (concat "\\(" + (mapconcat 'identity + (delq nil + (list + py-shell-input-prompt-1-regexp + py-shell-input-prompt-2-regexp + py-ipython-input-prompt-re + py-ipython-output-prompt-re + py-pdbtrack-input-prompt + py-pydbtrack-input-prompt + "[.]\\{3,\\}:? *" + )) + "\\|") + "\\)")) + +(defcustom py-honor-IPYTHONDIR-p nil + "When non-nil ipython-history file is constructed by $IPYTHONDIR. + +Default is nil. +Otherwise value of `py-ipython-history' is used." + :type 'boolean + :tag "py-honor-IPYTHONDIR-p" + :group 'python-mode) + +(defcustom py-ipython-history "~/.ipython/history" + "Ipython-history default file. + +Used when `py-honor-IPYTHONDIR-p' is nil - th default" + + :type 'string + :tag "py-ipython-history" + :group 'python-mode) + +(defcustom py-honor-PYTHONHISTORY-p nil + "When non-nil python-history file is set by $PYTHONHISTORY. + +Default is nil. +Otherwise value of `py-python-history' is used." + :type 'boolean + :tag "py-honor-PYTHONHISTORY-p" + :group 'python-mode) + +(defcustom py-python-history "~/.python_history" + "Python-history default file. + +Used when `py-honor-PYTHONHISTORY-p' is nil (default)." + + :type 'string + :tag "py-python-history" + :group 'python-mode) + +(defcustom py-switch-buffers-on-execute-p nil + "When non-nil switch to the Python output buffer. + +If `py-keep-windows-configuration' is t, this will take precedence +over setting here." + + :type 'boolean + :tag "py-switch-buffers-on-execute-p" + :group 'python-mode) +;; made buffer-local as pdb might need t in all circumstances +(make-variable-buffer-local 'py-switch-buffers-on-execute-p) + +(defcustom py-split-window-on-execute 'just-two + "When non-nil split windows. + +Default is just-two - when code is send to interpreter. +Splits screen into source-code buffer and current `py-shell' result. +Other buffer will be hidden that way. + +When set to t, `python-mode' tries to reuse existing windows +and will split only if needed. + +With \\='always, results will displayed in a new window. + +Both t and `always' is experimental still. + +For the moment: If a multitude of python-shells/buffers should be +visible, open them manually and set `py-keep-windows-configuration' to t. + +See also `py-keep-windows-configuration'" + :type `(choice + (const :tag "default" just-two) + (const :tag "reuse" t) + (const :tag "no split" nil) + (const :tag "just-two" just-two) + (const :tag "always" always)) + :tag "py-split-window-on-execute" + :group 'python-mode) + +;; (defun py-toggle-py-split-window-on-execute () +;; "Toggle between customized value and nil." +;; (interactive) +;; (setq py-split-window-on-execute (not py-split-window-on-execute)) +;; (when (called-interactively-p 'interactive) +;; (message "py-split-window-on-execute: %s" py-split-window-on-execute) +;; py-split-window-on-execute)) + +(defcustom py-split-window-on-execute-threshold 3 + "Maximal number of displayed windows. + +Honored, when `py-split-window-on-execute' is t, i.e. \"reuse\". +Don't split when max number of displayed windows is reached." + :type 'number + :tag "py-split-window-on-execute-threshold" + :group 'python-mode) + +(defcustom py-split-windows-on-execute-function 'split-window-vertically + "How window should get splitted to display results of py-execute-... functions." + :type '(choice (const :tag "split-window-vertically" split-window-vertically) + (const :tag "split-window-horizontally" split-window-horizontally) + ) + :tag "py-split-windows-on-execute-function" + :group 'python-mode) + +(defcustom py-shell-fontify-p 'input + "Fontify current input in Python shell. Default is input. + +INPUT will leave output unfontified. + +At any case only current input gets fontified." + :type '(choice (const :tag "Default" all) + (const :tag "Input" input) + (const :tag "Nil" nil) + ) + :tag "py-shell-fontify-p" + :group 'python-mode) + +(defcustom py-hide-show-keywords + '("class" "def" "elif" "else" "except" + "for" "if" "while" "finally" "try" + "with" "match" "case") + "Keywords composing visible heads." + :type '(repeat string) + :tag "py-hide-show-keywords +" + :group 'python-mode) + +(defcustom py-hide-show-hide-docstrings t + "Controls if doc strings can be hidden by hide-show." + :type 'boolean + :tag "py-hide-show-hide-docstrings" + :group 'python-mode) + +(defcustom py-hide-comments-when-hiding-all t + "Hide the comments too when you do an `hs-hide-all'." + :type 'boolean + :tag "py-hide-comments-when-hiding-all" + :group 'python-mode) + +(defcustom py-outline-mode-keywords + '("class" "def" "elif" "else" "except" + "for" "if" "while" "finally" "try" + "with" "match" "case") + "Keywords composing visible heads." + :type '(repeat string) + :tag "py-outline-mode-keywords +" + :group 'python-mode) + +(defcustom python-mode-hook nil + "Hook run when entering Python mode." + + :type 'hook + :tag "python-mode-hook" + :group 'python-mode + ) + +;; (defcustom py-shell-name +;; (if (eq system-type 'windows-nt) +;; "C:/Python27/python" +;; "python") + +;; "A PATH/TO/EXECUTABLE or default value `py-shell' may look for. + +;; If no shell is specified by command. + +;; On Windows default is C:/Python27/python +;; --there is no garantee it exists, please check your system-- + +;; Else python" +;; :type 'string +;; :tag "py-shell-name +;; " +;; :group 'python-mode) + +(defcustom py-python-command + (if (eq system-type 'windows-nt) + ;; "C:\\Python27\\python.exe" + "python" + ;; "C:/Python33/Lib/site-packages/IPython" + "python") + + "Make sure directory in in the PATH-variable. + +Windows: edit in \"Advanced System Settings/Environment Variables\" +Commonly \"C:\\\\Python27\\\\python.exe\" +With Anaconda for example the following works here: +\"C:\\\\Users\\\\My-User-Name\\\\Anaconda\\\\Scripts\\\\python.exe\" + +Else /usr/bin/python" + + :type 'string + :tag "py-python-command +" + :group 'python-mode) + +(defvaralias 'py-shell-name 'py-python-command) + +(defcustom py-python-command-args '("-i") + "String arguments to be used when starting a Python shell." + :type '(repeat string) + :tag "py-python-command-args" + :group 'python-mode) + +(defcustom py-python2-command + (if (eq system-type 'windows-nt) + "C:\\Python27\\python" + ;; "python2" + "python2") + + "Make sure, the directory where python.exe resides in in the PATH-variable. + +Windows: If needed, edit in +\"Advanced System Settings/Environment Variables\" +Commonly +\"C:\\\\Python27\\\\python.exe\" +With Anaconda for example the following works here: +\"C:\\\\Users\\\\My-User-Name\\\\Anaconda\\\\Scripts\\\\python.exe\" + +Else /usr/bin/python" + + :type 'string + :tag "py-python2-command +" + :group 'python-mode) + +(defcustom py-python2-command-args '("-i") + "String arguments to be used when starting a Python shell." + :type '(repeat string) + :tag "py-python2-command-args" + :group 'python-mode) + +;; "/usr/bin/python3" +(defcustom py-python3-command + (if (eq system-type 'windows-nt) + "C:/Python33/python" + "python3") + + "A PATH/TO/EXECUTABLE or default value `py-shell' may look for. + +Unless shell is specified by command. + +On Windows see C:/Python3/python.exe +--there is no garantee it exists, please check your system-- + +At GNU systems see /usr/bin/python3" + + :type 'string + :tag "py-python3-command +" + :group 'python-mode) + +(defcustom py-python3-command-args '("-i") + "String arguments to be used when starting a Python3 shell." + :type '(repeat string) + :tag "py-python3-command-args" + :group 'python-mode) + +(defcustom py-ipython-command + (if (eq system-type 'windows-nt) + ;; "ipython" + "C:\\Python27\\python" + ;; "C:/Python33/Lib/site-packages/IPython" + ;; "/usr/bin/ipython" + "ipython") + + "A PATH/TO/EXECUTABLE or default value. + +`M-x IPython RET' may look for, +Unless IPython-shell is specified by command. + +On Windows default is \"C:\\\\Python27\\\\python.exe\" +While with Anaconda for example the following works here: +\"C:\\\\Users\\\\My-User-Name\\\\Anaconda\\\\Scripts\\\\ipython.exe\" + +Else /usr/bin/ipython" + + :type 'string + :tag "py-ipython-command +" + :group 'python-mode) + +(defcustom py-ipython-command-args + (if (eq system-type 'windows-nt) + '("-i" "C:\\Python27\\Scripts\\ipython-script.py") + ;; --simple-prompt seems to exist from IPython 5. + (if (string-match "^[0-4]" (shell-command-to-string (concat "ipython" " -V"))) + '("--pylab" "--automagic") + '("--pylab" "--automagic" "--simple-prompt"))) + "String arguments to be used when starting a IPython shell. + +At Windows make sure ipython-script.py is PATH. +Also setting PATH/TO/SCRIPT here should work, for example; +C:\\Python27\\Scripts\\ipython-script.py +With Anaconda the following is known to work: +\"C:\\\\Users\\\\My-User-Name\\\\Anaconda\\\\Scripts\\\\ipython-script-py\"" + :type '(repeat string) + :tag "py-ipython-command-args" + :group 'python-mode) + +(defcustom py-jython-command + (if (eq system-type 'windows-nt) + '("jython") + '("/usr/bin/jython")) + + "A PATH/TO/EXECUTABLE or default value. +`M-x Jython RET' may look for, if no Jython-shell is specified by command. + +Not known to work at windows +Default /usr/bin/jython" + + :type '(repeat string) + :tag "py-jython-command +" + :group 'python-mode) + +(defcustom py-jython-command-args '("-i") + "String arguments to be used when starting a Jython shell." + :type '(repeat string) + :tag "py-jython-command-args" + :group 'python-mode) + +(defcustom py-shell-toggle-1 py-python2-command + "A PATH/TO/EXECUTABLE or default value used by `py-toggle-shell'." + :type 'string + :tag "py-shell-toggle-1" + :group 'python-mode) + +(defcustom py-shell-toggle-2 py-python3-command + "A PATH/TO/EXECUTABLE or default value used by `py-toggle-shell'." + :type 'string + :tag "py-shell-toggle-2" + :group 'python-mode) + +(defcustom py--imenu-create-index-p nil + "Non-nil means Python mode creates and displays an index menu. + +Of functions and global variables." + :type 'boolean + :tag "py--imenu-create-index-p" + :group 'python-mode) + +(defvar py-history-filter-regexp "\\`\\s-*\\S-?\\S-?\\s-*\\'\\|'''/tmp/" + "Input matching this regexp is not saved on the history list. +Default ignores all inputs of 0, 1, or 2 non-blank characters.") + +(defcustom py-match-paren-mode nil + "Non-nil means, cursor will jump to beginning or end of a block. +This vice versa, to beginning first. +Sets `py-match-paren-key' in `python-mode-map'. +Customize `py-match-paren-key' which key to use." + :type 'boolean + :tag "py-match-paren-mode" + :group 'python-mode) + +(defcustom py-match-paren-key "%" + "String used by \\[comment-region] to comment out a block of code. +This should follow the convention for non-indenting comment lines so +that the indentation commands won't get confused (i.e., the string +should be of the form `#x...' where `x' is not a blank or a tab, and + `...' is arbitrary). +However, this string should not end in whitespace." + :type 'string + :tag "py-match-paren-key" + :group 'python-mode) + +(defcustom py-kill-empty-line t + "If t, `py-indent-forward-line' kills empty lines." + :type 'boolean + :tag "py-kill-empty-line" + :group 'python-mode) + +(defcustom py-imenu-show-method-args-p nil + "Controls echoing of arguments of functions & methods in the Imenu buffer. +When non-nil, arguments are printed." + :type 'boolean + :tag "py-imenu-show-method-args-p" + :group 'python-mode) + +(defcustom py-use-local-default nil + "If t, `py-shell' will use `py-shell-local-path'. + +Alternative to default Python. + +Making switch between several virtualenv's easier,`python-mode' should +deliver an installer, named-shells pointing to virtualenv's will be available." + :type 'boolean + :tag "py-use-local-default" + :group 'python-mode) + +(defcustom py-edit-only-p nil + "Don't check for installed Python executables. + +Default is nil. + +See bug report at launchpad, lp:944093." + :type 'boolean + :tag "py-edit-only-p" + :group 'python-mode) + +(defcustom py-force-py-shell-name-p nil + "When t, execution specified in `py-shell-name' is enforced. + +Possibly shebang doesn't take precedence." + + :type 'boolean + :tag "py-force-py-shell-name-p" + :group 'python-mode) + +(defcustom python-mode-v5-behavior-p nil + "Execute region through `shell-command-on-region'. + +As v5 did it - lp:990079. +This might fail with certain chars - see UnicodeEncodeError lp:550661" + + :type 'boolean + :tag "python-mode-v5-behavior-p" + :group 'python-mode) + +(defun py-toggle-python-mode-v5-behavior () + "Switch the values of `python-mode-v5-behavior-p'." + (interactive) + (setq python-mode-v5-behavior-p (not python-mode-v5-behavior-p)) + (when (called-interactively-p 'interactive) + (message "python-mode-v5-behavior-p: %s" python-mode-v5-behavior-p))) + +(defun py-toggle-py-verbose-p () + "Switch the values of `py-verbose-p'. + +Default is nil. +If on, messages value of `py-result' for instance." + (interactive) + (setq py-verbose-p (not py-verbose-p)) + (when (called-interactively-p 'interactive) + (message "py-verbose-p: %s" py-verbose-p))) + +(defcustom py-trailing-whitespace-smart-delete-p nil + "Default is nil. + +When t, `python-mode' calls +\(add-hook \\='before-save-hook \\='delete-trailing-whitespace nil \\='local) + +Also commands may delete trailing whitespace by the way. +When editing other peoples code, this may produce a larger diff than expected" + :type 'boolean + :tag "py-trailing-whitespace-smart-delete-p" + :group 'python-mode) + +(defcustom py-newline-delete-trailing-whitespace-p t + "Delete trailing whitespace maybe left by `py-newline-and-indent'. + +Default is t. See lp:1100892" + :type 'boolean + :tag "py-newline-delete-trailing-whitespace-p" + :group 'python-mode) + +(defcustom py--warn-tmp-files-left-p nil + "Warn, when `py-temp-directory' contains files susceptible being left. + +WRT previous Python-mode sessions. See also lp:987534." + :type 'boolean + :tag "py--warn-tmp-files-left-p" + :group 'python-mode) + +(defcustom py-complete-ac-sources '(ac-source-pycomplete) + "List of `auto-complete' sources assigned to `ac-sources'. + +In `py-complete-initialize'. + +Default is known to work an Ubuntu 14.10 - having python- +mode, pymacs and auto-complete-el, with the following minimal +Emacs initialization: + +\(require \\='pymacs) +\(require \\='auto-complete-config) +\(ac-config-default)" + :type 'hook + :tag "py-complete-ac-sources" + :options '(ac-source-pycomplete ac-source-abbrev ac-source-dictionary ac-source-words-in-same-mode-buffers) + :group 'python-mode) + +(defcustom py-remove-cwd-from-path t + "Whether to allow loading of Python modules from the current directory. +If this is non-nil, Emacs removes '' from sys.path when starting +a Python process. This is the default, for security +reasons, as it is easy for the Python process to be started +without the user's realization (e.g. to perform completion)." + :type 'boolean + :tag "py-remove-cwd-from-path" + :group 'python-mode) + +(defcustom py-shell-local-path "" + "`py-shell' will use EXECUTABLE indicated here incl. path. + +If `py-use-local-default' is non-nil." + + :type 'string + :tag "py-shell-local-path" + :group 'python-mode) + +(defcustom py-python-edit-version "" + "When not empty, fontify according to Python version specified. + +Default is the empty string, a useful value \"python3\" maybe. + +When empty, version is guessed via `py-choose-shell'." + + :type 'string + :tag "py-python-edit-version" + :group 'python-mode) + +(defcustom py-ipython-execute-delay 0.3 + "Delay needed by execute functions when no IPython shell is running." + :type 'float + :tag "py-ipython-execute-delay" + :group 'python-mode) + +(defvar py-shell-completion-setup-code + "try: + import readline +except ImportError: + def __COMPLETER_all_completions(text): [] +else: + import rlcompleter + readline.set_completer(rlcompleter.Completer().complete) + def __COMPLETER_all_completions(text): + import sys + completions = [] + try: + i = 0 + while True: + res = readline.get_completer()(text, i) + if not res: break + i += 1 + completions.append(res) + except NameError: + pass + return completions" + "Code used to setup completion in Python processes.") + +(defvar py-shell-module-completion-code "';'.join(__COMPLETER_all_completions('''%s'''))" + "Python code used to get completions separated by semicolons for imports.") + +(defvar py-ipython-module-completion-code + "import IPython +version = IPython.__version__ +if \'0.10\' < version: + from IPython.core.completerlib import module_completion +" + "For IPython v0.11 or greater. +Use the following as the value of this variable: + +';'.join(module_completion('''%s'''))") + +(defvar py-ipython-module-completion-string + "';'.join(module_completion('''%s'''))" + "See also `py-ipython-module-completion-code'.") + +(defcustom py--imenu-create-index-function 'py--imenu-index + "Switch between `py--imenu-create-index-new' and series 5. index-machine." + :type '(choice + (const :tag "'py--imenu-create-index-new, also lists modules variables " py--imenu-create-index-new) + + (const :tag "py--imenu-create-index, series 5. index-machine" py--imenu-create-index) + (const :tag "py--imenu-index, honor type annotations" py--imenu-index) + + ) + :tag "py--imenu-create-index-function" + :group 'python-mode) + +(defvar py-line-re "^" + "Used by generated functions." ) + +(defvar py-input-filter-re "\\`\\s-*\\S-?\\S-?\\s-*\\'" + "Input matching this regexp is not saved on the history list. +Default ignores all inputs of 0, 1, or 2 non-blank characters.") + +(defvar strip-chars-before "\\`[ \t\r\n]*" + "Regexp indicating which chars shall be stripped before STRING. + +See also `string-chars-preserve'") + +(defvar strip-chars-after "[ \t\r\n]*\\'" + "Regexp indicating which chars shall be stripped after STRING. + +See also `string-chars-preserve'") + +(defcustom py-docstring-style 'pep-257-nn + "Implemented styles: + + are DJANGO, ONETWO, PEP-257, PEP-257-NN,SYMMETRIC, and NIL. + +A value of NIL won't care about quotes +position and will treat docstrings a normal string, any other +value may result in one of the following docstring styles: + +DJANGO: + + \"\"\" + Process foo, return bar. + \"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\" + +ONETWO: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + + \"\"\" + +PEP-257: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\"Process foo, return bar. + + If processing fails throw ProcessingError. + + \"\"\" + +PEP-257-NN: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\"Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\" + +SYMMETRIC: + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\"" + :type '(choice + + (const :tag "Don't format docstrings" nil) + (const :tag "Django's coding standards style." django) + (const :tag "One newline and start and Two at end style." onetwo) + (const :tag "PEP-257 with 2 newlines at end of string." pep-257) + (const :tag "PEP-257-nn with 1 newline at end of string." pep-257-nn) + (const :tag "Symmetric style." symmetric)) + :tag "py-docstring-style" + :group 'python-mode) + +(defcustom py-execute-directory nil + "Stores the file's default directory-name py-execute-... functions act upon. + +Used by Python-shell for output of `py-execute-buffer' and related commands. +See also `py-use-current-dir-when-execute-p'" + :type 'string + :tag "py-execute-directory" + :group 'python-mode) + +(defcustom py-use-current-dir-when-execute-p t + "Current directory used for output. + +See also `py-execute-directory'" + :type 'boolean + :tag "py-use-current-dir-when-execute-p" + :group 'python-mode) + +(defcustom py-keep-shell-dir-when-execute-p nil + "Don't change Python shell's current working directory when sending code. + +See also `py-execute-directory'" + :type 'boolean + :tag "py-keep-shell-dir-when-execute-p" + :group 'python-mode) + +(defcustom py-fileless-buffer-use-default-directory-p t + "`default-directory' sets current working directory of Python output shell. + +When `py-use-current-dir-when-execute-p' is non-nil and no buffer-file exists." + :type 'boolean + :tag "py-fileless-buffer-use-default-directory-p" + :group 'python-mode) + +(defcustom py-check-command "pychecker --stdlib" + "Command used to check a Python file." + :type 'string + :tag "py-check-command" + :group 'python-mode) + +;; (defvar py-this-abbrevs-changed nil +;; "Internally used by `python-mode-hook'.") + +(defvar py-buffer-name nil + "Internal use. + +The buffer last output was sent to.") + +(defvar py-orig-buffer-or-file nil + "Internal use.") + +(defcustom py-keep-windows-configuration nil + "Takes precedence over: + + `py-split-window-on-execute' and `py-switch-buffers-on-execute-p'. +See lp:1239498 + +To suppres window-changes due to error-signaling also. +Set `py-keep-windows-configuration' onto \\'force + +Default is nil" + + :type '(choice + (const :tag "nil" nil) + (const :tag "t" t) + (const :tag "force" 'force)) + :tag "py-keep-windows-configuration" + :group 'python-mode) + +(defvar py-output-buffer "*Python Output*" + "Used if `python-mode-v5-behavior-p' is t. + +Otherwise output buffer is created dynamically according to version process.") + +(defcustom py-force-default-output-buffer-p nil + "Enforce sending output to the default output `buffer-name'. + +Set by defvar `py-output-buffer' +Bug #31 - wrong fontification caused by string-delimiters in output" + + :type 'boolean + :tag "py-force-default-output-buffer-p" + :group 'python-mode) + +(defcustom py-shell-unbuffered t + "Should shell output be unbuffered?. +When non-nil, this may prevent delayed and missing output in the +Python shell. See commentary for details." + :type 'boolean + :safe 'booleanp + :tag "py-shell-unbuffered" + :group 'python-mode) + +(defcustom py-shell-process-environment nil + "List of overridden environment variables for subprocesses to inherit. +Each element should be a string of the form ENVVARNAME=VALUE. +When this variable is non-nil, values are exported into the +process environment before starting it. Any variables already +present in the current environment are superseded by variables +set here." + :type '(repeat string) + :tag "py-shell-process-environment" + :group 'python-mode) + +(defcustom py-shell-extra-pythonpaths nil + "List of extra pythonpaths for Python shell. +When this variable is non-nil, values added at the beginning of +the PYTHONPATH before starting processes. Any values present +here that already exists in PYTHONPATH are moved to the beginning +of the list so that they are prioritized when looking for +modules." + :type '(repeat string) + :tag "py-shell-extra-pythonpaths" + :group 'python-mode) + +(defcustom py-shell-exec-path nil + "List of paths for searching executables. +When this variable is non-nil, values added at the beginning of +the PATH before starting processes. Any values present here that +already exists in PATH are moved to the beginning of the list so +that they are prioritized when looking for executables." + :type '(repeat string) + :tag "py-shell-exec-path" + :group 'python-mode) + +(defcustom py-shell-remote-exec-path nil + "List of paths to be ensured remotely for searching executables. +When this variable is non-nil, values are exported into remote +hosts PATH before starting processes. Values defined in +`py-shell-exec-path' will take precedence to paths defined +here. Normally you wont use this variable directly unless you +plan to ensure a particular set of paths to all Python shell +executed through tramp connections." + :version "25.1" + :type '(repeat string) + :tag "py-shell-remote-exec-path" + :group 'python-mode) + +(defcustom py-shell-virtualenv-root nil + "Path to virtualenv root. +This variable, when set to a string, makes the environment to be +modified such that shells are started within the specified +virtualenv." + :type '(choice (const nil) string) + :tag "py-shell-virtualenv-root" + :group 'python-mode) + +(defvar py-shell-completion-native-redirect-buffer + " *Py completions redirect*" + "Buffer to be used to redirect output of readline commands.") + +(defvar py-shell--block-prompt nil + "Input block prompt for inferior python shell. +Do not set this variable directly, instead use +`py-shell-prompt-set-calculated-regexps'.") + +(defvar py-shell-output-filter-in-progress nil) +(defvar py-shell-output-filter-buffer nil) + +(defvar py-shell--prompt-calculated-input-regexp nil + "Calculated input prompt regexp for inferior python shell. +Do not set this variable directly. + +Iff `py-shell--prompt-calculated-input-regexp' +or `py-shell--prompt-calculated-output-regexp' are set +`py-shell-prompt-set-calculated-regexps' isn't run.") + +(defvar py-shell--prompt-calculated-output-regexp nil + "Calculated output prompt regexp for inferior python shell. + +`py-shell-prompt-set-calculated-regexps' +Do not set this variable directly. + +Iff `py-shell--prompt-calculated-input-regexp' +or `py-shell--prompt-calculated-output-regexp' are set +`py-shell-prompt-set-calculated-regexps' isn't run.") + +(defvar py-shell-prompt-output-regexp "" + "See `py-shell-prompt-output-regexps'.") + +(defvar py-shell-prompt-output-regexps + '("" ; Python + "Out\\[[0-9]+\\]: " ; IPython + "Out :") ; ipdb safeguard + "List of regular expressions matching output prompts.") + +(defvar py-underscore-word-syntax-p t + "This is set later by defcustom, only initial value here. + +If underscore chars should be of `syntax-class' `word', not of `symbol'. +Underscores in word-class makes `forward-word'. +Travels the indentifiers. Default is t. +See also command `py-toggle-underscore-word-syntax-p'") + +(defvar py-autofill-timer nil) +(defvar py-fill-column-orig fill-column + "Used to reset fill-column") + +;; defvared value isn't updated maybe +(defvar python-mode-message-string + (if (or (string= "python-mode.el" (buffer-name)) + (ignore-errors (string-match "python-mode.el" (py--buffer-filename-remote-maybe)))) + "python-mode.el" + "python-components-mode") + "Internally used. Reports the `python-mode' branch.") + +;; defvared value isn't updated maybe +(setq python-mode-message-string + (if (or (string= "python-mode.el" (buffer-name)) + (ignore-errors (string-match "python-mode.el" (py--buffer-filename-remote-maybe)))) + "python-mode.el" + "python-components-mode")) + +(defun py-escaped-p (&optional pos) + "Return t if char at POS is preceded by an odd number of backslashes. " + (save-excursion + (when pos (goto-char pos)) + (< 0 (% (abs (skip-chars-backward "\\\\")) 2)))) + +(defvar python-mode-syntax-table nil + "Give punctuation syntax to ASCII that normally has symbol. + +Syntax or has word syntax and isn't a letter.") + +(setq python-mode-syntax-table + (let ((table (make-syntax-table))) + ;; Give punctuation syntax to ASCII that normally has symbol + ;; syntax or has word syntax and isn't a letter. + (let ((symbol (string-to-syntax "_")) + (sst (standard-syntax-table))) + (dotimes (i 128) + (unless (= i ?_) + (if (equal symbol (aref sst i)) + (modify-syntax-entry i "." table))))) + (modify-syntax-entry ?$ "." table) + (modify-syntax-entry ?% "." table) + ;; exceptions + (modify-syntax-entry ?# "<" table) + (modify-syntax-entry ?\n ">" table) + (modify-syntax-entry ?' "\"" table) + (modify-syntax-entry ?` "$" table) + (if py-underscore-word-syntax-p + (modify-syntax-entry ?\_ "w" table) + (modify-syntax-entry ?\_ "_" table)) + table)) + +(defvar py-ipython-completion-command-string nil + "Select command according to IPython version. + +Either `py-ipython0.10-completion-command-string' +or `py-ipython0.11-completion-command-string'. + +`py-ipython0.11-completion-command-string' also covers version 0.12") + +(defvar py-ipython0.10-completion-command-string + "print(';'.join(__IP.Completer.all_completions('%s'))) #PYTHON-MODE SILENT\n" + "The string send to ipython to query for all possible completions.") + +(defvar py-ipython0.11-completion-command-string + "print(';'.join(get_ipython().Completer.all_completions('%s'))) #PYTHON-MODE SILENT\n" + "The string send to ipython to query for all possible completions.") + +(defvar py-encoding-string-re "^[ \t]*#[ \t]*-\\*-[ \t]*coding:.+-\\*-" + "Matches encoding string of a Python file.") + +(defvar py-shebang-regexp "#![ \t]?\\([^ \t\n]+\\)[ \t]*\\([biptj]+ython[^ \t\n]*\\)" + "Detecting the shell in head of file.") + +(defvar py-temp-directory + (let ((ok '(lambda (x) + (and x + (setq x (expand-file-name x)) ; always true + (file-directory-p x) + (file-writable-p x) + x))) + erg) + (or + (and (not (string= "" py-custom-temp-directory)) + (if (funcall ok py-custom-temp-directory) + (setq erg (expand-file-name py-custom-temp-directory)) + (if (file-directory-p (expand-file-name py-custom-temp-directory)) + (error "Py-custom-temp-directory set but not writable") + (error "Py-custom-temp-directory not an existing directory")))) + (and (funcall ok (getenv "TMPDIR")) + (setq erg (getenv "TMPDIR"))) + (and (funcall ok (getenv "TEMP/TMP")) + (setq erg (getenv "TEMP/TMP"))) + (and (funcall ok "/usr/tmp") + (setq erg "/usr/tmp")) + (and (funcall ok "/tmp") + (setq erg "/tmp")) + (and (funcall ok "/var/tmp") + (setq erg "/var/tmp")) + (and (eq system-type 'darwin) + (funcall ok "/var/folders") + (setq erg "/var/folders")) + (and (or (eq system-type 'ms-dos)(eq system-type 'windows-nt)) + (funcall ok (concat "c:" py-separator-char "Users")) + (setq erg (concat "c:" py-separator-char "Users"))) + ;; (funcall ok ".") + (error + "Couldn't find a usable temp directory -- set `py-temp-directory'")) + (when erg (setq py-temp-directory erg))) + "Directory used for temporary files created by a *Python* process. +By default, guesses the first directory from this list that exists and that you +can write into: the value (if any) of the environment variable TMPDIR, +/usr/tmp, /tmp, /var/tmp, or the current directory. + + `py-custom-temp-directory' will take precedence when setq") + +(defvar py-exec-command nil + "Internally used.") + +(defvar py-which-bufname "Python") + +(defvar py-pychecker-history nil) + +(defvar py-pyflakes-history nil) + +(defvar py-pep8-history nil) + +(defvar py-pyflakespep8-history nil) + +(defvar py-pylint-history nil) + +(defvar py-mode-output-map nil + "Keymap used in *Python Output* buffers.") + +(defvar hs-hide-comments-when-hiding-all t + "Defined in hideshow.el, silence compiler warnings here.") + +(defvar py-shell-complete-debug nil + "For interal use when debugging, stores completions." ) + +(defvar py-debug-p nil + "Activate extra code for analysis and test purpose when non-nil. + +Temporary files are not deleted. Other functions might implement +some logging, etc. +For normal operation, leave it set to nil, its default. +Defined with a defvar form to allow testing the loading of new versions.") + +(defun py-toggle-py-debug-p () + "Toggle value of `py-debug-p'." + (interactive) + (setq py-debug-p (not py-debug-p)) + (when (called-interactively-p 'interactive) (message "py-debug-p: %s" py-debug-p))) + +(defcustom py-shell-complete-p nil + "Enable native completion. + +Set TAB accordingly." + + :type 'boolean + :tag "py-shell-complete-p" + :group 'python-mode) +(make-variable-buffer-local 'py-shell-complete-p) + +(defcustom py-section-start "# {{" + "Delimit arbitrary chunks of code." + :type 'string + :tag "py-section-start" + :group 'python-mode) + +(defcustom py-section-end "# }}" + "Delimit arbitrary chunks of code." + :type 'string + :tag "py-section-end" + :group 'python-mode) + +(defvar py-section-re py-section-start) + +(defvar py-last-window-configuration nil + "Internal use. + +Restore `py-restore-window-configuration'.") + +(defvar py-exception-buffer nil + "Will be set internally. + +Remember source buffer where error might occur.") + +(defvar py-string-delim-re "\\(\"\"\"\\|'''\\|\"\\|'\\)" + "When looking at beginning of string.") + +(defvar py-labelled-re "[ \\t]*:[[:graph:]]+" + "When looking at label.") +;; (setq py-labelled-re "[ \\t]*:[[:graph:]]+") + +(defvar py-expression-skip-regexp "[^ (=:#\t\r\n\f]" + "Expression possibly composing a `py-expression'.") + +(defvar py-expression-skip-chars "^ (=#\t\r\n\f" + "Chars composing a `py-expression'.") + +(setq py-expression-skip-chars "^ [{(=#\t\r\n\f") + +(defvar py-expression-re "[^ =#\t\r\n\f]+" + "Expression possibly composing a `py-expression'.") + +(defcustom py-paragraph-re paragraph-start + "Allow Python specific `paragraph-start' var." + :type 'string + :tag "py-paragraph-re" + :group 'python-mode) + +(defvar py-not-expression-regexp "[ .=#\t\r\n\f)]+" + "Regexp indicated probably will not compose a `py-expression'.") + +(defvar py-not-expression-chars " #\t\r\n\f" + "Chars indicated probably will not compose a `py-expression'.") + +(defvar py-partial-expression-backward-chars "^] .=,\"'()[{}:#\t\r\n\f" + "Chars indicated possibly compose a `py-partial-expression', skip it.") +;; (setq py-partial-expression-backward-chars "^] .=,\"'()[{}:#\t\r\n\f") + +(defvar py-partial-expression-forward-chars "^ .\"')}]:#\t\r\n\f") +;; (setq py-partial-expression-forward-chars "^ .\"')}]:#\t\r\n\f") + +(defvar py-partial-expression-re (concat "[" py-partial-expression-backward-chars (substring py-partial-expression-forward-chars 1) "]+")) +(setq py-partial-expression-re (concat "[" py-partial-expression-backward-chars "]+")) + +(defvar py-statement-re py-partial-expression-re) +(defvar py-indent-re ".+" + "This var is introduced for regularity only.") +(setq py-indent-re ".+") + +(defvar py-operator-re "[ \t]*\\(\\.\\|+\\|-\\|*\\|//\\|//\\|&\\|%\\||\\|\\^\\|>>\\|<<\\|<\\|<=\\|>\\|>=\\|==\\|!=\\|=\\)[ \t]*" + "Matches most of Python syntactical meaningful characters. + +See also `py-assignment-re'") + +;; (setq py-operator-re "[ \t]*\\(\\.\\|+\\|-\\|*\\|//\\|//\\|&\\|%\\||\\|\\^\\|>>\\|<<\\|<\\|<=\\|>\\|>=\\|==\\|!=\\|=\\)[ \t]*") + +(defvar py-delimiter-re "\\(\\.[[:alnum:]]\\|,\\|;\\|:\\)[ \t\n]" + "Delimiting elements of lists or other programming constructs.") + +(defvar py-line-number-offset 0 + "When an exception occurs as a result of `py-execute-region'. + +A subsequent `py-up-exception' needs the line number where the region +started, in order to jump to the correct file line. +This variable is set in `py-execute-region' and used in `py--jump-to-exception'.") + +(defvar py-match-paren-no-use-syntax-pps nil) + +(defvar py-traceback-line-re + "[ \t]+File \"\\([^\"]+\\)\", line \\([0-9]+\\)" + "Regular expression that describes tracebacks.") + +(defvar py-XXX-tag-face 'py-XXX-tag-face) + +(defvar py-pseudo-keyword-face 'py-pseudo-keyword-face) + +(defface py-variable-name-face + '((t (:inherit font-lock-variable-name-face))) + "Face method decorators." + :tag "py-variable-name-face" + :group 'python-mode) + +(defvar py-variable-name-face 'py-variable-name-face) + +(defvar py-number-face 'py-number-face) + +(defvar py-decorators-face 'py-decorators-face) + +(defvar py-object-reference-face 'py-object-reference-face) + +(defvar py-builtins-face 'py-builtins-face) + +(defvar py-class-name-face 'py-class-name-face) + +(defvar py-def-face 'py-def-face) + +(defvar py-exception-name-face 'py-exception-name-face) + +(defvar py-import-from-face 'py-import-from-face) + +(defvar py-def-class-face 'py-def-class-face) + +(defvar py-try-if-face 'py-try-if-face) + +(defvar py-file-queue nil + "Queue of Python temp files awaiting execution. +Currently-active file is at the head of the list.") + +(defvar jython-mode-hook nil + "Hook called by `jython-mode'. +`jython-mode' also calls `python-mode-hook'.") + +(defvar py-shell-hook nil + "Hook called by `py-shell'.") + +;; (defvar python-font-lock-keywords nil) + +(defvar py-dotted-expression-syntax-table + (let ((table (make-syntax-table python-mode-syntax-table))) + (modify-syntax-entry ?_ "_" table) + (modify-syntax-entry ?."_" table) + table) + "Syntax table used to identify Python dotted expressions.") + +(defvar python-default-template "if" + "Default template to expand by `python-expand-template'. +Updated on each expansion.") + +(defvar-local py-already-guessed-indent-offset nil + "Internal use by `py-indent-line'. + +When `this-command' is `eq' to `last-command', use the guess already computed.") + +(defvar py-shell-template " +\(defun NAME (&optional argprompt) + \"Start an DOCNAME interpreter in another window. + +With optional \\\\[universal-argument] user is prompted +for options to pass to the DOCNAME interpreter. \" + (interactive \"P\") + (let\* ((py-shell-name \"FULLNAME\")) + (py-shell argprompt) + (when (called-interactively-p 'interactive) + (switch-to-buffer (current-buffer)) + (goto-char (point-max))))) +") + +;; Constants +(defconst py-block-closing-keywords-re + "[ \t]*\\_<\\(return\\|raise\\|break\\|continue\\|pass\\)\\_>[ \n\t]" + "Matches the beginning of a class, method or compound statement.") + +(setq py-block-closing-keywords-re + "[ \t]*\\_<\\(return\\|raise\\|break\\|continue\\|pass\\)\\_>[ \n\t]") + +(defconst py-finally-re + "[ \t]*\\_" + "Matches the beginning of a `except' block.") + +;; (defconst py-except-re +;; "[ \t]*\\_[:( \n\t]*" +;; "Regular expression matching keyword which composes a try-block.") + +(defconst py-return-re + ".*:?[ \t]*\\_<\\(return\\)\\_>[ \n\t]*" + "Regular expression matching keyword which typically closes a function.") + +(defconst py-decorator-re + "[ \t]*@[^ ]+\\_>[ \n\t]*" + "Regular expression matching keyword which typically closes a function.") + +(defcustom py-outdent-re-raw + (list + "case" + "elif" + "else" + "except" + "finally" + ) + "Used by `py-outdent-re'." + :type '(repeat string) + :tag "py-outdent-re-raw" + :group 'python-mode + ) + +(defconst py-outdent-re + (concat + "[ \t]*" + (regexp-opt py-outdent-re-raw 'symbols) + "[)\t]*") + "Regular expression matching statements to be dedented one level.") + +(defcustom py-no-outdent-re-raw + (list + "break" + "continue" + "import" + "pass" + "raise" + "return") + "Uused by `py-no-outdent-re'." + :type '(repeat string) + :tag "py-no-outdent-re-raw" + :group 'python-mode) + +(defconst py-no-outdent-re + (concat + "[ \t]*" + (regexp-opt py-no-outdent-re-raw 'symbols) + "[)\t]*$") +"Regular expression matching lines not to augment indent after. + +See `py-no-outdent-re-raw' for better readable content") + +(defconst py-assignment-re "\\(\\_<\\w+\\_>[[:alnum:]:, \t]*[ \t]*\\)\\(=\\|+=\\|*=\\|%=\\|&=\\|^=\\|<<=\\|-=\\|/=\\|**=\\||=\\|>>=\\|//=\\)\\(.*\\)" + "If looking at the beginning of an assignment.") + +;; 'name': +(defconst py-dict-re "'\\_<\\w+\\_>':") + +(defcustom py-block-re-raw + (list + "async def" + "async for" + "async with" + "class" + "def" + "for" + "if" + "match" + "try" + "while" + "with" + ) + "Matches the beginning of a compound statement but not it's clause." + :type '(repeat string) + :tag "py-block-re-raw" + :group 'python-mode) + +(defconst py-block-re (concat + ;; "[ \t]*" + (regexp-opt py-block-re-raw 'symbols) + "[:( \n\t]" + ) + "Matches the beginning of a compound statement.") + +(defconst py-minor-block-re-raw (list + "async for" + "async with" + "case" + "except" + "for" + "if" + "match" + "try" + "with" + ) + "Matches the beginning of an case `for', `if', `try', `except' or `with' block.") + +(defconst py-minor-block-re + (concat + "[ \t]*" + (regexp-opt py-minor-block-re-raw 'symbols) + "[:( \n\t]") + + "Regular expression matching lines not to augment indent after. + +See `py-minor-block-re-raw' for better readable content") + +(defconst py-try-re "[ \t]*\\_[: \n\t]" + "Matches the beginning of a `try' block.") + +(defconst py-case-re "[ \t]*\\_[: \t][^:]*:" + "Matches a `case' clause.") + +(defconst py-match-case-re "[ \t]*\\_[: \t][^:]*:" + "Matches a `case' clause.") + +(defconst py-for-re "[ \t]*\\_<\\(async for\\|for\\)\\_> +[[:alpha:]_][[:alnum:]_]* +in +[[:alpha:]_][[:alnum:]_()]* *[: \n\t]" + "Matches the beginning of a `try' block.") + +(defconst py-if-re "[ \t]*\\_ +[^\n\r\f]+ *[: \n\t]" + "Matches the beginning of an `if' block.") + +(defconst py-else-re "[ \t]*\\_[( \n\t]" + "Matches the beginning of a compound if-statement's clause exclusively.") + +;; (defconst py-elif-block-re "[ \t]*\\_ +[[:alpha:]_][[:alnum:]_]* *[: \n\t]" +;; "Matches the beginning of an `elif' block.") + +(defconst py-class-re "[ \t]*\\_<\\(class\\)\\_>[ \n\t]" + "Matches the beginning of a class definition.") + +(defconst py-def-or-class-re "[ \t]*\\_<\\(async def\\|class\\|def\\)\\_>[ \n\t]+\\([[:alnum:]_]*\\)" + "Matches the beginning of a class- or functions definition. + +Second group grabs the name") + +;; (setq py-def-or-class-re "[ \t]*\\_<\\(async def\\|class\\|def\\)\\_>[ \n\t]") + +;; (defconst py-def-re "[ \t]*\\_<\\(async def\\|def\\)\\_>[ \n\t]" +(defconst py-def-re "[ \t]*\\_<\\(def\\|async def\\)\\_>[ \n\t]" + "Matches the beginning of a functions definition.") + +(defcustom py-block-or-clause-re-raw + (list + "async for" + "async with" + "async def" + "async class" + "class" + "def" + "elif" + "else" + "except" + "finally" + "for" + "if" + "try" + "while" + "with" + "match" + "case" + ) + "Matches the beginning of a compound statement or it's clause." + :type '(repeat string) + :tag "py-block-or-clause-re-raw" + :group 'python-mode) + +(defvar py-block-or-clause-re + (concat + "[ \t]*" + (regexp-opt py-block-or-clause-re-raw 'symbols) + "[( \t]*.*:?") + "See `py-block-or-clause-re-raw', which it reads.") + +(defcustom py-extended-block-or-clause-re-raw + (list + "async def" + "async for" + "async with" + "class" + "def" + "elif" + "else" + "except" + "finally" + "for" + "if" + "try" + "while" + "with" + "match" + "case" + ) + "Matches the beginning of a compound statement or it's clause." + :type '(repeat string) + :tag "py-extended-block-or-clause-re-raw" + :group 'python-mode) + +(defconst py-extended-block-or-clause-re + (concat + "[ \t]*" + (regexp-opt py-extended-block-or-clause-re-raw 'symbols) + "[( \t:]+") + "See `py-block-or-clause-re-raw', which it reads.") + +(defun py--arglist-indent (nesting &optional indent-offset) + "Internally used by `py-compute-indentation'" + (if + (and (eq 1 nesting) + (save-excursion + (back-to-indentation) + (looking-at py-extended-block-or-clause-re))) + (progn + (back-to-indentation) + (1+ (+ (current-column) (* 2 (or indent-offset py-indent-offset))))) + (+ (current-indentation) (or indent-offset py-indent-offset)))) + +(defconst py-clause-re py-extended-block-or-clause-re + "See also py-minor-clause re.") + +(defcustom py-minor-clause-re-raw + (list + "case" + "elif" + "else" + "except" + "finally" + ) + "Matches the beginning of a clause." + :type '(repeat string) + :tag "py-minor-clause-re-raw" + :group 'python-mode) + +(defconst py-minor-clause-re + (concat + "[ \t]*" + (regexp-opt py-minor-clause-re-raw 'symbols) + "[( \t]*.*:") + "See `py-minor-clause-re-raw', which it reads.") + +(defcustom py-top-level-re + (concat + "^[a-zA-Z_]" + (regexp-opt py-extended-block-or-clause-re-raw) + "[( \t]*.*:?") + "A form which starts at zero indent level, but is not a comment." + :type '(regexp) + :tag "py-top-level-re" + :group 'python-mode + ) + +(defvar py-comment-re comment-start + "Needed for normalized processing.") + +(defconst py-block-keywords + (regexp-opt py-block-or-clause-re-raw 'symbols) + "Matches known keywords opening a block. + +Customizing `py-block-or-clause-re-raw' will change values here") + +(defconst py-try-clause-re + (concat + "[ \t]*\\_<\\(" + (mapconcat 'identity + (list + "else" + "except" + "finally") + "\\|") + "\\)\\_>[( \t]*.*:") + "Matches the beginning of a compound try-statement's clause.") + +(defcustom py-compilation-regexp-alist + `((,(rx line-start (1+ (any " \t")) "File \"" + (group (1+ (not (any "\"<")))) ; avoid `' &c + "\", line " (group (1+ digit))) + 1 2) + (,(rx " in file " (group (1+ not-newline)) " on line " + (group (1+ digit))) + 1 2) + (,(rx line-start "> " (group (1+ (not (any "(\"<")))) + "(" (group (1+ digit)) ")" (1+ (not (any "("))) "()") + 1 2)) + "Fetch errors from Py-shell. +hooked into `compilation-error-regexp-alist'" + :type '(alist string) + :tag "py-compilation-regexp-alist" + :group 'python-mode) + +(defun py--quote-syntax (n) + "Put `syntax-table' property correctly on triple quote. +Used for syntactic keywords. N is the match number (1, 2 or 3)." + ;; Given a triple quote, we have to check the context to know + ;; whether this is an opening or closing triple or whether it's + ;; quoted anyhow, and should be ignored. (For that we need to do + ;; the same job as `syntax-ppss' to be correct and it seems to be OK + ;; to use it here despite initial worries.) We also have to sort + ;; out a possible prefix -- well, we don't _have_ to, but I think it + ;; should be treated as part of the string. + + ;; Test cases: + ;; ur"""ar""" x='"' # """ + ;; x = ''' """ ' a + ;; ''' + ;; x '"""' x """ \"""" x + (save-excursion + (goto-char (match-beginning 0)) + (cond + ;; Consider property for the last char if in a fenced string. + ((= n 3) + (let* ((syntax (parse-partial-sexp (point-min) (point)))) + (when (eq t (nth 3 syntax)) ; after unclosed fence + (goto-char (nth 8 syntax)) ; fence position + ;; (skip-chars-forward "uUrR") ; skip any prefix + ;; Is it a matching sequence? + (if (eq (char-after) (char-after (match-beginning 2))) + (eval-when-compile (string-to-syntax "|")))))) + ;; Consider property for initial char, accounting for prefixes. + ((or (and (= n 2) ; leading quote (not prefix) + (not (match-end 1))) ; prefix is null + (and (= n 1) ; prefix + (match-end 1))) ; non-empty + (unless (eq 'string (syntax-ppss-context (parse-partial-sexp (point-min) (point)))) + (eval-when-compile (string-to-syntax "|")))) + ;; Otherwise (we're in a non-matching string) the property is + ;; nil, which is OK. + ))) + +(defconst py-font-lock-syntactic-keywords + ;; Make outer chars of matching triple-quote sequences into generic + ;; string delimiters. Fixme: Is there a better way? + ;; First avoid a sequence preceded by an odd number of backslashes. + `((,(concat "\\(?:^\\|[^\\]\\(?:\\\\.\\)*\\)" ;Prefix. + "\\(?1:\"\\)\\(?2:\"\\)\\(?3:\"\\)\\(?4:\"\\)\\(?5:\"\\)\\(?6:\"\\)\\|\\(?1:\"\\)\\(?2:\"\\)\\(?3:\"\\)\\|\\(?1:'\\)\\(?2:'\\)\\(?3:'\\)\\(?4:'\\)\\(?5:'\\)\\(?6:'\\)\\|\\(?1:'\\)\\(?2:'\\)\\(?3:'\\)\\(?4:'\\)\\(?5:'\\)\\(?6:'\\)\\|\\(?1:'\\)\\(?2:'\\)\\(?3:'\\)") + (1 (py--quote-syntax 1) t t) + (2 (py--quote-syntax 2) t t) + (3 (py--quote-syntax 3) t t) + (6 (py--quote-syntax 1) t t)))) + +(defconst py--windows-config-register 313465889 + "Internal used by `window-configuration-to-register'.") + +(put 'py-indent-offset 'safe-local-variable 'integerp) + +;; testing +(defvar py-ert-test-default-executables + (list "python" "python3" "ipython") + "Serialize tests employing dolist.") + +(defcustom py-shell-unfontify-p t + "Run `py--run-unfontify-timer' unfontifying the shell banner-text. + +Default is nil" + + :type 'boolean + :tag "py-shell-unfontify-p" + :group 'python-mode) + +;; Pdb +;; #62, pdb-track in a shell buffer +(defcustom pdb-track-stack-from-shell-p t + "If t, track source from shell-buffer. + +Default is t. +Add hook \\='comint-output-filter-functions \\='py--pdbtrack-track-stack-file" + + :type 'boolean + :tag "pdb-track-stack-from-shell-p" + :group 'python-mode) + +(defcustom py-update-gud-pdb-history-p t + "If pdb should provide suggestions WRT file to check and `py-pdb-path'. + +Default is t +See lp:963253" + :type 'boolean + :tag "py-update-gud-pdb-history-p" + :group 'python-mode) + +(defcustom py-pdb-executable nil + "Indicate PATH/TO/pdb. + +Default is nil +See lp:963253" + :type 'string + :tag "py-pdb-executable" + :group 'python-mode) + +(defcustom py-pdb-path + (if (or (eq system-type 'ms-dos)(eq system-type 'windows-nt)) + (quote c:/python27/python\ -i\ c:/python27/Lib/pdb.py) + '/usr/lib/python2.7/pdb.py) + "Where to find pdb.py. Edit this according to your system. +For example \"/usr/lib/python3.4\" might be an option too. + +If you ignore the location `M-x py-guess-pdb-path' might display it." + :type 'variable + :tag "py-pdb-path" + :group 'python-mode) + +(defvar py-python-ms-pdb-command "" + "MS-systems might use that.") + +(defcustom py-shell-prompt-pdb-regexp "[(<]*[Ii]?[Pp]db[>)]+ " + "Regular expression matching pdb input prompt of Python shell. +It should not contain a caret (^) at the beginning." + :type 'string + :tag "py-shell-prompt-pdb-regexp" + :group 'python-mode) + +(defcustom py-pdbtrack-stacktrace-info-regexp + "> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" + "Regular expression matching stacktrace information. +Used to extract the current line and module being inspected." + :type 'string + :safe 'stringp + :tag "py-pdbtrack-stacktrace-info-regexp" + :group 'python-mode) + +(defvar py-pdbtrack-tracked-buffer nil + "Variable containing the value of the current tracked buffer. +Never set this variable directly, use +`py-pdbtrack-set-tracked-buffer' instead.") + +(defvar py-pdbtrack-buffers-to-kill nil + "List of buffers to be deleted after tracking finishes.") + +(defcustom py-pdbtrack-do-tracking-p t + "Controls whether the pdbtrack feature is enabled or not. +When non-nil, pdbtrack is enabled in all comint-based buffers, +e.g. shell buffers and the *Python* buffer. When using pdb to debug a +Python program, pdbtrack notices the pdb prompt and displays the +source file and line that the program is stopped at, much the same way +as `gud-mode' does for debugging C programs with gdb." + :type 'boolean + :tag "py-pdbtrack-do-tracking-p" + :group 'python-mode) +(make-variable-buffer-local 'py-pdbtrack-do-tracking-p) + +(defcustom py-pdbtrack-filename-mapping nil + "Supports mapping file paths when opening file buffers in pdbtrack. +When non-nil this is an alist mapping paths in the Python interpreter +to paths in Emacs." + :type 'alist + :tag "py-pdbtrack-filename-mapping" + :group 'python-mode) + +(defcustom py-pdbtrack-minor-mode-string " PDB" + "String to use in the minor mode list when pdbtrack is enabled." + :type 'string + :tag "py-pdbtrack-minor-mode-string" + :group 'python-mode) + +(defconst py-pdbtrack-stack-entry-regexp + (concat ".*\\("py-shell-input-prompt-1-regexp">\\|"py-ipython-input-prompt-re">\\|>\\) *\\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>()]+\\)()") + "Regular expression pdbtrack uses to find a stack trace entry.") + +(defconst py-pdbtrack-marker-regexp-file-group 2 + "Group position in gud-pydb-marker-regexp that matches the file name.") + +(defconst py-pdbtrack-marker-regexp-line-group 3 + "Group position in gud-pydb-marker-regexp that matches the line number.") + +(defconst py-pdbtrack-marker-regexp-funcname-group 4 + "Group position in gud-pydb-marker-regexp that matches the function name.") + +(defconst py-pdbtrack-track-range 10000 + "Max number of characters from end of buffer to search for stack entry.") + +(defvar py-pdbtrack-is-tracking-p nil) + +(defvar py--docbeg nil + "Internally used by `py--write-edit'.") + +(defvar py--docend nil + "Internally used by `py--write-edit'.") + +(defcustom py-completion-setup-code + " +def __PYTHON_EL_get_completions(text): + completions = [] + completer = None + + try: + import readline + + try: + import __builtin__ + except ImportError: + # Python 3 + import builtins as __builtin__ + builtins = dir(__builtin__) + + is_ipython = ('__IPYTHON__' in builtins or + '__IPYTHON__active' in builtins) + splits = text.split() + is_module = splits and splits[0] in ('from', 'import') + + if is_ipython and is_module: + from IPython.core.completerlib import module_completion + completions = module_completion(text.strip()) + elif is_ipython and '__IP' in builtins: + completions = __IP.complete(text) + elif is_ipython and 'get_ipython' in builtins: + completions = get_ipython().Completer.all_completions(text) + else: + # Try to reuse current completer. + completer = readline.get_completer() + if not completer: + # importing rlcompleter sets the completer, use it as a + # last resort to avoid breaking customizations. + import rlcompleter + completer = readline.get_completer() + if getattr(completer, 'PYTHON_EL_WRAPPED', False): + completer.print_mode = False + i = 0 + while True: + completion = completer(text, i) + if not completion: + break + i += 1 + completions.append(completion) + except: + pass + finally: + if getattr(completer, 'PYTHON_EL_WRAPPED', False): + completer.print_mode = True + return completions" + "Code used to setup completion in inferior Python processes." + :type 'string + :tag "py-completion-setup-code" + :group 'python-mode) + +(defcustom py-shell-completion-string-code + "';'.join(__PYTHON_EL_get_completions('''%s'''))" + "Python code used to get a string of completions separated by semicolons. +The string passed to the function is the current python name or +the full statement in the case of imports." + :type 'string + :tag "py-shell-completion-string-code" + :group 'python-mode) + +(defface py-XXX-tag-face + '((t (:inherit font-lock-string-face))) + "XXX\\|TODO\\|FIXME " + :tag "py-XXX-tag-face" + :group 'python-mode) + +(defface py-pseudo-keyword-face + '((t (:inherit font-lock-keyword-face))) + "Face for pseudo keywords in Python mode, like self, True, False, + Ellipsis. + +See also `py-object-reference-face'" + :tag "py-pseudo-keyword-face" + :group 'python-mode) + +(defface py-object-reference-face + '((t (:inherit py-pseudo-keyword-face))) + "Face when referencing object members from its class resp. method., +commonly \"cls\" and \"self\"" + :tag "py-object-reference-face" + :group 'python-mode) + +(defface py-number-face + '((t (:inherit nil))) + "Highlight numbers." + :tag "py-number-face" + :group 'python-mode) + +(defface py-try-if-face + '((t (:inherit font-lock-keyword-face))) + "Highlight keywords." + :tag "py-try-if-face" + :group 'python-mode) + +(defface py-import-from-face + '((t (:inherit font-lock-keyword-face))) + "Highlight keywords." + :tag "py-import-from-face" + :group 'python-mode) + +(defface py-def-class-face + '((t (:inherit font-lock-keyword-face))) + "Highlight keywords." + :tag "py-def-class-face" + :group 'python-mode) + + ;; PEP 318 decorators +(defface py-decorators-face + '((t (:inherit font-lock-keyword-face))) + "Face method decorators." + :tag "py-decorators-face" + :group 'python-mode) + +(defface py-builtins-face + '((t (:inherit font-lock-builtin-face))) + "Face for builtins like TypeError, object, open, and exec." + :tag "py-builtins-face" + :group 'python-mode) + +(defface py-class-name-face + '((t (:inherit font-lock-type-face))) + "Face for classes." + :tag "py-class-name-face" + :group 'python-mode) + +(defface py-def-face + '((t (:inherit font-lock-function-name-face))) + "Face for classes." + :tag "py-class-name-face" + :group 'python-mode) + +(defface py-exception-name-face + '((t (:inherit font-lock-builtin-face))) + "Face for Python exceptions." + :tag "py-exception-name-face" + :group 'python-mode) + +;; subr-x.el might not exist yet +;; #73, Byte compilation on Emacs 25.3 fails on different trim-right signature + +(defsubst py--string-trim-left (strg &optional regexp) + "Trim STRING of leading string matching REGEXP. + +REGEXP defaults to \"[ \\t\\n\\r]+\"." + (if (string-match (concat "\\`\\(?:" (or regexp "[ \t\n\r]+") "\\)") strg) + (replace-match "" t t strg) + strg)) + +(defsubst py--string-trim-right (strg &optional regexp) + "Trim STRING of trailing string matching REGEXP. + +REGEXP defaults to \"[ \\t\\n\\r]+\"." + (if (string-match (concat "\\(?:" (or regexp "[ \t\n\r]+") "\\)\\'") strg) + (replace-match "" t t strg) + strg)) + +(defsubst py--string-trim (strg &optional trim-left trim-right) + "Trim STRING of leading and trailing strings matching TRIM-LEFT and TRIM-RIGHT. + +TRIM-LEFT and TRIM-RIGHT default to \"[ \\t\\n\\r]+\"." + (py--string-trim-left (py--string-trim-right strg trim-right) trim-left)) + +(defsubst string-blank-p (strg) + "Check whether STRING is either empty or only whitespace." + (string-match-p "\\`[ \t\n\r]*\\'" strg)) + +(defsubst string-remove-prefix (prefix strg) + "Remove PREFIX from STRING if present." + (if (string-prefix-p prefix strg) + (substring strg (length prefix)) + strg)) + +(defun py-toggle-imenu-create-index () + "Toggle value of `py--imenu-create-index-p'." + (interactive) + (setq py--imenu-create-index-p (not py--imenu-create-index-p)) + (when (called-interactively-p 'interactive) + (message "py--imenu-create-index-p: %s" py--imenu-create-index-p))) + +(defun py-toggle-shell-completion () + "Switch value of buffer-local var `py-shell-complete-p'." + (interactive) + (setq py-shell-complete-p (not py-shell-complete-p)) + (when (called-interactively-p 'interactive) + (message "py-shell-complete-p: %s" py-shell-complete-p))) + +(defun py--at-raw-string () + "If at beginning of a raw-string." + (and (looking-at "\"\"\"\\|'''") (member (char-before) (list ?u ?U ?r ?R)))) + +(defmacro py-current-line-backslashed-p () + "Return t if current line is a backslashed continuation line." + `(save-excursion + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (and (eq (char-before (point)) ?\\ ) + (py-escaped-p)))) + +(defmacro py-preceding-line-backslashed-p () + "Return t if preceding line is a backslashed continuation line." + `(save-excursion + (beginning-of-line) + (skip-chars-backward " \t\r\n\f") + (and (eq (char-before (point)) ?\\ ) + (py-escaped-p)))) + +(defun py--skip-to-comment-or-semicolon (done) + "Returns position if comment or semicolon found. " + (let ((orig (point))) + (cond ((and done (< 0 (abs (skip-chars-forward "^#;" (line-end-position)))) + (member (char-after) (list ?# ?\;))) + (when (eq ?\; (char-after)) + (skip-chars-forward ";" (line-end-position)))) + ((and (< 0 (abs (skip-chars-forward "^#;" (line-end-position)))) + (member (char-after) (list ?# ?\;))) + (when (eq ?\; (char-after)) + (skip-chars-forward ";" (line-end-position)))) + ((not done) + (end-of-line))) + (skip-chars-backward " \t" (line-beginning-position)) + (and (< orig (point))(setq done (point)) + done))) + +;; Statement +(defun py-forward-statement (&optional orig done repeat) + "Go to the last char of current statement. + +ORIG - consider orignial position or point. +DONE - transaktional argument +REPEAT - count and consider repeats" + (interactive) + (unless (eobp) + (let ((repeat (or (and repeat (1+ repeat)) 0)) + (orig (or orig (point))) + erg last + ;; use by scan-lists + forward-sexp-function pps err) + (setq pps (parse-partial-sexp (point-min) (point))) + ;; (origline (or origline (py-count-lines))) + (cond + ;; which-function-mode, lp:1235375 + ((< py-max-specpdl-size repeat) + (error "py-forward-statement reached loops max. If no error, customize `py-max-specpdl-size'")) + ;; list + ((nth 1 pps) + (if (<= orig (point)) + (progn + (setq orig (point)) + ;; do not go back at a possible unclosed list + (goto-char (nth 1 pps)) + (if + (ignore-errors (forward-list)) + (progn + (when (looking-at ":[ \t]*$") + (forward-char 1)) + (setq done t) + (skip-chars-forward "^#" (line-end-position)) + (skip-chars-backward " \t\r\n\f" (line-beginning-position)) + (py-forward-statement orig done repeat)) + (setq err (py--record-list-error pps)) + (goto-char orig))))) + ;; in comment + ((and comment-start (looking-at (concat " *" comment-start))) + (goto-char (match-end 0)) + (py-forward-statement orig done repeat)) + ((nth 4 pps) + (py--end-of-comment-intern (point)) + (py--skip-to-comment-or-semicolon done) + (while (and (eq (char-before (point)) ?\\) + (py-escaped-p) (setq last (point))) + (forward-line 1) (end-of-line)) + (and last (goto-char last) + (forward-line 1) + (back-to-indentation)) + ;; py-forward-statement-test-3JzvVW + (unless (or (looking-at (concat " *" comment-start))(eolp)) + (py-forward-statement orig done repeat))) + ;; string + ((looking-at py-string-delim-re) + (goto-char (match-end 0)) + (py-forward-statement orig done repeat)) + ((nth 3 pps) + (when (py-end-of-string) + (end-of-line) + (skip-chars-forward " \t\r\n\f") + (setq pps (parse-partial-sexp (point-min) (point))) + (unless (and done (not (or (nth 1 pps) (nth 8 pps))) (eolp)) (py-forward-statement orig done repeat)))) + ((py-current-line-backslashed-p) + (end-of-line) + (skip-chars-backward " \t\r\n\f" (line-beginning-position)) + (while (and (eq (char-before (point)) ?\\) + (py-escaped-p)) + (forward-line 1) + (end-of-line) + (skip-chars-backward " \t\r\n\f" (line-beginning-position))) + (unless (eobp) + (py-forward-statement orig done repeat))) + ((eq orig (point)) + (if (eolp) + (skip-chars-forward " \t\r\n\f#'\"") + (end-of-line) + (skip-chars-backward " \t\r\n\f" orig)) + ;; point at orig due to a trailing whitespace + (and (eq (point) orig) (skip-chars-forward " \t\r\n\f")) + (setq done t) + (py-forward-statement orig done repeat)) + ((eq (current-indentation) (current-column)) + (py--skip-to-comment-or-semicolon done) + (setq pps (parse-partial-sexp orig (point))) + (if (nth 1 pps) + (py-forward-statement orig done repeat) + (unless done + (py-forward-statement orig done repeat)))) + ((and (looking-at "[[:print:]]+$") (not done) (py--skip-to-comment-or-semicolon done)) + (py-forward-statement orig done repeat))) + (unless + (or + (eq (point) orig) + (member (char-before) (list 10 32 9 ?#))) + (setq erg (point))) + (if (and py-verbose-p err) + (py--message-error err)) + erg))) + +(defun py-backward-statement (&optional orig done limit ignore-in-string-p repeat maxindent) + "Go to the initial line of a simple statement. + +For beginning of compound statement use `py-backward-block'. +For beginning of clause `py-backward-clause'. + +`ignore-in-string-p' allows moves inside a docstring, used when +computing indents +ORIG - consider orignial position or point. +DONE - transaktional argument +LIMIT - honor limit +IGNORE-IN-STRING-P - also much inside a string +REPEAT - count and consider repeats +Optional MAXINDENT: don't stop if indentation is larger" + (interactive) + (save-restriction + (unless (bobp) + (let* ((repeat (or (and repeat (1+ repeat)) 0)) + (orig (or orig (point))) + (pps (parse-partial-sexp (or limit (point-min))(point))) + (done done) + erg) + ;; lp:1382788 + (unless done + (and (< 0 (abs (skip-chars-backward " \t\r\n\f"))) + (setq pps (parse-partial-sexp (or limit (point-min))(point))))) + (cond + ((< py-max-specpdl-size repeat) + (error "Py-forward-statement reached loops max. If no error, customize `py-max-specpdl-size'")) + ((and (bolp) (eolp)) + (skip-chars-backward " \t\r\n\f") + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ;; inside string + ((and (nth 3 pps) (not ignore-in-string-p)) + (setq done t) + (goto-char (nth 8 pps)) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ((nth 4 pps) + (while (ignore-errors (goto-char (nth 8 pps))) + (skip-chars-backward " \t\r\n\f") + (setq pps (parse-partial-sexp (line-beginning-position) (point)))) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ((nth 1 pps) + (goto-char (1- (nth 1 pps))) + (when (py--skip-to-semicolon-backward (save-excursion (back-to-indentation) (point))) + (setq done t)) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ((py-preceding-line-backslashed-p) + (forward-line -1) + (back-to-indentation) + (setq done t) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ;; at raw-string + ;; (and (looking-at "\"\"\"\\|'''") (member (char-before) (list ?u ?U ?r ?R))) + ((and (looking-at "\"\"\"\\|'''") (member (char-before) (list ?u ?U ?r ?R))) + (forward-char -1) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ;; BOL or at space before comment + ((and (looking-at "[ \t]*#") (looking-back "^[ \t]*" (line-beginning-position))) + (forward-comment -1) + (while (and (not (bobp)) (looking-at "[ \t]*#") (looking-back "^[ \t]*" (line-beginning-position))) + (forward-comment -1)) + (unless (bobp) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent))) + ;; at inline comment + ((looking-at "[ \t]*#") + (when (py--skip-to-semicolon-backward (save-excursion (back-to-indentation) (point))) + (setq done t)) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ;; at beginning of string + ((looking-at py-string-delim-re) + (when (< 0 (abs (skip-chars-backward " \t\r\n\f"))) + (setq done t)) + (back-to-indentation) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ;; after end of statement + ((and (not done) (eq (char-before) ?\;)) + (skip-chars-backward ";") + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ;; travel until indentation or semicolon + ((and (not done) (py--skip-to-semicolon-backward)) + (unless (and maxindent (< maxindent (current-indentation))) + (setq done t)) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ;; at current indent + ((and (not done) (not (eq 0 (skip-chars-backward " \t\r\n\f")))) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent)) + ((and maxindent (< maxindent (current-indentation))) + (forward-line -1) + (py-backward-statement orig done limit ignore-in-string-p repeat maxindent))) + ;; return nil when before comment + (unless (and (looking-at "[ \t]*#") (looking-back "^[ \t]*" (line-beginning-position))) + (when (< (point) orig)(setq erg (point)))) + erg)))) + +(defun py-backward-statement-bol () + "Goto beginning of line where statement start. +Returns position reached, if successful, nil otherwise. + +See also `py-up-statement'" + (interactive) + (let* ((orig (point)) + erg) + (unless (bobp) + (cond ((bolp) + (and (py-backward-statement orig) + (progn (beginning-of-line) + (setq erg (point))))) + (t (setq erg + (and + (py-backward-statement) + (progn (beginning-of-line) (point))))))) + erg)) + +(defun py-forward-statement-bol () + "Go to the `beginning-of-line' following current statement." + (interactive) + (py-forward-statement) + (py--beginning-of-line-form)) + +(defun py-beginning-of-statement-p () + (interactive) + (save-restriction + (eq (point) + (save-excursion + (py-forward-statement) + (py-backward-statement))))) + +(defun py-up-statement () + "go to the beginning of next statement upwards in buffer. + +Return position if statement found, nil otherwise." + (interactive) + (if (py--beginning-of-statement-p) + (py-backward-statement) + (progn (and (py-backward-statement) (py-backward-statement))))) + +(defun py--end-of-statement-p () + "Return position, if cursor is at the end of a statement, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-statement) + (py-forward-statement) + (when (eq orig (point)) + orig)))) + +(defun py-down-statement () + "Go to the beginning of next statement downwards in buffer. + +Corresponds to backward-up-list in Elisp +Return position if statement found, nil otherwise." + (interactive) + (let* ((orig (point))) + (cond ((py--end-of-statement-p) + (progn + (and + (py-forward-statement) + (py-backward-statement) + (< orig (point)) + (point)))) + ((ignore-errors (< orig (and (py-forward-statement) (py-backward-statement)))) + (point)) + ((ignore-errors (< orig (and (py-forward-statement) (py-forward-statement)(py-backward-statement)))) + (point))))) + +(defun py--backward-regexp (regexp &optional indent condition orig regexpvalue) + "Search backward next regexp not in string or comment. + +Return and move to match-beginning if successful" + (save-match-data + (unless (py-beginning-of-statement-p) (skip-chars-backward " \t\r\n\f") + (py-backward-comment (point))) + (let* (pps + (regexpvalue (or regexpvalue (symbol-value regexp))) + (indent (cond ((eq regexp 'py-match-case-re) + nil) + (t (or indent (current-indentation))))) + (condition (or condition '<=)) + (orig (or orig (point)))) + (if (eq (current-indentation) (current-column)) + (while (and + (not (bobp)) + ;; # class kugel(object) -> a[1:2]: + ;; class kugel(object): + ;; (re-search-backward regexpvalue nil 'move 1) + ;; (re-search-backward (concat "^ \\{0,"(format "%s" indent) "\\}"regexpvalue) nil 'move 1) + (re-search-backward regexpvalue nil 'move 1) + ;; (re-search-backward (concat "^" "def") nil 'move 1) + ;; re-search-backward not greedy + (not (and (looking-back "async *" (line-beginning-position)) + (goto-char (match-beginning 0)))) + (or (and + (setq pps (nth 8 (parse-partial-sexp (point-min) (point)))) + (goto-char pps)) + ;; needed by py-backward-clause + (and (not (eq (current-column) 0)) indent + (funcall condition indent (current-indentation)))))) + (back-to-indentation) + (and + (setq pps (nth 8 (parse-partial-sexp (point-min) (point)))) + (goto-char pps)) + (unless (and (< (point) orig) (looking-at regexpvalue)) + (py--backward-regexp regexp (current-indentation) condition orig))) + (unless (or (eq (point) orig)(bobp)) (back-to-indentation)) + (and (looking-at regexpvalue) (not (nth 8 (parse-partial-sexp (point-min) (point))))(point))))) + +(defun py--fetch-indent-statement-above (orig) + "Report the preceding indent. " + (save-excursion + (goto-char orig) + (forward-line -1) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (back-to-indentation) + (if (or (looking-at comment-start)(py-beginning-of-statement-p)) + (current-indentation) + (py-backward-statement) + (current-indentation)))) + +(defun py--docstring-p (pos) + "Check to see if there is a docstring at POS." + (save-excursion + (let ((erg + (progn + (goto-char pos) + (and (looking-at "\"\"\"\\|'''") + ;; https://github.com/swig/swig/issues/889 + ;; def foo(rho, x): + ;; r"""Calculate :math:`D^\nu \rho(x)`.""" + ;; return True + (if (py--at-raw-string) + (progn + (forward-char -1) + (point)) + (point)))))) + (when (and erg (py-backward-statement)) + (when (or (bobp) (looking-at py-def-or-class-re)(looking-at "\\_<__[[:alnum:]_]+__\\_>")) + erg))))) + +(defun py--font-lock-syntactic-face-function (state) + "STATE expected as result von (parse-partial-sexp (point-min) (point)." + (if (nth 3 state) + (if (py--docstring-p (nth 8 state)) + font-lock-doc-face + font-lock-string-face) + font-lock-comment-face)) + +(and (fboundp 'make-obsolete-variable) + (make-obsolete-variable 'py-mode-hook 'python-mode-hook nil)) + +(defun py-choose-shell-by-shebang (&optional shebang) + "Choose shell by looking at #! on the first line. + +If SHEBANG is non-nil, returns the shebang as string, +otherwise the Python resp. Jython shell command name." + (interactive) + ;; look for an interpreter specified in the first line + (let* (erg res) + (save-excursion + (goto-char (point-min)) + (when (looking-at py-shebang-regexp) + (if shebang + (setq erg (match-string-no-properties 0)) + (setq erg (split-string (match-string-no-properties 0) "[#! \t]")) + (dolist (ele erg) + (when (string-match "[bijp]+ython" ele) + (setq res ele)))))) + (when (and py-verbose-p (called-interactively-p 'any)) (message "%s" res)) + res)) + +(defun py--choose-shell-by-import () + "Choose CPython or Jython mode based imports. + +If a file imports any packages in `py-jython-packages', within +`py-import-check-point-max' characters from the start of the file, +return `jython', otherwise return nil." + (let (mode) + (save-excursion + (goto-char (point-min)) + (while (and (not mode) + (search-forward-regexp + "^\\(\\(from\\)\\|\\(import\\)\\) \\([^ \t\n.]+\\)" + py-import-check-point-max t)) + (setq mode (and (member (match-string 4) py-jython-packages) + 'jython)))) + mode)) + +(defun py-choose-shell-by-path (&optional separator-char) + "SEPARATOR-CHAR according to system variable `path-separator'. + +Select Python executable according to version desplayed in path. +Returns versioned string, nil if nothing appropriate found" + (interactive) + (let ((path (py--buffer-filename-remote-maybe)) + (separator-char (or separator-char py-separator-char)) + erg) + (when (and path separator-char + (string-match (concat separator-char "[iI]?[pP]ython[0-9.]+" separator-char) path)) + (setq erg (substring path + (1+ (string-match (concat separator-char "[iI]?[pP]ython[0-9.]+" separator-char) path)) (1- (match-end 0))))) + (when (called-interactively-p 'any) (message "%s" erg)) + erg)) + +(defun py-which-python (&optional shell) + "Return version of Python of current environment, a number. +Optional argument SHELL selected shell." + (interactive) + (let* ((cmd (or shell (py-choose-shell))) + (treffer (string-match "\\([23]*\\.?[0-9\\.]*\\)$" cmd)) + version erg) + (if treffer + ;; if a number if part of python name, assume it's the version + (setq version (substring-no-properties cmd treffer)) + (setq erg (shell-command-to-string (concat cmd " --version"))) + (setq version (cond ((string-match (concat "\\(on top of Python \\)" "\\([0-9]\\.[0-9]+\\)") erg) + (match-string-no-properties 2 erg)) + ((string-match "\\([0-9]\\.[0-9]+\\)" erg) + (substring erg 7 (1- (length erg))))))) + (when (called-interactively-p 'any) + (if version + (when py-verbose-p (message "%s" version)) + (message "%s" "Could not detect Python on your system"))) + (string-to-number version))) + +(defun py-python-current-environment () + "Return path of current Python installation." + (interactive) + (let* ((cmd (py-choose-shell)) + (denv (shell-command-to-string (concat "type " cmd))) + (erg (substring denv (string-match "/" denv)))) + (when (called-interactively-p 'any) + (if erg + (message "%s" erg) + (message "%s" "Could not detect Python on your system"))) + erg)) + + ;; requested by org-mode still +(defalias 'py-toggle-shells 'py-choose-shell) + +(defun py--cleanup-process-name (res) + "Make res ready for use by `executable-find'. + +Returns RES or substring of RES" + (if (string-match "<" res) + (substring res 0 (match-beginning 0)) + res)) + +(defalias 'py-which-shell 'py-choose-shell) +(defun py-choose-shell (&optional shell) + "Return an appropriate executable as a string. + +Does the following: + - look for an interpreter with `py-choose-shell-by-shebang' + - examine imports using `py--choose-shell-by-import' + - look if Path/To/File indicates a Python version + - if not successful, return default value of `py-shell-name' + +When interactivly called, messages the SHELL name +Return nil, if no executable found." + (interactive) + ;; org-babel uses `py-toggle-shells' with arg, just return it + (or shell + (let* (res + done + (erg + (cond (py-force-py-shell-name-p + (default-value 'py-shell-name)) + (py-use-local-default + (if (not (string= "" py-shell-local-path)) + (expand-file-name py-shell-local-path) + (message "Abort: `py-use-local-default' is set to `t' but `py-shell-local-path' is empty. Maybe call `py-toggle-local-default-use'"))) + ((and (not py-fast-process-p) + (comint-check-proc (current-buffer)) + (setq done t) + (string-match "ython" (process-name (get-buffer-process (current-buffer))))) + (setq res (process-name (get-buffer-process (current-buffer)))) + (py--cleanup-process-name res)) + ((py-choose-shell-by-shebang)) + ((py--choose-shell-by-import)) + ((py-choose-shell-by-path)) + (t (or + py-python-command + "python3")))) + (cmd (if (or + ;; comint-check-proc was succesful + done + py-edit-only-p) + erg + (executable-find erg)))) + (if cmd + (when (called-interactively-p 'any) + (message "%s" cmd)) + (when (called-interactively-p 'any) (message "%s" "Could not detect Python on your system. Maybe set `py-edit-only-p'?"))) + erg))) + +(defun py--normalize-directory (directory) + "Make sure DIRECTORY ends with a file-path separator char. + +Returns DIRECTORY" + (cond ((string-match (concat py-separator-char "$") directory) + directory) + ((not (string= "" directory)) + (concat directory py-separator-char)))) + +(defun py--normalize-pythonpath (pythonpath) + "Make sure PYTHONPATH ends with a colon. + +Returns PYTHONPATH" + (let ((erg (cond ((string-match (concat path-separator "$") pythonpath) + pythonpath) + ((not (string= "" pythonpath)) + (concat pythonpath path-separator)) + (t pythonpath)))) + erg)) + +(defun py-install-directory-check () + "Do some sanity check for `py-install-directory'. + +Returns t if successful." + (interactive) + (let ((erg (and (boundp 'py-install-directory) (stringp py-install-directory) (< 1 (length py-install-directory))))) + (when (called-interactively-p 'any) (message "py-install-directory-check: %s" erg)) + erg)) + +(defun py--buffer-filename-remote-maybe (&optional file-name) + "Argument FILE-NAME: the value of variable `buffer-file-name'." + (let ((file-name (or file-name + (and + (ignore-errors (file-readable-p (buffer-file-name))) + (buffer-file-name))))) + (if (and (featurep 'tramp) (tramp-tramp-file-p file-name)) + (tramp-file-name-localname + (tramp-dissect-file-name file-name)) + file-name))) + +(defun py-guess-py-install-directory () + "If `(locate-library \"python-mode\")' is not succesful. + +Used only, if `py-install-directory' is empty." + (interactive) + (cond (;; don't reset if it already exists + py-install-directory) + ;; ((locate-library "python-mode") + ;; (file-name-directory (locate-library "python-mode"))) + ((ignore-errors (string-match "python-mode" (py--buffer-filename-remote-maybe))) + (file-name-directory (py--buffer-filename-remote-maybe))) + (t (if + (and (get-buffer "python-mode.el") + (set-buffer (get-buffer "python-mode.el")) + ;; (setq py-install-directory (ignore-errors (file-name-directory (buffer-file-name (get-buffer "python-mode.el"))))) + (buffer-file-name (get-buffer "python-mode.el"))) + (setq py-install-directory (file-name-directory (buffer-file-name (get-buffer "python-mode.el")))) + (if + (and (get-buffer "python-components-mode.el") + (set-buffer (get-buffer "python-components-mode.el")) + (buffer-file-name (get-buffer "python-components-mode.el"))) + (setq py-install-directory (file-name-directory (buffer-file-name (get-buffer "python-components-mode.el")))))) + ))) + +(defun py--fetch-pythonpath () + "Consider settings of `py-pythonpath'." + (if (string= "" py-pythonpath) + (getenv "PYTHONPATH") + (concat (py--normalize-pythonpath (getenv "PYTHONPATH")) py-pythonpath))) + +(defun py-load-pymacs () + "Load Pymacs as delivered. + +Pymacs has been written by François Pinard and many others. +See original source: http://pymacs.progiciels-bpi.ca" + (interactive) + (let ((pyshell (py-choose-shell)) + (path (py--fetch-pythonpath)) + (py-install-directory (cond ((string= "" py-install-directory) + (py-guess-py-install-directory)) + (t (py--normalize-directory py-install-directory))))) + (if (py-install-directory-check) + (progn + ;; If Pymacs has not been loaded before, prepend py-install-directory to + ;; PYTHONPATH, so that the Pymacs delivered with python-mode is used. + (unless (featurep 'pymacs) + (setenv "PYTHONPATH" (concat + (expand-file-name py-install-directory) + (if path (concat path-separator path))))) + (setenv "PYMACS_PYTHON" (if (string-match "IP" pyshell) + "python" + pyshell)) + (require 'pymacs)) + (error "`py-install-directory' not set, see INSTALL")))) + +(when py-load-pymacs-p (py-load-pymacs)) + +(when (and py-load-pymacs-p (featurep 'pymacs)) + (defun py-load-pycomplete () + "Load Pymacs based pycomplete." + (interactive) + (let* ((path (py--fetch-pythonpath)) + (py-install-directory (cond ((string= "" py-install-directory) + (py-guess-py-install-directory)) + (t (py--normalize-directory py-install-directory)))) + (pycomplete-directory (concat (expand-file-name py-install-directory) "completion"))) + (if (py-install-directory-check) + (progn + ;; If the Pymacs process is already running, augment its path. + (when (and (get-process "pymacs") (fboundp 'pymacs-exec)) + (pymacs-exec (concat "sys.path.insert(0, '" pycomplete-directory "')"))) + (require 'pymacs) + (setenv "PYTHONPATH" (concat + pycomplete-directory + (if path (concat path-separator path)))) + (push pycomplete-directory load-path) + (require 'pycomplete) + (add-hook 'python-mode-hook 'py-complete-initialize)) + (error "`py-install-directory' not set, see INSTALL"))))) + +(when (functionp 'py-load-pycomplete) + (py-load-pycomplete)) + +(defun py-set-load-path () + "Include needed subdirs of `python-mode' directory." + (interactive) + (let ((install-directory (py--normalize-directory py-install-directory))) + (if py-install-directory + (cond ((and (not (string= "" install-directory))(stringp install-directory)) + (push (expand-file-name install-directory) load-path) + (push (concat (expand-file-name install-directory) "completion") load-path) + (push (concat (expand-file-name install-directory) "extensions") load-path) + (push (concat (expand-file-name install-directory) "test") load-path) + ) + (t (error "Please set `py-install-directory', see INSTALL"))) + (error "Please set `py-install-directory', see INSTALL"))) + (when (called-interactively-p 'interactive) (message "%s" load-path))) + +(defun py-count-lines (&optional beg end) + "Count lines in accessible part until current line. + +See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7115 +Optional argument BEG specify beginning. +Optional argument END specify end." + (interactive) + (save-excursion + (let ((count 0) + (beg (or beg (point-min))) + (end (or end (point)))) + (save-match-data + (if (or (eq major-mode 'comint-mode) + (eq major-mode 'py-shell-mode)) + (if + (re-search-backward py-shell-prompt-regexp nil t 1) + (goto-char (match-end 0)) + ;; (when py-debug-p (message "%s" "py-count-lines: Don't see a prompt here")) + (goto-char beg)) + (goto-char beg))) + (while (and (< (point) end)(not (eobp)) (skip-chars-forward "^\n" end)) + (setq count (1+ count)) + (unless (or (not (< (point) end)) (eobp)) (forward-char 1) + (setq count (+ count (abs (skip-chars-forward "\n" end)))))) + (when (bolp) (setq count (1+ count))) + (when (and py-debug-p (called-interactively-p 'any)) (message "%s" count)) + count))) + +(defun py--escape-doublequotes (start end) + "Escape doublequotes in region by START END." + (let ((end (copy-marker end))) + (save-excursion + (goto-char start) + (while (and (not (eobp)) (< 0 (abs (skip-chars-forward "^\"" end)))) + (when (eq (char-after) ?\") + (unless (py-escaped-p) + (insert "\\") + (forward-char 1))))))) + +(defun py--escape-open-paren-col1 (start end) + "Start from position START until position END." + (goto-char start) + (while (re-search-forward "^(" end t 1) + (insert "\\") + (end-of-line))) + +(and py-company-pycomplete-p (require 'company-pycomplete)) + +(defcustom py-empty-line-p-chars "^[ \t\r]*$" + "Empty-line-p-chars." + :type 'regexp + :tag "py-empty-line-p-chars" + :group 'python-mode) + +(defcustom py-default-working-directory "" + "If not empty used by `py-set-current-working-directory'." + :type 'string + :tag "py-default-working-directory" + :group 'python-mode) + +(defun py-empty-line-p () + "Return t if cursor is at an empty line, nil otherwise." + (save-excursion + (beginning-of-line) + (looking-at py-empty-line-p-chars))) + +(defun py-toggle-closing-list-dedents-bos (&optional arg) + "Switch boolean variable `py-closing-list-dedents-bos'. + +With optional ARG message state switched to" + (interactive "p") + (setq py-closing-list-dedents-bos (not py-closing-list-dedents-bos)) + (when arg (message "py-closing-list-dedents-bos: %s" py-closing-list-dedents-bos))) + +(defun py-comint-delete-output () + "Delete all output from interpreter since last input. +Does not delete the prompt." + (interactive) + (let ((proc (get-buffer-process (current-buffer))) + (replacement nil) + (inhibit-read-only t)) + (save-excursion + (let ((pmark (progn (goto-char (process-mark proc)) + (forward-line 0) + (point-marker)))) + (delete-region comint-last-input-end pmark) + (goto-char (process-mark proc)) + (setq replacement (concat "*** output flushed ***\n" + (buffer-substring pmark (point)))) + (delete-region pmark (point)))) + ;; Output message and put back prompt + (comint-output-filter proc replacement))) + +(defun py-in-comment-p () + "Return the beginning of current line's comment, if inside. " + (interactive) + (let* ((pps (parse-partial-sexp (point-min) (point))) + (erg (and (nth 4 pps) (nth 8 pps)))) + erg)) +;; +(defun py-in-string-or-comment-p () + "Returns beginning position if inside a string or comment, nil otherwise. " + (or (nth 8 (parse-partial-sexp (point-min) (point))) + (when (or (looking-at "\"")(looking-at "[ \t]*#[ \t]*")) + (point)))) + +(defvar python-mode-map nil) +(when py-org-cycle-p + (define-key python-mode-map (kbd "") 'org-cycle)) + +(defun py-forward-buffer () + "A complementary form used by auto-generated commands. + +Returns position reached if successful" + (interactive) + (unless (eobp) + (goto-char (point-max)))) + +(defun py-backward-buffer () + "A complementary form used by auto-generated commands. + +Returns position reached if successful" + (interactive) + (unless (bobp) + (goto-char (point-min)))) + +(defun py--end-of-comment-intern (pos) + (while (and (not (eobp)) + (forward-comment 99999))) + ;; forward-comment fails sometimes + (and (eq pos (point)) (prog1 (forward-line 1) (back-to-indentation)) + (while (member (char-after) (list (string-to-char comment-start) 10))(forward-line 1)(back-to-indentation)))) + +(defun py--beginning-of-line-form () + "Internal use: Go to beginning of line following end of form. + +Return position." + (if (eobp) + (point) + (forward-line 1) + (beginning-of-line) + (point))) + +(defun py--skip-to-semicolon-backward (&optional limit) + "Fetch the beginning of statement after a semicolon. + +Returns `t' if point was moved" + (prog1 + (< 0 (abs (skip-chars-backward "^;" (or limit (line-beginning-position))))) + (skip-chars-forward " \t" (line-end-position)))) + +(defun py-forward-comment () + "Go to the end of comment at point." + (let ((orig (point)) + last) + (while (and (not (eobp)) (nth 4 (parse-partial-sexp (line-beginning-position) (point))) (setq last (line-end-position))) + (forward-line 1) + (end-of-line)) + (when + (< orig last) + (goto-char last)(point)))) + +(defun py--forward-string-maybe (&optional start) + "Go to the end of string. + +Expects START position of string +Return position of moved, nil otherwise." + (let ((orig (point))) + (when start (goto-char start) + (when (looking-at "\"\"\"\\|'''") + (goto-char (1- (match-end 0))) + (forward-sexp)) + ;; maybe at the inner fence + (when (looking-at "\"\"\\|''") + (goto-char (match-end 0))) + (and (< orig (point)) (point))))) + +(defun py-load-skeletons () + "Load skeletons from extensions. " + (interactive) + (load (concat py-install-directory "/extensions/python-components-skeletons.el"))) + +(defun py--kill-emacs-hook () + "Delete files in `py-file-queue'. +These are Python temporary files awaiting execution." + (mapc #'(lambda (filename) + (ignore-errors (delete-file filename))) + py-file-queue)) + +(add-hook 'kill-emacs-hook 'py--kill-emacs-hook) + +;; Add a designator to the minor mode strings +(or (assq 'py-pdbtrack-is-tracking-p minor-mode-alist) + (push '(py-pdbtrack-is-tracking-p py-pdbtrack-minor-mode-string) + minor-mode-alist)) + +(defun py--update-lighter (shell) + "Select lighter for mode-line display" + (setq py-modeline-display + (cond + ;; ((eq 2 (prefix-numeric-value argprompt)) + ;; py-python2-command-args) + ((string-match "^[^-]+3" shell) + py-python3-modeline-display) + ((string-match "^[^-]+2" shell) + py-python2-modeline-display) + ((string-match "^.[Ii]" shell) + py-ipython-modeline-display) + ((string-match "^.[Jj]" shell) + py-jython-modeline-display) + (t + python-mode-modeline-display)))) + +;; bottle.py +;; py = sys.version_info +;; py3k = py >= (3,0,0) +;; py25 = py < (2,6,0) +;; py31 = (3,1,0) <= py < (3,2,0) + +;; sys.version_info[0] +(defun py-python-version (&optional executable verbose) + "Returns versions number of a Python EXECUTABLE, string. + +If no EXECUTABLE given, `py-shell-name' is used. +Interactively output of `--version' is displayed. " + (interactive) + (let* ((executable (or executable py-shell-name)) + (erg (py--string-strip (shell-command-to-string (concat executable " --version"))))) + (when (called-interactively-p 'any) (message "%s" erg)) + (unless verbose (setq erg (cadr (split-string erg)))) + erg)) + +(defun py-version () + "Echo the current version of `python-mode' in the minibuffer." + (interactive) + (message "Using `python-mode' version %s" py-version)) + +(declare-function compilation-shell-minor-mode "compile" (&optional arg)) + +(defun py--warn-tmp-files-left () + "Detect and warn about file of form \"py11046IoE\" in py-temp-directory." + (let ((erg1 (file-readable-p (concat py-temp-directory py-separator-char (car (directory-files py-temp-directory nil "py[[:alnum:]]+$")))))) + (when erg1 + (message "py--warn-tmp-files-left: %s ?" (concat py-temp-directory py-separator-char (car (directory-files py-temp-directory nil "py[[:alnum:]]*$"))))))) + +(defun py--fetch-indent-line-above (&optional orig) + "Report the preceding indent. " + (save-excursion + (when orig (goto-char orig)) + (forward-line -1) + (current-indentation))) + +(defun py-continuation-offset (&optional arg) + "Set if numeric ARG differs from 1. " + (interactive "p") + (and (numberp arg) (not (eq 1 arg)) (setq py-continuation-offset arg)) + (when (and py-verbose-p (called-interactively-p 'any)) (message "%s" py-continuation-offset)) + py-continuation-offset) + +(defun py-list-beginning-position (&optional start) + "Return lists beginning position, nil if not inside. + +Optional ARG indicates a start-position for `parse-partial-sexp'." + (nth 1 (parse-partial-sexp (or start (point-min)) (point)))) + +(defun py-end-of-list-position (&optional arg) + "Return end position, nil if not inside. + +Optional ARG indicates a start-position for `parse-partial-sexp'." + (interactive) + (let* ((ppstart (or arg (point-min))) + (erg (parse-partial-sexp ppstart (point))) + (beg (nth 1 erg)) + end) + (when beg + (save-excursion + (goto-char beg) + (forward-list 1) + (setq end (point)))) + (when (and py-verbose-p (called-interactively-p 'any)) (message "%s" end)) + end)) + +(defun py--in-comment-p () + "Return the beginning of current line's comment, if inside or at comment-start. " + (save-restriction + (widen) + (let* ((pps (parse-partial-sexp (point-min) (point))) + (erg (when (nth 4 pps) (nth 8 pps)))) + (unless erg + (when (ignore-errors (looking-at (concat "[ \t]*" comment-start))) + (setq erg (point)))) + erg))) + +(defun py-in-triplequoted-string-p () + "Returns character address of start tqs-string, nil if not inside. " + (interactive) + (let* ((pps (parse-partial-sexp (point-min) (point))) + (erg (when (and (nth 3 pps) (nth 8 pps))(nth 2 pps)))) + (save-excursion + (unless erg (setq erg + (progn + (when (looking-at "\"\"\"\\|''''") + (goto-char (match-end 0)) + (setq pps (parse-partial-sexp (point-min) (point))) + (when (and (nth 3 pps) (nth 8 pps)) (nth 2 pps))))))) + (when (and py-verbose-p (called-interactively-p 'any)) (message "%s" erg)) + erg)) + +(defun py-in-string-p-intern (pps) + (goto-char (nth 8 pps)) + (list (point) (char-after)(skip-chars-forward (char-to-string (char-after))))) + +(defun py-in-string-p () + "if inside a double- triple- or singlequoted string, + +If non-nil, return a list composed of +- beginning position +- the character used as string-delimiter (in decimal) +- and length of delimiter, commonly 1 or 3 " + (interactive) + (save-excursion + (let* ((pps (parse-partial-sexp (point-min) (point))) + (erg (when (nth 3 pps) + (py-in-string-p-intern pps)))) + (unless erg + (when (looking-at "\"\\|'") + (forward-char 1) + (setq pps (parse-partial-sexp (line-beginning-position) (point))) + (when (nth 3 pps) + (setq erg (py-in-string-p-intern pps))))) + erg))) + +(defun py-toggle-local-default-use () + "Toggle boolean value of `py-use-local-default'. + +Returns `py-use-local-default' + +See also `py-install-local-shells' +Installing named virualenv shells is the preffered way, +as it leaves your system default unchanged." + (interactive) + (setq py-use-local-default (not py-use-local-default)) + (when (called-interactively-p 'any) (message "py-use-local-default set to %s" py-use-local-default)) + py-use-local-default) + +(defun py--beginning-of-buffer-position () + "Provided for abstract reasons." + (point-min)) + +(defun py--end-of-buffer-position () + "Provided for abstract reasons." + (point-max)) + +(defun py-backward-comment (&optional pos) + "Got to beginning of a commented section. + +Start from POS if specified" + (interactive) + (let ((erg pos) + last) + (when erg (goto-char erg)) + (while (and (not (bobp)) (setq erg (py-in-comment-p))) + (when (< erg (point)) + (goto-char erg) + (setq last (point))) + (skip-chars-backward " \t\r\n\f")) + (when last (goto-char last)) + last)) + +(defun py-go-to-beginning-of-comment () + "Go to the beginning of current line's comment, if any. + +From a programm use macro `py-backward-comment' instead" + (interactive) + (let ((erg (py-backward-comment))) + (when (and py-verbose-p (called-interactively-p 'any)) + (message "%s" erg)))) + +(defun py--up-decorators-maybe (indent) + (let ((last (point))) + (while (and (not (bobp)) + (py-backward-statement) + (eq (current-indentation) indent) + (if (looking-at py-decorator-re) + (progn (setq last (point)) nil) + t))) + (goto-char last))) + +(defun py-leave-comment-or-string-backward () + "If inside a comment or string, leave it backward." + (interactive) + (let ((pps + (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (parse-partial-sexp (point-min) (point))))) + (when (nth 8 pps) + (goto-char (1- (nth 8 pps)))))) + +;; Decorator +(defun py-backward-decorator () + "Go to the beginning of a decorator. + +Returns position if succesful" + (interactive) + (let ((orig (point))) + (unless (bobp) (forward-line -1) + (back-to-indentation) + (while (and (progn (looking-at "@\\w+")(not (looking-at "\\w+"))) + (not + ;; (py-empty-line-p) + (member (char-after) (list 9 10))) + (not (bobp))(forward-line -1)) + (back-to-indentation)) + (or (and (looking-at "@\\w+") (match-beginning 0)) + (goto-char orig))))) + +(defun py-forward-decorator () + "Go to the end of a decorator. + +Returns position if succesful" + (interactive) + (let ((orig (point)) erg) + (unless (looking-at "@\\w+") + (setq erg (py-backward-decorator))) + (when erg + (if + (re-search-forward py-def-or-class-re nil t) + (progn + (back-to-indentation) + (skip-chars-backward " \t\r\n\f") + (py-leave-comment-or-string-backward) + (skip-chars-backward " \t\r\n\f") + (setq erg (point))) + (goto-char orig) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (when (ignore-errors (goto-char (py-list-beginning-position))) + (forward-list)) + (when (< orig (point)) + (setq erg (point)))) + erg))) + +(defun py-beginning-of-list-pps (&optional iact last ppstart orig done) + "Go to the beginning of a list. + +IACT - if called interactively +LAST - was last match. +Optional PPSTART indicates a start-position for `parse-partial-sexp'. +ORIG - consider orignial position or point. +DONE - transaktional argument +Return beginning position, nil if not inside." + (interactive "p") + (let* ((orig (or orig (point))) + (ppstart (or ppstart (re-search-backward "^[a-zA-Z]" nil t 1) (point-min))) + erg) + (unless done (goto-char orig)) + (setq done t) + (if + (setq erg (nth 1 (if (featurep 'xemacs) + (parse-partial-sexp ppstart (point)) + (parse-partial-sexp (point-min) (point))))) + (progn + (setq last erg) + (goto-char erg) + (py-beginning-of-list-pps iact last ppstart orig done)) + last))) + +(defun py-end-of-string (&optional beginning-of-string-position) + "Go to end of string at point if any, if successful return position. " + (interactive) + (let ((orig (point)) + (beginning-of-string-position (or beginning-of-string-position (and (nth 3 (parse-partial-sexp 1 (point)))(nth 8 (parse-partial-sexp 1 (point)))) + (and (looking-at "\"\"\"\\|'''\\|\"\\|\'")(match-beginning 0)))) + erg) + (if beginning-of-string-position + (progn + (goto-char beginning-of-string-position) + (when + ;; work around parse-partial-sexp error + (and (nth 3 (parse-partial-sexp 1 (point)))(nth 8 (parse-partial-sexp 1 (point)))) + (goto-char (nth 3 (parse-partial-sexp 1 (point))))) + (if (ignore-errors (setq erg (scan-sexps (point) 1))) + (goto-char erg) + (goto-char orig))) + + (error (concat "py-end-of-string: don't see end-of-string at " (buffer-name (current-buffer)) "at pos " (point)))) + erg)) + +(defun py--record-list-error (pps) + "When encountering a missing parenthesis, store its line, position. +`py-verbose-p' must be t" + (let ((this-err + (save-excursion + (list + (nth 1 pps) + (progn + (goto-char (nth 1 pps)) + (py-count-lines (point-min) (point))))))) + this-err)) + +(defun py--message-error (err) + "Receives a list (position line) " + (message "Closing paren missed: line %s pos %s" (cadr err) (car err))) + +(defun py--end-base-determine-secondvalue (regexp) + "Expects being at block-opener. + +REGEXP: a symbol" + (cond + ((eq regexp 'py-minor-block-re) + (cond ((looking-at py-else-re) + nil) + ((or (looking-at (concat py-try-re))) + (concat py-elif-re "\\|" py-else-re "\\|" py-except-re)) + ((or (looking-at (concat py-except-re "\\|" py-elif-re "\\|" py-if-re))) + (concat py-elif-re "\\|" py-else-re)))) + ((member regexp + (list + 'py-block-re + 'py-block-or-clause-re + 'py-clause-re + 'py-if-re + )) + (cond ((looking-at py-if-re) + (concat py-elif-re "\\|" py-else-re)) + ((looking-at py-elif-re) + (concat py-elif-re "\\|" py-else-re)) + ((looking-at py-else-re)) + ((looking-at py-try-re) + (concat py-except-re "\\|" py-else-re "\\|" py-finally-re)) + ((looking-at py-except-re) + (concat py-else-re "\\|" py-finally-re)) + ((looking-at py-finally-re) + nil))) + ((eq regexp 'py-for-re) nil) + ((eq regexp 'py-try-re) + (cond + ((looking-at py-try-re) + (concat py-except-re "\\|" py-else-re "\\|" py-finally-re)) + ((looking-at py-except-re) + (concat py-else-re "\\|" py-finally-re)) + ((looking-at py-finally-re)))))) + +(defun py--go-to-keyword (regexp &optional maxindent condition ignoreindent) + "Expects being called from beginning of a statement. + +Argument REGEXP: a symbol. + +Return a list, whose car is indentation, cdr position. + +Keyword detected from REGEXP +Honor MAXINDENT if provided +Optional IGNOREINDENT: find next keyword at any indentation" + (unless (bobp) + ;; (when (py-empty-line-p) (skip-chars-backward " \t\r\n\f")) + (let* ((orig (point)) + (condition + (or condition (if (eq regexp 'py-clause-re) '< '<=))) + ;; py-clause-re would not match block + (regexp (if (eq regexp 'py-clause-re) 'py-extended-block-or-clause-re regexp)) + (regexpvalue (symbol-value regexp)) + (maxindent + (if ignoreindent + ;; just a big value + 9999 + (or maxindent + ;; (if + ;; (or (looking-at regexpvalue) (eq 0 (current-indentation))) + ;; (current-indentation) + ;; (abs + ;; (- (current-indentation) py-indent-offset))) + (min (current-column) (current-indentation))))) + (lep (line-end-position)) + erg) + (unless (py-beginning-of-statement-p) + (py-backward-statement)) + (cond + ((looking-at (concat (symbol-value regexp))) + (if (eq (point) orig) + (setq erg (py--backward-regexp regexp maxindent condition orig regexpvalue)) + (setq erg (point)))) + ((looking-at py-block-closing-keywords-re) + ;; maybe update maxindent, if already behind the form closed here + (unless ;; do not update if still starting line + (eq (line-end-position) lep) + (setq maxindent (min maxindent (- (current-indentation) py-indent-offset)))) + (setq erg (py--backward-regexp regexp maxindent condition orig regexpvalue))) + (t (setq erg (py--backward-regexp regexp maxindent condition orig regexpvalue)))) + (when erg (setq erg (cons (current-indentation) erg))) + (list (car erg) (cdr erg) (py--end-base-determine-secondvalue regexp))))) + +(defun py-up-base (regexp &optional indent) + "Expects a symbol as REGEXP like `'py-clause-re'" + (unless (py-beginning-of-statement-p) (py-backward-statement)) + (unless (looking-at (symbol-value regexp)) + (py--go-to-keyword regexp (or indent (current-indentation)) '<)) + ;; now from beginning-of-block go one indent level upwards + (py--go-to-keyword regexp (- (or indent (current-indentation)) py-indent-offset) '<)) + +(defun py--forward-regexp (regexp) + "Search forward next regexp not in string or comment. + +Return and move to match-beginning if successful" + (save-match-data + (let (erg) + (while (and + (setq erg (re-search-forward regexp nil 'move 1)) + (nth 8 (parse-partial-sexp (point-min) (point))))) + (unless + (nth 8 (parse-partial-sexp (point-min) (point))) + erg)))) +(defun py--forward-regexp-keep-indent (regexp &optional indent) + "Search forward next regexp not in string or comment. + +Return and move to match-beginning if successful" + (save-match-data + (let ((indent (or indent (current-indentation))) + (regexp (if (stringp regexp) + regexp + (symbol-value regexp))) + (orig (point)) + last done) + (forward-line 1) + (beginning-of-line) + (while (and + (not done) + (re-search-forward regexp nil 'move 1) + (or (nth 8 (parse-partial-sexp (point-min) (point))) + (or (< indent (current-indentation))(setq done t)) + (setq last (line-end-position))))) + (unless + (nth 8 (parse-partial-sexp (point-min) (point))) + (if last (goto-char last) + (back-to-indentation)) + (and (< orig (point)) (point)))))) + +(defun py-down-base (regexp &optional indent bol) + (let ((indent (or indent (current-indentation)))) + (and (py--forward-regexp-keep-indent regexp indent) + (progn + (if bol + (beginning-of-line) + (back-to-indentation)) + (point))))) + +(defun py--beginning-of-statement-p (&optional pps) + "Return position, if cursor is at the beginning of a `statement', nil otherwise." + (interactive) + (save-excursion + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps) (nth 1 pps))) + (looking-at py-statement-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column) (current-indentation)) + (eq (point) (progn (py-forward-statement) (py-backward-statement))) + (point))))) + +(defun py--beginning-of-statement-bol-p (&optional pps) + "Return position, if cursor is at the beginning of a `statement', nil otherwise." + (save-excursion + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps) (nth 1 pps))) + (looking-at py-statement-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (point) (progn (py-forward-statement-bol) (py-backward-statement-bol))) + (point))))) + +(defun py--refine-regexp-maybe (regexp) + "Use a more specific regexp if possible. " + (let ((regexpvalue (symbol-value regexp))) + (if (looking-at regexpvalue) + (setq regexp + (cond ((looking-at py-if-re) + 'py-if-re) + ((looking-at py-try-re) + 'py-try-re) + ((looking-at py-def-re) + 'py-def-re) + ((looking-at py-class-re) + 'py-class-re) + (t regexp))) + regexp))) + +(defun py-forward-clause-intern (indent) + (end-of-line) + (let (last) + (while + (and + (py-forward-statement) + (save-excursion (py-backward-statement) (< indent (current-indentation))) + (setq last (point)) + )) + (when last (goto-char last)))) + +(defun py--down-according-to-indent (regexp secondvalue &optional indent use-regexp) + "Return position if moved, nil otherwise. + +Optional ENFORCE-REGEXP: search for regexp only." + (unless (eobp) + (let* ((orig (point)) + (indent (or indent 0)) + done + (regexpvalue (if (member regexp (list 'py-def-re 'py-def-or-class-re 'py-class-re)) + (concat (symbol-value regexp) "\\|" (symbol-value 'py-decorator-re)) + (symbol-value regexp))) + (lastvalue (and secondvalue + (pcase regexp + (`py-try-re py-finally-re) + (`py-if-re py-else-re))))) + (if (eq regexp 'py-clause-re) + (py-forward-clause-intern indent) + (while + (and + (not done) + (progn (end-of-line) + (cond (use-regexp + ;; using regexpvalue might stop behind global settings, missing the end of form + (re-search-forward (concat "^ \\{0,"(format "%s" indent) "\\}"regexpvalue) nil 'move 1)) + (t (re-search-forward (concat "^ \\{"(format "0,%s" indent) "\\}[[:alnum:]_@]+") nil 'move 1)))) + (or (nth 8 (parse-partial-sexp (point-min) (point))) + (progn (back-to-indentation) (py--forward-string-maybe (nth 8 (parse-partial-sexp orig (point))))) + (and secondvalue (looking-at secondvalue)) + (and lastvalue (looking-at lastvalue)) + (and (looking-at regexpvalue) (setq done t)) + ;; py-forward-def-or-class-test-3JzvVW + ;; (setq done t) + ))) + (and (< orig (point)) (point)))))) + +(defun py--backward-empty-lines-or-comment () + "Travel backward" + (while + (or (< 0 (abs (skip-chars-backward " \t\r\n\f"))) + (py-backward-comment)))) + +;; (defun py-kill-buffer-unconditional (buffer) +;; "Kill buffer unconditional, kill buffer-process if existing. " +;; (interactive +;; (list (current-buffer))) +;; (ignore-errors (with-current-buffer buffer +;; (let (kill-buffer-query-functions) +;; (set-buffer-modified-p nil) +;; (ignore-errors (kill-process (get-buffer-process buffer))) +;; (kill-buffer buffer))))) + +(defun py--down-end-form () + "Return position." + (progn (py--backward-empty-lines-or-comment) + (point))) + +(defun py--which-delay-process-dependent (buffer) + "Call a `py-ipython-send-delay' or `py-python-send-delay' according to process" + (if (string-match "^.[IJ]" buffer) + py-ipython-send-delay + py-python-send-delay)) + +(defun py-temp-file-name (strg) + (let* ((temporary-file-directory + (if (file-remote-p default-directory) + (concat (file-remote-p default-directory) "/tmp") + temporary-file-directory)) + (temp-file-name (make-temp-file "py"))) + + (with-temp-file temp-file-name + (insert strg) + (delete-trailing-whitespace)) + temp-file-name)) + +(defun py--fetch-error (output-buffer &optional origline filename) + "Highlight exceptions found in BUF. + +If an exception occurred return error-string, otherwise return nil. +BUF must exist. + +Indicate LINE if code wasn't run from a file, +thus remember ORIGLINE of source buffer" + (with-current-buffer output-buffer + (when py-debug-p (switch-to-buffer (current-buffer))) + ;; (setq py-error (buffer-substring-no-properties (point) (point-max))) + (goto-char (point-max)) + (when (re-search-backward "File \"\\(.+\\)\", line \\([0-9]+\\)\\(.*\\)$" nil t) + (when (and filename (re-search-forward "File \"\\(.+\\)\", line \\([0-9]+\\)\\(.*\\)$" nil t) + (replace-match filename nil nil nil 1)) + (when (and origline (re-search-forward "line \\([0-9]+\\)\\(.*\\)$" (line-end-position) t 1)) + (replace-match origline nil nil nil 2))) + (setq py-error (buffer-substring-no-properties (point) (point-max)))) + py-error)) + +(defvar py-debug-p nil + "Used for development purposes.") + +(defun py--fetch-result (buffer limit &optional cmd) + "CMD: some shells echo the command in output-buffer +Delete it here" + (when py-debug-p (message "(current-buffer): %s" (current-buffer)) + (switch-to-buffer (current-buffer))) + (cond (python-mode-v5-behavior-p + (with-current-buffer buffer + (py--string-trim (buffer-substring-no-properties (point-min) (point-max)) nil "\n"))) + ((and cmd (< limit (point-max))) + (replace-regexp-in-string cmd "" (py--string-trim (replace-regexp-in-string py-shell-prompt-regexp "" (buffer-substring-no-properties limit (point-max)))))) + (t (when (< limit (point-max)) + (py--string-trim (replace-regexp-in-string py-shell-prompt-regexp "" (buffer-substring-no-properties limit (point-max)))))))) + +(defun py--postprocess (output-buffer origline limit &optional cmd filename) + "Provide return values, check result for error, manage windows. + +According to OUTPUT-BUFFER ORIGLINE ORIG" + ;; py--fast-send-string doesn't set origline + (when (or py-return-result-p py-store-result-p) + (with-current-buffer output-buffer + (when py-debug-p (switch-to-buffer (current-buffer))) + (sit-for (py--which-delay-process-dependent (prin1-to-string output-buffer))) + ;; (catch 'py--postprocess + (setq py-result (py--fetch-result output-buffer limit cmd)) + ;; (throw 'py--postprocess (error "py--postprocess failed")) + ;;) + (if (and py-result (not (string= "" py-result))) + (if (string-match "^Traceback" py-result) + (if filename + (setq py-error py-result) + (progn + (with-temp-buffer + (insert py-result) + (sit-for 0.1 t) + (setq py-error (py--fetch-error origline filename))))) + (when py-store-result-p + (kill-new py-result)) + (when py-verbose-p (message "py-result: %s" py-result)) + py-result) + (when py-verbose-p (message "py--postprocess: %s" "Don't see any result")))))) + +(defun py-fetch-py-master-file () + "Lookup if a `py-master-file' is specified. + +See also doku of variable `py-master-file'" + (interactive) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (when (re-search-forward "^ *# Local Variables:" nil (quote move) 1) + (when + (re-search-forward (concat "^\\( *# py-master-file: *\\)\"\\([^ \t]+\\)\" *$") nil t 1) + (setq py-master-file (match-string-no-properties 2)))))) + ;; (when (called-interactively-p 'any) (message "%s" py-master-file)) + ) + +(defun py-ipython--which-version (shell) + "Returns IPython version as string" + (shell-command-to-string (concat (downcase (replace-regexp-in-string "[[:punct:]+]" "" shell)) " -V"))) + +(defun py--provide-command-args (shell fast-process) + "Unbuffered WRT fast-process" + (let ((erg + (delq nil + (cond + ;; ((eq 2 (prefix-numeric-value argprompt)) + ;; py-python2-command-args) + ((string-match "^[Ii]" shell) + (if (string-match "^[0-4]" (py-ipython--which-version shell)) + (remove "--simple-prompt" py-ipython-command-args) + (if (member "--simple-prompt" py-ipython-command-args) + py-ipython-command-args + (cons "--simple-prompt" py-ipython-command-args)))) + ((string-match "^[^-]+3" shell) + py-python3-command-args) + ((string-match "^[jy]" shell) + py-jython-command-args) + (t + py-python-command-args))))) + (if (and fast-process (not (member "-u" erg))) + (cons "-u" erg) + erg))) + +;; This and other stuff from python.el +(defun py-info-encoding-from-cookie () + "Detect current buffer's encoding from its coding cookie. +Returns the encoding as a symbol." + (let ((first-two-lines + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line 2) + (buffer-substring-no-properties + (point) + (point-min)))))) + (when (string-match + ;; (py-rx coding-cookie) + "^#[[:space:]]*\\(?:coding[:=][[:space:]]*\\(?1:\\(?:[[:word:]]\\|-\\)+\\)\\|-\\*-[[:space:]]*coding:[[:space:]]*\\(?1:\\(?:[[:word:]]\\|-\\)+\\)[[:space:]]*-\\*-\\|vim:[[:space:]]*set[[:space:]]+fileencoding[[:space:]]*=[[:space:]]*\\(?1:\\(?:[[:word:]]\\|-\\)+\\)[[:space:]]*:\\)" + first-two-lines) + (intern (match-string-no-properties 1 first-two-lines))))) + +(defun py-info-encoding () + "Return encoding for file. +Try `py-info-encoding-from-cookie', if none is found then +default to utf-8." + (or (py-info-encoding-from-cookie) + 'utf-8)) + +(defun py-indentation-of-statement () + "Returns the indenation of the statement at point. " + (interactive) + (let ((erg (save-excursion + (back-to-indentation) + (or (py--beginning-of-statement-p) + (py-backward-statement)) + (current-indentation)))) + (when (and py-verbose-p (called-interactively-p 'any)) (message "%s" erg)) + erg)) + +(defun py--filter-result (strg) + "Set `py-result' according to `py-fast-filter-re'. + +Remove trailing newline" + (py--string-trim + (replace-regexp-in-string + py-fast-filter-re + "" + (ansi-color-filter-apply strg)))) + +(defun py--cleanup-shell (orig buffer) + (with-current-buffer buffer + (with-silent-modifications + (sit-for py-python3-send-delay) + (when py-debug-p (switch-to-buffer (current-buffer))) + (delete-region orig (point-max))))) + +(defun py-shell--save-temp-file (strg) + (let* ((temporary-file-directory + (if (file-remote-p default-directory) + (concat (file-remote-p default-directory) "/tmp") + temporary-file-directory)) + (temp-file-name (make-temp-file "py")) + (coding-system-for-write (py-info-encoding))) + (with-temp-file temp-file-name + (insert strg) + (delete-trailing-whitespace)) + temp-file-name)) + +(defun py--get-process (&optional argprompt args dedicated shell buffer) + "Get appropriate Python process for current buffer and return it. + +Optional ARGPROMPT DEDICATED SHELL BUFFER" + (interactive) + (or (and buffer (get-buffer-process buffer)) + (get-buffer-process (current-buffer)) + (get-buffer-process (py-shell argprompt args dedicated shell buffer)))) + +(defun py-shell-send-file (file-name &optional process temp-file-name + delete) + "Send FILE-NAME to Python PROCESS. + +If TEMP-FILE-NAME is passed then that file is used for processing +instead, while internally the shell will continue to use +FILE-NAME. If TEMP-FILE-NAME and DELETE are non-nil, then +TEMP-FILE-NAME is deleted after evaluation is performed. When +optional argument." + (interactive + (list + (read-file-name "File to send: "))) + (let* ((proc (or process (py--get-process))) + (encoding (with-temp-buffer + (insert-file-contents + (or temp-file-name file-name)) + (py-info-encoding))) + (file-name (expand-file-name (file-local-name file-name))) + (temp-file-name (when temp-file-name + (expand-file-name + (file-local-name temp-file-name))))) + (py-shell-send-string + (format + (concat + "import codecs, os;" + "__pyfile = codecs.open('''%s''', encoding='''%s''');" + "__code = __pyfile.read().encode('''%s''');" + "__pyfile.close();" + (when (and delete temp-file-name) + (format "os.remove('''%s''');" temp-file-name)) + "exec(compile(__code, '''%s''', 'exec'));") + (or temp-file-name file-name) encoding encoding file-name) + proc))) + +(defun py-shell-send-string (strg &optional process) + "Send STRING to Python PROCESS. + +Uses `comint-send-string'." + (interactive + (list (read-string "Python command: ") nil t)) + (let ((process (or process (py--get-process)))) + (if (string-match ".\n+." strg) ;Multiline. + (let* ((temp-file-name (py-shell--save-temp-file strg)) + (file-name (or (buffer-file-name) temp-file-name))) + (py-shell-send-file file-name process temp-file-name t)) + (comint-send-string process strg) + (when (or (not (string-match "\n\\'" strg)) + (string-match "\n[ \t].*\n?\\'" strg)) + (comint-send-string process "\n"))))) + +(defun py-fast-process (&optional buffer) + "Connect am (I)Python process suitable for large output. + +Output buffer displays \"Fast\" by default +It is not in interactive, i.e. comint-mode, +as its bookkeepings seem linked to the freeze reported by lp:1253907" + (interactive) + (let ((this-buffer + (set-buffer (or (and buffer (get-buffer-create buffer)) + (get-buffer-create py-shell-name))))) + (let ((proc (start-process py-shell-name this-buffer py-shell-name))) + (with-current-buffer this-buffer + (erase-buffer)) + proc))) + +(defun py-proc (&optional argprompt) + "Return the current Python process. + +Start a new process if necessary. " + (interactive "P") + (let ((erg + (cond ((comint-check-proc (current-buffer)) + (get-buffer-process (buffer-name (current-buffer)))) + (t (py-shell argprompt))))) + erg)) + +(defun py-process-file (filename &optional output-buffer error-buffer) + "Process \"python FILENAME\". + +Optional OUTPUT-BUFFER and ERROR-BUFFER might be given." + (interactive "fDatei:") + (let ((coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + (output-buffer (or output-buffer (make-temp-name "py-process-file-output"))) + (pcmd (py-choose-shell))) + (unless (buffer-live-p output-buffer) + (set-buffer (get-buffer-create output-buffer))) + (shell-command (concat pcmd " " filename) output-buffer error-buffer) + (when py-switch-buffers-on-execute-p (switch-to-buffer output-buffer)))) + +(defvar py-last-exeption-buffer nil + "Internal use only - when `py-up-exception' is called. + +In source-buffer, this will deliver the exception-buffer again.") + +(defun py-remove-overlays-at-point () + "Remove overlays as set when `py-highlight-error-source-p' is non-nil." + (interactive "*") + (delete-overlay (car (overlays-at (point))))) + +(defun py--jump-to-exception-intern (act exception-buffer origline) + (let (erg) + (set-buffer exception-buffer) + (goto-char (point-min)) + (forward-line (1- origline)) + (and (search-forward act (line-end-position) t) + (and py-verbose-p (message "exception-buffer: %s on line %d" py-exception-buffer origline)) + (and py-highlight-error-source-p + (setq erg (make-overlay (match-beginning 0) (match-end 0))) + (overlay-put erg + 'face 'highlight))))) + +(defun py--jump-to-exception (perr origline &optional file) + "Jump to the PERR Python code at ORIGLINE in optional FILE." + (let ( + (inhibit-point-motion-hooks t) + (file (or file (car perr))) + (act (nth 2 perr))) + (cond ((and py-exception-buffer + (buffer-live-p py-exception-buffer)) + ;; (pop-to-buffer procbuf) + (py--jump-to-exception-intern act py-exception-buffer origline)) + ((ignore-errors (file-readable-p file)) + (find-file file) + (py--jump-to-exception-intern act (get-buffer (file-name-nondirectory file)) origline)) + ((buffer-live-p (get-buffer file)) + (set-buffer file) + (py--jump-to-exception-intern act file origline)) + (t (setq file (find-file (read-file-name "Exception file: " + nil + file t))) + (py--jump-to-exception-intern act file origline))))) + +(defun py-goto-exception (&optional file line) + "Go to FILE and LINE indicated by the traceback." + (interactive) + (let ((file file) + (line line)) + (unless (and file line) + (save-excursion + (beginning-of-line) + (if (looking-at py-traceback-line-re) + (setq file (substring-no-properties (match-string 1)) + line (string-to-number (match-string 2)))))) + (if (not file) + (error "Not on a traceback line")) + (find-file file) + (goto-char (point-min)) + (forward-line (1- line)))) + +(defun py--find-next-exception (start buffer searchdir errwhere) + "Find the next Python exception and jump to the code that caused it. +START is the buffer position in BUFFER from which to begin searching +for an exception. SEARCHDIR is a function, either +`re-search-backward' or `re-search-forward' indicating the direction +to search. ERRWHERE is used in an error message if the limit (top or +bottom) of the trackback stack is encountered." + (let (file line) + (save-excursion + (with-current-buffer buffer + (goto-char start) + (if (funcall searchdir py-traceback-line-re nil t) + (setq file (match-string 1) + line (string-to-number (match-string 2)))))) + (if (and file line) + (py-goto-exception file line) + (error "%s of traceback" errwhere)))) + +(defun py-down-exception (&optional bottom) + "Go to the next line down in the traceback. +With \\[univeral-argument] (programmatically, optional argument +BOTTOM), jump to the bottom (innermost) exception in the exception +stack." + (interactive "P") + (let* ((buffer py-output-buffer)) + (if bottom + (py--find-next-exception 'eob buffer 're-search-backward "Bottom") + (py--find-next-exception 'eol buffer 're-search-forward "Bottom")))) + +(defun py-up-exception (&optional top) + "Go to the previous line up in the traceback. +With \\[universal-argument] (programmatically, optional argument TOP) +jump to the top (outermost) exception in the exception stack." + (interactive "P") + (let* ((buffer py-output-buffer)) + (if top + (py--find-next-exception 'bob buffer 're-search-forward "Top") + (py--find-next-exception 'bol buffer 're-search-backward "Top")))) + +;; ; +;; obsolete by py--fetch-result +;; followed by py--fetch-error +;; still used by py--execute-ge24.3 + +(defun py--find-next-exception-prepare (direction start) + "According to DIRECTION and START setup exception regexps. + +Depends from kind of Python shell." + (let* ((name (get-process (substring (buffer-name (current-buffer)) 1 -1))) + (buffer (cond (name (buffer-name (current-buffer))) + ((buffer-live-p (get-buffer py-output-buffer)) + py-output-buffer) + (py-last-exeption-buffer (buffer-name py-last-exeption-buffer)) + (t (error "Don't see exeption buffer"))))) + (when buffer (set-buffer (get-buffer buffer))) + (if (eq direction 'up) + (if (string= start "TOP") + (py--find-next-exception 'bob buffer 're-search-forward "Top") + (py--find-next-exception 'bol buffer 're-search-backward "Top")) + (if (string= start "BOTTOM") + (py--find-next-exception 'eob buffer 're-search-backward "Bottom") + (py--find-next-exception 'eol buffer 're-search-forward "Bottom"))))) + +(defun py-shell-comint-end-of-output-p (output) + "Return non-nil if OUTPUT ends with input prompt." + (ignore-errors (string-match + ;; XXX: It seems on macOS an extra carriage return is attached + ;; at the end of output, this handles that too. + (concat + "\r?\n?" + ;; Remove initial caret from calculated regexp + (ignore-errors (replace-regexp-in-string + (rx string-start ?^) "" + py-shell--prompt-calculated-input-regexp)) + (rx eos)) + output))) + +(defun py-comint-postoutput-scroll-to-bottom (output) + "Faster version of `comint-postoutput-scroll-to-bottom'. +Avoids `recenter' calls until OUTPUT is completely sent." + (when (and (not (string= "" output)) + (py-shell-comint-end-of-output-p + (ansi-color-filter-apply output))) + (comint-postoutput-scroll-to-bottom output)) + output) + +(defmacro py-shell--add-to-path-with-priority (pathvar paths) + "Modify PATHVAR and ensure PATHS are added only once at beginning." + `(dolist (path (reverse ,paths)) + (cl-delete path ,pathvar :test #'string=) + (cl-pushnew path ,pathvar :test #'string=))) + +(defun py-shell-tramp-refresh-remote-path (vec paths) + "Update VEC's remote-path giving PATHS priority." + (let ((remote-path (tramp-get-connection-property vec "remote-path" nil))) + (when remote-path + (py-shell--add-to-path-with-priority remote-path paths) + (tramp-set-connection-property vec "remote-path" remote-path) + (tramp-set-remote-path vec)))) + +(defun py-shell-tramp-refresh-process-environment (vec env) + "Update VEC's process environment with ENV." + ;; Stolen from `tramp-open-connection-setup-interactive-shell'. + (let ((env (append (when (fboundp 'tramp-get-remote-locale) + ;; Emacs<24.4 compat. + (list (tramp-get-remote-locale vec))) + (copy-sequence env))) + (tramp-end-of-heredoc + (if (boundp 'tramp-end-of-heredoc) + tramp-end-of-heredoc + (md5 tramp-end-of-output))) + unset vars item) + (while env + (setq item (split-string (car env) "=" 'omit)) + (setcdr item (mapconcat 'identity (cdr item) "=")) + (if (and (stringp (cdr item)) (not (string-equal (cdr item) ""))) + (push (format "%s %s" (car item) (cdr item)) vars) + (push (car item) unset)) + (setq env (cdr env))) + (when vars + (tramp-send-command + vec + (format "while read var val; do export $var=$val; done <<'%s'\n%s\n%s" + tramp-end-of-heredoc + (mapconcat 'identity vars "\n") + tramp-end-of-heredoc) + t)) + (when unset + (tramp-send-command + vec (format "unset %s" (mapconcat 'identity unset " ")) t)))) + +(defun py-shell-calculate-pythonpath () + "Calculate the PYTHONPATH using `py-shell-extra-pythonpaths'." + (let ((pythonpath + (split-string + (or (getenv "PYTHONPATH") "") path-separator 'omit))) + (py-shell--add-to-path-with-priority + pythonpath py-shell-extra-pythonpaths) + (mapconcat 'identity pythonpath path-separator))) + +(defun py-shell-calculate-exec-path () + "Calculate `exec-path'. +Prepends `py-shell-exec-path' and adds the binary directory +for virtualenv if `py-shell-virtualenv-root' is set - this +will use the python interpreter from inside the virtualenv when +starting the shell. If `default-directory' points to a remote host, +the returned value appends `py-shell-remote-exec-path' instead +of `exec-path'." + (let ((new-path (copy-sequence + (if (file-remote-p default-directory) + py-shell-remote-exec-path + exec-path))) + + ;; Windows and POSIX systems use different venv directory structures + (virtualenv-bin-dir (if (eq system-type 'windows-nt) "Scripts" "bin"))) + (py-shell--add-to-path-with-priority + new-path py-shell-exec-path) + (if (not py-shell-virtualenv-root) + new-path + (py-shell--add-to-path-with-priority + new-path + (list (expand-file-name virtualenv-bin-dir py-shell-virtualenv-root))) + new-path))) + +(defun py-shell-calculate-process-environment () + "Calculate `process-environment' or `tramp-remote-process-environment'. +Prepends `py-shell-process-environment', sets extra +pythonpaths from `py-shell-extra-pythonpaths' and sets a few +virtualenv related vars. If `default-directory' points to a +remote host, the returned value is intended for +`tramp-remote-process-environment'." + (let* ((remote-p (file-remote-p default-directory)) + (process-environment (if remote-p + tramp-remote-process-environment + process-environment)) + (virtualenv (when py-shell-virtualenv-root + (directory-file-name py-shell-virtualenv-root)))) + (dolist (env py-shell-process-environment) + (pcase-let ((`(,key ,value) (split-string env "="))) + (setenv key value))) + (when py-shell-unbuffered + (setenv "PYTHONUNBUFFERED" "1")) + (when py-shell-extra-pythonpaths + (setenv "PYTHONPATH" (py-shell-calculate-pythonpath))) + (if (not virtualenv) + process-environment + (setenv "PYTHONHOME" nil) + (setenv "VIRTUAL_ENV" virtualenv)) + process-environment)) + +(defmacro py-shell-with-environment (&rest body) + "Modify shell environment during execution of BODY. +Temporarily sets `process-environment' and `exec-path' during +execution of body. If `default-directory' points to a remote +machine then modifies `tramp-remote-process-environment' and +`py-shell-remote-exec-path' instead." + (declare (indent 0) (debug (body))) + (let ((vec (make-symbol "vec"))) + `(progn + (let* ((,vec + (when (file-remote-p default-directory) + (ignore-errors + (tramp-dissect-file-name default-directory 'noexpand)))) + (process-environment + (if ,vec + process-environment + (py-shell-calculate-process-environment))) + (exec-path + (if ,vec + exec-path + (py-shell-calculate-exec-path))) + (tramp-remote-process-environment + (if ,vec + (py-shell-calculate-process-environment) + tramp-remote-process-environment))) + (when (tramp-get-connection-process ,vec) + ;; For already existing connections, the new exec path must + ;; be re-set, otherwise it won't take effect. One example + ;; of such case is when remote dir-locals are read and + ;; *then* subprocesses are triggered within the same + ;; connection. + (py-shell-tramp-refresh-remote-path + ,vec (py-shell-calculate-exec-path)) + ;; The `tramp-remote-process-environment' variable is only + ;; effective when the started process is an interactive + ;; shell, otherwise (like in the case of processes started + ;; with `process-file') the environment is not changed. + ;; This makes environment modifications effective + ;; unconditionally. + (py-shell-tramp-refresh-process-environment + ,vec tramp-remote-process-environment)) + ,(macroexp-progn body))))) + +(defun py-shell-prompt-detect () + "Detect prompts for the current interpreter. +When prompts can be retrieved successfully from the +interpreter run with +`py-python-command-args', returns a list of +three elements, where the first two are input prompts and the +last one is an output prompt. When no prompts can be detected +shows a warning with instructions to avoid hangs and returns nil. +When `py-shell-prompt-detect-p' is nil avoids any +detection and just returns nil." + (when py-shell-prompt-detect-p + (py-shell-with-environment + (let* ((code (concat + "import sys\n" + "ps = [getattr(sys, 'ps%s' % i, '') for i in range(1,4)]\n" + ;; JSON is built manually for compatibility + "ps_json = '\\n[\"%s\", \"%s\", \"%s\"]\\n' % tuple(ps)\n" + "print (ps_json)\n" + "sys.exit(0)\n")) + ;; (interpreter py-shell-name) + ;; (interpreter-arg py-python-command-args) + (output + (with-temp-buffer + ;; TODO: improve error handling by using + ;; `condition-case' and displaying the error message to + ;; the user in the no-prompts warning. + (ignore-errors + (let ((code-file + ;; Python 2.x on Windows does not handle + ;; carriage returns in unbuffered mode. + (let ((inhibit-eol-conversion (getenv "PYTHONUNBUFFERED"))) + (py-shell--save-temp-file code)))) + (unwind-protect + ;; Use `process-file' as it is remote-host friendly. + (process-file + py-shell-name + code-file + '(t nil) + nil + py-python-command-args) + ;; Try to cleanup + (delete-file code-file)))) + (buffer-string))) + (prompts + (catch 'prompts + (dolist (line (split-string output "\n" t)) + (let ((res + ;; Check if current line is a valid JSON array + (and (string= (substring line 0 2) "[\"") + (ignore-errors + ;; Return prompts as a list, not vector + (append (json-read-from-string line) nil))))) + ;; The list must contain 3 strings, where the first + ;; is the input prompt, the second is the block + ;; prompt and the last one is the output prompt. The + ;; input prompt is the only one that can't be empty. + (when (and (= (length res) 3) + (cl-every #'stringp res) + (not (string= (car res) ""))) + (throw 'prompts res)))) + nil))) + (if (not prompts) + (lwarn + '(python py-shell-prompt-regexp) + :warning + (concat + "Python shell prompts cannot be detected.\n" + "If your emacs session hangs when starting python shells\n" + "recover with `keyboard-quit' and then try fixing the\n" + "interactive flag for your interpreter by adjusting the\n" + "`py-python-command-args' or add regexps\n" + "matching shell prompts in the directory-local friendly vars:\n" + " + `py-shell-prompt-regexp'\n" + " + `py-shell-input-prompt-2-regexp'\n" + " + `py-shell-prompt-output-regexp'\n" + "Or alternatively in:\n" + " + `py-shell-input-prompt-regexps'\n" + " + `py-shell-prompt-output-regexps'")) + prompts))))) + +(defun python-util-valid-regexp-p (regexp) + "Return non-nil if REGEXP is valid." + (ignore-errors (string-match regexp "") t)) + +(defun py-shell-prompt-validate-regexps () + "Validate all user provided regexps for prompts. +Signals `user-error' if any of these vars contain invalid +regexps: `py-shell-prompt-regexp', +`py-shell-input-prompt-2-regexp', +`py-shell-prompt-pdb-regexp', +`py-shell-prompt-output-regexp', +`py-shell-input-prompt-regexps', +`py-shell-prompt-output-regexps'." + (dolist (symbol (list 'py-shell-input-prompt-1-regexp + 'py-shell-prompt-output-regexps + 'py-shell-input-prompt-2-regexp + 'py-shell-prompt-pdb-regexp)) + (dolist (regexp (let ((regexps (symbol-value symbol))) + (if (listp regexps) + regexps + (list regexps)))) + (when (not (python-util-valid-regexp-p regexp)) + (user-error "Invalid regexp %s in `%s'" + regexp symbol))))) + +(defun py-shell-prompt-set-calculated-regexps () + "Detect and set input and output prompt regexps. + +Build and set the values for input- and output-prompt regexp +using the values from `py-shell-prompt-regexp', +`py-shell-input-prompt-2-regexp', `py-shell-prompt-pdb-regexp', +`py-shell-prompt-output-regexp', `py-shell-input-prompt-regexps', + and detected prompts from `py-shell-prompt-detect'." + (when (not (and py-shell--prompt-calculated-input-regexp + py-shell--prompt-calculated-output-regexp)) + (let* ((detected-prompts (py-shell-prompt-detect)) + (input-prompts nil) + (output-prompts nil) + (build-regexp + (lambda (prompts) + (concat "^\\(" + (mapconcat #'identity + (sort prompts + (lambda (a b) + (let ((length-a (length a)) + (length-b (length b))) + (if (= length-a length-b) + (string< a b) + (> (length a) (length b)))))) + "\\|") + "\\)")))) + ;; Validate ALL regexps + (py-shell-prompt-validate-regexps) + ;; Collect all user defined input prompts + (dolist (prompt (append py-shell-input-prompt-regexps + (list py-shell-input-prompt-2-regexp + py-shell-prompt-pdb-regexp))) + (cl-pushnew prompt input-prompts :test #'string=)) + ;; Collect all user defined output prompts + (dolist (prompt (cons py-shell-prompt-output-regexp + py-shell-prompt-output-regexps)) + (cl-pushnew prompt output-prompts :test #'string=)) + ;; Collect detected prompts if any + (when detected-prompts + (dolist (prompt (butlast detected-prompts)) + (setq prompt (regexp-quote prompt)) + (cl-pushnew prompt input-prompts :test #'string=)) + (setq py-shell--block-prompt (nth 1 detected-prompts)) + (cl-pushnew (regexp-quote + (car (last detected-prompts))) + output-prompts :test #'string=)) + ;; Set input and output prompt regexps from collected prompts + (setq py-shell--prompt-calculated-input-regexp + (funcall build-regexp input-prompts) + py-shell--prompt-calculated-output-regexp + (funcall build-regexp output-prompts))))) + +(defun py-shell-output-filter (strg) + "Filter used in `py-shell-send-string-no-output' to grab output. +STRING is the output received to this point from the process. +This filter saves received output from the process in +`py-shell-output-filter-buffer' and stops receiving it after +detecting a prompt at the end of the buffer." + (let ((py-shell--prompt-calculated-output-regexp + (or py-shell--prompt-calculated-output-regexp (py-shell-prompt-set-calculated-regexps)))) + (setq + strg (ansi-color-filter-apply strg) + py-shell-output-filter-buffer + (concat py-shell-output-filter-buffer strg)) + (when (py-shell-comint-end-of-output-p + py-shell-output-filter-buffer) + ;; Output ends when `py-shell-output-filter-buffer' contains + ;; the prompt attached at the end of it. + (setq py-shell-output-filter-in-progress nil + py-shell-output-filter-buffer + (substring py-shell-output-filter-buffer + 0 (match-beginning 0))) + (when (string-match + py-shell--prompt-calculated-output-regexp + py-shell-output-filter-buffer) + ;; Some shells, like IPython might append a prompt before the + ;; output, clean that. + (setq py-shell-output-filter-buffer + (substring py-shell-output-filter-buffer (match-end 0))))) + "")) + +(defun py--fast-send-string-no-output-intern (strg proc limit output-buffer no-output) + (let (erg) + (with-current-buffer output-buffer + ;; (when py-debug-p (switch-to-buffer (current-buffer))) + ;; (erase-buffer) + (process-send-string proc strg) + (or (string-match "\n$" strg) + (process-send-string proc "\n") + (goto-char (point-max)) + ) + (cond (no-output + (delete-region (field-beginning) (field-end)) + ;; (erase-buffer) + ;; (delete-region (point-min) (line-beginning-position)) + ) + (t + (if + (setq erg (py--fetch-result output-buffer limit strg)) + (setq py-result (py--filter-result erg)) + (dotimes (_ 3) (unless (setq erg (py--fetch-result output-buffer limit))(sit-for 1 t))) + (or (py--fetch-result output-buffer limit)) + (error "py--fast-send-string-no-output-intern: py--fetch-result: no result"))))))) + +(defun py-execute-string (strg &optional process result no-output orig output-buffer fast argprompt args dedicated shell exception-buffer split switch internal) + "Evaluate STRG in Python PROCESS. + +With optional Arg PROCESS send to process. +With optional Arg RESULT store result in var `py-result', also return it. +With optional Arg NO-OUTPUT don't display any output +With optional Arg ORIG deliver original position. +With optional Arg OUTPUT-BUFFER specify output-buffer" + (interactive "sPython command: ") + (save-excursion + (let* ((buffer (or output-buffer (or (and process (buffer-name (process-buffer process))) (buffer-name (py-shell argprompt args dedicated shell output-buffer fast exception-buffer split switch internal))))) + (proc (or process (get-buffer-process buffer))) + ;; nil nil nil nil (buffer-name buffer)))) + (orig (or orig (point))) + (limit (ignore-errors (marker-position (process-mark proc))))) + (cond ((and no-output fast) + (py--fast-send-string-no-output-intern strg proc limit buffer no-output)) + (no-output + (py-send-string-no-output strg proc)) + ((and (string-match ".\n+." strg) (string-match "^[Ii]" + ;; (buffer-name buffer) + buffer + )) ;; multiline + (let* ((temp-file-name (py-temp-file-name strg)) + (file-name (or (buffer-file-name) temp-file-name))) + (py-execute-file file-name proc))) + (t (with-current-buffer buffer + (comint-send-string proc strg) + (when (or (not (string-match "\n\\'" strg)) + (string-match "\n[ \t].*\n?\\'" strg)) + (comint-send-string proc "\n")) + (sit-for py-python-send-delay) + (cond (result + (setq py-result + (py--fetch-result buffer limit strg))) + (no-output + (and orig (py--cleanup-shell orig buffer)))))))))) + +(defun py--execute-file-base (filename &optional proc cmd procbuf origline fast interactivep) + "Send to Python interpreter process PROC. + +In Python version 2.. \"execfile('FILENAME')\". + +Takes also CMD PROCBUF ORIGLINE NO-OUTPUT. + +Make that process's buffer visible and force display. Also make +comint believe the user typed this string so that +`kill-output-from-shell' does The Right Thing. +Returns position where output starts." + (let* ((filename (expand-file-name filename)) + (buffer (or procbuf (and proc (process-buffer proc)) (py-shell nil nil nil nil nil fast))) + (proc (or proc (get-buffer-process buffer))) + (limit (marker-position (process-mark proc))) + (cmd (or cmd (py-execute-file-command filename))) + erg) + (if fast + (process-send-string proc cmd) + (py-execute-string cmd proc)) + ;; (message "%s" (current-buffer)) + (with-current-buffer buffer + (when (or py-return-result-p py-store-result-p) + (setq erg (py--postprocess buffer origline limit cmd filename)) + (if py-error + (setq py-error (prin1-to-string py-error)) + erg))) + (when (or interactivep + (or py-switch-buffers-on-execute-p py-split-window-on-execute)) + (py--shell-manage-windows buffer (find-file-noselect filename) py-split-window-on-execute py-switch-buffers-on-execute-p)))) + +(defun py-restore-window-configuration () + "Restore `py-restore-window-configuration'." + (let (val) + (and (setq val (get-register py--windows-config-register))(and (consp val) (window-configuration-p (car val))(markerp (cadr val)))(marker-buffer (cadr val)) + (jump-to-register py--windows-config-register)))) + +(defun py-toggle-split-window-function () + "If window is splitted vertically or horizontally. + +When code is executed and `py-split-window-on-execute' is t, +the result is displays in an output-buffer, \"\*Python\*\" by default. + +Customizable variable `py-split-windows-on-execute-function' +tells how to split the screen." + (interactive) + (if (eq 'split-window-vertically py-split-windows-on-execute-function) + (setq py-split-windows-on-execute-function'split-window-horizontally) + (setq py-split-windows-on-execute-function 'split-window-vertically)) + (when (and py-verbose-p (called-interactively-p 'any)) + (message "py-split-windows-on-execute-function set to: %s" py-split-windows-on-execute-function))) + +(defun py--manage-windows-set-and-switch (buffer) + "Switch to output BUFFER, go to `point-max'. + +Internal use" + (set-buffer buffer) + (goto-char (process-mark (get-buffer-process (current-buffer))))) + +(defun py--alternative-split-windows-on-execute-function () + "Toggle split-window-horizontally resp. vertically." + (if (eq py-split-windows-on-execute-function 'split-window-vertically) + 'split-window-horizontally + 'split-window-vertically)) + +(defun py--get-splittable-window () + "Search `window-list' for a window suitable for splitting." + (or (and (window-left-child)(split-window (window-left-child))) + (and (window-top-child)(split-window (window-top-child))) + (and (window-parent)(ignore-errors (split-window (window-parent)))) + (and (window-atom-root)(split-window (window-atom-root))))) + +(defun py--manage-windows-split (buffer) + "If one window, split BUFFER. + +according to `py-split-windows-on-execute-function'." + (interactive) + (set-buffer buffer) + (or + ;; (split-window (selected-window) nil ’below) + (ignore-errors (funcall py-split-windows-on-execute-function)) + ;; If call didn't succeed according to settings of + ;; `split-height-threshold', `split-width-threshold' + ;; resp. `window-min-height', `window-min-width' + ;; try alternative split + (unless (ignore-errors (funcall (py--alternative-split-windows-on-execute-function))) + ;; if alternative split fails, look for larger window + (py--get-splittable-window) + (ignore-errors (funcall (py--alternative-split-windows-on-execute-function)))))) + +;; (defun py--display-windows (output-buffer) +;; "Otherwise new window appears above" +;; (display-buffer output-buffer) +;; (select-window py-exception-window)) + +(defun py--split-t-not-switch-wm (output-buffer number-of-windows exception-buffer) + (unless (window-live-p output-buffer) + (with-current-buffer (get-buffer exception-buffer) + + (when (< number-of-windows py-split-window-on-execute-threshold) + (unless + (member (get-buffer-window output-buffer) (window-list)) + (py--manage-windows-split exception-buffer))) + (display-buffer output-buffer t) + (switch-to-buffer exception-buffer) + ))) + +(defun py--shell-manage-windows (output-buffer &optional exception-buffer split switch) + "Adapt or restore window configuration from OUTPUT-BUFFER. + +Optional EXCEPTION-BUFFER SPLIT SWITCH +Return nil." + (let* ((exception-buffer (or exception-buffer (other-buffer))) + (old-window-list (window-list)) + (number-of-windows (length old-window-list)) + (split (or split py-split-window-on-execute)) + (switch + (or py-switch-buffers-on-execute-p switch py-pdbtrack-tracked-buffer))) + ;; (output-buffer-displayed-p) + (cond + (py-keep-windows-configuration + (py-restore-window-configuration) + (set-buffer output-buffer) + (goto-char (point-max))) + ((and (eq split 'always) + switch) + (if (member (get-buffer-window output-buffer) (window-list)) + ;; (delete-window (get-buffer-window output-buffer)) + (select-window (get-buffer-window output-buffer)) + (py--manage-windows-split exception-buffer) + ;; otherwise new window appears above + (save-excursion + (other-window 1) + (switch-to-buffer output-buffer)) + (display-buffer exception-buffer))) + ((and + (eq split 'always) + (not switch)) + (if (member (get-buffer-window output-buffer) (window-list)) + (select-window (get-buffer-window output-buffer)) + (py--manage-windows-split exception-buffer) + (display-buffer output-buffer) + (pop-to-buffer exception-buffer))) + ((and + (eq split 'just-two) + switch) + (switch-to-buffer (current-buffer)) + (delete-other-windows) + (py--manage-windows-split exception-buffer) + ;; otherwise new window appears above + (other-window 1) + (set-buffer output-buffer) + (switch-to-buffer (current-buffer))) + ((and + (eq split 'just-two) + (not switch)) + (switch-to-buffer exception-buffer) + (delete-other-windows) + (unless + (member (get-buffer-window output-buffer) (window-list)) + (py--manage-windows-split exception-buffer)) + ;; Fixme: otherwise new window appears above + (save-excursion + (other-window 1) + (pop-to-buffer output-buffer) + (goto-char (point-max)) + (other-window 1))) + ((and + split + (not switch)) + ;; https://bugs.launchpad.net/python-mode/+bug/1478122 + ;; > If the shell is visible in any of the windows it should re-use that window + ;; > I did double check and py-keep-window-configuration is nil and split is t. + (py--split-t-not-switch-wm output-buffer number-of-windows exception-buffer)) + ((and split switch) + (unless + (member (get-buffer-window output-buffer) (window-list)) + (py--manage-windows-split exception-buffer)) + ;; Fixme: otherwise new window appears above + ;; (save-excursion + ;; (other-window 1) + ;; (pop-to-buffer output-buffer) + ;; [Bug 1579309] python buffer window on top when using python3 + (set-buffer output-buffer) + (switch-to-buffer output-buffer) + (goto-char (point-max)) + ;; (other-window 1) + ) + ((not switch) + (let (pop-up-windows) + (py-restore-window-configuration)))))) + +(defun py-execute-file (filename &optional proc) + "When called interactively, user is prompted for FILENAME." + (interactive "fFilename: ") + (let (;; postprocess-output-buffer might want origline + (origline 1) + (py-exception-buffer filename) + erg) + (if (file-readable-p filename) + (if py-store-result-p + (setq erg (py--execute-file-base (expand-file-name filename) nil nil nil origline)) + (py--execute-file-base (expand-file-name filename) proc)) + (message "%s not readable. %s" filename "Do you have write permissions?")) + (py--shell-manage-windows py-output-buffer py-exception-buffer nil + (or (called-interactively-p 'interactive))) + erg)) + +(defun py-send-string-no-output (strg &optional process buffer-name) + "Send STRING to PROCESS and inhibit output. + +Return the output." + (let* ((proc (or process (py--get-process))) + (buffer (or buffer-name (if proc (buffer-name (process-buffer proc)) (py-shell)))) + (comint-preoutput-filter-functions + '(py-shell-output-filter)) + (py-shell-output-filter-in-progress t) + (inhibit-quit t) + (delay (py--which-delay-process-dependent buffer)) + temp-file-name) + (or + (with-local-quit + (if (and (string-match ".\n+." strg) (string-match "^\*[Ii]" buffer)) ;; IPython or multiline + (let ((file-name (or (buffer-file-name) (setq temp-file-name (py-temp-file-name strg))))) + (py-execute-file file-name proc) + (when temp-file-name (delete-file temp-file-name))) + (py-shell-send-string strg proc)) + ;; (switch-to-buffer buffer) + ;; (accept-process-output proc 9) + (while py-shell-output-filter-in-progress + ;; `py-shell-output-filter' takes care of setting + ;; `py-shell-output-filter-in-progress' to NIL after it + ;; detects end of output. + (accept-process-output proc delay)) + (prog1 + py-shell-output-filter-buffer + (setq py-shell-output-filter-buffer nil))) + (with-current-buffer (process-buffer proc) + (comint-interrupt-subjob))))) + +(defun py--leave-backward-string-list-and-comment-maybe (pps) + (while (or (and (nth 8 pps) (goto-char (nth 8 pps))) + (and (nth 1 pps) (goto-char (nth 1 pps))) + (and (nth 4 pps) (goto-char (nth 4 pps)))) + ;; (back-to-indentation) + (when (or (looking-at comment-start)(member (char-after) (list ?\" ?'))) + (skip-chars-backward " \t\r\n\f")) + (setq pps (parse-partial-sexp (point-min) (point))))) + +(defun py-set-ipython-completion-command-string (shell) + "Set and return `py-ipython-completion-command-string' according to SHELL." + (interactive) + (let* ((ipython-version (py-ipython--which-version shell))) + (if (string-match "[0-9]" ipython-version) + (setq py-ipython-completion-command-string + (cond ((string-match "^[^0].+" ipython-version) + py-ipython0.11-completion-command-string) + ((string-match "^0.1[1-3]" ipython-version) + py-ipython0.11-completion-command-string) + ((string= "^0.10" ipython-version) + py-ipython0.10-completion-command-string))) + (error ipython-version)))) + +(defun py-ipython--module-completion-import (proc) + "Import module-completion according to PROC." + (interactive) + (let ((ipython-version (shell-command-to-string (concat py-shell-name " -V")))) + (when (and (string-match "^[0-9]" ipython-version) + (string-match "^[^0].+" ipython-version)) + (process-send-string proc "from IPython.core.completerlib import module_completion")))) + +(defun py--compose-buffer-name-initials (liste) + (let (erg) + (dolist (ele liste) + (unless (string= "" ele) + (setq erg (concat erg (char-to-string (aref ele 0)))))) + erg)) + +(defun py--remove-home-directory-from-list (liste) + "Prepare for compose-buffer-name-initials according to LISTE." + (let ((case-fold-search t) + (liste liste) + erg) + (if (listp (setq erg (split-string (expand-file-name "~") "\/"))) + erg + (setq erg (split-string (expand-file-name "~") "\\\\"))) + (while erg + (when (member (car erg) liste) + (setq liste (cdr (member (car erg) liste)))) + (setq erg (cdr erg))) + (butlast liste))) + +(defun py--prepare-shell-name (erg) + "Provide a readable shell name by capitalizing etc." + (cond ((string-match "^ipython" erg) + (replace-regexp-in-string "ipython" "IPython" erg)) + ((string-match "^jython" erg) + (replace-regexp-in-string "jython" "Jython" erg)) + ((string-match "^python" erg) + (replace-regexp-in-string "python" "Python" erg)) + ((string-match "^python2" erg) + (replace-regexp-in-string "python2" "Python2" erg)) + ((string-match "^python3" erg) + (replace-regexp-in-string "python3" "Python3" erg)) + ((string-match "^pypy" erg) + (replace-regexp-in-string "pypy" "PyPy" erg)) + (t erg))) + +(defun py--choose-buffer-name (&optional name dedicated fast-process) + "Return an appropriate NAME to display in modeline. + +Optional DEDICATED FAST-PROCESS +SEPCHAR is the file-path separator of your system." + (let* ((name-first (or name py-shell-name)) + (erg (when name-first (if (stringp name-first) name-first (prin1-to-string name-first)))) + (fast-process (or fast-process py-fast-process-p)) + prefix) + (when (string-match "^py-" erg) + (setq erg (nth 1 (split-string erg "-")))) + ;; remove home-directory from prefix to display + (unless py-modeline-acronym-display-home-p + (save-match-data + (let ((case-fold-search t)) + (when (string-match (concat ".*" (expand-file-name "~")) erg) + (setq erg (replace-regexp-in-string (concat "^" (expand-file-name "~")) "" erg)))))) + (if (or (and (setq prefix (split-string erg "\\\\")) + (< 1 (length prefix))) + (and (setq prefix (split-string erg "\/")) + (< 1 (length prefix)))) + (progn + ;; exect something like default py-shell-name + (setq erg (car (last prefix))) + (unless py-modeline-acronym-display-home-p + ;; home-directory may still inside + (setq prefix (py--remove-home-directory-from-list prefix)) + (setq prefix (py--compose-buffer-name-initials prefix)))) + (setq erg (or erg py-shell-name)) + (setq prefix nil)) + (when fast-process (setq erg (concat erg " Fast"))) + (setq erg + (py--prepare-shell-name erg)) + (when (or dedicated py-dedicated-process-p) + (setq erg (make-temp-name (concat erg "-")))) + (cond ((and prefix (string-match "^\*" erg)) + (setq erg (replace-regexp-in-string "^\*" (concat "*" prefix " ") erg))) + (prefix + (setq erg (concat "*" prefix " " erg "*"))) + (t (unless (string-match "^\*" erg) (setq erg (concat "*" erg "*"))))) + erg)) + +(defun py-shell (&optional argprompt args dedicated shell buffer fast exception-buffer split switch internal) + "Connect process to BUFFER. + +Start an interpreter according to `py-shell-name' or SHELL. + +Optional ARGPROMPT: with \\[universal-argument] start in a new +dedicated shell. + +Optional ARGS: Specify other than default command args. + +Optional DEDICATED: start in a new dedicated shell. +Optional string SHELL overrides default `py-shell-name'. +Optional string BUFFER allows a name, the Python process is connected to +Optional FAST: no fontification in process-buffer. +Optional EXCEPTION-BUFFER: point to error. +Optional SPLIT: see var `py-split-window-on-execute' +Optional SWITCH: see var `py-switch-buffers-on-execute-p' +Optional INTERNAL shell will be invisible for users + +Reusing existing processes: For a given buffer and same values, +if a process is already running for it, it will do nothing. + +Runs the hook `py-shell-mode-hook' after +`comint-mode-hook' is run. (Type \\[describe-mode] in the +process buffer for a list of commands.)" + (interactive "p") + (let* ((interactivep (and argprompt (eq 1 (prefix-numeric-value argprompt)))) + (fast (unless (eq major-mode 'org-mode) + (or fast py-fast-process-p))) + (dedicated (or (eq 4 (prefix-numeric-value argprompt)) dedicated py-dedicated-process-p)) + (shell (if shell + (if (executable-find shell) + shell + (error (concat "py-shell: Can't see an executable for `"shell "' on your system. Maybe needs a link?"))) + (py-choose-shell))) + (args (or args (py--provide-command-args shell fast))) + ;; Make sure a new one is created if required + (buffer-name + (or buffer + (and python-mode-v5-behavior-p (get-buffer-create "*Python Output*")) + (py--choose-buffer-name shell dedicated fast))) + (proc (get-buffer-process buffer-name)) + (done nil) + (delay nil) + (buffer + (or + (and (ignore-errors (process-buffer proc)) + (save-excursion (with-current-buffer (process-buffer proc) + ;; point might not be left there + (goto-char (point-max)) + (push-mark) + (setq done t) + (process-buffer proc)))) + (save-excursion + (py-shell-with-environment + (if fast + (process-buffer (apply 'start-process shell buffer-name shell args)) + (apply #'make-comint-in-buffer shell buffer-name + shell nil args)))))) + ;; (py-shell-prompt-detect-p (or (string-match "^\*IP" buffer) py-shell-prompt-detect-p)) + ) + (setq py-output-buffer (buffer-name (if python-mode-v5-behavior-p (get-buffer "*Python Output*") buffer))) + (unless done + (with-current-buffer buffer + (setq delay (py--which-delay-process-dependent buffer-name)) + (unless fast + (when interactivep + (cond ((string-match "^.I" buffer-name) + (message "Waiting according to `py-ipython-send-delay:' %s" delay)) + ((string-match "^.+3" buffer-name) + (message "Waiting according to `py-python3-send-delay:' %s" delay)))) + (setq py-modeline-display (py--update-lighter buffer-name)) + ;; (sit-for delay t) + ))) + (if (setq proc (get-buffer-process buffer)) + (progn + (with-current-buffer buffer + (unless (or done fast) (py-shell-mode)) + (and internal (set-process-query-on-exit-flag proc nil))) + (when (or interactivep + (or switch py-switch-buffers-on-execute-p py-split-window-on-execute)) + (py--shell-manage-windows buffer exception-buffer split (or interactivep switch))) + buffer) + (error (concat "py-shell:" (py--fetch-error py-output-buffer)))))) + +;; python-components-rx + +;; The `rx--translate...' functions below return (REGEXP . PRECEDENCE), +;; where REGEXP is a list of string expressions that will be +;; concatenated into a regexp, and PRECEDENCE is one of +;; +;; t -- can be used as argument to postfix operators (eg. "a") +;; seq -- can be concatenated in sequence with other seq or higher (eg. "ab") +;; lseq -- can be concatenated to the left of rseq or higher (eg. "^a") +;; rseq -- can be concatenated to the right of lseq or higher (eg. "a$") +;; nil -- can only be used in alternatives (eg. "a\\|b") +;; +;; They form a lattice: +;; +;; t highest precedence +;; | +;; seq +;; / \ +;; lseq rseq +;; \ / +;; nil lowest precedence + + +(defconst rx--char-classes + '((digit . digit) + (numeric . digit) + (num . digit) + (control . cntrl) + (cntrl . cntrl) + (hex-digit . xdigit) + (hex . xdigit) + (xdigit . xdigit) + (blank . blank) + (graphic . graph) + (graph . graph) + (printing . print) + (print . print) + (alphanumeric . alnum) + (alnum . alnum) + (letter . alpha) + (alphabetic . alpha) + (alpha . alpha) + (ascii . ascii) + (nonascii . nonascii) + (lower . lower) + (lower-case . lower) + (punctuation . punct) + (punct . punct) + (space . space) + (whitespace . space) + (white . space) + (upper . upper) + (upper-case . upper) + (word . word) + (wordchar . word) + (unibyte . unibyte) + (multibyte . multibyte)) + "Alist mapping rx symbols to character classes. +Most of the names are from SRE.") + +(defvar rx-constituents nil + "Alist of old-style rx extensions, for compatibility. +For new code, use `rx-define', `rx-let' or `rx-let-eval'. + +Each element is (SYMBOL . DEF). + +If DEF is a symbol, then SYMBOL is an alias of DEF. + +If DEF is a string, then SYMBOL is a plain rx symbol defined as the + regexp string DEF. + +If DEF is a list on the form (FUN MIN-ARGS MAX-ARGS PRED), then + SYMBOL is an rx form with at least MIN-ARGS and at most + MAX-ARGS arguments. If MAX-ARGS is nil, then there is no upper limit. + FUN is a function taking the entire rx form as single argument + and returning the translated regexp string. + If PRED is non-nil, it is a predicate that all actual arguments must + satisfy.") + +(defvar rx--local-definitions nil + "Alist of dynamic local rx definitions. +Each entry is: + (NAME DEF) -- NAME is an rx symbol defined as the rx form DEF. + (NAME ARGS DEF) -- NAME is an rx form with arglist ARGS, defined + as the rx form DEF (which can contain members of ARGS).") + +(defsubst rx--lookup-def (name) + "Current definition of NAME: (DEF) or (ARGS DEF), or nil if none." + (or (cdr (assq name rx--local-definitions)) + (get name 'rx-definition))) + +(defun rx--expand-def (form) + "FORM expanded (once) if a user-defined construct; otherwise nil." + (cond ((symbolp form) + (let ((def (rx--lookup-def form))) + (and def + (if (cdr def) + (error "Not an `rx' symbol definition: %s" form) + (car def))))) + ((and (consp form) (symbolp (car form))) + (let* ((op (car form)) + (def (rx--lookup-def op))) + (and def + (if (cdr def) + (rx--expand-template + op (cdr form) (nth 0 def) (nth 1 def)) + (error "Not an `rx' form definition: %s" op))))))) + +;; TODO: Additions to consider: +;; - A construct like `or' but without the match order guarantee, +;; maybe `unordered-or'. Useful for composition or generation of +;; alternatives; permits more effective use of regexp-opt. + +(defun rx--translate-symbol (sym) + "Translate an rx symbol. Return (REGEXP . PRECEDENCE)." + (pcase sym + ;; Use `list' instead of a quoted list to wrap the strings here, + ;; since the return value may be mutated. + ((or 'nonl 'not-newline 'any) (cons (list ".") t)) + ((or 'anychar 'anything) (cons (list "[^z-a]") t)) + ('unmatchable (rx--empty)) + ((or 'bol 'line-start) (cons (list "^") 'lseq)) + ((or 'eol 'line-end) (cons (list "$") 'rseq)) + ((or 'bos 'string-start 'bot 'buffer-start) (cons (list "\\`") t)) + ((or 'eos 'string-end 'eot 'buffer-end) (cons (list "\\'") t)) + ('point (cons (list "\\=") t)) + ((or 'bow 'word-start) (cons (list "\\<") t)) + ((or 'eow 'word-end) (cons (list "\\>") t)) + ('word-boundary (cons (list "\\b") t)) + ('not-word-boundary (cons (list "\\B") t)) + ('symbol-start (cons (list "\\_<") t)) + ('symbol-end (cons (list "\\_>") t)) + ('not-wordchar (cons (list "\\W") t)) + (_ + (cond + ((let ((class (cdr (assq sym rx--char-classes)))) + (and class (cons (list (concat "[[:" (symbol-name class) ":]]")) t)))) + + ((let ((expanded (rx--expand-def sym))) + (and expanded (rx--translate expanded)))) + + ;; For compatibility with old rx. + ((let ((entry (assq sym rx-constituents))) + (and (progn + (while (and entry (not (stringp (cdr entry)))) + (setq entry + (if (symbolp (cdr entry)) + ;; Alias for another entry. + (assq (cdr entry) rx-constituents) + ;; Wrong type, try further down the list. + (assq (car entry) + (cdr (memq entry rx-constituents)))))) + entry) + (cons (list (cdr entry)) nil)))) + (t (error "Unknown rx symbol `%s'" sym)))))) + +(defun rx--enclose (left-str rexp right-str) + "Bracket REXP by LEFT-STR and RIGHT-STR." + (append (list left-str) rexp (list right-str))) + +(defun rx--bracket (rexp) + (rx--enclose "\\(?:" rexp "\\)")) + +(defun rx--sequence (left right) + "Return the sequence (concatenation) of two translated items, +each on the form (REGEXP . PRECEDENCE), returning (REGEXP . PRECEDENCE)." + ;; Concatenation rules: + ;; seq ++ seq -> seq + ;; lseq ++ seq -> lseq + ;; seq ++ rseq -> rseq + ;; lseq ++ rseq -> nil + (cond ((not (car left)) right) + ((not (car right)) left) + (t + (let ((l (if (memq (cdr left) '(nil rseq)) + (cons (rx--bracket (car left)) t) + left)) + (r (if (memq (cdr right) '(nil lseq)) + (cons (rx--bracket (car right)) t) + right))) + (cons (append (car l) (car r)) + (if (eq (cdr l) 'lseq) + (if (eq (cdr r) 'rseq) + nil ; lseq ++ rseq + 'lseq) ; lseq ++ seq + (if (eq (cdr r) 'rseq) + 'rseq ; seq ++ rseq + 'seq))))))) ; seq ++ seq + +(defun rx--translate-seq (body) + "Translate a sequence of zero or more rx items. +Return (REGEXP . PRECEDENCE)." + (if body + (let* ((items (mapcar #'rx--translate body)) + (result (car items))) + (dolist (item (cdr items)) + (setq result (rx--sequence result item))) + result) + (cons nil 'seq))) + +(defun rx--empty () + "Regexp that never matches anything." + (cons (list regexp-unmatchable) 'seq)) + +;; `cl-every' replacement to avoid bootstrapping problems. +(defun rx--every (pred list) + "Whether PRED is true for every element of LIST." + (while (and list (funcall pred (car list))) + (setq list (cdr list))) + (null list)) + +(defun rx--foldl (f x l) + "(F (F (F X L0) L1) L2) ... +Left-fold the list L, starting with X, by the binary function F." + (while l + (setq x (funcall f x (car l))) + (setq l (cdr l))) + x) + +(defun rx--normalise-or-arg (form) + "Normalize the `or' argument FORM. +Characters become strings, user-definitions and `eval' forms are expanded, +and `or' forms are normalized recursively." + (cond ((characterp form) + (char-to-string form)) + ((and (consp form) (memq (car form) '(or |))) + (cons (car form) (mapcar #'rx--normalise-or-arg (cdr form)))) + ((and (consp form) (eq (car form) 'eval)) + (rx--normalise-or-arg (rx--expand-eval (cdr form)))) + (t + (let ((expanded (rx--expand-def form))) + (if expanded + (rx--normalise-or-arg expanded) + form))))) + +(defun rx--all-string-or-args (body) + "If BODY only consists of strings or such `or' forms, return all the strings. +Otherwise throw `rx--nonstring'." + (mapcan (lambda (form) + (cond ((stringp form) (list form)) + ((and (consp form) (memq (car form) '(or |))) + (rx--all-string-or-args (cdr form))) + (t (throw 'rx--nonstring nil)))) + body)) + +(defun rx--translate-or (body) + "Translate an or-pattern of zero or more rx items. +Return (REGEXP . PRECEDENCE)." + ;; FIXME: Possible improvements: + ;; + ;; - Flatten sub-patterns first: (or (or A B) (or C D)) -> (or A B C D) + ;; Then call regexp-opt on runs of string arguments. Example: + ;; (or (+ digit) "CHARLIE" "CHAN" (+ blank)) + ;; -> (or (+ digit) (or "CHARLIE" "CHAN") (+ blank)) + ;; + ;; - Optimize single-character alternatives better: + ;; * classes: space, alpha, ... + ;; * (syntax S), for some S (whitespace, word) + ;; so that (or "@" "%" digit (any "A-Z" space) (syntax word)) + ;; -> (any "@" "%" digit "A-Z" space word) + ;; -> "[A-Z@%[:digit:][:space:][:word:]]" + (cond + ((null body) ; No items: a never-matching regexp. + (rx--empty)) + ((null (cdr body)) ; Single item. + (rx--translate (car body))) + (t + (let* ((args (mapcar #'rx--normalise-or-arg body)) + (all-strings (catch 'rx--nonstring (rx--all-string-or-args args)))) + (cond + (all-strings ; Only strings. + (cons (list (regexp-opt all-strings nil)) + t)) + ((rx--every #'rx--charset-p args) ; All charsets. + (rx--translate-union nil args)) + (t + (cons (append (car (rx--translate (car args))) + (mapcan (lambda (item) + (cons "\\|" (car (rx--translate item)))) + (cdr args))) + nil))))))) + +(defun rx--charset-p (form) + "Whether FORM looks like a charset, only consisting of character intervals +and set operations." + (or (and (consp form) + (or (and (memq (car form) '(any in char)) + (rx--every (lambda (x) (not (symbolp x))) (cdr form))) + (and (memq (car form) '(not or | intersection)) + (rx--every #'rx--charset-p (cdr form))))) + (characterp form) + (and (stringp form) (= (length form) 1)) + (and (or (symbolp form) (consp form)) + (let ((expanded (rx--expand-def form))) + (and expanded + (rx--charset-p expanded)))))) + +(defun rx--string-to-intervals (str) + "Decode STR as intervals: A-Z becomes (?A . ?Z), and the single +character X becomes (?X . ?X). Return the intervals in a list." + ;; We could just do string-to-multibyte on the string and work with + ;; that instead of this `decode-char' workaround. + (let ((decode-char + (if (multibyte-string-p str) + #'identity + #'unibyte-char-to-multibyte)) + (len (length str)) + (i 0) + (intervals nil)) + (while (< i len) + (cond ((and (< i (- len 2)) + (= (aref str (1+ i)) ?-)) + ;; Range. + (let ((start (funcall decode-char (aref str i))) + (end (funcall decode-char (aref str (+ i 2))))) + (cond ((and (<= start #x7f) (>= end #x3fff80)) + ;; Ranges between ASCII and raw bytes are split to + ;; avoid having them absorb Unicode characters + ;; caught in-between. + (push (cons start #x7f) intervals) + (push (cons #x3fff80 end) intervals)) + ((<= start end) + (push (cons start end) intervals)) + (t + (error "Invalid rx `any' range: %s" + (substring str i (+ i 3))))) + (setq i (+ i 3)))) + (t + ;; Single character. + (let ((char (funcall decode-char (aref str i)))) + (push (cons char char) intervals)) + (setq i (+ i 1))))) + intervals)) + +(defun rx--condense-intervals (intervals) + "Merge adjacent and overlapping intervals by mutation, preserving the order. +INTERVALS is a list of (START . END) with START ≤ END, sorted by START." + (let ((tail intervals) + d) + (while (setq d (cdr tail)) + (if (>= (cdar tail) (1- (caar d))) + (progn + (setcdr (car tail) (max (cdar tail) (cdar d))) + (setcdr tail (cdr d))) + (setq tail d))) + intervals)) + +(defun rx--parse-any (body) + "Parse arguments of an (any ...) construct. +Return (INTERVALS . CLASSES), where INTERVALS is a sorted list of +disjoint intervals (each a cons of chars), and CLASSES +a list of named character classes in the order they occur in BODY." + (let ((classes nil) + (strings nil) + (conses nil)) + ;; Collect strings, conses and characters, and classes in separate bins. + (dolist (arg body) + (cond ((stringp arg) + (push arg strings)) + ((and (consp arg) + (characterp (car arg)) + (characterp (cdr arg)) + (<= (car arg) (cdr arg))) + ;; Copy the cons, in case we need to modify it. + (push (cons (car arg) (cdr arg)) conses)) + ((characterp arg) + (push (cons arg arg) conses)) + ((and (symbolp arg) + (let ((class (cdr (assq arg rx--char-classes)))) + (and class + (or (memq class classes) + (progn (push class classes) t)))))) + (t (error "Invalid rx `any' argument: %s" arg)))) + (cons (rx--condense-intervals + (sort (append conses + (mapcan #'rx--string-to-intervals strings)) + #'car-less-than-car)) + (reverse classes)))) + +(defun rx--generate-alt (negated intervals classes) + "Generate a character alternative. Return (REGEXP . PRECEDENCE). +If NEGATED is non-nil, negate the result; INTERVALS is a sorted +list of disjoint intervals and CLASSES a list of named character +classes." + (let ((items (append intervals classes))) + ;; Move lone ] and range ]-x to the start. + (let ((rbrac-l (assq ?\] items))) + (when rbrac-l + (setq items (cons rbrac-l (delq rbrac-l items))))) + + ;; Split x-] and move the lone ] to the start. + (let ((rbrac-r (rassq ?\] items))) + (when (and rbrac-r (not (eq (car rbrac-r) ?\]))) + (setcdr rbrac-r ?\\) + (setq items (cons '(?\] . ?\]) items)))) + + ;; Split ,-- (which would end up as ,- otherwise). + (let ((dash-r (rassq ?- items))) + (when (eq (car dash-r) ?,) + (setcdr dash-r ?,) + (setq items (nconc items '((?- . ?-)))))) + + ;; Remove - (lone or at start of interval) + (let ((dash-l (assq ?- items))) + (when dash-l + (if (eq (cdr dash-l) ?-) + (setq items (delq dash-l items)) ; Remove lone - + (setcar dash-l ?.)) ; Reduce --x to .-x + (setq items (nconc items '((?- . ?-)))))) + + ;; Deal with leading ^ and range ^-x. + (when (and (consp (car items)) + (eq (caar items) ?^) + (cdr items)) + ;; Move ^ and ^-x to second place. + (setq items (cons (cadr items) + (cons (car items) (cddr items))))) + + (cond + ;; Empty set: if negated, any char, otherwise match-nothing. + ((null items) + (if negated + (rx--translate-symbol 'anything) + (rx--empty))) + ;; Single non-negated character. + ((and (null (cdr items)) + (consp (car items)) + (eq (caar items) (cdar items)) + (not negated)) + (cons (list (regexp-quote (char-to-string (caar items)))) + t)) + ;; Negated newline. + ((and (equal items '((?\n . ?\n))) + negated) + (rx--translate-symbol 'nonl)) + ;; At least one character or class, possibly negated. + (t + (cons + (list + (concat + "[" + (and negated "^") + (mapconcat (lambda (item) + (cond ((symbolp item) + (format "[:%s:]" item)) + ((eq (car item) (cdr item)) + (char-to-string (car item))) + ((eq (1+ (car item)) (cdr item)) + (string (car item) (cdr item))) + (t + (string (car item) ?- (cdr item))))) + items nil) + "]")) + t))))) + +(defun rx--translate-any (negated body) + "Translate an (any ...) construct. Return (REGEXP . PRECEDENCE). +If NEGATED, negate the sense." + (let ((parsed (rx--parse-any body))) + (rx--generate-alt negated (car parsed) (cdr parsed)))) + +(defun rx--intervals-to-alt (negated intervals) + "Generate a character alternative from an interval set. +Return (REGEXP . PRECEDENCE). +INTERVALS is a sorted list of disjoint intervals. +If NEGATED, negate the sense." + ;; Detect whether the interval set is better described in + ;; complemented form. This is not just a matter of aesthetics: any + ;; range from ASCII to raw bytes will automatically exclude the + ;; entire non-ASCII Unicode range by the regexp engine. + (if (rx--every (lambda (iv) (not (<= (car iv) #x3ffeff (cdr iv)))) + intervals) + (rx--generate-alt negated intervals nil) + (rx--generate-alt + (not negated) (rx--complement-intervals intervals) nil))) + +;; FIXME: Consider turning `not' into a variadic operator, following SRE: +;; (not A B) = (not (or A B)) = (intersection (not A) (not B)), and +;; (not) = anychar. +;; Maybe allow singleton characters as arguments. + +(defun rx--translate-not (negated body) + "Translate a (not ...) construct. Return (REGEXP . PRECEDENCE). +If NEGATED, negate the sense (thus making it positive)." + (unless (and body (null (cdr body))) + (error "rx `not' form takes exactly one argument")) + (let ((arg (car body))) + (cond + ((and (consp arg) + (pcase (car arg) + ((or 'any 'in 'char) + (rx--translate-any (not negated) (cdr arg))) + ('syntax + (rx--translate-syntax (not negated) (cdr arg))) + ('category + (rx--translate-category (not negated) (cdr arg))) + ('not + (rx--translate-not (not negated) (cdr arg))) + ((or 'or '|) + (rx--translate-union (not negated) (cdr arg))) + ('intersection + (rx--translate-intersection (not negated) (cdr arg)))))) + ((let ((class (cdr (assq arg rx--char-classes)))) + (and class + (rx--generate-alt (not negated) nil (list class))))) + ((eq arg 'word-boundary) + (rx--translate-symbol + (if negated 'word-boundary 'not-word-boundary))) + ((characterp arg) + (rx--generate-alt (not negated) (list (cons arg arg)) nil)) + ((and (stringp arg) (= (length arg) 1)) + (let ((char (string-to-char arg))) + (rx--generate-alt (not negated) (list (cons char char)) nil))) + ((let ((expanded (rx--expand-def arg))) + (and expanded + (rx--translate-not negated (list expanded))))) + (t (error "Illegal argument to rx `not': %S" arg))))) + +(defun rx--complement-intervals (intervals) + "Complement of the interval list INTERVALS." + (let ((compl nil) + (c 0)) + (dolist (iv intervals) + (when (< c (car iv)) + (push (cons c (1- (car iv))) compl)) + (setq c (1+ (cdr iv)))) + (when (< c (max-char)) + (push (cons c (max-char)) compl)) + (nreverse compl))) + +(defun rx--intersect-intervals (ivs-a ivs-b) + "Intersection of the interval lists IVS-A and IVS-B." + (let ((isect nil)) + (while (and ivs-a ivs-b) + (let ((a (car ivs-a)) + (b (car ivs-b))) + (cond + ((< (cdr a) (car b)) (setq ivs-a (cdr ivs-a))) + ((> (car a) (cdr b)) (setq ivs-b (cdr ivs-b))) + (t + (push (cons (max (car a) (car b)) + (min (cdr a) (cdr b))) + isect) + (setq ivs-a (cdr ivs-a)) + (setq ivs-b (cdr ivs-b)) + (cond ((< (cdr a) (cdr b)) + (push (cons (1+ (cdr a)) (cdr b)) + ivs-b)) + ((> (cdr a) (cdr b)) + (push (cons (1+ (cdr b)) (cdr a)) + ivs-a))))))) + (nreverse isect))) + +(defun rx--union-intervals (ivs-a ivs-b) + "Union of the interval lists IVS-A and IVS-B." + (rx--complement-intervals + (rx--intersect-intervals + (rx--complement-intervals ivs-a) + (rx--complement-intervals ivs-b)))) + +(defun rx--charset-intervals (charset) + "Return a sorted list of non-adjacent disjoint intervals from CHARSET. +CHARSET is any expression allowed in a character set expression: +characters, single-char strings, `any' forms (no classes permitted), +or `not', `or' or `intersection' forms whose arguments are charsets." + (pcase charset + (`(,(or 'any 'in 'char) . ,body) + (let ((parsed (rx--parse-any body))) + (when (cdr parsed) + (error + "Character class not permitted in set operations: %S" + (cadr parsed))) + (car parsed))) + (`(not ,x) (rx--complement-intervals (rx--charset-intervals x))) + (`(,(or 'or '|) . ,body) (rx--charset-union body)) + (`(intersection . ,body) (rx--charset-intersection body)) + ((pred characterp) + (list (cons charset charset))) + ((guard (and (stringp charset) (= (length charset) 1))) + (let ((char (string-to-char charset))) + (list (cons char char)))) + (_ (let ((expanded (rx--expand-def charset))) + (if expanded + (rx--charset-intervals expanded) + (error "Bad character set: %S" charset)))))) + +(defun rx--charset-union (charsets) + "Union of CHARSETS, as a set of intervals." + (rx--foldl #'rx--union-intervals nil + (mapcar #'rx--charset-intervals charsets))) + +(defconst rx--charset-all (list (cons 0 (max-char)))) + +(defun rx--charset-intersection (charsets) + "Intersection of CHARSETS, as a set of intervals." + (rx--foldl #'rx--intersect-intervals rx--charset-all + (mapcar #'rx--charset-intervals charsets))) + +(defun rx--translate-union (negated body) + "Translate an (or ...) construct of charsets. Return (REGEXP . PRECEDENCE). +If NEGATED, negate the sense." + (rx--intervals-to-alt negated (rx--charset-union body))) + +(defun rx--translate-intersection (negated body) + "Translate an (intersection ...) construct. Return (REGEXP . PRECEDENCE). +If NEGATED, negate the sense." + (rx--intervals-to-alt negated (rx--charset-intersection body))) + +(defun rx--atomic-regexp (item) + "ITEM is (REGEXP . PRECEDENCE); return a regexp of precedence t." + (if (eq (cdr item) t) + (car item) + (rx--bracket (car item)))) + +(defun rx--translate-counted-repetition (min-count max-count body) + (let ((operand (rx--translate-seq body))) + (if (car operand) + (cons (append + (rx--atomic-regexp operand) + (list (concat "\\{" + (number-to-string min-count) + (cond ((null max-count) ",") + ((< min-count max-count) + (concat "," (number-to-string max-count)))) + "\\}"))) + t) + operand))) + +(defun rx--check-repeat-arg (name min-args body) + (unless (>= (length body) min-args) + (error "rx `%s' requires at least %d argument%s" + name min-args (if (= min-args 1) "" "s"))) + ;; There seems to be no reason to disallow zero counts. + (unless (natnump (car body)) + (error "rx `%s' first argument must be nonnegative" name))) + +(defun rx--translate-bounded-repetition (name body) + (let ((min-count (car body)) + (max-count (cadr body)) + (items (cddr body))) + (unless (and (natnump min-count) + (natnump max-count) + (<= min-count max-count)) + (error "rx `%s' range error" name)) + (rx--translate-counted-repetition min-count max-count items))) + +(defun rx--translate-repeat (body) + (rx--check-repeat-arg 'repeat 2 body) + (if (= (length body) 2) + (rx--translate-counted-repetition (car body) (car body) (cdr body)) + (rx--translate-bounded-repetition 'repeat body))) + +(defun rx--translate-** (body) + (rx--check-repeat-arg '** 2 body) + (rx--translate-bounded-repetition '** body)) + +(defun rx--translate->= (body) + (rx--check-repeat-arg '>= 1 body) + (rx--translate-counted-repetition (car body) nil (cdr body))) + +(defun rx--translate-= (body) + (rx--check-repeat-arg '= 1 body) + (rx--translate-counted-repetition (car body) (car body) (cdr body))) + +(defvar rx--greedy t) + +(defun rx--translate-rep (op-string greedy body) + "Translate a repetition; OP-STRING is one of \"*\", \"+\" or \"?\". +GREEDY is a boolean. Return (REGEXP . PRECEDENCE)." + (let ((operand (rx--translate-seq body))) + (if (car operand) + (cons (append (rx--atomic-regexp operand) + (list (concat op-string (unless greedy "?")))) + ;; The result has precedence seq to avoid (? (* "a")) -> "a*?" + 'seq) + operand))) + +(defun rx--control-greedy (greedy body) + "Translate the sequence BODY with greediness GREEDY. +Return (REGEXP . PRECEDENCE)." + (let ((rx--greedy greedy)) + (rx--translate-seq body))) + +(defun rx--translate-group (body) + "Translate the `group' form. Return (REGEXP . PRECEDENCE)." + (cons (rx--enclose "\\(" + (car (rx--translate-seq body)) + "\\)") + t)) + +(defun rx--translate-group-n (body) + "Translate the `group-n' form. Return (REGEXP . PRECEDENCE)." + (unless (and (integerp (car body)) (> (car body) 0)) + (error "rx `group-n' requires a positive number as first argument")) + (cons (rx--enclose (concat "\\(?" (number-to-string (car body)) ":") + (car (rx--translate-seq (cdr body))) + "\\)") + t)) + +(defun rx--translate-backref (body) + "Translate the `backref' form. Return (REGEXP . PRECEDENCE)." + (unless (and (= (length body) 1) (integerp (car body)) (<= 1 (car body) 9)) + (error "rx `backref' requires an argument in the range 1..9")) + (cons (list "\\" (number-to-string (car body))) t)) + +(defconst rx--syntax-codes + '((whitespace . ?-) ; SPC also accepted + (punctuation . ?.) + (word . ?w) ; W also accepted + (symbol . ?_) + (open-parenthesis . ?\() + (close-parenthesis . ?\)) + (expression-prefix . ?\') + (string-quote . ?\") + (paired-delimiter . ?$) + (escape . ?\\) + (character-quote . ?/) + (comment-start . ?<) + (comment-end . ?>) + (string-delimiter . ?|) + (comment-delimiter . ?!))) + +(defun rx--translate-syntax (negated body) + "Translate the `syntax' form. Return (REGEXP . PRECEDENCE)." + (unless (and body (null (cdr body))) + (error "rx `syntax' form takes exactly one argument")) + (let* ((sym (car body)) + (syntax (cdr (assq sym rx--syntax-codes)))) + (unless syntax + (cond + ;; Syntax character directly (sregex compatibility) + ((and (characterp sym) (rassq sym rx--syntax-codes)) + (setq syntax sym)) + ;; Syntax character as symbol (sregex compatibility) + ((symbolp sym) + (let ((name (symbol-name sym))) + (when (= (length name) 1) + (let ((char (string-to-char name))) + (when (rassq char rx--syntax-codes) + (setq syntax char))))))) + (unless syntax + (error "Unknown rx syntax name `%s'" sym))) + (cons (list (string ?\\ (if negated ?S ?s) syntax)) + t))) + +(defconst rx--categories + '((space-for-indent . ?\s) + (base . ?.) + (consonant . ?0) + (base-vowel . ?1) + (upper-diacritical-mark . ?2) + (lower-diacritical-mark . ?3) + (tone-mark . ?4) + (symbol . ?5) + (digit . ?6) + (vowel-modifying-diacritical-mark . ?7) + (vowel-sign . ?8) + (semivowel-lower . ?9) + (not-at-end-of-line . ?<) + (not-at-beginning-of-line . ?>) + (alpha-numeric-two-byte . ?A) + (chinese-two-byte . ?C) + (chinse-two-byte . ?C) ; A typo in Emacs 21.1-24.3. + (greek-two-byte . ?G) + (japanese-hiragana-two-byte . ?H) + (indian-two-byte . ?I) + (japanese-katakana-two-byte . ?K) + (strong-left-to-right . ?L) + (korean-hangul-two-byte . ?N) + (strong-right-to-left . ?R) + (cyrillic-two-byte . ?Y) + (combining-diacritic . ?^) + (ascii . ?a) + (arabic . ?b) + (chinese . ?c) + (ethiopic . ?e) + (greek . ?g) + (korean . ?h) + (indian . ?i) + (japanese . ?j) + (japanese-katakana . ?k) + (latin . ?l) + (lao . ?o) + (tibetan . ?q) + (japanese-roman . ?r) + (thai . ?t) + (vietnamese . ?v) + (hebrew . ?w) + (cyrillic . ?y) + (can-break . ?|))) + +(defun rx--translate-category (negated body) + "Translate the `category' form. Return (REGEXP . PRECEDENCE)." + (unless (and body (null (cdr body))) + (error "rx `category' form takes exactly one argument")) + (let* ((arg (car body)) + (category + (cond ((symbolp arg) + (let ((cat (assq arg rx--categories))) + (unless cat + (error "Unknown rx category `%s'" arg)) + (cdr cat))) + ((characterp arg) arg) + (t (error "Invalid rx `category' argument `%s'" arg))))) + (cons (list (string ?\\ (if negated ?C ?c) category)) + t))) + +(defvar rx--delayed-evaluation nil + "Whether to allow certain forms to be evaluated at runtime.") + +(defun rx--translate-literal (body) + "Translate the `literal' form. Return (REGEXP . PRECEDENCE)." + (unless (and body (null (cdr body))) + (error "rx `literal' form takes exactly one argument")) + (let ((arg (car body))) + (cond ((stringp arg) + (cons (list (regexp-quote arg)) (if (= (length arg) 1) t 'seq))) + (rx--delayed-evaluation + (cons (list (list 'regexp-quote arg)) 'seq)) + (t (error "rx `literal' form with non-string argument"))))) + +(defun rx--expand-eval (body) + "Expand `eval' arguments. Return a new rx form." + (unless (and body (null (cdr body))) + (error "rx `eval' form takes exactly one argument")) + (eval (car body))) + +(defun rx--translate-eval (body) + "Translate the `eval' form. Return (REGEXP . PRECEDENCE)." + (rx--translate (rx--expand-eval body))) + +(defvar rx--regexp-atomic-regexp nil) + +(defun rx--translate-regexp (body) + "Translate the `regexp' form. Return (REGEXP . PRECEDENCE)." + (unless (and body (null (cdr body))) + (error "rx `regexp' form takes exactly one argument")) + (let ((arg (car body))) + (cond ((stringp arg) + ;; Generate the regexp when needed, since rx isn't + ;; necessarily present in the byte-compilation environment. + (unless rx--regexp-atomic-regexp + (setq rx--regexp-atomic-regexp + ;; Match atomic (precedence t) regexps: may give + ;; false negatives but no false positives, assuming + ;; the target string is syntactically correct. + (rx-to-string + '(seq + bos + (or (seq "[" + (opt "^") + (opt "]") + (* (or (seq "[:" (+ (any "a-z")) ":]") + (not (any "]")))) + "]") + (not (any "*+?^$[\\")) + (seq "\\" + (or anything + (seq (any "sScC_") anything) + (seq "(" + (* (or (not (any "\\")) + (seq "\\" (not (any ")"))))) + "\\)")))) + eos) + t))) + (cons (list arg) + (if (string-match-p rx--regexp-atomic-regexp arg) t nil))) + (rx--delayed-evaluation + (cons (list arg) nil)) + (t (error "rx `regexp' form with non-string argument"))))) + +(defun rx--translate-compat-form (def form) + "Translate a compatibility form from `rx-constituents'. +DEF is the definition tuple. Return (REGEXP . PRECEDENCE)." + (let* ((fn (nth 0 def)) + (min-args (nth 1 def)) + (max-args (nth 2 def)) + (predicate (nth 3 def)) + (nargs (1- (length form)))) + (when (< nargs min-args) + (error "The `%s' form takes at least %d argument(s)" + (car form) min-args)) + (when (and max-args (> nargs max-args)) + (error "The `%s' form takes at most %d argument(s)" + (car form) max-args)) + (when (and predicate (not (rx--every predicate (cdr form)))) + (error "The `%s' form requires arguments satisfying `%s'" + (car form) predicate)) + (let ((regexp (funcall fn form))) + (unless (stringp regexp) + (error "The `%s' form did not expand to a string" (car form))) + (cons (list regexp) nil)))) + +(defun rx--substitute (bindings form) + "Substitute BINDINGS in FORM. BINDINGS is an alist of (NAME . VALUES) +where VALUES is a list to splice into FORM wherever NAME occurs. +Return the substitution result wrapped in a list, since a single value +can expand to any number of values." + (cond ((symbolp form) + (let ((binding (assq form bindings))) + (if binding + (cdr binding) + (list form)))) + ((consp form) + (if (listp (cdr form)) + ;; Proper list. We substitute variables even in the head + ;; position -- who knows, might be handy one day. + (list (mapcan (lambda (x) (copy-sequence + (rx--substitute bindings x))) + form)) + ;; Cons pair (presumably an interval). + (let ((first (rx--substitute bindings (car form))) + (second (rx--substitute bindings (cdr form)))) + (if (and first (not (cdr first)) + second (not (cdr second))) + (list (cons (car first) (car second))) + (error + "Cannot substitute a &rest parameter into a dotted pair"))))) + (t (list form)))) + +;; FIXME: Consider adding extensions in Lisp macro style, where +;; arguments are passed unevaluated to code that returns the rx form +;; to use. Example: +;; +;; (rx-let ((radix-digit (radix) +;; :lisp (list 'any (cons ?0 (+ ?0 (eval radix) -1))))) +;; (rx (radix-digit (+ 5 3)))) +;; => +;; "[0-7]" +;; +;; While this would permit more powerful extensions, it's unclear just +;; how often they would be used in practice. Let's wait until there is +;; demand for it. + +;; FIXME: An alternative binding syntax would be +;; +;; (NAME RXs...) +;; and +;; ((NAME ARGS...) RXs...) +;; +;; which would have two minor advantages: multiple RXs with implicit +;; `seq' in the definition, and the arglist is no longer an optional +;; element in the middle of the list. On the other hand, it's less +;; like traditional lisp arglist constructs (defun, defmacro). +;; Since it's a Scheme-like syntax, &rest parameters could be done using +;; dotted lists: +;; (rx-let (((name arg1 arg2 . rest) ...definition...)) ...) + +(defun rx--expand-template (op values arglist template) + "Return TEMPLATE with variables in ARGLIST replaced with VALUES." + (let ((bindings nil) + (value-tail values) + (formals arglist)) + (while formals + (pcase (car formals) + ('&rest + (unless (cdr formals) + (error + "Expanding rx def `%s': missing &rest parameter name" op)) + (push (cons (cadr formals) value-tail) bindings) + (setq formals nil) + (setq value-tail nil)) + (name + (unless value-tail + (error + "Expanding rx def `%s': too few arguments (got %d, need %s%d)" + op (length values) + (if (memq '&rest arglist) "at least " "") + (- (length arglist) (length (memq '&rest arglist))))) + (push (cons name (list (car value-tail))) bindings) + (setq value-tail (cdr value-tail)))) + (setq formals (cdr formals))) + (when value-tail + (error + "Expanding rx def `%s': too many arguments (got %d, need %d)" + op (length values) (length arglist))) + (let ((subst (rx--substitute bindings template))) + (if (and subst (not (cdr subst))) + (car subst) + (error "Expanding rx def `%s': must result in a single value" op))))) + +(defun rx--translate-form (form) + "Translate an rx form (list structure). Return (REGEXP . PRECEDENCE)." + (let ((body (cdr form))) + (pcase (car form) + ((or 'seq : 'and 'sequence) (rx--translate-seq body)) + ((or 'or '|) (rx--translate-or body)) + ((or 'any 'in 'char) (rx--translate-any nil body)) + ('not-char (rx--translate-any t body)) + ('not (rx--translate-not nil body)) + ('intersection (rx--translate-intersection nil body)) + + ('repeat (rx--translate-repeat body)) + ('= (rx--translate-= body)) + ('>= (rx--translate->= body)) + ('** (rx--translate-** body)) + + ((or 'zero-or-more '0+) (rx--translate-rep "*" rx--greedy body)) + ((or 'one-or-more '1+) (rx--translate-rep "+" rx--greedy body)) + ((or 'zero-or-one 'opt 'optional) (rx--translate-rep "?" rx--greedy body)) + + ('* (rx--translate-rep "*" t body)) + ('+ (rx--translate-rep "+" t body)) + ((or '\? ?\s) (rx--translate-rep "?" t body)) + + ('*? (rx--translate-rep "*" nil body)) + ('+? (rx--translate-rep "+" nil body)) + ((or '\?? ??) (rx--translate-rep "?" nil body)) + + ('minimal-match (rx--control-greedy nil body)) + ('maximal-match (rx--control-greedy t body)) + + ((or 'group 'submatch) (rx--translate-group body)) + ((or 'group-n 'submatch-n) (rx--translate-group-n body)) + ('backref (rx--translate-backref body)) + + ('syntax (rx--translate-syntax nil body)) + ('not-syntax (rx--translate-syntax t body)) + ('category (rx--translate-category nil body)) + + ('literal (rx--translate-literal body)) + ('eval (rx--translate-eval body)) + ((or 'regexp 'regex) (rx--translate-regexp body)) + + (op + (cond + ((not (symbolp op)) (error "Bad rx operator `%S'" op)) + + ((let ((expanded (rx--expand-def form))) + (and expanded + (rx--translate expanded)))) + + ;; For compatibility with old rx. + ((let ((entry (assq op rx-constituents))) + (and (progn + (while (and entry (not (consp (cdr entry)))) + (setq entry + (if (symbolp (cdr entry)) + ;; Alias for another entry. + (assq (cdr entry) rx-constituents) + ;; Wrong type, try further down the list. + (assq (car entry) + (cdr (memq entry rx-constituents)))))) + entry) + (rx--translate-compat-form (cdr entry) form)))) + + (t (error "Unknown rx form `%s'" op))))))) + +(defconst rx--builtin-forms + '(seq sequence : and or | any in char not-char not intersection + repeat = >= ** + zero-or-more 0+ * + one-or-more 1+ + + zero-or-one opt optional \? + *? +? \?? + minimal-match maximal-match + group submatch group-n submatch-n backref + syntax not-syntax category + literal eval regexp regex) + "List of built-in rx function-like symbols.") + +(defconst rx--builtin-symbols + (append '(nonl not-newline any anychar anything unmatchable + bol eol line-start line-end + bos eos string-start string-end + bow eow word-start word-end + symbol-start symbol-end + point word-boundary not-word-boundary not-wordchar) + (mapcar #'car rx--char-classes)) + "List of built-in rx variable-like symbols.") + +(defconst rx--builtin-names + (append rx--builtin-forms rx--builtin-symbols) + "List of built-in rx names. These cannot be redefined by the user.") + +(defun rx--translate (item) + "Translate the rx-expression ITEM. Return (REGEXP . PRECEDENCE)." + (cond + ((stringp item) + (if (= (length item) 0) + (cons nil 'seq) + (cons (list (regexp-quote item)) (if (= (length item) 1) t 'seq)))) + ((characterp item) + (cons (list (regexp-quote (char-to-string item))) t)) + ((symbolp item) + (rx--translate-symbol item)) + ((consp item) + (rx--translate-form item)) + (t (error "Bad rx expression: %S" item)))) + + +(defun rx-to-string (form &optional no-group) + "Translate FORM from `rx' sexp syntax into a string regexp. +The arguments to `literal' and `regexp' forms inside FORM must be +constant strings. +If NO-GROUP is non-nil, don't bracket the result in a non-capturing +group. + +For extending the `rx' notation in FORM, use `rx-define' or `rx-let-eval'." + (let* ((item (rx--translate form)) + (exprs (if no-group + (car item) + (rx--atomic-regexp item)))) + (apply #'concat exprs))) + +(defun rx--to-expr (form) + "Translate the rx-expression FORM to a Lisp expression yielding a regexp." + (let* ((rx--delayed-evaluation t) + (elems (car (rx--translate form))) + (args nil)) + ;; Merge adjacent strings. + (while elems + (let ((strings nil)) + (while (and elems (stringp (car elems))) + (push (car elems) strings) + (setq elems (cdr elems))) + (let ((s (apply #'concat (nreverse strings)))) + (unless (zerop (length s)) + (push s args)))) + (when elems + (push (car elems) args) + (setq elems (cdr elems)))) + (cond ((null args) "") ; 0 args + ((cdr args) (cons 'concat (nreverse args))) ; ≥2 args + (t (car args))))) ; 1 arg + + +(defmacro rx (&rest regexps) + "Translate regular expressions REGEXPS in sexp form to a regexp string. +Each argument is one of the forms below; RX is a subform, and RX... stands +for zero or more RXs. For details, see Info node `(elisp) Rx Notation'. +See `rx-to-string' for the corresponding function. + +STRING Match a literal string. +CHAR Match a literal character. + +(seq RX...) Match the RXs in sequence. Alias: :, sequence, and. +(or RX...) Match one of the RXs. Alias: |. + +(zero-or-more RX...) Match RXs zero or more times. Alias: 0+. +(one-or-more RX...) Match RXs one or more times. Alias: 1+. +(zero-or-one RX...) Match RXs or the empty string. Alias: opt, optional. +(* RX...) Match RXs zero or more times; greedy. +(+ RX...) Match RXs one or more times; greedy. +(? RX...) Match RXs or the empty string; greedy. +(*? RX...) Match RXs zero or more times; non-greedy. +(+? RX...) Match RXs one or more times; non-greedy. +(?? RX...) Match RXs or the empty string; non-greedy. +(= N RX...) Match RXs exactly N times. +(>= N RX...) Match RXs N or more times. +(** N M RX...) Match RXs N to M times. Alias: repeat. +(minimal-match RX) Match RX, with zero-or-more, one-or-more, zero-or-one + and aliases using non-greedy matching. +(maximal-match RX) Match RX, with zero-or-more, one-or-more, zero-or-one + and aliases using greedy matching, which is the default. + +(any SET...) Match a character from one of the SETs. Each SET is a + character, a string, a range as string \"A-Z\" or cons + (?A . ?Z), or a character class (see below). Alias: in, char. +(not CHARSPEC) Match one character not matched by CHARSPEC. CHARSPEC + can be a character, single-char string, (any ...), (or ...), + (intersection ...), (syntax ...), (category ...), + or a character class. +(intersection CHARSET...) Match all CHARSETs. + CHARSET is (any...), (not...), (or...) or (intersection...), + a character or a single-char string. +not-newline Match any character except a newline. Alias: nonl. +anychar Match any character. Alias: anything. +unmatchable Never match anything at all. + +CHARCLASS Match a character from a character class. One of: + alpha, alphabetic, letter Alphabetic characters (defined by Unicode). + alnum, alphanumeric Alphabetic or decimal digit chars (Unicode). + digit, numeric, num 0-9. + xdigit, hex-digit, hex 0-9, A-F, a-f. + cntrl, control ASCII codes 0-31. + blank Horizontal whitespace (Unicode). + space, whitespace, white Chars with whitespace syntax. + lower, lower-case Lower-case chars, from current case table. + upper, upper-case Upper-case chars, from current case table. + graph, graphic Graphic characters (Unicode). + print, printing Whitespace or graphic (Unicode). + punct, punctuation Not control, space, letter or digit (ASCII); + not word syntax (non-ASCII). + word, wordchar Characters with word syntax. + ascii ASCII characters (codes 0-127). + nonascii Non-ASCII characters (but not raw bytes). + +(syntax SYNTAX) Match a character with syntax SYNTAX, being one of: + whitespace, punctuation, word, symbol, open-parenthesis, + close-parenthesis, expression-prefix, string-quote, + paired-delimiter, escape, character-quote, comment-start, + comment-end, string-delimiter, comment-delimiter + +(category CAT) Match a character in category CAT, being one of: + space-for-indent, base, consonant, base-vowel, + upper-diacritical-mark, lower-diacritical-mark, tone-mark, symbol, + digit, vowel-modifying-diacritical-mark, vowel-sign, + semivowel-lower, not-at-end-of-line, not-at-beginning-of-line, + alpha-numeric-two-byte, chinese-two-byte, greek-two-byte, + japanese-hiragana-two-byte, indian-two-byte, + japanese-katakana-two-byte, strong-left-to-right, + korean-hangul-two-byte, strong-right-to-left, cyrillic-two-byte, + combining-diacritic, ascii, arabic, chinese, ethiopic, greek, + korean, indian, japanese, japanese-katakana, latin, lao, + tibetan, japanese-roman, thai, vietnamese, hebrew, cyrillic, + can-break + +Zero-width assertions: these all match the empty string in specific places. + line-start At the beginning of a line. Alias: bol. + line-end At the end of a line. Alias: eol. + string-start At the start of the string or buffer. + Alias: buffer-start, bos, bot. + string-end At the end of the string or buffer. + Alias: buffer-end, eos, eot. + point At point. + word-start At the beginning of a word. Alias: bow. + word-end At the end of a word. Alias: eow. + word-boundary At the beginning or end of a word. + not-word-boundary Not at the beginning or end of a word. + symbol-start At the beginning of a symbol. + symbol-end At the end of a symbol. + +(group RX...) Match RXs and define a capture group. Alias: submatch. +(group-n N RX...) Match RXs and define capture group N. Alias: submatch-n. +(backref N) Match the text that capture group N matched. + +(literal EXPR) Match the literal string from evaluating EXPR at run time. +(regexp EXPR) Match the string regexp from evaluating EXPR at run time. +(eval EXPR) Match the rx sexp from evaluating EXPR at macro-expansion + (compile) time. + +Additional constructs can be defined using `rx-define' and `rx-let', +which see. + +\(fn REGEXPS...)" + ;; Retrieve local definitions from the macroexpansion environment. + ;; (It's unclear whether the previous value of `rx--local-definitions' + ;; should be included, and if so, in which order.) + (let ((rx--local-definitions + (cdr (assq :rx-locals macroexpand-all-environment)))) + (rx--to-expr (cons 'seq regexps)))) + +(defun rx--make-binding (name tail) + "Make a definitions entry out of TAIL. +TAIL is on the form ([ARGLIST] DEFINITION)." + (unless (symbolp name) + (error "Bad `rx' definition name: %S" name)) + ;; FIXME: Consider using a hash table or symbol property, for speed. + (when (memq name rx--builtin-names) + (error "Cannot redefine built-in rx name `%s'" name)) + (pcase tail + (`(,def) + (list def)) + (`(,args ,def) + (unless (and (listp args) (rx--every #'symbolp args)) + (error "Bad argument list for `rx' definition %s: %S" name args)) + (list args def)) + (_ (error "Bad `rx' definition of %s: %S" name tail)))) + +(defun rx--make-named-binding (bindspec) + "Make a definitions entry out of BINDSPEC. +BINDSPEC is on the form (NAME [ARGLIST] DEFINITION)." + (unless (consp bindspec) + (error "Bad `rx-let' binding: %S" bindspec)) + (cons (car bindspec) + (rx--make-binding (car bindspec) (cdr bindspec)))) + +(defun rx--extend-local-defs (bindspecs) + (append (mapcar #'rx--make-named-binding bindspecs) + rx--local-definitions)) + +(defmacro rx-let-eval (bindings &rest body) + "Evaluate BODY with local BINDINGS for `rx-to-string'. +BINDINGS, after evaluation, is a list of definitions each on the form +(NAME [(ARGS...)] RX), in effect for calls to `rx-to-string' +in BODY. + +For bindings without an ARGS list, NAME is defined as an alias +for the `rx' expression RX. Where ARGS is supplied, NAME is +defined as an `rx' form with ARGS as argument list. The +parameters are bound from the values in the (NAME ...) form and +are substituted in RX. ARGS can contain `&rest' parameters, +whose values are spliced into RX where the parameter name occurs. + +Any previous definitions with the same names are shadowed during +the expansion of BODY only. +For extensions when using the `rx' macro, use `rx-let'. +To make global rx extensions, use `rx-define'. +For more details, see Info node `(elisp) Extending Rx'. + +\(fn BINDINGS BODY...)" + (declare (indent 1) (debug (form body))) + ;; FIXME: this way, `rx--extend-local-defs' may need to be autoloaded. + `(let ((rx--local-definitions (rx--extend-local-defs ,bindings))) + ,@body)) + +(defmacro rx-let (bindings &rest body) + "Evaluate BODY with local BINDINGS for `rx'. +BINDINGS is an unevaluated list of bindings each on the form +(NAME [(ARGS...)] RX). +They are bound lexically and are available in `rx' expressions in +BODY only. + +For bindings without an ARGS list, NAME is defined as an alias +for the `rx' expression RX. Where ARGS is supplied, NAME is +defined as an `rx' form with ARGS as argument list. The +parameters are bound from the values in the (NAME ...) form and +are substituted in RX. ARGS can contain `&rest' parameters, +whose values are spliced into RX where the parameter name occurs. + +Any previous definitions with the same names are shadowed during +the expansion of BODY only. +For local extensions to `rx-to-string', use `rx-let-eval'. +To make global rx extensions, use `rx-define'. +For more details, see Info node `(elisp) Extending Rx'. + +\(fn BINDINGS BODY...)" + (declare (indent 1) (debug (sexp body))) + (let ((prev-locals (cdr (assq :rx-locals macroexpand-all-environment))) + (new-locals (mapcar #'rx--make-named-binding bindings))) + (macroexpand-all (cons 'progn body) + (cons (cons :rx-locals (append new-locals prev-locals)) + macroexpand-all-environment)))) + +(defmacro rx-define (name &rest definition) + "Define NAME as a global `rx' definition. +If the ARGS list is omitted, define NAME as an alias for the `rx' +expression RX. + +If the ARGS list is supplied, define NAME as an `rx' form with +ARGS as argument list. The parameters are bound from the values +in the (NAME ...) form and are substituted in RX. +ARGS can contain `&rest' parameters, whose values are spliced +into RX where the parameter name occurs. + +Any previous global definition of NAME is overwritten with the new one. +To make local rx extensions, use `rx-let' for `rx', +`rx-let-eval' for `rx-to-string'. +For more details, see Info node `(elisp) Extending Rx'. + +\(fn NAME [(ARGS...)] RX)" + (declare (indent defun)) + `(eval-and-compile + (put ',name 'rx-definition ',(rx--make-binding name definition)) + ',name)) + +;; During `rx--pcase-transform', list of defined variables in right-to-left +;; order. +(defvar rx--pcase-vars) + +;; FIXME: The rewriting strategy for pcase works so-so with extensions; +;; definitions cannot expand to `let' or named `backref'. If this ever +;; becomes a problem, we can handle those forms in the ordinary parser, +;; using a dynamic variable for activating the augmented forms. + +(defun rx--pcase-transform (rx) + "Transform RX, an rx-expression augmented with `let' and named `backref', +into a plain rx-expression, collecting names into `rx--pcase-vars'." + (pcase rx + (`(let ,name . ,body) + (let* ((index (length (memq name rx--pcase-vars))) + (i (if (zerop index) + (length (push name rx--pcase-vars)) + index))) + `(group-n ,i ,(rx--pcase-transform (cons 'seq body))))) + ((and `(backref ,ref) + (guard (symbolp ref))) + (let ((index (length (memq ref rx--pcase-vars)))) + (when (zerop index) + (error "rx `backref' variable must be one of: %s" + (mapconcat #'symbol-name rx--pcase-vars " "))) + `(backref ,index))) + ((and `(,head . ,rest) + (guard (and (or (symbolp head) (memq head '(?\s ??))) + (not (memq head '(literal regexp regex eval)))))) + (cons head (mapcar #'rx--pcase-transform rest))) + (_ rx))) + +(defun rx--reduce-right (f l) + "Right-reduction on L by F. L must be non-empty." + (if (cdr l) + (funcall f (car l) (rx--reduce-right f (cdr l))) + (car l))) + +(pcase-defmacro rx (&rest regexps) + "A pattern that matches strings against `rx' REGEXPS in sexp form. +REGEXPS are interpreted as in `rx'. The pattern matches any +string that is a match for REGEXPS, as if by `string-match'. + +In addition to the usual `rx' syntax, REGEXPS can contain the +following constructs: + + (let REF RX...) binds the symbol REF to a submatch that matches + the regular expressions RX. REF is bound in + CODE to the string of the submatch or nil, but + can also be used in `backref'. + (backref REF) matches whatever the submatch REF matched. + REF can be a number, as usual, or a name + introduced by a previous (let REF ...) + construct." + (let* ((rx--pcase-vars nil) + (regexp (rx--to-expr (rx--pcase-transform (cons 'seq regexps))))) + `(and (pred stringp) + ,(pcase (length rx--pcase-vars) + (0 + ;; No variables bound: a single predicate suffices. + `(pred (string-match ,regexp))) + (1 + ;; Create a match value that on a successful regexp match + ;; is the submatch value, 0 on failure. We can't use nil + ;; for failure because it is a valid submatch value. + `(app (lambda (s) + (if (string-match ,regexp s) + (match-string 1 s) + 0)) + (and ,(car rx--pcase-vars) (pred (not numberp))))) + (nvars + ;; Pack the submatches into a dotted list which is then + ;; immediately destructured into individual variables again. + ;; This is of course slightly inefficient. + ;; A dotted list is used to reduce the number of conses + ;; to create and take apart. + `(app (lambda (s) + (and (string-match ,regexp s) + ,(rx--reduce-right + (lambda (a b) `(cons ,a ,b)) + (mapcar (lambda (i) `(match-string ,i s)) + (number-sequence 1 nvars))))) + ,(list '\` + (rx--reduce-right + #'cons + (mapcar (lambda (name) (list '\, name)) + (reverse rx--pcase-vars)))))))))) + +;; Obsolete internal symbol, used in old versions of the `flycheck' package. +(define-obsolete-function-alias 'rx-submatch-n 'rx-to-string "27.1") + +;; python-components-extra + +(defun py-util-comint-last-prompt () + "Return comint last prompt overlay start and end. +This is for compatibility with Emacs < 24.4." + (cond ((bound-and-true-p comint-last-prompt-overlay) + (cons (overlay-start comint-last-prompt-overlay) + (overlay-end comint-last-prompt-overlay))) + ((bound-and-true-p comint-last-prompt) + comint-last-prompt) + (t nil))) + +(defun py-shell-accept-process-output (process &optional timeout regexp) + "Accept PROCESS output with TIMEOUT until REGEXP is found. +Optional argument TIMEOUT is the timeout argument to +`accept-process-output' calls. Optional argument REGEXP +overrides the regexp to match the end of output, defaults to +`comint-prompt-regexp'. Returns non-nil when output was +properly captured. + +This utility is useful in situations where the output may be +received in chunks, since `accept-process-output' gives no +guarantees they will be grabbed in a single call. An example use +case for this would be the CPython shell start-up, where the +banner and the initial prompt are received separately." + (let ((regexp (or regexp comint-prompt-regexp))) + (catch 'found + (while t + (when (not (accept-process-output process timeout)) + (throw 'found nil)) + (when (looking-back + regexp (car (py-util-comint-last-prompt))) + (throw 'found t)))))) + +(defun py-shell-completion-get-completions (process import input) + "Do completion at point using PROCESS for IMPORT or INPUT. +When IMPORT is non-nil takes precedence over INPUT for +completion." + (setq input (or import input)) + (with-current-buffer (process-buffer process) + (let ((completions + (ignore-errors + (py--string-trim + (py-send-string-no-output + (format + (concat py-completion-setup-code + "\nprint (" py-shell-completion-string-code ")") + input) process (buffer-name (current-buffer))))))) + (when (> (length completions) 2) + (split-string completions + "^'\\|^\"\\|;\\|'$\\|\"$" t))))) + +(defun py-shell-completion-at-point (&optional process) + "Function for `completion-at-point-functions' in `py-shell-mode'. +Optional argument PROCESS forces completions to be retrieved +using that one instead of current buffer's process." + ;; (setq process (or process (get-buffer-process (current-buffer)))) + (let* + ((process (or process (get-buffer-process (current-buffer)))) + (line-start (if (derived-mode-p 'py-shell-mode) + ;; Working on a shell buffer: use prompt end. + (or (cdr (py-util-comint-last-prompt)) + (line-beginning-position)) + (line-beginning-position))) + (import-statement + (when (string-match-p + (rx (* space) word-start (or "from" "import") word-end space) + (buffer-substring-no-properties line-start (point))) + (buffer-substring-no-properties line-start (point)))) + (start + (save-excursion + (if (not (re-search-backward + ;; (py-rx + ;; (or whitespace open-paren close-paren string-delimiter simple-operator)) + "[[:space:]]\\|[([{]\\|[])}]\\|\\(?:[^\"'\\]\\|\\=\\|\\(?:[^\\]\\|\\=\\)\\\\\\(?:\\\\\\\\\\)*[\"']\\)\\(?:\\\\\\\\\\)*\\(\\(?:\"\"\"\\|'''\\|[\"']\\)\\)\\|[%&*+/<->^|~-]" + line-start + t 1)) + line-start + (forward-char (length (match-string-no-properties 0))) + (point)))) + (end (point)) + (completion-fn + (with-current-buffer (process-buffer process) + #'py-shell-completion-get-completions))) + (list start end + (completion-table-dynamic + (apply-partially + completion-fn + process import-statement))))) + +(defun py-comint-watch-for-first-prompt-output-filter (output) + "Run `py-shell-first-prompt-hook' when first prompt is found in OUTPUT." + (when (not py-shell--first-prompt-received) + (set (make-local-variable 'py-shell--first-prompt-received-output-buffer) + (concat py-shell--first-prompt-received-output-buffer + (ansi-color-filter-apply output))) + (when (py-shell-comint-end-of-output-p + py-shell--first-prompt-received-output-buffer) + (if (string-match-p + (concat py-shell-prompt-pdb-regexp (rx eos)) + (or py-shell--first-prompt-received-output-buffer "")) + ;; Skip pdb prompts and reset the buffer. + (setq py-shell--first-prompt-received-output-buffer nil) + (set (make-local-variable 'py-shell--first-prompt-received) t) + (setq py-shell--first-prompt-received-output-buffer nil) + (with-current-buffer (current-buffer) + (let ((inhibit-quit nil)) + (run-hooks 'py-shell-first-prompt-hook)))))) + output) + +(defun py-shell-font-lock-get-or-create-buffer () + "Get or create a font-lock buffer for current inferior process." + (with-current-buffer (current-buffer) + (if py-shell--font-lock-buffer + py-shell--font-lock-buffer + (let ((process-name + (process-name (get-buffer-process (current-buffer))))) + (generate-new-buffer + (format " *%s-font-lock*" process-name)))))) + +(defun py-font-lock-kill-buffer () + "Kill the font-lock buffer safely." + (when (and py-shell--font-lock-buffer + (buffer-live-p py-shell--font-lock-buffer)) + (kill-buffer py-shell--font-lock-buffer) + (when (derived-mode-p 'py-shell-mode) + (setq py-shell--font-lock-buffer nil)))) + +(defmacro py-shell-font-lock-with-font-lock-buffer (&rest body) + "Execute the forms in BODY in the font-lock buffer. +The value returned is the value of the last form in BODY. See +also `with-current-buffer'." + (declare (indent 0) (debug t)) + `(save-current-buffer + (when (not (and py-shell--font-lock-buffer + (get-buffer py-shell--font-lock-buffer))) + (setq py-shell--font-lock-buffer + (py-shell-font-lock-get-or-create-buffer))) + (set-buffer py-shell--font-lock-buffer) + (when (not font-lock-mode) + (font-lock-mode 1)) + (set (make-local-variable 'delay-mode-hooks) t) + (let (py-smart-indentation) + (when (not (derived-mode-p 'python-mode)) + (python-mode)) + ,@body))) + +(defun py-shell-font-lock-cleanup-buffer () + "Cleanup the font-lock buffer. +Provided as a command because this might be handy if something +goes wrong and syntax highlighting in the shell gets messed up." + (interactive) + (with-current-buffer (current-buffer) + (py-shell-font-lock-with-font-lock-buffer + (erase-buffer)))) + +(defun py-shell-font-lock-comint-output-filter-function (output) + "Clean up the font-lock buffer after any OUTPUT." + (if (and (not (string= "" output)) + ;; Is end of output and is not just a prompt. + (not (member + (py-shell-comint-end-of-output-p + (ansi-color-filter-apply output)) + '(nil 0)))) + ;; If output is other than an input prompt then "real" output has + ;; been received and the font-lock buffer must be cleaned up. + (py-shell-font-lock-cleanup-buffer) + ;; Otherwise just add a newline. + (py-shell-font-lock-with-font-lock-buffer + (goto-char (point-max)) + (newline 1))) + output) + +(defun py-font-lock-post-command-hook () + "Fontifies current line in shell buffer." + (let ((prompt-end + (or (cdr (py-util-comint-last-prompt)) + (progn (sit-for 0.1) + (cdr (py-util-comint-last-prompt)))))) + (when (and prompt-end (> (point) prompt-end) + (process-live-p (get-buffer-process (current-buffer)))) + (let* ((input (buffer-substring-no-properties + prompt-end (point-max))) + (deactivate-mark nil) + (start-pos prompt-end) + (buffer-undo-list t) + (font-lock-buffer-pos nil) + (replacement + (py-shell-font-lock-with-font-lock-buffer + (delete-region (line-beginning-position) + (point-max)) + (setq font-lock-buffer-pos (point)) + (insert input) + ;; Ensure buffer is fontified, keeping it + ;; compatible with Emacs < 24.4. + (when py-shell-fontify-p + (if (fboundp 'font-lock-ensure) + (funcall 'font-lock-ensure) + (font-lock-default-fontify-buffer))) + (buffer-substring font-lock-buffer-pos + (point-max)))) + (replacement-length (length replacement)) + (i 0)) + ;; Inject text properties to get input fontified. + (while (not (= i replacement-length)) + (let* ((plist (text-properties-at i replacement)) + (next-change (or (next-property-change i replacement) + replacement-length)) + (plist (let ((face (plist-get plist 'face))) + (if (not face) + plist + ;; Replace FACE text properties with + ;; FONT-LOCK-FACE so input is fontified. + (plist-put plist 'face nil) + (plist-put plist 'font-lock-face face))))) + (set-text-properties + (+ start-pos i) (+ start-pos next-change) plist) + (setq i next-change))))))) + +(defun py-shell-font-lock-turn-on (&optional msg) + "Turn on shell font-lock. +With argument MSG show activation message." + (interactive "p") + (save-current-buffer + (py-font-lock-kill-buffer) + (set (make-local-variable 'py-shell--font-lock-buffer) nil) + (add-hook 'post-command-hook + #'py-font-lock-post-command-hook nil 'local) + (add-hook 'kill-buffer-hook + #'py-font-lock-kill-buffer nil 'local) + (add-hook 'comint-output-filter-functions + #'py-shell-font-lock-comint-output-filter-function + 'append 'local) + (when msg + (message "Shell font-lock is enabled")))) + +(defun py-shell-font-lock-turn-off (&optional msg) + "Turn off shell font-lock. +With argument MSG show deactivation message." + (interactive "p") + (with-current-buffer (current-buffer) + (py-font-lock-kill-buffer) + (when (py-util-comint-last-prompt) + ;; Cleanup current fontification + (remove-text-properties + (cdr (py-util-comint-last-prompt)) + (line-end-position) + '(face nil font-lock-face nil))) + (set (make-local-variable 'py-shell--font-lock-buffer) nil) + (remove-hook 'post-command-hook + #'py-font-lock-post-command-hook 'local) + (remove-hook 'kill-buffer-hook + #'py-font-lock-kill-buffer 'local) + (remove-hook 'comint-output-filter-functions + #'py-shell-font-lock-comint-output-filter-function + 'local) + (when msg + (message "Shell font-lock is disabled")))) + +(defun py-shell-font-lock-toggle (&optional msg) + "Toggle font-lock for shell. +With argument MSG show activation/deactivation message." + (interactive "p") + (with-current-buffer (current-buffer) + (set (make-local-variable 'py-shell-fontify-p) + (not py-shell-fontify-p)) + (if py-shell-fontify-p + (py-shell-font-lock-turn-on msg) + (py-shell-font-lock-turn-off msg)) + py-shell-fontify-p)) + +(when (featurep 'comint-mime) + (defun comint-mime-setup-py-shell () + "Enable `comint-mime'. + +Setup code specific to `py-shell-mode'." + (interactive) + ;; (if (not py-shell--first-prompt-received) + ;; (add-hook 'py-shell-first-prompt-hook #'comint-mime-setup-py-shell nil t) + (setq py-python-command "ipython3" + py-ipython-command "ipython3" + py-ipython-command-args '("--pylab" "--matplotlib=inline" "--automagic" "--simple-prompt") + py-python-command-args '("--pylab" "--matplotlib=inline" "--automagic" "--simple-prompt")) + (py-send-string-no-output + (format "%s\n__COMINT_MIME_setup('''%s''')" + (with-temp-buffer + (switch-to-buffer (current-buffer)) + (insert-file-contents + (expand-file-name "comint-mime.py" + comint-mime-setup-script-dir)) + (buffer-string)) + (if (listp comint-mime-enabled-types) + (string-join comint-mime-enabled-types ";") + comint-mime-enabled-types)))) + + (add-hook 'py-shell-mode-hook 'comint-mime-setup-py-shell) + (push '(py-shell-mode . comint-mime-setup-py-shell) + comint-mime-setup-function-alist) + ;; (setq py-python-command "ipython3" + ;; py-ipython-command "ipython3" + ;; py-python-command-args '("--pylab" "--matplotlib=inline" "--automagic" "--simple-prompt") + ;; ;; "-i" doesn't work with `isympy3' + ;; py-ipython-command-args '("--pylab" "--matplotlib=inline" "--automagic" "--simple-prompt")) + ) + +;; python-components-shift-forms + + +(defun py-shift-left (&optional count start end) + "Dedent region according to `py-indent-offset' by COUNT times. + +If no region is active, current line is dedented. +Return indentation reached +Optional COUNT: COUNT times `py-indent-offset' +Optional START: region beginning +Optional END: region end" + (interactive "p") + (py--shift-intern (- count) start end)) + +(defun py-shift-right (&optional count beg end) + "Indent region according to `py-indent-offset' by COUNT times. + +If no region is active, current line is indented. +Return indentation reached +Optional COUNT: COUNT times `py-indent-offset' +Optional BEG: region beginning +Optional END: region end" + (interactive "p") + (py--shift-intern count beg end)) + +(defun py--shift-intern (count &optional start end) + (save-excursion + (let* ((inhibit-point-motion-hooks t) + deactivate-mark + (beg (cond (start) + ((use-region-p) + (save-excursion + (goto-char + (region-beginning)))) + (t (line-beginning-position)))) + (end (cond (end) + ((use-region-p) + (save-excursion + (goto-char + (region-end)))) + (t (line-end-position))))) + (setq beg (copy-marker beg)) + (setq end (copy-marker end)) + (if (< 0 count) + (indent-rigidly beg end py-indent-offset) + (indent-rigidly beg end (- py-indent-offset))) + (push-mark beg t) + (goto-char end) + (skip-chars-backward " \t\r\n\f")) + (py-indentation-of-statement))) + +(defun py--shift-forms-base (form arg &optional beg end) + (let* ((begform (intern-soft (concat "py-backward-" form))) + (endform (intern-soft (concat "py-forward-" form))) + (orig (copy-marker (point))) + (beg (cond (beg) + ((use-region-p) + (save-excursion + (goto-char (region-beginning)) + (line-beginning-position))) + (t (save-excursion + (funcall begform) + (line-beginning-position))))) + (end (cond (end) + ((use-region-p) + (region-end)) + (t (funcall endform)))) + (erg (py--shift-intern arg beg end))) + (goto-char orig) + erg)) + +(defun py-shift-block-right (&optional arg) + "Indent block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "block" (or arg py-indent-offset))) + +(defun py-shift-block-left (&optional arg) + "Dedent block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "block" (- (or arg py-indent-offset)))) + +(defun py-shift-block-or-clause-right (&optional arg) + "Indent block-or-clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "block-or-clause" (or arg py-indent-offset))) + +(defun py-shift-block-or-clause-left (&optional arg) + "Dedent block-or-clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "block-or-clause" (- (or arg py-indent-offset)))) + +(defun py-shift-class-right (&optional arg) + "Indent class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "class" (or arg py-indent-offset))) + +(defun py-shift-class-left (&optional arg) + "Dedent class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "class" (- (or arg py-indent-offset)))) + +(defun py-shift-clause-right (&optional arg) + "Indent clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "clause" (or arg py-indent-offset))) + +(defun py-shift-clause-left (&optional arg) + "Dedent clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "clause" (- (or arg py-indent-offset)))) + +(defun py-shift-comment-right (&optional arg) + "Indent comment by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "comment" (or arg py-indent-offset))) + +(defun py-shift-comment-left (&optional arg) + "Dedent comment by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "comment" (- (or arg py-indent-offset)))) + +(defun py-shift-def-right (&optional arg) + "Indent def by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "def" (or arg py-indent-offset))) + +(defun py-shift-def-left (&optional arg) + "Dedent def by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "def" (- (or arg py-indent-offset)))) + +(defun py-shift-def-or-class-right (&optional arg) + "Indent def-or-class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "def-or-class" (or arg py-indent-offset))) + +(defun py-shift-def-or-class-left (&optional arg) + "Dedent def-or-class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "def-or-class" (- (or arg py-indent-offset)))) + +(defun py-shift-indent-right (&optional arg) + "Indent indent by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "indent" (or arg py-indent-offset))) + +(defun py-shift-indent-left (&optional arg) + "Dedent indent by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "indent" (- (or arg py-indent-offset)))) + +(defun py-shift-minor-block-right (&optional arg) + "Indent minor-block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "minor-block" (or arg py-indent-offset))) + +(defun py-shift-minor-block-left (&optional arg) + "Dedent minor-block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "minor-block" (- (or arg py-indent-offset)))) + +(defun py-shift-paragraph-right (&optional arg) + "Indent paragraph by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "paragraph" (or arg py-indent-offset))) + +(defun py-shift-paragraph-left (&optional arg) + "Dedent paragraph by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "paragraph" (- (or arg py-indent-offset)))) + +(defun py-shift-region-right (&optional arg) + "Indent region by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "region" (or arg py-indent-offset))) + +(defun py-shift-region-left (&optional arg) + "Dedent region by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "region" (- (or arg py-indent-offset)))) + +(defun py-shift-statement-right (&optional arg) + "Indent statement by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "statement" (or arg py-indent-offset))) + +(defun py-shift-statement-left (&optional arg) + "Dedent statement by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "statement" (- (or arg py-indent-offset)))) + +(defun py-shift-top-level-right (&optional arg) + "Indent top-level by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "top-level" (or arg py-indent-offset))) + +(defun py-shift-top-level-left (&optional arg) + "Dedent top-level by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use \[universal-argument] to specify a different value. + +Return outmost indentation reached." + (interactive "*P") + (py--shift-forms-base "top-level" (- (or arg py-indent-offset)))) + +;; python-components-down + + +(defun py-down-block (&optional indent) + "Go to the beginning of next block downwards according to INDENT. + +Return position if block found, nil otherwise." + (interactive) + (py-down-base 'py-block-re indent)) + +(defun py-down-class (&optional indent) + "Go to the beginning of next class downwards according to INDENT. + +Return position if class found, nil otherwise." + (interactive) + (py-down-base 'py-class-re indent)) + +(defun py-down-clause (&optional indent) + "Go to the beginning of next clause downwards according to INDENT. + +Return position if clause found, nil otherwise." + (interactive) + (py-down-base 'py-clause-re indent)) + +(defun py-down-block-or-clause (&optional indent) + "Go to the beginning of next block-or-clause downwards according to INDENT. + +Return position if block-or-clause found, nil otherwise." + (interactive) + (py-down-base 'py-block-or-clause-re indent)) + +(defun py-down-def (&optional indent) + "Go to the beginning of next def downwards according to INDENT. + +Return position if def found, nil otherwise." + (interactive) + (py-down-base 'py-def-re indent)) + +(defun py-down-def-or-class (&optional indent) + "Go to the beginning of next def-or-class downwards according to INDENT. + +Return position if def-or-class found, nil otherwise." + (interactive) + (py-down-base 'py-def-or-class-re indent)) + +(defun py-down-minor-block (&optional indent) + "Go to the beginning of next minor-block downwards according to INDENT. + +Return position if minor-block found, nil otherwise." + (interactive) + (py-down-base 'py-minor-block-re indent)) + +(defun py-down-block-bol (&optional indent) + "Go to the beginning of next block below according to INDENT. + +Go to beginning of line +Optional INDENT: honor indentation +Return position if block found, nil otherwise " + (interactive) + (py-down-base 'py-block-re indent t) + (progn (beginning-of-line)(point))) + +(defun py-down-class-bol (&optional indent) + "Go to the beginning of next class below according to INDENT. + +Go to beginning of line +Optional INDENT: honor indentation +Return position if class found, nil otherwise " + (interactive) + (py-down-base 'py-class-re indent t) + (progn (beginning-of-line)(point))) + +(defun py-down-clause-bol (&optional indent) + "Go to the beginning of next clause below according to INDENT. + +Go to beginning of line +Optional INDENT: honor indentation +Return position if clause found, nil otherwise " + (interactive) + (py-down-base 'py-clause-re indent t) + (progn (beginning-of-line)(point))) + +(defun py-down-block-or-clause-bol (&optional indent) + "Go to the beginning of next block-or-clause below according to INDENT. + +Go to beginning of line +Optional INDENT: honor indentation +Return position if block-or-clause found, nil otherwise " + (interactive) + (py-down-base 'py-block-or-clause-re indent t) + (progn (beginning-of-line)(point))) + +(defun py-down-def-bol (&optional indent) + "Go to the beginning of next def below according to INDENT. + +Go to beginning of line +Optional INDENT: honor indentation +Return position if def found, nil otherwise " + (interactive) + (py-down-base 'py-def-re indent t) + (progn (beginning-of-line)(point))) + +(defun py-down-def-or-class-bol (&optional indent) + "Go to the beginning of next def-or-class below according to INDENT. + +Go to beginning of line +Optional INDENT: honor indentation +Return position if def-or-class found, nil otherwise " + (interactive) + (py-down-base 'py-def-or-class-re indent t) + (progn (beginning-of-line)(point))) + +(defun py-down-minor-block-bol (&optional indent) + "Go to the beginning of next minor-block below according to INDENT. + +Go to beginning of line +Optional INDENT: honor indentation +Return position if minor-block found, nil otherwise " + (interactive) + (py-down-base 'py-minor-block-re indent t) + (progn (beginning-of-line)(point))) + +;; python-components-down.el ends here +;; python-components-start-Zf98zM + +(defun py--end-base (regexp &optional orig bol repeat) + "Used internal by functions going to the end FORM. + +Returns the indentation of FORM-start +Arg REGEXP, a symbol" + (unless (eobp) + (let (;; not looking for an assignment + (use-regexp (member regexp (list 'py-def-re 'py-class-re 'py-def-or-class-re))) + (orig (or orig (point)))) + (unless (eobp) + (unless (py-beginning-of-statement-p) + (py-backward-statement)) + (let* (;; when at block-start, be specific + ;; (regexp (py--refine-regexp-maybe regexp)) + (regexpvalue (symbol-value regexp)) + ;; (regexp (or regexp (symbol-value 'py-extended-block-or-clause-re))) + (repeat (if repeat (1+ repeat) 0)) + (indent (if + (looking-at regexpvalue) + (if (bolp) 0 + (abs + (- (current-indentation) py-indent-offset))) + (current-indentation))) + ;; when at block-start, be specific + ;; return current-indentation, position and possibly needed clause-regexps (secondvalue) + (res + (cond + ((and (py-beginning-of-statement-p) + ;; (eq 0 (current-column)) + (or (looking-at regexpvalue) + (and (member regexp (list 'py-def-re 'py-def-or-class-re 'py-class-re)) + (looking-at py-decorator-re) + (py-down-def-or-class (current-indentation))) + (and (member regexp (list 'py-minor-block-re 'py-if-re 'py-for-re 'py-try-re)) + (looking-at py-minor-clause-re)))) + (list (current-indentation) (point) (py--end-base-determine-secondvalue regexp))) + ((looking-at regexpvalue) + (list (current-indentation) (point) (py--end-base-determine-secondvalue regexp))) + ((eq 0 (current-indentation)) + (py--down-according-to-indent regexp nil 0 use-regexp)) + ;; look upward + (t (py--go-to-keyword regexp)))) + (secondvalue (ignore-errors (nth 2 res))) + erg) + ;; (py-for-block-p (looking-at py-for-re)) + (setq indent (or (and res (car-safe res)) indent)) + (cond + (res (setq erg + (and + (py--down-according-to-indent regexp secondvalue (current-indentation)) + ;; (if (>= indent (current-indentation)) + (py--down-end-form) + ;; (py--end-base regexp orig bol repeat) + ;; ) + ))) + (t (unless (< 0 repeat) (goto-char orig)) + (py--forward-regexp (symbol-value regexp)) + (beginning-of-line) + (setq erg (and + (py--down-according-to-indent regexp secondvalue (current-indentation) t) + (py--down-end-form))))) + (cond ((< orig (point)) + (setq erg (point)) + (progn + (and erg bol (setq erg (py--beginning-of-line-form))) + (and erg (cons (current-indentation) erg)))) + ((eq (point) orig) + (unless (eobp) + (cond + ((and (< repeat 1) + (or + ;; looking next indent as part of body + (py--down-according-to-indent regexp secondvalue + indent + ;; if expected indent is 0, + ;; search for new start, + ;; search for regexp only + (eq 0 indent)) + (and + ;; next block-start downwards, reduce expected indent maybe + (setq indent (or (and (< 0 indent) (- indent py-indent-offset)) indent)) + (py--down-according-to-indent regexp secondvalue + indent t)))) + (py--end-base regexp orig bol (1+ repeat)))))) + ((< (point) orig) + (goto-char orig) + (when (py--down-according-to-indent regexp secondvalue nil t) + (py--end-base regexp (point) bol (1+ repeat)))))))))) + + +;; python-components-start-Zf98zM.el ends here +;; python-components-backward-forms + +(defun py-backward-region () + "Go to the beginning of current region." + (interactive) + (let ((beg (region-beginning))) + (when beg (goto-char beg)))) + +(defun py-backward-block () + "Go to beginning of `block'. + +If already at beginning, go one `block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (let (erg) + (setq erg (car-safe (cdr-safe (py--go-to-keyword 'py-block-re)))) + (when py-mark-decorators (and (py-backward-decorator) + (setq erg (point)))) + erg)) + +(defun py-backward-class () + "Go to beginning of `class'. + +If already at beginning, go one `class' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (let (erg) + (setq erg (car-safe (cdr-safe (py--go-to-keyword 'py-class-re)))) + (when py-mark-decorators (and (py-backward-decorator) + (setq erg (point)))) + erg)) + +(defun py-backward-def () + "Go to beginning of `def'. + +If already at beginning, go one `def' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (let (erg) + (setq erg (car-safe (cdr-safe (py--go-to-keyword 'py-def-re)))) + (when py-mark-decorators (and (py-backward-decorator) + (setq erg (point)))) + erg)) + +(defun py-backward-def-or-class () + "Go to beginning of `def-or-class'. + +If already at beginning, go one `def-or-class' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (let (erg) + (setq erg (car-safe (cdr-safe (py--go-to-keyword 'py-def-or-class-re)))) + (when py-mark-decorators (and (py-backward-decorator) + (setq erg (point)))) + erg)) + +(defun py-backward-block-bol () + "Go to beginning of `block', go to BOL. +If already at beginning, go one `block' backward. +Return beginning of `block' if successful, nil otherwise" + (interactive) + (and (py-backward-block) + (progn (beginning-of-line)(point)))) + +;;;###autoload +(defun py-backward-class-bol () + "Go to beginning of `class', go to BOL. +If already at beginning, go one `class' backward. +Return beginning of `class' if successful, nil otherwise" + (interactive) + (and (py-backward-class) + (progn (beginning-of-line)(point)))) + +;;;###autoload +(defun py-backward-def-bol () + "Go to beginning of `def', go to BOL. +If already at beginning, go one `def' backward. +Return beginning of `def' if successful, nil otherwise" + (interactive) + (and (py-backward-def) + (progn (beginning-of-line)(point)))) + +;;;###autoload +(defun py-backward-def-or-class-bol () + "Go to beginning of `def-or-class', go to BOL. +If already at beginning, go one `def-or-class' backward. +Return beginning of `def-or-class' if successful, nil otherwise" + (interactive) + (and (py-backward-def-or-class) + (progn (beginning-of-line)(point)))) + +(defun py-backward-assignment () + "Go to beginning of `assignment'. + +If already at beginning, go one `assignment' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-assignment-re)))) + +(defun py-backward-block-or-clause () + "Go to beginning of `block-or-clause'. + +If already at beginning, go one `block-or-clause' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-block-or-clause-re)))) + +(defun py-backward-clause () + "Go to beginning of `clause'. + +If already at beginning, go one `clause' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-clause-re)))) + +(defun py-backward-elif-block () + "Go to beginning of `elif-block'. + +If already at beginning, go one `elif-block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-elif-re)))) + +(defun py-backward-else-block () + "Go to beginning of `else-block'. + +If already at beginning, go one `else-block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-else-re)))) + +(defun py-backward-except-block () + "Go to beginning of `except-block'. + +If already at beginning, go one `except-block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-except-re)))) + +(defun py-backward-for-block () + "Go to beginning of `for-block'. + +If already at beginning, go one `for-block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-for-re)))) + +(defun py-backward-if-block () + "Go to beginning of `if-block'. + +If already at beginning, go one `if-block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-if-re)))) + +(defun py-backward-minor-block () + "Go to beginning of `minor-block'. + +If already at beginning, go one `minor-block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-minor-block-re)))) + +(defun py-backward-try-block () + "Go to beginning of `try-block'. + +If already at beginning, go one `try-block' backward. +Return beginning of form if successful, nil otherwise" + (interactive) + (car-safe (cdr-safe (py--go-to-keyword 'py-try-re)))) + +(defun py-backward-assignment-bol () + "Go to beginning of `assignment', go to BOL. +If already at beginning, go one `assignment' backward. +Return beginning of `assignment' if successful, nil otherwise" + (interactive) + (and (py-backward-assignment) + (progn (beginning-of-line)(point)))) + +(defun py-backward-block-or-clause-bol () + "Go to beginning of `block-or-clause', go to BOL. +If already at beginning, go one `block-or-clause' backward. +Return beginning of `block-or-clause' if successful, nil otherwise" + (interactive) + (and (py-backward-block-or-clause) + (progn (beginning-of-line)(point)))) + +(defun py-backward-clause-bol () + "Go to beginning of `clause', go to BOL. +If already at beginning, go one `clause' backward. +Return beginning of `clause' if successful, nil otherwise" + (interactive) + (and (py-backward-clause) + (progn (beginning-of-line)(point)))) + +(defun py-backward-elif-block-bol () + "Go to beginning of `elif-block', go to BOL. +If already at beginning, go one `elif-block' backward. +Return beginning of `elif-block' if successful, nil otherwise" + (interactive) + (and (py-backward-elif-block) + (progn (beginning-of-line)(point)))) + +(defun py-backward-else-block-bol () + "Go to beginning of `else-block', go to BOL. +If already at beginning, go one `else-block' backward. +Return beginning of `else-block' if successful, nil otherwise" + (interactive) + (and (py-backward-else-block) + (progn (beginning-of-line)(point)))) + +(defun py-backward-except-block-bol () + "Go to beginning of `except-block', go to BOL. +If already at beginning, go one `except-block' backward. +Return beginning of `except-block' if successful, nil otherwise" + (interactive) + (and (py-backward-except-block) + (progn (beginning-of-line)(point)))) + +(defun py-backward-for-block-bol () + "Go to beginning of `for-block', go to BOL. +If already at beginning, go one `for-block' backward. +Return beginning of `for-block' if successful, nil otherwise" + (interactive) + (and (py-backward-for-block) + (progn (beginning-of-line)(point)))) + +(defun py-backward-if-block-bol () + "Go to beginning of `if-block', go to BOL. +If already at beginning, go one `if-block' backward. +Return beginning of `if-block' if successful, nil otherwise" + (interactive) + (and (py-backward-if-block) + (progn (beginning-of-line)(point)))) + +(defun py-backward-minor-block-bol () + "Go to beginning of `minor-block', go to BOL. +If already at beginning, go one `minor-block' backward. +Return beginning of `minor-block' if successful, nil otherwise" + (interactive) + (and (py-backward-minor-block) + (progn (beginning-of-line)(point)))) + +(defun py-backward-try-block-bol () + "Go to beginning of `try-block', go to BOL. +If already at beginning, go one `try-block' backward. +Return beginning of `try-block' if successful, nil otherwise" + (interactive) + (and (py-backward-try-block) + (progn (beginning-of-line)(point)))) + +;; python-components-forward-forms + + +(defun py-forward-assignment (&optional orig bol) + "Go to end of assignment. + +Return end of `assignment' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-assignment-re orig bol))) + +(defun py-forward-assignment-bol () + "Goto beginning of line following end of `assignment'. + +Return position reached, if successful, nil otherwise. +See also `py-down-assignment'." + (interactive) + (py-forward-assignment nil t)) + +(defun py-forward-region () + "Go to the end of current region." + (interactive) + (let ((end (region-end))) + (when end (goto-char end)))) + +(defun py-forward-block (&optional orig bol) + "Go to end of block. + +Return end of `block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-block-re orig bol))) + +(defun py-forward-block-bol () + "Goto beginning of line following end of `block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-block'." + (interactive) + (py-forward-block nil t)) + +(defun py-forward-block-or-clause (&optional orig bol) + "Go to end of block-or-clause. + +Return end of `block-or-clause' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-block-or-clause-re orig bol))) + +(defun py-forward-block-or-clause-bol () + "Goto beginning of line following end of `block-or-clause'. + +Return position reached, if successful, nil otherwise. +See also `py-down-block-or-clause'." + (interactive) + (py-forward-block-or-clause nil t)) + +(defun py-forward-class (&optional orig bol) + "Go to end of class. + +Return end of `class' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-class-re orig bol))) + +(defun py-forward-class-bol () + "Goto beginning of line following end of `class'. + +Return position reached, if successful, nil otherwise. +See also `py-down-class'." + (interactive) + (py-forward-class nil t)) + +(defun py-forward-clause (&optional orig bol) + "Go to end of clause. + +Return end of `clause' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-clause-re orig bol))) + +(defun py-forward-clause-bol () + "Goto beginning of line following end of `clause'. + +Return position reached, if successful, nil otherwise. +See also `py-down-clause'." + (interactive) + (py-forward-clause nil t)) + +(defun py-forward-def (&optional orig bol) + "Go to end of def. + +Return end of `def' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-def-re orig bol))) + +(defun py-forward-def-bol () + "Goto beginning of line following end of `def'. + +Return position reached, if successful, nil otherwise. +See also `py-down-def'." + (interactive) + (py-forward-def nil t)) + +(defun py-forward-def-or-class (&optional orig bol) + "Go to end of def-or-class. + +Return end of `def-or-class' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-def-or-class-re orig bol))) + +(defun py-forward-def-or-class-bol () + "Goto beginning of line following end of `def-or-class'. + +Return position reached, if successful, nil otherwise. +See also `py-down-def-or-class'." + (interactive) + (py-forward-def-or-class nil t)) + +(defun py-forward-elif-block (&optional orig bol) + "Go to end of elif-block. + +Return end of `elif-block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-elif-re orig bol))) + +(defun py-forward-elif-block-bol () + "Goto beginning of line following end of `elif-block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-elif-block'." + (interactive) + (py-forward-elif-block nil t)) + +(defun py-forward-else-block (&optional orig bol) + "Go to end of else-block. + +Return end of `else-block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-else-re orig bol))) + +(defun py-forward-else-block-bol () + "Goto beginning of line following end of `else-block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-else-block'." + (interactive) + (py-forward-else-block nil t)) + +(defun py-forward-except-block (&optional orig bol) + "Go to end of except-block. + +Return end of `except-block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-except-re orig bol))) + +(defun py-forward-except-block-bol () + "Goto beginning of line following end of `except-block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-except-block'." + (interactive) + (py-forward-except-block nil t)) + +(defun py-forward-for-block (&optional orig bol) + "Go to end of for-block. + +Return end of `for-block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-for-re orig bol))) + +(defun py-forward-for-block-bol () + "Goto beginning of line following end of `for-block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-for-block'." + (interactive) + (py-forward-for-block nil t)) + +(defun py-forward-if-block (&optional orig bol) + "Go to end of if-block. + +Return end of `if-block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-if-re orig bol))) + +(defun py-forward-if-block-bol () + "Goto beginning of line following end of `if-block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-if-block'." + (interactive) + (py-forward-if-block nil t)) + +(defun py-forward-minor-block (&optional orig bol) + "Go to end of minor-block. + +Return end of `minor-block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-minor-block-re orig bol))) + +(defun py-forward-minor-block-bol () + "Goto beginning of line following end of `minor-block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-minor-block'." + (interactive) + (py-forward-minor-block nil t)) + +(defun py-forward-try-block (&optional orig bol) + "Go to end of try-block. + +Return end of `try-block' if successful, nil otherwise +Optional ORIG: start position +Optional BOL: go to beginning of line following end-position" + (interactive) + (cdr-safe (py--end-base 'py-try-re orig bol))) + +(defun py-forward-try-block-bol () + "Goto beginning of line following end of `try-block'. + +Return position reached, if successful, nil otherwise. +See also `py-down-try-block'." + (interactive) + (py-forward-try-block nil t)) + +;; python-components-forward-forms.el ends here +;; python-components-start2 + + +(defun py--fix-start (strg) + "Internal use by py-execute... functions. + +Takes STRG +Avoid empty lines at the beginning." + ;; (when py-debug-p (message "py--fix-start:")) + (let (py--imenu-create-index-p + py-guess-py-install-directory-p + py-autopair-mode + py-complete-function + py-load-pymacs-p + py-load-skeletons-p + erg) + (with-temp-buffer + (with-current-buffer (current-buffer) + (when py-debug-p + (switch-to-buffer (current-buffer))) + ;; (python-mode) + (insert strg) + (goto-char (point-min)) + (when (< 0 (setq erg (skip-chars-forward " \t\r\n\f" (line-end-position)))) + (dotimes (_ erg) + (indent-rigidly-left (point-min) (point-max)))) + (unless (py--beginning-of-statement-p) + (py-forward-statement)) + (while (not (eq (current-indentation) 0)) + (py-shift-left py-indent-offset)) + (goto-char (point-max)) + (unless (py-empty-line-p) + (newline 1)) + (buffer-substring-no-properties 1 (point-max)))))) + +(defun py-fast-send-string (strg &optional proc output-buffer result no-output argprompt args dedicated shell exception-buffer) + (interactive + (list (read-string "Python command: "))) + (py-execute-string strg proc result no-output nil output-buffer t argprompt args dedicated shell exception-buffer)) + +(defun py--fast-send-string-no-output (strg &optional proc output-buffer result) + (py-fast-send-string strg proc output-buffer result t)) + +(defun py--send-to-fast-process (strg proc output-buffer result) + "Called inside of `py--execute-base-intern'. + +Optional STRG PROC OUTPUT-BUFFER RETURN" + (let ((output-buffer (or output-buffer (process-buffer proc))) + (inhibit-read-only t)) + ;; (switch-to-buffer (current-buffer)) + (with-current-buffer output-buffer + ;; (erase-buffer) + (py-fast-send-string strg + proc + output-buffer result)))) + +(defun py--point (position) + "Returns the value of point at certain commonly referenced POSITIONs. +POSITION can be one of the following symbols: + + bol -- beginning of line + eol -- end of line + bod -- beginning of def or class + eod -- end of def or class + bob -- beginning of buffer + eob -- end of buffer + boi -- back to indentation + bos -- beginning of statement + +This function does not modify point or mark." + (save-excursion + (progn + (cond + ((eq position 'bol) (beginning-of-line)) + ((eq position 'eol) (end-of-line)) + ((eq position 'bod) (py-backward-def-or-class)) + ((eq position 'eod) (py-forward-def-or-class)) + ;; Kind of funny, I know, but useful for py-up-exception. + ((eq position 'bob) (goto-char (point-min))) + ((eq position 'eob) (goto-char (point-max))) + ((eq position 'boi) (back-to-indentation)) + ((eq position 'bos) (py-backward-statement)) + (t (error "Unknown buffer position requested: %s" position)))))) + +(defun py-backward-top-level () + "Go up to beginning of statments until level of indentation is null. + +Returns position if successful, nil otherwise " + (interactive) + (let (erg done) + (unless (bobp) + (while (and (not done)(not (bobp)) + (setq erg (re-search-backward "^[[:alpha:]_'\"]" nil t 1))) + (if + (nth 8 (parse-partial-sexp (point-min) (point))) + (setq erg nil) + (setq done t))) + erg))) + +;; might be slow due to repeated calls of `py-down-statement' +(defun py-forward-top-level () + "Go to end of top-level form at point. + +Returns position if successful, nil otherwise" + (interactive) + (let ((orig (point)) + erg) + (unless (eobp) + (unless (py--beginning-of-statement-p) + (py-backward-statement)) + (unless (eq 0 (current-column)) + (py-backward-top-level)) + (cond ((looking-at py-def-re) + (setq erg (py-forward-def))) + ((looking-at py-class-re) + (setq erg (py-forward-class))) + ((looking-at py-block-re) + (setq erg (py-forward-block))) + (t (setq erg (py-forward-statement)))) + (unless (< orig (point)) + (while (and (not (eobp)) (py-down-statement)(< 0 (current-indentation)))) + (if (looking-at py-block-re) + (setq erg (py-forward-block)) + (setq erg (py-forward-statement)))) + erg))) + +;; python-components-start3 + +(defun toggle-force-py-shell-name-p (&optional arg) + "If customized default `py-shell-name' should be enforced upon execution. + +If `py-force-py-shell-name-p' should be on or off. +Returns value of `py-force-py-shell-name-p' switched to. + +Optional ARG +See also commands +`force-py-shell-name-p-on' +`force-py-shell-name-p-off' + +Caveat: Completion might not work that way." + (interactive) + (let ((arg (or arg (if py-force-py-shell-name-p -1 1)))) + (if (< 0 arg) + (setq py-force-py-shell-name-p t) + (setq py-force-py-shell-name-p nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-force-py-shell-name-p: %s" py-force-py-shell-name-p)) + py-force-py-shell-name-p)) + +(defun force-py-shell-name-p-on () + "Switch `py-force-py-shell-name-p' on. + +Customized default `py-shell-name' will be enforced upon execution. +Returns value of `py-force-py-shell-name-p'. + +Caveat: Completion might not work that way." + (interactive) + (toggle-force-py-shell-name-p 1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-force-py-shell-name-p: %s" py-force-py-shell-name-p)) + py-force-py-shell-name-p) + +(defun force-py-shell-name-p-off () + "Make sure, `py-force-py-shell-name-p' is off. + +Function to use by executes will be guessed from environment. +Returns value of `py-force-py-shell-name-p'." + (interactive) + (toggle-force-py-shell-name-p -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-force-py-shell-name-p: %s" py-force-py-shell-name-p)) + py-force-py-shell-name-p) + +(defun py--fix-if-name-main-permission (strg) + "Remove \"if __name__ == '__main__ '\" STRG from code to execute. + +See `py-if-name-main-permission-p'" + (let ((strg (if py-if-name-main-permission-p strg + (replace-regexp-in-string + "if[( ]*__name__[) ]*==[( ]*['\"]\\{1,3\\}__main__['\"]\\{1,3\\}[) ]*:" + ;; space after __main__, i.e. will not be executed + "if __name__ == '__main__ ':" strg)))) + strg)) + +(defun py-symbol-at-point () + "Return the current Python symbol. + +When interactively called, copy and message it" + (interactive) + (let ((erg (with-syntax-table + py-dotted-expression-syntax-table + (current-word)))) + (when (called-interactively-p 'interactive) (kill-new erg) + (message "%s" erg)) + erg)) + +(defun py--line-backward-maybe () + "Return result of (< 0 (abs (skip-chars-backward \" \\t\\r\\n\\f\"))) " + (skip-chars-backward " \t\f" (line-beginning-position)) + (< 0 (abs (skip-chars-backward " \t\r\n\f")))) + +(defun py--after-empty-line () + "Return `t' if line before contains only whitespace characters. " + (save-excursion + (beginning-of-line) + (forward-line -1) + (beginning-of-line) + (looking-at "\\s-*$"))) + +(defun py-guessed-sanity-check (guessed) + (and (>= guessed 2)(<= guessed 8)(eq 0 (% guessed 2)))) + +(defun py--guess-indent-final (indents) + "Calculate and do sanity-check. + +Expects INDENTS, a cons" + (let* ((first (car indents)) + (second (cadr indents)) + (erg (if (and first second) + (if (< second first) + (- first second) + (- second first)) + (default-value 'py-indent-offset)))) + (setq erg (and (py-guessed-sanity-check erg) erg)) + erg)) + +(defun py--guess-indent-forward () + "Called when moving to end of a form and `py-smart-indentation' is on." + (let* ((first (if + (py--beginning-of-statement-p) + (current-indentation) + (progn + (py-forward-statement) + (py-backward-statement) + (current-indentation)))) + (second (if (or (looking-at py-extended-block-or-clause-re)(eq 0 first)) + (progn + (py-forward-statement) + (py-forward-statement) + (py-backward-statement) + (current-indentation)) + ;; when not starting from block, look above + (while (and (re-search-backward py-extended-block-or-clause-re nil 'movet 1) + (or (>= (current-indentation) first) + (nth 8 (parse-partial-sexp (point-min) (point)))))) + (current-indentation)))) + (list first second))) + +(defun py--guess-indent-backward () + "Called when moving to beginning of a form and `py-smart-indentation' is on." + (let* ((cui (current-indentation)) + (indent (if (< 0 cui) cui 999)) + (pos (progn (while (and (re-search-backward py-extended-block-or-clause-re nil 'move 1) + (or (>= (current-indentation) indent) + (nth 8 (parse-partial-sexp (point-min) (point)))))) + (unless (bobp) (point)))) + (first (and pos (current-indentation))) + (second (and pos (py-forward-statement) (py-forward-statement) (py-backward-statement)(current-indentation)))) + (list first second))) + +(defun py-guess-indent-offset (&optional direction) + "Guess `py-indent-offset'. + +Set local value of `py-indent-offset', return it + +Might change local value of `py-indent-offset' only when called +downwards from beginning of block followed by a statement. +Otherwise `default-value' is returned. +Unless DIRECTION is symbol \\='forward, go backward first" + (interactive) + (save-excursion + (let* ((indents + (cond (direction + (if (eq 'forward direction) + (py--guess-indent-forward) + (py--guess-indent-backward))) + ;; guess some usable indent is above current position + ((eq 0 (current-indentation)) + (py--guess-indent-forward)) + (t (py--guess-indent-backward)))) + (erg (py--guess-indent-final indents))) + (if erg (setq py-indent-offset erg) + (setq py-indent-offset + (default-value 'py-indent-offset))) + (when (called-interactively-p 'any) (message "%s" py-indent-offset)) + py-indent-offset))) + +(defun py--execute-buffer-finally (strg proc procbuf origline filename fast wholebuf) + (if (and filename wholebuf (not (buffer-modified-p))) + (unwind-protect + (py--execute-file-base filename proc nil procbuf origline fast)) + (let* ((tempfile (concat (expand-file-name py-temp-directory) py-separator-char "temp" (md5 (format "%s" (nth 3 (current-time)))) ".py"))) + (with-temp-buffer + (insert strg) + (write-file tempfile)) + (unwind-protect + (py--execute-file-base tempfile proc nil procbuf origline fast) + (and (file-readable-p tempfile) (delete-file tempfile py-debug-p)))))) + +(defun py--postprocess-intern (&optional origline exception-buffer output-buffer) + "Highlight exceptions found in BUF. + +Optional ORIGLINE EXCEPTION-BUFFER +If an exception occurred return error-string, +otherwise return nil. +BUF must exist. + +Indicate LINE if code wasn't run from a file, +thus remember line of source buffer" + (save-excursion + (with-current-buffer output-buffer + (let* (estring ecode erg) + ;; (switch-to-buffer (current-buffer)) + (goto-char (point-max)) + (sit-for 0.1) + (save-excursion + (unless (looking-back py-pdbtrack-input-prompt (line-beginning-position)) + (forward-line -1) + (end-of-line) + (when (re-search-backward py-shell-prompt-regexp t 1) + ;; (or (re-search-backward py-shell-prompt-regexp nil t 1) + ;; (re-search-backward (concat py-ipython-input-prompt-re "\\|" py-ipython-output-prompt-re) nil t 1)) + (save-excursion + (when (re-search-forward "File \"\\(.+\\)\", line \\([0-9]+\\)\\(.*\\)$" nil t) + (setq erg (copy-marker (point))) + (delete-region (progn (beginning-of-line) + (save-match-data + (when (looking-at + ;; all prompt-regexp known + py-shell-prompt-regexp) + (goto-char (match-end 0))))) + + (progn (skip-chars-forward " \t\r\n\f" (line-end-position))(point))) + (insert (concat " File " (buffer-name exception-buffer) ", line " + (prin1-to-string origline))))) + ;; these are let-bound as `tempbuf' + (and (boundp 'tempbuf) + ;; (message "%s" tempbuf) + (search-forward (buffer-name tempbuf) nil t) + (delete-region (line-beginning-position) (1+ (line-end-position)))) + ;; if no buffer-file exists, signal "Buffer", not "File(when + (when erg + (goto-char erg) + ;; (forward-char -1) + ;; (skip-chars-backward "^\t\r\n\f") + ;; (skip-chars-forward " \t") + (save-match-data + (and (not (py--buffer-filename-remote-maybe + (or + (get-buffer exception-buffer) + (get-buffer (file-name-nondirectory exception-buffer))))) + (string-match "^[ \t]*File" (buffer-substring-no-properties (point) (line-end-position))) + (looking-at "[ \t]*File") + (replace-match " Buffer"))) + (push origline py-error) + (push (buffer-name exception-buffer) py-error) + (forward-line 1) + (when (looking-at "[ \t]*\\([^\t\n\r\f]+\\)[ \t]*$") + (setq estring (match-string-no-properties 1)) + (setq ecode (replace-regexp-in-string "[ \n\t\f\r^]+" " " estring)) + (push 'py-error ecode)))))) + py-error)))) + +(defun py-execute-python-mode-v5 (start end origline filename) + "Take START END &optional EXCEPTION-BUFFER ORIGLINE." + (interactive "r") + (let ((output-buffer "*Python Output*") + (py-split-window-on-execute 'just-two) + (pcmd (concat py-shell-name (if (string-equal py-which-bufname + "Jython") + " -" + ;; " -c " + "")))) + (save-excursion + (shell-command-on-region start end + pcmd output-buffer)) + (if (not (get-buffer output-buffer)) + (message "No output.") + (setq py-result (py--fetch-result (get-buffer output-buffer) nil)) + (if (string-match "Traceback" py-result) + (message "%s" (setq py-error (py--fetch-error output-buffer origline filename))) + py-result)))) + +(defun py--execute-ge24.3 (start end execute-directory which-shell &optional exception-buffer proc file origline) + "An alternative way to do it. + +According to START END EXECUTE-DIRECTORY WHICH-SHELL +Optional EXCEPTION-BUFFER PROC FILE ORIGLINE +May we get rid of the temporary file?" + (and (py--buffer-filename-remote-maybe) buffer-offer-save (buffer-modified-p (py--buffer-filename-remote-maybe)) (y-or-n-p "Save buffer before executing? ") + (write-file (py--buffer-filename-remote-maybe))) + (let* ((start (copy-marker start)) + (end (copy-marker end)) + (exception-buffer (or exception-buffer (current-buffer))) + (line (py-count-lines (point-min) (if (eq start (line-beginning-position)) (1+ start) start))) + (strg (buffer-substring-no-properties start end)) + (tempfile (or (py--buffer-filename-remote-maybe) (concat (expand-file-name py-temp-directory) py-separator-char (replace-regexp-in-string py-separator-char "-" "temp") ".py"))) + + (proc (or proc (if py-dedicated-process-p + (get-buffer-process (py-shell nil nil t which-shell)) + (or (get-buffer-process py-buffer-name) + (get-buffer-process (py-shell nil nil py-dedicated-process-p which-shell py-buffer-name)))))) + (procbuf (process-buffer proc)) + (file (or file (with-current-buffer py-buffer-name + (concat (file-remote-p default-directory) tempfile)))) + (filebuf (get-buffer-create file))) + (set-buffer filebuf) + (erase-buffer) + (newline line) + (save-excursion + (insert strg)) + (py--fix-start (buffer-substring-no-properties (point) (point-max))) + (unless (string-match "[jJ]ython" which-shell) + ;; (when (and execute-directory py-use-current-dir-when-execute-p + ;; (not (string= execute-directory default-directory))) + ;; (message "Warning: options `execute-directory' and `py-use-current-dir-when-execute-p' may conflict")) + (and execute-directory + (process-send-string proc (concat "import os; os.chdir(\"" execute-directory "\")\n")))) + (set-buffer filebuf) + (process-send-string proc + (buffer-substring-no-properties + (point-min) (point-max))) + (sit-for 0.1 t) + (if (and (setq py-error (save-excursion (py--postprocess-intern origline exception-buffer))) + (car py-error) + (not (markerp py-error))) + (py--jump-to-exception py-error origline) + (unless (string= (buffer-name (current-buffer)) (buffer-name procbuf)) + (when py-verbose-p (message "Output buffer: %s" procbuf)))))) + +(defun py--execute-base-intern (strg filename proc wholebuf buffer origline execute-directory start end &optional fast) + "Select the handler according to: + +STRG FILENAME PROC FILE WHOLEBUF +BUFFER ORIGLINE EXECUTE-DIRECTORY START END WHICH-SHELL +Optional FAST RETURN" + (setq py-error nil) + (cond ;; (fast (py-fast-send-string strg proc buffer result)) + ;; enforce proceeding as python-mode.el v5 + (python-mode-v5-behavior-p + (py-execute-python-mode-v5 start end origline filename)) + (py-execute-no-temp-p + (py--execute-ge24.3 start end execute-directory py-shell-name py-exception-buffer proc filename origline)) + ((and filename wholebuf) + (py--execute-file-base filename proc nil buffer origline fast)) + (t + ;; (message "(current-buffer) %s" (current-buffer)) + (py--execute-buffer-finally strg proc buffer origline filename fast wholebuf) + ;; (py--delete-temp-file tempfile) + ))) + +(defun py--execute-base (&optional start end shell filename proc wholebuf fast dedicated split switch) + "Update optional variables. +START END SHELL FILENAME PROC FILE WHOLEBUF FAST DEDICATED SPLIT SWITCH." + (setq py-error nil) + (when py-debug-p (message "py--execute-base: (current-buffer): %s" (current-buffer))) + ;; (when (or fast py-fast-process-p) (ignore-errors (py-kill-buffer-unconditional py-output-buffer))) + (let* ((orig (point)) + (fast (or fast py-fast-process-p)) + (exception-buffer (current-buffer)) + (start (or start (and (use-region-p) (region-beginning)) (point-min))) + (end (or end (and (use-region-p) (region-end)) (point-max))) + (strg-raw (if py-if-name-main-permission-p + (buffer-substring-no-properties start end) + (py--fix-if-name-main-permission (buffer-substring-no-properties start end)))) + (strg (py--fix-start strg-raw)) + (wholebuf (unless filename (or wholebuf (and (eq (buffer-size) (- end start)))))) + ;; error messages may mention differently when running from a temp-file + (origline + (format "%s" (save-restriction + (widen) + (py-count-lines (point-min) orig)))) + ;; argument SHELL might be a string like "python", "IPython" "python3", a symbol holding PATH/TO/EXECUTABLE or just a symbol like 'python3 + (shell (or + (and shell + ;; shell might be specified in different ways + (or (and (stringp shell) shell) + (ignore-errors (eval shell)) + (and (symbolp shell) (format "%s" shell)))) + ;; (save-excursion + (py-choose-shell) + ;;) + )) + (shell (or shell (py-choose-shell))) + (buffer-name + (py--choose-buffer-name shell dedicated fast)) + (execute-directory + (cond ((ignore-errors (file-name-directory (file-remote-p (buffer-file-name) 'localname)))) + ((and py-use-current-dir-when-execute-p (buffer-file-name)) + (file-name-directory (buffer-file-name))) + ((and py-use-current-dir-when-execute-p + py-fileless-buffer-use-default-directory-p) + (expand-file-name default-directory)) + ((stringp py-execute-directory) + py-execute-directory) + ((getenv "VIRTUAL_ENV")) + (t (getenv "HOME")))) + (filename (or (and filename (expand-file-name filename)) + (py--buffer-filename-remote-maybe))) + (py-orig-buffer-or-file (or filename (current-buffer))) + (proc-raw (or proc (get-buffer-process buffer-name))) + + (proc (or proc-raw (get-buffer-process buffer-name) + (prog1 + (get-buffer-process (py-shell nil nil dedicated shell buffer-name fast exception-buffer split switch)) + (sit-for 1) + ))) + (split (if python-mode-v5-behavior-p 'just-two split))) + (setq py-output-buffer (or (and python-mode-v5-behavior-p py-output-buffer) (and proc (buffer-name (process-buffer proc))) + (py--choose-buffer-name shell dedicated fast))) + (py--execute-base-intern strg filename proc wholebuf py-output-buffer origline execute-directory start end fast) + (when (or split py-split-window-on-execute py-switch-buffers-on-execute-p) + (py--shell-manage-windows py-output-buffer exception-buffer (or split py-split-window-on-execute) switch)))) + +;; python-components-execute-file + +;; Execute file given + +(defun py-execute-file-ipython (filename) + "Send file to IPython interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "ipython" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-ipython3 (filename) + "Send file to IPython3 interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "ipython3" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-jython (filename) + "Send file to Jython interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "jython" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-python (filename) + "Send file to Python interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "python" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-python2 (filename) + "Send file to Python2 interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "python2" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-python3 (filename) + "Send file to Python3 interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "python3" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-pypy (filename) + "Send file to PyPy interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "pypy" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file- (filename) + "Send file to interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil nil "" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-ipython-dedicated (filename) + "Send file to a dedicatedIPython interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "ipython" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-ipython3-dedicated (filename) + "Send file to a dedicatedIPython3 interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "ipython3" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-jython-dedicated (filename) + "Send file to a dedicatedJython interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "jython" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-python-dedicated (filename) + "Send file to a dedicatedPython interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "python" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-python2-dedicated (filename) + "Send file to a dedicatedPython2 interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "python2" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-python3-dedicated (filename) + "Send file to a dedicatedPython3 interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "python3" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file-pypy-dedicated (filename) + "Send file to a dedicatedPyPy interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "pypy" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +(defun py-execute-file--dedicated (filename) + "Send file to a dedicated interpreter" + (interactive "fFile: ") + (let ((interactivep (called-interactively-p 'interactive)) + (buffer (py-shell nil nil t "" nil t))) + (py--execute-file-base filename (get-buffer-process buffer) nil buffer nil t interactivep))) + +;; python-components-up + + +(defun py-up-block (&optional indent) + "Go to the beginning of next block upwards according to INDENT. +Optional INDENT +Return position if block found, nil otherwise." + (interactive) + (py-up-base 'py-block-re indent)) + +(defun py-up-class (&optional indent) + "Go to the beginning of next class upwards according to INDENT. +Optional INDENT +Return position if class found, nil otherwise." + (interactive) + (py-up-base 'py-class-re indent)) + +(defun py-up-clause (&optional indent) + "Go to the beginning of next clause upwards according to INDENT. +Optional INDENT +Return position if clause found, nil otherwise." + (interactive) + (py-up-base 'py-clause-re indent)) + +(defun py-up-block-or-clause (&optional indent) + "Go to the beginning of next block-or-clause upwards according to INDENT. +Optional INDENT +Return position if block-or-clause found, nil otherwise." + (interactive) + (py-up-base 'py-block-or-clause-re indent)) + +(defun py-up-def (&optional indent) + "Go to the beginning of next def upwards according to INDENT. +Optional INDENT +Return position if def found, nil otherwise." + (interactive) + (py-up-base 'py-def-re indent)) + +(defun py-up-def-or-class (&optional indent) + "Go to the beginning of next def-or-class upwards according to INDENT. +Optional INDENT +Return position if def-or-class found, nil otherwise." + (interactive) + (py-up-base 'py-def-or-class-re indent)) + +(defun py-up-minor-block (&optional indent) + "Go to the beginning of next minor-block upwards according to INDENT. +Optional INDENT +Return position if minor-block found, nil otherwise." + (interactive) + (py-up-base 'py-minor-block-re indent)) + +(defun py-up-block-bol (&optional indent) + "Go to the beginning of next block upwards according to INDENT. + +Go to beginning of line. +Return position if block found, nil otherwise." + (interactive) + (py-up-base 'py-block-re indent) + (progn (beginning-of-line)(point))) + +(defun py-up-class-bol (&optional indent) + "Go to the beginning of next class upwards according to INDENT. + +Go to beginning of line. +Return position if class found, nil otherwise." + (interactive) + (py-up-base 'py-class-re indent) + (progn (beginning-of-line)(point))) + +(defun py-up-clause-bol (&optional indent) + "Go to the beginning of next clause upwards according to INDENT. + +Go to beginning of line. +Return position if clause found, nil otherwise." + (interactive) + (py-up-base 'py-clause-re indent) + (progn (beginning-of-line)(point))) + +(defun py-up-block-or-clause-bol (&optional indent) + "Go to the beginning of next block-or-clause upwards according to INDENT. + +Go to beginning of line. +Return position if block-or-clause found, nil otherwise." + (interactive) + (py-up-base 'py-block-or-clause-re indent) + (progn (beginning-of-line)(point))) + +(defun py-up-def-bol (&optional indent) + "Go to the beginning of next def upwards according to INDENT. + +Go to beginning of line. +Return position if def found, nil otherwise." + (interactive) + (py-up-base 'py-def-re indent) + (progn (beginning-of-line)(point))) + +(defun py-up-def-or-class-bol (&optional indent) + "Go to the beginning of next def-or-class upwards according to INDENT. + +Go to beginning of line. +Return position if def-or-class found, nil otherwise." + (interactive) + (py-up-base 'py-def-or-class-re indent) + (progn (beginning-of-line)(point))) + +(defun py-up-minor-block-bol (&optional indent) + "Go to the beginning of next minor-block upwards according to INDENT. + +Go to beginning of line. +Return position if minor-block found, nil otherwise." + (interactive) + (py-up-base 'py-minor-block-re indent) + (progn (beginning-of-line)(point))) + +;; python-components-up.el ends here +;; python-components-booleans-beginning-forms + +(defun py--beginning-of-comment-p (&optional pps) + "If cursor is at the beginning of a `comment'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at (concat "\\b" py-comment-re)) + (point)))) + +(defun py--beginning-of-expression-p (&optional pps) + "If cursor is at the beginning of a `expression'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at (concat "\\b" py-expression-re)) + (point)))) + +(defun py--beginning-of-line-p (&optional pps) + "If cursor is at the beginning of a `line'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at (concat "\\b" py-line-re)) + (point)))) + +(defun py--beginning-of-paragraph-p (&optional pps) + "If cursor is at the beginning of a `paragraph'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at (concat "\\b" py-paragraph-re)) + (point)))) + +(defun py--beginning-of-partial-expression-p (&optional pps) + "If cursor is at the beginning of a `partial-expression'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at (concat "\\b" py-partial-expression-re)) + (point)))) + +(defun py--beginning-of-section-p (&optional pps) + "If cursor is at the beginning of a `section'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at (concat "\\b" py-section-re)) + (point)))) + +(defun py--beginning-of-top-level-p (&optional pps) + "If cursor is at the beginning of a `top-level'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at (concat "\\b" py-top-level-re)) + (point)))) + +(defun py--beginning-of-assignment-p (&optional pps) + "If cursor is at the beginning of a `assignment'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-assignment-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-block-p (&optional pps) + "If cursor is at the beginning of a `block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-block-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-block-or-clause-p (&optional pps) + "If cursor is at the beginning of a `block-or-clause'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-block-or-clause-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-class-p (&optional pps) + "If cursor is at the beginning of a `class'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-class-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-clause-p (&optional pps) + "If cursor is at the beginning of a `clause'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-clause-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-def-p (&optional pps) + "If cursor is at the beginning of a `def'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-def-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-def-or-class-p (&optional pps) + "If cursor is at the beginning of a `def-or-class'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-def-or-class-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-elif-block-p (&optional pps) + "If cursor is at the beginning of a `elif-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-elif-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-else-block-p (&optional pps) + "If cursor is at the beginning of a `else-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-else-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-except-block-p (&optional pps) + "If cursor is at the beginning of a `except-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-except-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-for-block-p (&optional pps) + "If cursor is at the beginning of a `for-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-for-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-if-block-p (&optional pps) + "If cursor is at the beginning of a `if-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-if-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-indent-p (&optional pps) + "If cursor is at the beginning of a `indent'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-indent-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-minor-block-p (&optional pps) + "If cursor is at the beginning of a `minor-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-minor-block-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-try-block-p (&optional pps) + "If cursor is at the beginning of a `try-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-try-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (eq (current-column)(current-indentation)) + (point)))) + +(defun py--beginning-of-assignment-bol-p (&optional pps) + "If cursor is at the beginning of a `assignment'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-assignment-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-block-bol-p (&optional pps) + "If cursor is at the beginning of a `block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-block-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-block-or-clause-bol-p (&optional pps) + "If cursor is at the beginning of a `block-or-clause'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-block-or-clause-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-class-bol-p (&optional pps) + "If cursor is at the beginning of a `class'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-class-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-clause-bol-p (&optional pps) + "If cursor is at the beginning of a `clause'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-clause-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-def-bol-p (&optional pps) + "If cursor is at the beginning of a `def'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-def-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-def-or-class-bol-p (&optional pps) + "If cursor is at the beginning of a `def-or-class'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-def-or-class-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-elif-block-bol-p (&optional pps) + "If cursor is at the beginning of a `elif-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-elif-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-else-block-bol-p (&optional pps) + "If cursor is at the beginning of a `else-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-else-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-except-block-bol-p (&optional pps) + "If cursor is at the beginning of a `except-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-except-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-for-block-bol-p (&optional pps) + "If cursor is at the beginning of a `for-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-for-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-if-block-bol-p (&optional pps) + "If cursor is at the beginning of a `if-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-if-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-indent-bol-p (&optional pps) + "If cursor is at the beginning of a `indent'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-indent-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-minor-block-bol-p (&optional pps) + "If cursor is at the beginning of a `minor-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-minor-block-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +(defun py--beginning-of-try-block-bol-p (&optional pps) + "If cursor is at the beginning of a `try-block'. +Return position, nil otherwise." + (let ((pps (or pps (parse-partial-sexp (point-min) (point))))) + (and (bolp) + (not (or (nth 8 pps)(nth 1 pps))) + (looking-at py-try-re) + (looking-back "[^ \t]*" (line-beginning-position)) + (point)))) + +;; python-components-move + +(defun py-backward-paragraph () + "Go to beginning of current paragraph. + +If already at beginning, go to start of next paragraph upwards" + (interactive) + (backward-paragraph)(point)) + +(defun py-forward-paragraph () + "Go to end of current paragraph. + +If already at end, go to end of next paragraph downwards" + (interactive) + (and (forward-paragraph)(point))) + +;; Indentation +;; Travel current level of indentation +(defun py--travel-this-indent-backward (&optional indent) + "Travel current INDENT backward. + +With optional INDENT travel bigger or equal indentation" + (let ((indent (or indent (current-indentation))) + last) + (while (and (not (bobp)) + (py-backward-statement) + (<= indent (current-indentation)) + (setq last (point)))) + (when last (goto-char last)) + last)) + +(defun py-backward-indent () + "Go to the beginning of a section of equal indent. + +If already at the beginning or before a indent, go to next indent upwards +Returns final position when called from inside section, nil otherwise" + (interactive) + (unless (bobp) + (let (erg) + (setq erg (py--travel-this-indent-backward)) + (when erg (goto-char erg)) + erg))) + +(defun py--travel-this-indent-backward-bol (indent) + "Internal use. + +Travel this INDENT backward until bol" + (let (erg) + (while (and (py-backward-statement-bol) + (or indent (setq indent (current-indentation))) + (eq indent (current-indentation))(setq erg (point)) (not (bobp)))) + (when erg (goto-char erg)))) + +(defun py-backward-indent-bol () + "Go to the beginning of line of a section of equal indent. + +If already at the beginning or before an indent, +go to next indent in buffer upwards +Returns final position when called from inside section, nil otherwise" + (interactive) + (unless (bobp) + (let ((indent (when (eq (current-indentation) (current-column)) (current-column))) + erg) + (setq erg (py--travel-this-indent-backward-bol indent)) + erg))) + +(defun py--travel-this-indent-forward (indent) + "Internal use. + +Travel this INDENT forward" + (let (last erg) + (while (and (py-down-statement) + (eq indent (current-indentation)) + (setq last (point)))) + (when last (goto-char last)) + (setq erg (py-forward-statement)) + erg)) + +(defun py-forward-indent () + "Go to the end of a section of equal indentation. + +If already at the end, go down to next indent in buffer +Returns final position when moved, nil otherwise" + (interactive) + (let (done + (orig (line-beginning-position)) + (indent (current-indentation)) + (last (progn (back-to-indentation) (point)))) + (while (and (not (eobp)) (not done) + (progn (forward-line 1) (back-to-indentation) (or (py-empty-line-p) (and (<= indent (current-indentation))(< last (point)))))) + (unless (py-empty-line-p) (skip-chars-forward " \t\r\n\f")(setq last (point))) + (and (not (py-empty-line-p))(< (current-indentation) indent)(setq done t))) + (goto-char last) + (end-of-line) + (skip-chars-backward " \t\r\n\f") + (and (< orig (point))(point)))) + +(defun py-forward-indent-bol () + "Go to beginning of line following of a section of equal indentation. + +If already at the end, go down to next indent in buffer +Returns final position when called from inside section, nil otherwise" + (interactive) + (unless (eobp) + (when (py-forward-indent) + (unless (eobp) (progn (forward-line 1) (beginning-of-line) (point)))))) + +;; (defun py-forward-indent-bol () +;; "Go to beginning of line following of a section of equal indentation. + +;; If already at the end, go down to next indent in buffer +;; Returns final position when called from inside section, nil otherwise" +;; (interactive) +;; (unless (eobp) +;; (let (erg indent) +;; ;; (when (py-forward-statement) +;; (when (py-forward-indent) +;; ;; (save-excursion +;; ;; (setq indent (and (py-backward-statement)(current-indentation)))) +;; ;; (setq erg (py--travel-this-indent-forward indent)) +;; (unless (eobp) (forward-line 1) (beginning-of-line) (setq erg (point)))) +;; erg))) + +(defun py-backward-expression (&optional orig done repeat) + "Go to the beginning of a python expression. + +If already at the beginning or before a expression, +go to next expression in buffer upwards + +ORIG - consider orignial position or point. +DONE - transaktional argument +REPEAT - count and consider repeats" + (interactive) + (unless (bobp) + (unless done (skip-chars-backward " \t\r\n\f")) + (let ((repeat (or (and repeat (1+ repeat)) 0)) + (pps (parse-partial-sexp (point-min) (point))) + (orig (or orig (point))) + erg) + (if (< py-max-specpdl-size repeat) + (error "`py-backward-expression' reached loops max") + (cond + ;; comments + ((nth 8 pps) + (goto-char (nth 8 pps)) + (py-backward-expression orig done repeat)) + ;; lists + ((nth 1 pps) + (goto-char (nth 1 pps)) + (skip-chars-backward py-expression-skip-chars) + ) + ;; in string + ((nth 3 pps) + (goto-char (nth 8 pps))) + ;; after operator + ((and (not done) (looking-back py-operator-re (line-beginning-position))) + (skip-chars-backward "^ \t\r\n\f") + (skip-chars-backward " \t\r\n\f") + (py-backward-expression orig done repeat)) + ((and (not done) + (< 0 (abs (skip-chars-backward py-expression-skip-chars)))) + (setq done t) + (py-backward-expression orig done repeat)))) + (unless (or (eq (point) orig)(and (bobp)(eolp))) + (setq erg (point))) + erg))) + +(defun py-forward-expression (&optional orig done repeat) + "Go to the end of a compound python expression. + +Operators are ignored. +ORIG - consider orignial position or point. +DONE - transaktional argument +REPEAT - count and consider repeats" + (interactive) + (unless done (skip-chars-forward " \t\r\n\f")) + (unless (eobp) + (let ((repeat (or (and repeat (1+ repeat)) 0)) + (pps (parse-partial-sexp (point-min) (point))) + (orig (or orig (point))) + erg) + (if (< py-max-specpdl-size repeat) + (error "`py-forward-expression' reached loops max") + (cond + ;; in comment + ((nth 4 pps) + (or (< (point) (progn (forward-comment 1) (point)))(forward-line 1)) + (py-forward-expression orig done repeat)) + ;; empty before comment + ((and (looking-at "[ \t]*#") (looking-back "^[ \t]*" (line-beginning-position))) + (while (and (looking-at "[ \t]*#") (not (eobp))) + (forward-line 1)) + (py-forward-expression orig done repeat)) + ;; inside string + ((nth 3 pps) + (goto-char (nth 8 pps)) + (goto-char (scan-sexps (point) 1)) + (setq done t) + (py-forward-expression orig done repeat)) + ((looking-at "\"\"\"\\|'''\\|\"\\|'") + (goto-char (scan-sexps (point) 1)) + (setq done t) + (py-forward-expression orig done repeat)) + ;; looking at opening delimiter + ((eq 4 (car-safe (syntax-after (point)))) + (goto-char (scan-sexps (point) 1)) + (skip-chars-forward py-expression-skip-chars) + (setq done t)) + ((nth 1 pps) + (goto-char (nth 1 pps)) + (goto-char (scan-sexps (point) 1)) + (skip-chars-forward py-expression-skip-chars) + (setq done t) + (py-forward-expression orig done repeat)) + ((and (eq orig (point)) (looking-at py-operator-re)) + (goto-char (match-end 0)) + (py-forward-expression orig done repeat)) + ((and (not done) + (< 0 (skip-chars-forward py-expression-skip-chars))) + (setq done t) + (py-forward-expression orig done repeat)) + ;; at colon following arglist + ((looking-at ":[ \t]*$") + (forward-char 1))) + (unless (or (eq (point) orig)(and (eobp) (bolp))) + (setq erg (point))) + erg)))) + +(defun py-backward-partial-expression () + "Backward partial-expression." + (interactive) + (let ((orig (point)) + erg) + (and (< 0 (abs (skip-chars-backward " \t\r\n\f")))(not (bobp))(forward-char -1)) + (when (py--in-comment-p) + (py-backward-comment) + (skip-chars-backward " \t\r\n\f")) + ;; part of py-partial-expression-forward-chars + (when (member (char-after) (list ?\ ?\" ?' ?\) ?} ?\] ?: ?#)) + (forward-char -1)) + (skip-chars-backward py-partial-expression-forward-chars) + (when (< 0 (abs (skip-chars-backward py-partial-expression-backward-chars))) + (while (and (not (bobp)) (py--in-comment-p)(< 0 (abs (skip-chars-backward py-partial-expression-backward-chars)))))) + (when (< (point) orig) + (unless + (and (bobp) (member (char-after) (list ?\ ?\t ?\r ?\n ?\f))) + (setq erg (point)))) + erg)) + +(defun py-forward-partial-expression () + "Forward partial-expression." + (interactive) + (let (erg) + (skip-chars-forward py-partial-expression-backward-chars) + ;; group arg + (while + (looking-at "[\[{(]") + (goto-char (scan-sexps (point) 1))) + (setq erg (point)) + erg)) + +;; Partial- or Minor Expression +;; Line +(defun py-backward-line () + "Go to `beginning-of-line', return position. + +If already at `beginning-of-line' and not at BOB, +go to beginning of previous line." + (interactive) + (unless (bobp) + (let ((erg + (if (bolp) + (progn + (forward-line -1) + (progn (beginning-of-line)(point))) + (progn (beginning-of-line)(point))))) + erg))) + +(defun py-forward-line () + "Go to `end-of-line', return position. + +If already at `end-of-line' and not at EOB, go to end of next line." + (interactive) + (unless (eobp) + (let ((orig (point))) + (when (eolp) (forward-line 1)) + (end-of-line) + (when (< orig (point))(point))))) + +(defun py-forward-into-nomenclature (&optional arg) + "Move forward to end of a nomenclature symbol. + +With \\[universal-argument] (programmatically, optional argument ARG), do it that many times. +IACT - if called interactively +A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores." + (interactive "p") + (or arg (setq arg 1)) + (let ((case-fold-search nil) + (orig (point)) + erg) + (if (> arg 0) + (while (and (not (eobp)) (> arg 0)) + ;; (setq erg (re-search-forward "\\(\\W+[_[:lower:][:digit:]ß]+\\)" nil t 1)) + (cond + ((or (not (eq 0 (skip-chars-forward "[[:blank:][:punct:]\n\r]"))) + (not (eq 0 (skip-chars-forward "_")))) + (when (or + (< 1 (skip-chars-forward "[:upper:]")) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]ß]"))) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]]")))) + (setq arg (1- arg)))) + ((or + (< 1 (skip-chars-forward "[:upper:]")) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]ß]"))) + (not (eq 0 (skip-chars-forward "[[:lower:][:digit:]]")))) + (setq arg (1- arg))))) + (while (and (not (bobp)) (< arg 0)) + (when (not (eq 0 (skip-chars-backward "[[:blank:][:punct:]\n\r\f_]"))) + + (forward-char -1)) + (or + (not (eq 0 (skip-chars-backward "[:upper:]"))) + (not (eq 0 (skip-chars-backward "[[:lower:][:digit:]ß]"))) + (skip-chars-backward "[[:lower:][:digit:]ß]")) + (setq arg (1+ arg)))) + (if (< (point) orig) + (progn + (when (looking-back "[[:upper:]]" (line-beginning-position)) + ;; (looking-back "[[:blank:]]" + (forward-char -1)) + (if (looking-at "[[:alnum:]ß]") + (setq erg (point)) + (setq erg nil))) + (if (and (< orig (point)) (not (eobp))) + (setq erg (point)) + (setq erg nil))) + erg)) + +(defun py-backward-into-nomenclature (&optional arg) + "Move backward to beginning of a nomenclature symbol. + +With optional ARG, move that many times. If ARG is negative, move +forward. + +A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores." + (interactive "p") + (setq arg (or arg 1)) + (py-forward-into-nomenclature (- arg))) + +(defun py--travel-current-indent (indent &optional orig) + "Move down until clause is closed, i.e. current indentation is reached. + +Takes a list, INDENT and ORIG position." + (unless (eobp) + (let ((orig (or orig (point))) + last) + (while (and (setq last (point))(not (eobp))(py-forward-statement) + (save-excursion (or (<= indent (progn (py-backward-statement)(current-indentation)))(eq last (line-beginning-position)))) + ;; (py--end-of-statement-p) +)) + (goto-char last) + (when (< orig last) + last)))) + +(defun py-backward-block-current-column () +"Reach next beginning of block upwards which start at current column. + +Return position" +(interactive) +(let* ((orig (point)) + (cuco (current-column)) + (str (make-string cuco ?\s)) + pps erg) + (while (and (not (bobp))(re-search-backward (concat "^" str py-block-keywords) nil t)(or (nth 8 (setq pps (parse-partial-sexp (point-min) (point)))) (nth 1 pps)))) + (back-to-indentation) + (and (< (point) orig)(setq erg (point))) + erg)) + +(defun py-backward-section () + "Go to next section start upward in buffer. + +Return position if successful" + (interactive) + (let ((orig (point))) + (while (and (re-search-backward py-section-start nil t 1) + (nth 8 (parse-partial-sexp (point-min) (point))))) + (when (and (looking-at py-section-start)(< (point) orig)) + (point)))) + +(defun py-forward-section () + "Go to next section end downward in buffer. + +Return position if successful" + (interactive) + (let ((orig (point)) + last) + (while (and (re-search-forward py-section-end nil t 1) + (setq last (point)) + (goto-char (match-beginning 0)) + (nth 8 (parse-partial-sexp (point-min) (point))) + (goto-char (match-end 0)))) + (and last (goto-char last)) + (when (and (looking-back py-section-end (line-beginning-position))(< orig (point))) + (point)))) + +(defun py-beginning-of-assignment() + "Go to beginning of assigment if inside. + +Return position of successful, nil of not started from inside." + (interactive) + (let* (last + (erg + (or (py--beginning-of-assignment-p) + (progn + (while (and (setq last (py-backward-statement)) + (not (looking-at py-assignment-re)) + ;; (not (bolp)) + )) + (and (looking-at py-assignment-re) last))))) + erg)) + +;; (defun py--forward-assignment-intern () +;; (and (looking-at py-assignment-re) +;; (goto-char (match-end 2)) +;; (skip-chars-forward " \t\r\n\f") +;; ;; (eq (car (syntax-after (point))) 4) +;; (progn (forward-sexp) (point)))) + +;; (defun py-forward-assignment() +;; "Go to end of assigment at point if inside. + +;; Return position of successful, nil of not started from inside" +;; (interactive) +;; (unless (eobp) +;; (if (eq last-command 'py-backward-assignment) +;; ;; assume at start of an assignment +;; (py--forward-assignment-intern) +;; ;; `py-backward-assignment' here, avoid `py--beginning-of-assignment-p' a second time +;; (let* (last +;; (beg +;; (or (py--beginning-of-assignment-p) +;; (progn +;; (while (and (setq last (py-backward-statement)) +;; (not (looking-at py-assignment-re)) +;; ;; (not (bolp)) +;; )) +;; (and (looking-at py-assignment-re) last)))) +;; erg) +;; (and beg (setq erg (py--forward-assignment-intern))) +;; erg)))) + + +(defun py-up () + (interactive) + (cond + ((py--beginning-of-class-p) + (py-up-class (current-indentation))) + ((py--beginning-of-def-p) + (py-up-def (current-indentation))) + ((py--beginning-of-block-p) + (py-up-block (current-indentation))) + ((py--beginning-of-clause-p) + (py-backward-block)) + ((py-beginning-of-statement-p) + (py-backward-block-or-clause)) + (t (py-backward-statement)) + )) + + + + +;; python-components-end-position-forms + + +(defun py--end-of-block-position () + "Return end of block position." + (save-excursion (py-forward-block))) + +(defun py--end-of-block-or-clause-position () + "Return end of block-or-clause position." + (save-excursion (py-forward-block-or-clause))) + +(defun py--end-of-class-position () + "Return end of class position." + (save-excursion (py-forward-class))) + +(defun py--end-of-clause-position () + "Return end of clause position." + (save-excursion (py-forward-clause))) + +(defun py--end-of-comment-position () + "Return end of comment position." + (save-excursion (py-forward-comment))) + +(defun py--end-of-def-position () + "Return end of def position." + (save-excursion (py-forward-def))) + +(defun py--end-of-def-or-class-position () + "Return end of def-or-class position." + (save-excursion (py-forward-def-or-class))) + +(defun py--end-of-expression-position () + "Return end of expression position." + (save-excursion (py-forward-expression))) + +(defun py--end-of-except-block-position () + "Return end of except-block position." + (save-excursion (py-forward-except-block))) + +(defun py--end-of-if-block-position () + "Return end of if-block position." + (save-excursion (py-forward-if-block))) + +(defun py--end-of-indent-position () + "Return end of indent position." + (save-excursion (py-forward-indent))) + +(defun py--end-of-line-position () + "Return end of line position." + (save-excursion (py-forward-line))) + +(defun py--end-of-minor-block-position () + "Return end of minor-block position." + (save-excursion (py-forward-minor-block))) + +(defun py--end-of-partial-expression-position () + "Return end of partial-expression position." + (save-excursion (py-forward-partial-expression))) + +(defun py--end-of-paragraph-position () + "Return end of paragraph position." + (save-excursion (py-forward-paragraph))) + +(defun py--end-of-section-position () + "Return end of section position." + (save-excursion (py-forward-section))) + +(defun py--end-of-statement-position () + "Return end of statement position." + (save-excursion (py-forward-statement))) + +(defun py--end-of-top-level-position () + "Return end of top-level position." + (save-excursion (py-forward-top-level))) + +(defun py--end-of-try-block-position () + "Return end of try-block position." + (save-excursion (py-forward-try-block))) + +(defun py--end-of-block-position-bol () + "Return end of block position at `beginning-of-line'." + (save-excursion (py-forward-block-bol))) + +(defun py--end-of-block-or-clause-position-bol () + "Return end of block-or-clause position at `beginning-of-line'." + (save-excursion (py-forward-block-or-clause-bol))) + +(defun py--end-of-class-position-bol () + "Return end of class position at `beginning-of-line'." + (save-excursion (py-forward-class-bol))) + +(defun py--end-of-clause-position-bol () + "Return end of clause position at `beginning-of-line'." + (save-excursion (py-forward-clause-bol))) + +(defun py--end-of-def-position-bol () + "Return end of def position at `beginning-of-line'." + (save-excursion (py-forward-def-bol))) + +(defun py--end-of-def-or-class-position-bol () + "Return end of def-or-class position at `beginning-of-line'." + (save-excursion (py-forward-def-or-class-bol))) + +(defun py--end-of-elif-block-position-bol () + "Return end of elif-block position at `beginning-of-line'." + (save-excursion (py-forward-elif-block-bol))) + +(defun py--end-of-else-block-position-bol () + "Return end of else-block position at `beginning-of-line'." + (save-excursion (py-forward-else-block-bol))) + +(defun py--end-of-except-block-position-bol () + "Return end of except-block position at `beginning-of-line'." + (save-excursion (py-forward-except-block-bol))) + +(defun py--end-of-for-block-position-bol () + "Return end of for-block position at `beginning-of-line'." + (save-excursion (py-forward-for-block-bol))) + +(defun py--end-of-if-block-position-bol () + "Return end of if-block position at `beginning-of-line'." + (save-excursion (py-forward-if-block-bol))) + +(defun py--end-of-indent-position-bol () + "Return end of indent position at `beginning-of-line'." + (save-excursion (py-forward-indent-bol))) + +(defun py--end-of-minor-block-position-bol () + "Return end of minor-block position at `beginning-of-line'." + (save-excursion (py-forward-minor-block-bol))) + +(defun py--end-of-statement-position-bol () + "Return end of statement position at `beginning-of-line'." + (save-excursion (py-forward-statement-bol))) + +(defun py--end-of-try-block-position-bol () + "Return end of try-block position at `beginning-of-line'." + (save-excursion (py-forward-try-block-bol))) + +;; python-components-beginning-position-forms + + +(defun py--beginning-of-block-position () + "Return beginning of block position." + (save-excursion + (or (py--beginning-of-block-p) + (py-backward-block)))) + +(defun py--beginning-of-block-or-clause-position () + "Return beginning of block-or-clause position." + (save-excursion + (or (py--beginning-of-block-or-clause-p) + (py-backward-block-or-clause)))) + +(defun py--beginning-of-class-position () + "Return beginning of class position." + (save-excursion + (or (py--beginning-of-class-p) + (py-backward-class)))) + +(defun py--beginning-of-clause-position () + "Return beginning of clause position." + (save-excursion + (or (py--beginning-of-clause-p) + (py-backward-clause)))) + +(defun py--beginning-of-comment-position () + "Return beginning of comment position." + (save-excursion + (or (py--beginning-of-comment-p) + (py-backward-comment)))) + +(defun py--beginning-of-def-position () + "Return beginning of def position." + (save-excursion + (or (py--beginning-of-def-p) + (py-backward-def)))) + +(defun py--beginning-of-def-or-class-position () + "Return beginning of def-or-class position." + (save-excursion + (or (py--beginning-of-def-or-class-p) + (py-backward-def-or-class)))) + +(defun py--beginning-of-expression-position () + "Return beginning of expression position." + (save-excursion + (or (py--beginning-of-expression-p) + (py-backward-expression)))) + +(defun py--beginning-of-except-block-position () + "Return beginning of except-block position." + (save-excursion + (or (py--beginning-of-except-block-p) + (py-backward-except-block)))) + +(defun py--beginning-of-if-block-position () + "Return beginning of if-block position." + (save-excursion + (or (py--beginning-of-if-block-p) + (py-backward-if-block)))) + +(defun py--beginning-of-indent-position () + "Return beginning of indent position." + (save-excursion + (or (py--beginning-of-indent-p) + (py-backward-indent)))) + +(defun py--beginning-of-line-position () + "Return beginning of line position." + (save-excursion + (or (py--beginning-of-line-p) + (py-backward-line)))) + +(defun py--beginning-of-minor-block-position () + "Return beginning of minor-block position." + (save-excursion + (or (py--beginning-of-minor-block-p) + (py-backward-minor-block)))) + +(defun py--beginning-of-partial-expression-position () + "Return beginning of partial-expression position." + (save-excursion + (or (py--beginning-of-partial-expression-p) + (py-backward-partial-expression)))) + +(defun py--beginning-of-paragraph-position () + "Return beginning of paragraph position." + (save-excursion + (or (py--beginning-of-paragraph-p) + (py-backward-paragraph)))) + +(defun py--beginning-of-section-position () + "Return beginning of section position." + (save-excursion + (or (py--beginning-of-section-p) + (py-backward-section)))) + +(defun py--beginning-of-statement-position () + "Return beginning of statement position." + (save-excursion + (or (py--beginning-of-statement-p) + (py-backward-statement)))) + +(defun py--beginning-of-top-level-position () + "Return beginning of top-level position." + (save-excursion + (or (py--beginning-of-top-level-p) + (py-backward-top-level)))) + +(defun py--beginning-of-try-block-position () + "Return beginning of try-block position." + (save-excursion + (or (py--beginning-of-try-block-p) + (py-backward-try-block)))) + +(defun py--beginning-of-block-position-bol () + "Return beginning of block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-block-bol-p) + (py-backward-block-bol)))) + +(defun py--beginning-of-block-or-clause-position-bol () + "Return beginning of block-or-clause position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-block-or-clause-bol-p) + (py-backward-block-or-clause-bol)))) + +(defun py--beginning-of-class-position-bol () + "Return beginning of class position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-class-bol-p) + (py-backward-class-bol)))) + +(defun py--beginning-of-clause-position-bol () + "Return beginning of clause position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-clause-bol-p) + (py-backward-clause-bol)))) + +(defun py--beginning-of-def-position-bol () + "Return beginning of def position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-def-bol-p) + (py-backward-def-bol)))) + +(defun py--beginning-of-def-or-class-position-bol () + "Return beginning of def-or-class position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-def-or-class-bol-p) + (py-backward-def-or-class-bol)))) + +(defun py--beginning-of-elif-block-position-bol () + "Return beginning of elif-block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-elif-block-bol-p) + (py-backward-elif-block-bol)))) + +(defun py--beginning-of-else-block-position-bol () + "Return beginning of else-block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-else-block-bol-p) + (py-backward-else-block-bol)))) + +(defun py--beginning-of-except-block-position-bol () + "Return beginning of except-block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-except-block-bol-p) + (py-backward-except-block-bol)))) + +(defun py--beginning-of-for-block-position-bol () + "Return beginning of for-block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-for-block-bol-p) + (py-backward-for-block-bol)))) + +(defun py--beginning-of-if-block-position-bol () + "Return beginning of if-block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-if-block-bol-p) + (py-backward-if-block-bol)))) + +(defun py--beginning-of-indent-position-bol () + "Return beginning of indent position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-indent-bol-p) + (py-backward-indent-bol)))) + +(defun py--beginning-of-minor-block-position-bol () + "Return beginning of minor-block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-minor-block-bol-p) + (py-backward-minor-block-bol)))) + +(defun py--beginning-of-statement-position-bol () + "Return beginning of statement position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-statement-bol-p) + (py-backward-statement-bol)))) + +(defun py--beginning-of-try-block-position-bol () + "Return beginning of try-block position at `beginning-of-line'." + (save-excursion + (or (py--beginning-of-try-block-bol-p) + (py-backward-try-block-bol)))) + +;; python-components-extended-executes + +(defun py--execute-prepare (form shell &optional dedicated switch beg end filename fast proc wholebuf split) + "Update some vars." + (save-excursion + (let* ((form (prin1-to-string form)) + (origline (py-count-lines)) + (fast + (or fast py-fast-process-p)) + (py-exception-buffer (current-buffer)) + (beg (unless filename + (prog1 + (or beg (funcall (intern-soft (concat "py--beginning-of-" form "-p"))) + (funcall (intern-soft (concat "py-backward-" form))) + (push-mark))))) + (end (unless filename + (or end (save-excursion (funcall (intern-soft (concat "py-forward-" form)))))))) + ;; (setq py-buffer-name nil) + (if filename + (if (file-readable-p filename) + (py--execute-file-base (expand-file-name filename) nil nil nil origline) + (message "%s not readable. %s" filename "Do you have write permissions?")) + (py--execute-base beg end shell filename proc wholebuf fast dedicated split switch))))) + +(defun py-execute-block-ipython (&optional dedicated fast split switch proc) + "Send block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-ipython-dedicated (&optional fast split switch proc) + "Send block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-ipython3 (&optional dedicated fast split switch proc) + "Send block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-ipython3-dedicated (&optional fast split switch proc) + "Send block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-jython (&optional dedicated fast split switch proc) + "Send block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-jython-dedicated (&optional fast split switch proc) + "Send block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-python (&optional dedicated fast split switch proc) + "Send block at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-python-dedicated (&optional fast split switch proc) + "Send block at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-python2 (&optional dedicated fast split switch proc) + "Send block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-python2-dedicated (&optional fast split switch proc) + "Send block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-python3 (&optional dedicated fast split switch proc) + "Send block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-python3-dedicated (&optional fast split switch proc) + "Send block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-pypy (&optional dedicated fast split switch proc) + "Send block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-pypy-dedicated (&optional fast split switch proc) + "Send block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block (&optional shell dedicated fast split switch proc) + "Send block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-dedicated (&optional shell fast split switch proc) + "Send block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-ipython (&optional dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-ipython-dedicated (&optional fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-ipython3 (&optional dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-ipython3-dedicated (&optional fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-jython (&optional dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-jython-dedicated (&optional fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-python (&optional dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-python-dedicated (&optional fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-python2 (&optional dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-python2-dedicated (&optional fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-python3 (&optional dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-python3-dedicated (&optional fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-pypy (&optional dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-pypy-dedicated (&optional fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause (&optional shell dedicated fast split switch proc) + "Send block-or-clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-block-or-clause-dedicated (&optional shell fast split switch proc) + "Send block-or-clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'block-or-clause shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-buffer-ipython (&optional dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'ipython dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-ipython-dedicated (&optional fast split switch proc) + "Send buffer at point to a python3 unique interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'ipython t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-ipython3 (&optional dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'ipython3 dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-ipython3-dedicated (&optional fast split switch proc) + "Send buffer at point to a python3 unique interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'ipython3 t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-jython (&optional dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'jython dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-jython-dedicated (&optional fast split switch proc) + "Send buffer at point to a python3 unique interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'jython t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-python (&optional dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'python dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-python-dedicated (&optional fast split switch proc) + "Send buffer at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'python t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-python2 (&optional dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'python2 dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-python2-dedicated (&optional fast split switch proc) + "Send buffer at point to a python3 unique interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'python2 t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-python3 (&optional dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'python3 dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-python3-dedicated (&optional fast split switch proc) + "Send buffer at point to a python3 unique interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'python3 t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-pypy (&optional dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'pypy dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-pypy-dedicated (&optional fast split switch proc) + "Send buffer at point to a python3 unique interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer 'pypy t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer (&optional shell dedicated fast split switch proc) + "Send buffer at point to a python3 interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer shell dedicated switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-buffer-dedicated (&optional shell fast split switch proc) + "Send buffer at point to a python3 unique interpreter." + (interactive) + (let ((py-master-file (or py-master-file (py-fetch-py-master-file))) + (wholebuf t) + filename buffer) + (when py-master-file + (setq filename (expand-file-name py-master-file) + buffer (or (get-file-buffer filename) + (find-file-noselect filename))) + (set-buffer buffer)) + (py--execute-prepare 'buffer shell t switch (point-min) (point-max) nil fast proc wholebuf split))) + +(defun py-execute-class-ipython (&optional dedicated fast split switch proc) + "Send class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-ipython-dedicated (&optional fast split switch proc) + "Send class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-ipython3 (&optional dedicated fast split switch proc) + "Send class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-ipython3-dedicated (&optional fast split switch proc) + "Send class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-jython (&optional dedicated fast split switch proc) + "Send class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-jython-dedicated (&optional fast split switch proc) + "Send class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-python (&optional dedicated fast split switch proc) + "Send class at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-python-dedicated (&optional fast split switch proc) + "Send class at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-python2 (&optional dedicated fast split switch proc) + "Send class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-python2-dedicated (&optional fast split switch proc) + "Send class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-python3 (&optional dedicated fast split switch proc) + "Send class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-python3-dedicated (&optional fast split switch proc) + "Send class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-pypy (&optional dedicated fast split switch proc) + "Send class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-pypy-dedicated (&optional fast split switch proc) + "Send class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class (&optional shell dedicated fast split switch proc) + "Send class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-class-dedicated (&optional shell fast split switch proc) + "Send class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'class shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-ipython (&optional dedicated fast split switch proc) + "Send clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-ipython-dedicated (&optional fast split switch proc) + "Send clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-ipython3 (&optional dedicated fast split switch proc) + "Send clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-ipython3-dedicated (&optional fast split switch proc) + "Send clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-jython (&optional dedicated fast split switch proc) + "Send clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-jython-dedicated (&optional fast split switch proc) + "Send clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-python (&optional dedicated fast split switch proc) + "Send clause at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-python-dedicated (&optional fast split switch proc) + "Send clause at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-python2 (&optional dedicated fast split switch proc) + "Send clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-python2-dedicated (&optional fast split switch proc) + "Send clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-python3 (&optional dedicated fast split switch proc) + "Send clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-python3-dedicated (&optional fast split switch proc) + "Send clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-pypy (&optional dedicated fast split switch proc) + "Send clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-pypy-dedicated (&optional fast split switch proc) + "Send clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause (&optional shell dedicated fast split switch proc) + "Send clause at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-clause-dedicated (&optional shell fast split switch proc) + "Send clause at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'clause shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-ipython (&optional dedicated fast split switch proc) + "Send def at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-ipython-dedicated (&optional fast split switch proc) + "Send def at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-ipython3 (&optional dedicated fast split switch proc) + "Send def at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-ipython3-dedicated (&optional fast split switch proc) + "Send def at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-jython (&optional dedicated fast split switch proc) + "Send def at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-jython-dedicated (&optional fast split switch proc) + "Send def at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-python (&optional dedicated fast split switch proc) + "Send def at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-python-dedicated (&optional fast split switch proc) + "Send def at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-python2 (&optional dedicated fast split switch proc) + "Send def at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-python2-dedicated (&optional fast split switch proc) + "Send def at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-python3 (&optional dedicated fast split switch proc) + "Send def at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-python3-dedicated (&optional fast split switch proc) + "Send def at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-pypy (&optional dedicated fast split switch proc) + "Send def at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-pypy-dedicated (&optional fast split switch proc) + "Send def at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def (&optional shell dedicated fast split switch proc) + "Send def at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-dedicated (&optional shell fast split switch proc) + "Send def at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-ipython (&optional dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-ipython-dedicated (&optional fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-ipython3 (&optional dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-ipython3-dedicated (&optional fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-jython (&optional dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-jython-dedicated (&optional fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-python (&optional dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-python-dedicated (&optional fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-python2 (&optional dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-python2-dedicated (&optional fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-python3 (&optional dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-python3-dedicated (&optional fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-pypy (&optional dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-pypy-dedicated (&optional fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class (&optional shell dedicated fast split switch proc) + "Send def-or-class at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-def-or-class-dedicated (&optional shell fast split switch proc) + "Send def-or-class at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'def-or-class shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-ipython (&optional dedicated fast split switch proc) + "Send expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-ipython-dedicated (&optional fast split switch proc) + "Send expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-ipython3 (&optional dedicated fast split switch proc) + "Send expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-ipython3-dedicated (&optional fast split switch proc) + "Send expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-jython (&optional dedicated fast split switch proc) + "Send expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-jython-dedicated (&optional fast split switch proc) + "Send expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-python (&optional dedicated fast split switch proc) + "Send expression at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-python-dedicated (&optional fast split switch proc) + "Send expression at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-python2 (&optional dedicated fast split switch proc) + "Send expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-python2-dedicated (&optional fast split switch proc) + "Send expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-python3 (&optional dedicated fast split switch proc) + "Send expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-python3-dedicated (&optional fast split switch proc) + "Send expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-pypy (&optional dedicated fast split switch proc) + "Send expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-pypy-dedicated (&optional fast split switch proc) + "Send expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression (&optional shell dedicated fast split switch proc) + "Send expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-expression-dedicated (&optional shell fast split switch proc) + "Send expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'expression shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-ipython (&optional dedicated fast split switch proc) + "Send indent at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-ipython-dedicated (&optional fast split switch proc) + "Send indent at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-ipython3 (&optional dedicated fast split switch proc) + "Send indent at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-ipython3-dedicated (&optional fast split switch proc) + "Send indent at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-jython (&optional dedicated fast split switch proc) + "Send indent at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-jython-dedicated (&optional fast split switch proc) + "Send indent at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-python (&optional dedicated fast split switch proc) + "Send indent at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-python-dedicated (&optional fast split switch proc) + "Send indent at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-python2 (&optional dedicated fast split switch proc) + "Send indent at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-python2-dedicated (&optional fast split switch proc) + "Send indent at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-python3 (&optional dedicated fast split switch proc) + "Send indent at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-python3-dedicated (&optional fast split switch proc) + "Send indent at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-pypy (&optional dedicated fast split switch proc) + "Send indent at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-pypy-dedicated (&optional fast split switch proc) + "Send indent at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent (&optional shell dedicated fast split switch proc) + "Send indent at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-indent-dedicated (&optional shell fast split switch proc) + "Send indent at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'indent shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-ipython (&optional dedicated fast split switch proc) + "Send line at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-ipython-dedicated (&optional fast split switch proc) + "Send line at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-ipython3 (&optional dedicated fast split switch proc) + "Send line at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-ipython3-dedicated (&optional fast split switch proc) + "Send line at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-jython (&optional dedicated fast split switch proc) + "Send line at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-jython-dedicated (&optional fast split switch proc) + "Send line at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-python (&optional dedicated fast split switch proc) + "Send line at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-python-dedicated (&optional fast split switch proc) + "Send line at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-python2 (&optional dedicated fast split switch proc) + "Send line at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-python2-dedicated (&optional fast split switch proc) + "Send line at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-python3 (&optional dedicated fast split switch proc) + "Send line at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-python3-dedicated (&optional fast split switch proc) + "Send line at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-pypy (&optional dedicated fast split switch proc) + "Send line at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-pypy-dedicated (&optional fast split switch proc) + "Send line at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line (&optional shell dedicated fast split switch proc) + "Send line at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-line-dedicated (&optional shell fast split switch proc) + "Send line at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'line shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-ipython (&optional dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-ipython-dedicated (&optional fast split switch proc) + "Send minor-block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-ipython3 (&optional dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-ipython3-dedicated (&optional fast split switch proc) + "Send minor-block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-jython (&optional dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-jython-dedicated (&optional fast split switch proc) + "Send minor-block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-python (&optional dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-python-dedicated (&optional fast split switch proc) + "Send minor-block at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-python2 (&optional dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-python2-dedicated (&optional fast split switch proc) + "Send minor-block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-python3 (&optional dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-python3-dedicated (&optional fast split switch proc) + "Send minor-block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-pypy (&optional dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-pypy-dedicated (&optional fast split switch proc) + "Send minor-block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block (&optional shell dedicated fast split switch proc) + "Send minor-block at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-minor-block-dedicated (&optional shell fast split switch proc) + "Send minor-block at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'minor-block shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-ipython (&optional dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-ipython-dedicated (&optional fast split switch proc) + "Send paragraph at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-ipython3 (&optional dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-ipython3-dedicated (&optional fast split switch proc) + "Send paragraph at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-jython (&optional dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-jython-dedicated (&optional fast split switch proc) + "Send paragraph at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-python (&optional dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-python-dedicated (&optional fast split switch proc) + "Send paragraph at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-python2 (&optional dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-python2-dedicated (&optional fast split switch proc) + "Send paragraph at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-python3 (&optional dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-python3-dedicated (&optional fast split switch proc) + "Send paragraph at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-pypy (&optional dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-pypy-dedicated (&optional fast split switch proc) + "Send paragraph at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph (&optional shell dedicated fast split switch proc) + "Send paragraph at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-paragraph-dedicated (&optional shell fast split switch proc) + "Send paragraph at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'paragraph shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-ipython (&optional dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-ipython-dedicated (&optional fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-ipython3 (&optional dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-ipython3-dedicated (&optional fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-jython (&optional dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-jython-dedicated (&optional fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-python (&optional dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-python-dedicated (&optional fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-python2 (&optional dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-python2-dedicated (&optional fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-python3 (&optional dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-python3-dedicated (&optional fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-pypy (&optional dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-pypy-dedicated (&optional fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression (&optional shell dedicated fast split switch proc) + "Send partial-expression at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-partial-expression-dedicated (&optional shell fast split switch proc) + "Send partial-expression at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'partial-expression shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-region-ipython (beg end &optional dedicated fast split switch proc) + "Send region at point to a python3 interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'ipython dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-ipython-dedicated (beg end &optional fast split switch proc) + "Send region at point to a python3 unique interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'ipython t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-ipython3 (beg end &optional dedicated fast split switch proc) + "Send region at point to a python3 interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'ipython3 dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-ipython3-dedicated (beg end &optional fast split switch proc) + "Send region at point to a python3 unique interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'ipython3 t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-jython (beg end &optional dedicated fast split switch proc) + "Send region at point to a python3 interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'jython dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-jython-dedicated (beg end &optional fast split switch proc) + "Send region at point to a python3 unique interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'jython t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-python (beg end &optional dedicated fast split switch proc) + "Send region at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'python dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-python-dedicated (beg end &optional fast split switch proc) + "Send region at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'python t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-python2 (beg end &optional dedicated fast split switch proc) + "Send region at point to a python3 interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'python2 dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-python2-dedicated (beg end &optional fast split switch proc) + "Send region at point to a python3 unique interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'python2 t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-python3 (beg end &optional dedicated fast split switch proc) + "Send region at point to a python3 interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'python3 dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-python3-dedicated (beg end &optional fast split switch proc) + "Send region at point to a python3 unique interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'python3 t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-pypy (beg end &optional dedicated fast split switch proc) + "Send region at point to a python3 interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'pypy dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-pypy-dedicated (beg end &optional fast split switch proc) + "Send region at point to a python3 unique interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region 'pypy t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region (beg end &optional shell dedicated fast split switch proc) + "Send region at point to a python3 interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region shell dedicated switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-region-dedicated (beg end &optional shell fast split switch proc) + "Send region at point to a python3 unique interpreter." + (interactive "r") + (let ((wholebuf nil)) + (py--execute-prepare 'region shell t switch (or beg (region-beginning)) (or end (region-end)) nil fast proc wholebuf split))) + +(defun py-execute-statement-ipython (&optional dedicated fast split switch proc) + "Send statement at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-ipython-dedicated (&optional fast split switch proc) + "Send statement at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-ipython3 (&optional dedicated fast split switch proc) + "Send statement at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-ipython3-dedicated (&optional fast split switch proc) + "Send statement at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-jython (&optional dedicated fast split switch proc) + "Send statement at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-jython-dedicated (&optional fast split switch proc) + "Send statement at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-python (&optional dedicated fast split switch proc) + "Send statement at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-python-dedicated (&optional fast split switch proc) + "Send statement at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-python2 (&optional dedicated fast split switch proc) + "Send statement at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-python2-dedicated (&optional fast split switch proc) + "Send statement at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-python3 (&optional dedicated fast split switch proc) + "Send statement at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-python3-dedicated (&optional fast split switch proc) + "Send statement at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-pypy (&optional dedicated fast split switch proc) + "Send statement at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-pypy-dedicated (&optional fast split switch proc) + "Send statement at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement (&optional shell dedicated fast split switch proc) + "Send statement at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-statement-dedicated (&optional shell fast split switch proc) + "Send statement at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'statement shell t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-ipython (&optional dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'ipython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-ipython-dedicated (&optional fast split switch proc) + "Send top-level at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'ipython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-ipython3 (&optional dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'ipython3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-ipython3-dedicated (&optional fast split switch proc) + "Send top-level at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'ipython3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-jython (&optional dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'jython dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-jython-dedicated (&optional fast split switch proc) + "Send top-level at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'jython t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-python (&optional dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'python dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-python-dedicated (&optional fast split switch proc) + "Send top-level at point to a python3 unique interpreter. + +For `default' see value of `py-shell-name'" + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'python t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-python2 (&optional dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'python2 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-python2-dedicated (&optional fast split switch proc) + "Send top-level at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'python2 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-python3 (&optional dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'python3 dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-python3-dedicated (&optional fast split switch proc) + "Send top-level at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'python3 t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-pypy (&optional dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'pypy dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-pypy-dedicated (&optional fast split switch proc) + "Send top-level at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level 'pypy t switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level (&optional shell dedicated fast split switch proc) + "Send top-level at point to a python3 interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level shell dedicated switch nil nil nil fast proc wholebuf split))) + +(defun py-execute-top-level-dedicated (&optional shell fast split switch proc) + "Send top-level at point to a python3 unique interpreter." + (interactive) + (let ((wholebuf nil)) + (py--execute-prepare 'top-level shell t switch nil nil nil fast proc wholebuf split))) + +;; python-components-execute + +(defun py-switch-to-python (eob-p) + "Switch to the Python process buffer, maybe starting new process. + +With EOB-P, go to end of buffer." + (interactive "p") + (pop-to-buffer (process-buffer (py-proc)) t) ;Runs python if needed. + (when eob-p + (goto-char (point-max)))) + +;; Split-Windows-On-Execute forms +(defun py-toggle-split-windows-on-execute (&optional arg) + "If `py-split-window-on-execute' should be on or off. + +optional ARG + Returns value of `py-split-window-on-execute' switched to." + (interactive) + (let ((arg (or arg (if py-split-window-on-execute -1 1)))) + (if (< 0 arg) + (setq py-split-window-on-execute t) + (setq py-split-window-on-execute nil)) + (when (called-interactively-p 'any) (message "py-split-window-on-execute: %s" py-split-window-on-execute)) + py-split-window-on-execute)) + +(defun py-split-windows-on-execute-on (&optional arg) + "Make sure, `py-split-window-on-execute' according to ARG. + +Returns value of `py-split-window-on-execute'." + (interactive "p") + (let ((arg (or arg 1))) + (py-toggle-split-windows-on-execute arg)) + (when (called-interactively-p 'any) (message "py-split-window-on-execute: %s" py-split-window-on-execute)) + py-split-window-on-execute) + +(defun py-split-windows-on-execute-off () + "Make sure, `py-split-window-on-execute' is off. + +Returns value of `py-split-window-on-execute'." + (interactive) + (py-toggle-split-windows-on-execute -1) + (when (called-interactively-p 'any) (message "py-split-window-on-execute: %s" py-split-window-on-execute)) + py-split-window-on-execute) + +;; Shell-Switch-Buffers-On-Execute forms +(defun py-toggle-switch-buffers-on-execute (&optional arg) + "If `py-switch-buffers-on-execute-p' according to ARG. + + Returns value of `py-switch-buffers-on-execute-p' switched to." + (interactive) + (let ((arg (or arg (if py-switch-buffers-on-execute-p -1 1)))) + (if (< 0 arg) + (setq py-switch-buffers-on-execute-p t) + (setq py-switch-buffers-on-execute-p nil)) + (when (called-interactively-p 'any) (message "py-shell-switch-buffers-on-execute: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p)) + +(defun py-switch-buffers-on-execute-on (&optional arg) + "Make sure, `py-switch-buffers-on-execute-p' according to ARG. + +Returns value of `py-switch-buffers-on-execute-p'." + (interactive "p") + (let ((arg (or arg 1))) + (py-toggle-switch-buffers-on-execute arg)) + (when (called-interactively-p 'any) (message "py-shell-switch-buffers-on-execute: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p) + +(defun py-switch-buffers-on-execute-off () + "Make sure, `py-switch-buffers-on-execute-p' is off. + +Returns value of `py-switch-buffers-on-execute-p'." + (interactive) + (py-toggle-switch-buffers-on-execute -1) + (when (called-interactively-p 'any) (message "py-shell-switch-buffers-on-execute: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p) + +(defun py-guess-default-python () + "Defaults to \"python\", if guessing didn't succeed." + (interactive) + (let* ((ptn (or py-shell-name (py-choose-shell) "python")) + (erg (if py-edit-only-p ptn (executable-find ptn)))) + (when (called-interactively-p 'any) + (if erg + (message "%s" ptn) + (message "%s" "Could not detect Python on your system"))))) + +;; from ipython.el +(defun py-dirstack-hook () + "To synchronize dir-changes." + (make-local-variable 'shell-dirstack) + (setq shell-dirstack nil) + (make-local-variable 'shell-last-dir) + (setq shell-last-dir nil) + (make-local-variable 'shell-dirtrackp) + (setq shell-dirtrackp t) + (add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t)) + +(defalias 'py-dedicated-shell 'py-shell-dedicated) +(defun py-shell-dedicated (&optional argprompt) + "Start an interpreter in another window according to ARGPROMPT. + +With optional \\[universal-argument] user is prompted by +`py-choose-shell' for command and options to pass to the Python +interpreter." + (interactive "P") + (py-shell argprompt nil t)) + +(defun py-kill-shell-unconditional (&optional shell) + "With optional argument SHELL. + +Otherwise kill default (I)Python shell. +Kill buffer and its process. +Receives a `buffer-name' as argument" + (interactive) + (let ((shell (or shell (py-shell)))) + (ignore-errors (py-kill-buffer-unconditional shell)))) + +(defun py-kill-default-shell-unconditional () + "Kill buffer \"\*Python\*\" and its process." + (interactive) + (ignore-errors (py-kill-buffer-unconditional "*Python*"))) + +(defun py--report-executable (buffer) + (let ((erg (downcase (replace-regexp-in-string + "<\\([0-9]+\\)>" "" + (replace-regexp-in-string + "\*" "" + (if + (string-match " " buffer) + (substring buffer (1+ (string-match " " buffer))) + buffer)))))) + (when (string-match "-" erg) + (setq erg (substring erg 0 (string-match "-" erg)))) + erg)) + +(defun py--guess-buffer-name (argprompt dedicated) + "Guess the `buffer-name' core string according to ARGPROMPT DEDICATED." + (when (and (not dedicated) argprompt + (eq 4 (prefix-numeric-value argprompt))) + (read-buffer "Py-Shell buffer: " + (generate-new-buffer-name (py--choose-buffer-name))))) + +(defun py--configured-shell (name) + "Return the configured PATH/TO/STRING if any according to NAME." + (if (string-match "//\\|\\\\" name) + name + (cond ((string-match "^[Ii]" name) + (or py-ipython-command name)) + ((string-match "[Pp]ython3" name) + (or py-python3-command name)) + ((string-match "[Pp]ython2" name) + (or py-python2-command name)) + ((string-match "[Jj]ython" name) + (or py-jython-command name)) + (t (or py-python-command name))))) + +(defun py--determine-local-default () + (if (not (string= "" py-shell-local-path)) + (expand-file-name py-shell-local-path) + (when py-use-local-default + (error "Abort: `py-use-local-default' is set to t but `py-shell-local-path' is empty. Maybe call `y-toggle-local-default-use'")))) + +(defun py-switch-to-shell () + "Switch to Python process buffer." + (interactive) + (pop-to-buffer (py-shell) t)) + +;; Code execution commands + +(defun py--store-result-maybe (erg) + "If no error occurred and `py-store-result-p' store ERG for yank." + (and (not py-error) erg (or py-debug-p py-store-result-p) (kill-new erg))) + +(defun py-current-working-directory () + "Return the directory of current python SHELL." + (interactive) + (let* ((proc (get-buffer-process (current-buffer))) + erg) + (if proc + (setq erg (py-execute-string (concat "import os\;os.getcwd()") proc nil t)) + (setq erg (replace-regexp-in-string "\n" "" (shell-command-to-string (concat py-shell-name " -c \"import os; print(os.getcwd())\""))))) + (when (called-interactively-p 'interactive) + (message "CWD: %s" erg)) + erg)) + +(defun py-set-working-directory (&optional directory) + "Set working directory according to optional DIRECTORY. + +When given, to value of `py-default-working-directory' otherwise" + (interactive) + (let* ((proc (get-buffer-process (current-buffer))) + (dir (or directory py-default-working-directory)) + erg) + ;; (py-execute-string (concat "import os\;os.chdir(\"" dir "\")") proc nil t) + (py-execute-string (concat "import os\;os.chdir(\"" dir "\")") proc nil t) + (setq erg (py-execute-string "os.getcwd()" proc nil t)) + (when (called-interactively-p 'interactive) + (message "CWD changed to: %s" erg)) + erg)) + +(defun py--update-execute-directory-intern (dir proc procbuf fast) + (let ((strg (concat "import os\;os.chdir(\"" dir "\")"))) + (if fast + (py-fast-send-string strg proc procbuf t t) + (py-execute-string strg proc nil t)))) +;; (comint-send-string proc (concat "import os;os.chdir(\"" dir "\")\n"))) + +(defun py--update-execute-directory (proc procbuf execute-directory fast) + (with-current-buffer procbuf + (let ((cwd (py-current-working-directory))) + (unless (string= execute-directory (concat cwd "/")) + (py--update-execute-directory-intern (or py-execute-directory execute-directory) proc procbuf fast))))) + +(defun py--close-execution (tempbuf tempfile) + "Delete TEMPBUF and TEMPFILE." + (unless py-debug-p + (when tempfile (py-delete-temporary tempfile tempbuf)))) + +(defun py--python-send-setup-code-intern (name buffer) + "Send setup code to BUFFER according to NAME, a string." + (save-excursion + (let ((setup-file (concat (py--normalize-directory py-temp-directory) "py-" name "-setup-code.py")) + py-return-result-p py-store-result-p) + (unless (file-readable-p setup-file) + (with-temp-buffer + (insert (eval (car (read-from-string (concat "py-" name "-setup-code"))))) + (write-file setup-file))) + (py--execute-file-base setup-file (get-buffer-process buffer) nil buffer) + ))) + +(defun py--python-send-completion-setup-code (buffer) + "For Python see py--python-send-setup-code. +Argument BUFFER the buffer completion code is sent to." + (py--python-send-setup-code-intern "shell-completion" buffer)) + +(defun py--ipython-import-module-completion () + "Setup IPython v0.11 or greater. + +Used by `py-ipython-module-completion-string'" + (let ((setup-file (concat (py--normalize-directory py-temp-directory) "py-ipython-module-completion.py"))) + (unless (file-readable-p setup-file) + (with-temp-buffer + (insert py-ipython-module-completion-code) + (write-file setup-file))) + (py--execute-file-base setup-file nil nil (current-buffer) nil t))) + +(defun py-delete-temporary (&optional file filebuf) + (when (file-readable-p file) + (delete-file file)) + (when (buffer-live-p filebuf) + (set-buffer filebuf) + (set-buffer-modified-p 'nil) + (kill-buffer filebuf))) + +(defun py--insert-offset-lines (line) + "Fix offline amount, make error point at the correct LINE." + (insert (make-string (- line (py-count-lines (point-min) (point))) 10))) + +(defun py-execute-string-dedicated (&optional strg shell switch fast) + "Send the argument STRG to an unique Python interpreter. + +Optional SHELL SWITCH FAST +See also `py-execute-region'." + (interactive) + (let ((strg (or strg (read-from-minibuffer "String: "))) + (shell (or shell (default-value 'py-shell-name)))) + (with-temp-buffer + (insert strg) + (py-execute-region (point-min) (point-max) shell t switch fast)))) + +(defun py--insert-execute-directory (directory &optional orig done) + (let ((orig (or orig (point))) + (done done)) + (if done (goto-char done) (goto-char (point-min))) + (cond ((re-search-forward "^from __future__ import " nil t 1) + (py-forward-statement) + (setq done (point)) + (py--insert-execute-directory directory orig done)) + ((re-search-forward py-encoding-string-re nil t 1) + (setq done (point)) + (py--insert-execute-directory directory orig done)) + ((re-search-forward py-shebang-regexp nil t 1) + (setq done (point)) + (py--insert-execute-directory directory orig done)) + (t (forward-line 1) + (unless (eq 9 (char-after)) (newline 1)) + (insert (concat "import os; os.chdir(\"" directory "\")\n")))))) + +;; `py-execute-line' calls void function, lp:1492054, lp:1519859 +(or (functionp 'indent-rigidly-left) + (defun indent-rigidly--pop-undo () + (and (memq last-command '(indent-rigidly-left indent-rigidly-right + indent-rigidly-left-to-tab-stop + indent-rigidly-right-to-tab-stop)) + (consp buffer-undo-list) + (eq (car buffer-undo-list) nil) + (pop buffer-undo-list))) + + (defun indent-rigidly-left (beg end) + "Indent all lines between BEG and END leftward by one space." + (interactive "r") + (indent-rigidly--pop-undo) + (indent-rigidly + beg end + (if (eq (current-bidi-paragraph-direction) 'right-to-left) 1 -1)))) + +(defun py--qualified-module-name (file) + "Return the fully qualified Python module name for FILE. + +FILE is a string. It may be an absolute or a relative path to +any file stored inside a Python package directory, although +typically it would be a (absolute or relative) path to a Python +source code file stored inside a Python package directory. + +This collects all directories names that have a __init__.py +file in them, starting with the directory of FILE and moving up." + (let ((module-name (file-name-sans-extension (file-name-nondirectory file))) + (dirname (file-name-directory (expand-file-name file)))) + (while (file-exists-p (expand-file-name "__init__.py" dirname)) + (setq module-name + (concat + (file-name-nondirectory (directory-file-name dirname)) + "." + module-name)) + (setq dirname (file-name-directory (directory-file-name dirname)))) + module-name)) + +(defun py-execute-import-or-reload (&optional shell) + "Import the current buffer's file in a Python interpreter. + +Optional SHELL +If the file has already been imported, then do reload instead to get +the latest version. + +If the file's name does not end in \".py\", then do execfile instead. + +If the current buffer is not visiting a file, do `py-execute-buffer' +instead. + +If the file local variable `py-master-file' is non-nil, import or +reload the named file instead of the buffer's file. The file may be +saved based on the value of `py-execute-import-or-reload-save-p'. + +See also `\\[py-execute-region]'. + +This may be preferable to `\\[py-execute-buffer]' because: + + - Definitions stay in their module rather than appearing at top + level, where they would clutter the global namespace and not affect + uses of qualified names (MODULE.NAME). + + - The Python debugger gets line number information about the functions." + (interactive) + ;; Check file local variable py-master-file + (when py-master-file + (let* ((filename (expand-file-name py-master-file)) + (buffer (or (get-file-buffer filename) + (find-file-noselect filename)))) + (set-buffer buffer))) + (let ((py-shell-name (or shell (py-choose-shell))) + (file (py--buffer-filename-remote-maybe))) + (if file + (let ((proc (or + (ignore-errors (get-process (file-name-directory shell))) + (get-buffer-process (py-shell nil nil py-dedicated-process-p shell (or shell (default-value 'py-shell-name))))))) + ;; Maybe save some buffers + (save-some-buffers (not py-ask-about-save) nil) + (py--execute-file-base file proc + (if (string-match "\\.py$" file) + (let ((m (py--qualified-module-name (expand-file-name file)))) + (if (string-match "python2" py-shell-name) + (format "import sys\nif sys.modules.has_key('%s'):\n reload(%s)\nelse:\n import %s\n" m m m) + (format "import sys,imp\nif'%s' in sys.modules:\n imp.reload(%s)\nelse:\n import %s\n" m m m))) + ;; (format "execfile(r'%s')\n" file) + (py-execute-file-command file)))) + (py-execute-buffer)))) + +;; python-components-intern + +;; Keymap + +;; Utility stuff + +(defun py--computer-closing-inner-list () + "Compute indentation according to py-closing-list-dedents-bos." + (if py-closing-list-dedents-bos + (+ (current-indentation) py-indent-offset) + (1+ (current-column)))) + +(defun py--compute-closing-outer-list () + "Compute indentation according to py-closing-list-dedents-bos." + (if py-closing-list-dedents-bos + (current-indentation) + (+ (current-indentation) py-indent-offset))) + +(defun py-compute-indentation-according-to-list-style (pps) + "See `py-indent-list-style' + +Choices are: + +\\='line-up-with-first-element (default) +\\='one-level-to-beginning-of-statement +\\='one-level-from-opener + +See also py-closing-list-dedents-bos" + (goto-char (nth 1 pps)) + (cond + ((and (looking-back py-assignment-re (line-beginning-position)) + ;; flexible-indentation-lp-328842 + (not (eq (match-beginning 0) (line-beginning-position)))) + (+ (current-indentation) py-indent-offset)) + (py-closing-list-dedents-bos + (current-indentation)) + (t (pcase py-indent-list-style + (`line-up-with-first-element + (if (and (eq (car (syntax-after (point))) 4) (save-excursion (forward-char 1) (eolp))) + ;; asdf = { + ;; 'a':{ + ;; 'b':3, + ;; 'c':4" + ;; + ;; b is at col 9 + ;; (+ (current-indentation) py-indent-offset) would yield 8 + ;; EOL-case dedent starts if larger at least 2 + (cond ((< 1 (- (1+ (current-column))(+ (current-indentation) py-indent-offset))) + (min (+ (current-indentation) py-indent-offset)(1+ (current-column)))) + (t (1+ (current-column)))) + (1+ (current-column)))) + (`one-level-to-beginning-of-statement + (+ (current-indentation) py-indent-offset)) + (`one-level-from-first-element + (+ 1 (current-column) py-indent-offset)))))) + +(defun py-compute-indentation-closing-list (pps) + (cond + ((< 1 (nth 0 pps)) + (goto-char (nth 1 pps)) + ;; reach the outer list + (goto-char (nth 1 (parse-partial-sexp (point-min) (point)))) + (py--computer-closing-inner-list)) + ;; just close an maybe outer list + ((eq 1 (nth 0 pps)) + (goto-char (nth 1 pps)) + (py-compute-indentation-according-to-list-style pps)))) + +(defun py-compute-indentation-in-list (pps line closing orig) + (if closing + (py-compute-indentation-closing-list pps) + (cond ((and (not line) (looking-back py-assignment-re (line-beginning-position))) + (py--fetch-indent-statement-above orig)) + ;; (py-compute-indentation-according-to-list-style pps iact orig origline line nesting repeat indent-offset liep) + (t (when (looking-back "[ \t]*\\(\\s(\\)" (line-beginning-position)) + (goto-char (match-beginning 1)) + (setq pps (parse-partial-sexp (point-min) (point)))) + (py-compute-indentation-according-to-list-style pps))))) + +(defun py-compute-comment-indentation (pps iact orig origline closing line nesting repeat indent-offset liep) + (cond ((nth 8 pps) + (goto-char (nth 8 pps)) + (cond ((and line (eq (current-column) (current-indentation))) + (current-indentation)) + ((and (eq liep (line-end-position))py-indent-honors-inline-comment) + (current-column)) + ((py--line-backward-maybe) + (setq line t) + (skip-chars-backward " \t") + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + (t (if py-indent-comments + (progn + (py-backward-comment) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + 0)))) + ((and + (looking-at (concat "[ \t]*" comment-start)) + (looking-back "^[ \t]*" (line-beginning-position))(not line) + (eq liep (line-end-position))) + (if py-indent-comments + (progn + (setq line t) + (skip-chars-backward " \t\r\n\f") + ;; as previous comment-line might + ;; be wrongly unindented, travel + ;; whole commented section + (py-backward-comment) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + 0)) + ((and + (looking-at (concat "[ \t]*" comment-start)) + (looking-back "^[ \t]*" (line-beginning-position)) + (not (eq liep (line-end-position)))) + (current-indentation)) + ((and (eq 11 (syntax-after (point))) line py-indent-honors-inline-comment) + (current-column)))) + +(defun py-compute-indentation--at-closer-maybe (pps) + (save-excursion + (when (looking-back "^[ \t]*\\(\\s)\\)" (line-beginning-position)) + (forward-char -1) + (setq pps (parse-partial-sexp (point-min) (point)))) + (when (and (nth 1 pps) + (looking-at "[ \t]*\\(\\s)\\)") (nth 0 pps)) + (cond + ;; no indent at empty argument (list + ((progn (skip-chars-backward " \t\r\n\f") (ignore-errors (eq 4 (car (syntax-after (1- (point))))))) + (current-indentation)) + ;; beyond list start? + ((ignore-errors (< (progn (unless (bobp) (forward-line -1) (line-beginning-position))) (nth 1 (setq pps (parse-partial-sexp (point-min) (point)))))) + (py-compute-indentation-according-to-list-style pps)) + (py-closing-list-dedents-bos + (- (current-indentation) py-indent-offset)) + (t (current-indentation)))))) + +(defun py-compute-indentation (&optional iact orig origline closing line nesting repeat indent-offset liep) + "Compute Python indentation. + +When HONOR-BLOCK-CLOSE-P is non-nil, statements such as `return', +`raise', `break', `continue', and `pass' force one level of dedenting. + +ORIG keeps original position +ORIGLINE keeps line where compute started +CLOSING is t when started at a char delimiting a list as \"]})\" +LINE indicates being not at origline now +NESTING is currently ignored, if executing from inside a list +REPEAT counter enables checks against `py-max-specpdl-size' +INDENT-OFFSET allows calculation of block-local values +LIEP stores line-end-position at point-of-interest +" + (interactive "p") + (save-excursion + (save-restriction + (widen) + ;; in shell, narrow from previous prompt + ;; needed by closing + (let* ((orig (or orig (copy-marker (point)))) + (origline (or origline (py-count-lines (point-min) (point)))) + ;; closing indicates: when started, looked + ;; at a single closing parenthesis + ;; line: moved already a line backward + (liep (or liep (line-end-position))) + (line (or line (not (eq origline (py-count-lines (point-min) (point)))))) + ;; (line line) + (pps (progn + (unless (eq (current-indentation) (current-column))(skip-chars-backward " " (line-beginning-position))) + ;; (when (eq 5 (car (syntax-after (1- (point))))) + ;; (forward-char -1)) + (parse-partial-sexp (point-min) (point)))) + (closing + (or closing + ;; returns update pps + ;; (and line (py-compute-indentation--at-closer-maybe pps)) + (py-compute-indentation--at-closer-maybe pps))) + ;; in a recursive call already + (repeat (if repeat + (setq repeat (1+ repeat)) + 0)) + ;; nesting: started nesting a list + (nesting nesting) + (cubuf (current-buffer)) + erg indent this-line) + (if (and (< repeat 1) + (and (comint-check-proc (current-buffer)) + (re-search-backward (concat py-shell-prompt-regexp "\\|" py-ipython-output-prompt-re "\\|" py-ipython-input-prompt-re) nil t 1))) + ;; common recursion not suitable because of prompt + (with-temp-buffer + ;; (switch-to-buffer (current-buffer)) + (insert-buffer-substring cubuf (match-end 0) orig) + (python-mode) + (setq indent (py-compute-indentation))) + (if (< py-max-specpdl-size repeat) + (error "`py-compute-indentation' reached loops max.") + (setq nesting (nth 0 pps)) + (setq indent + (cond ;; closing) + ((bobp) + (cond ((eq liep (line-end-position)) + 0) + ;; - ((looking-at py-outdent-re) + ;; - (+ (or indent-offset (and py-smart-indentation (py-guess-indent-offset)) py-indent-offset) (current-indentation))) + ((and line (looking-at py-block-or-clause-re)) + py-indent-offset) + ((looking-at py-outdent-re) + (+ (or indent-offset (and py-smart-indentation (py-guess-indent-offset)) py-indent-offset) (current-indentation))) + (t + (current-indentation)))) + ;; (cond ((eq liep (line-end-position)) + ;; 0) + ;; ((looking-at py-outdent-re) + ;; (+ (or indent-offset (and py-smart-indentation (py-guess-indent-offset)) py-indent-offset) (current-indentation))) + ;; (t + ;; (current-indentation))) + ;; in string + ((and (nth 3 pps) (nth 8 pps)) + (cond + ((py--docstring-p (nth 8 pps)) + (save-excursion + ;; (goto-char (match-beginning 0)) + (back-to-indentation) + (if (looking-at "[uUrR]?\"\"\"\\|[uUrR]?'''") + (progn + (skip-chars-backward " \t\r\n\f") + (back-to-indentation) + (if (looking-at py-def-or-class-re) + (+ (current-column) py-indent-offset) + (current-indentation))) + (skip-chars-backward " \t\r\n\f") + (back-to-indentation) + (current-indentation)))) + (t 0))) + ((and (looking-at "\"\"\"\\|'''") (not (bobp))) + (py-backward-statement) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + ;; comments + ((or + (nth 8 pps) + (and + (looking-at (concat "[ \t]*" comment-start)) + (looking-back "^[ \t]*" (line-beginning-position))(not line)) + (and (eq 11 (syntax-after (point))) line py-indent-honors-inline-comment)) + (py-compute-comment-indentation pps iact orig origline closing line nesting repeat indent-offset liep)) + ;; lists + ((nth 1 pps) + (if (< (nth 1 pps) (line-beginning-position)) + ;; Compute according to `py-indent-list-style' + + ;; Choices are: + + ;; \\='line-up-with-first-element (default) + ;; \\='one-level-to-beginning-of-statement + ;; \\='one-level-from-opener" + + ;; See also py-closing-list-dedents-bos + (py-compute-indentation-in-list pps line closing orig) + (back-to-indentation) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep))) + ((and (eq (char-after) (or ?\( ?\{ ?\[)) line) + (1+ (current-column))) + ((py-preceding-line-backslashed-p) + (progn + (py-backward-statement) + (setq this-line (py-count-lines)) + (if (< 1 (- origline this-line)) + (py--fetch-indent-line-above orig) + (if (looking-at "from +\\([^ \t\n]+\\) +import") + py-backslashed-lines-indent-offset + (+ (current-indentation) py-continuation-offset))))) + ((and (looking-at py-block-closing-keywords-re) + (eq liep (line-end-position))) + (skip-chars-backward "[ \t\r\n\f]") + (py-backward-statement) + (cond ((looking-at py-extended-block-or-clause-re) + (+ + ;; (if py-smart-indentation (py-guess-indent-offset) indent-offset) + (or indent-offset (and py-smart-indentation (py-guess-indent-offset)) py-indent-offset) + (current-indentation))) + ((looking-at py-block-closing-keywords-re) + (- (current-indentation) (or indent-offset py-indent-offset))) + (t (current-column)))) + ((looking-at py-block-closing-keywords-re) + (if (< (line-end-position) orig) + ;; #80, Lines after return cannot be correctly indented + (if (looking-at "return[ \\t]*$") + (current-indentation) + (- (current-indentation) (or indent-offset py-indent-offset))) + (py-backward-block-or-clause) + (current-indentation))) + ;; ((and (looking-at py-elif-re) (eq (py-count-lines) origline)) + ;; (when (py--line-backward-maybe) (setq line t)) + ;; (car (py--clause-lookup-keyword py-elif-re -1 nil origline))) + ((and (looking-at py-minor-clause-re) (not line) + (eq liep (line-end-position))) + + (cond + ((looking-at py-case-re) + (py--backward-regexp 'py-match-case-re) (+ (current-indentation) py-indent-offset)) + ((looking-at py-outdent-re) + ;; (and (py--backward-regexp 'py-block-or-clause-re) (current-indentation))) + (and (py--go-to-keyword 'py-block-or-clause-re (current-indentation) '< t) (current-indentation))) + ((bobp) 0) + (t (save-excursion + ;; (skip-chars-backward " \t\r\n\f") + (if (py-backward-block) + ;; (py--backward-regexp 'py-block-or-clause-re) + (+ py-indent-offset (current-indentation)) + 0))))) + ((looking-at py-extended-block-or-clause-re) + (cond ((and (not line) + (eq liep (line-end-position))) + (when (py--line-backward-maybe) (setq line t)) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + (t (+ + (cond (indent-offset) + (py-smart-indentation + (py-guess-indent-offset)) + (t py-indent-offset)) + (current-indentation))))) + ((and + (< (line-end-position) liep) + (eq (current-column) (current-indentation))) + (and + (looking-at py-assignment-re) + (goto-char (match-end 0))) + ;; multiline-assignment + (if (and nesting (looking-at " *[[{(]") (not (looking-at ".+[]})][ \t]*$"))) + (+ (current-indentation) (or indent-offset py-indent-offset)) + (current-indentation))) + ((looking-at py-assignment-re) + (py-backward-statement) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + ((and (< (current-indentation) (current-column))(not line)) + (back-to-indentation) + (unless line + (setq nesting (nth 0 (parse-partial-sexp (point-min) (point))))) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + ((and (not (py--beginning-of-statement-p)) (not (and line (eq 11 (syntax-after (point)))))) + (if (bobp) + (current-column) + (if (eq (point) orig) + (progn + (when (py--line-backward-maybe) (setq line t)) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + (py-backward-statement) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)))) + ((or (py--statement-opens-block-p py-extended-block-or-clause-re) (looking-at "@")) + (if (< (py-count-lines) origline) + (+ (or indent-offset (and py-smart-indentation (py-guess-indent-offset)) py-indent-offset) (current-indentation)) + (skip-chars-backward " \t\r\n\f") + (setq line t) + (back-to-indentation) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep))) + ((and py-empty-line-closes-p (py--after-empty-line)) + (progn (py-backward-statement) + (- (current-indentation) (or indent-offset py-indent-offset)))) + ;; still at orignial line + ((and (eq liep (line-end-position)) + (save-excursion + (and (setq erg (py--go-to-keyword 'py-extended-block-or-clause-re (* py-indent-offset 99))) + ;; maybe Result: (nil nil nil), which evaluates to `t' + (not (bobp)) + (if (and (not indent-offset) py-smart-indentation) (setq indent-offset (py-guess-indent-offset)) t) + (ignore-errors (< orig (or (py-forward-block-or-clause) (point))))))) + (+ (car erg) (if py-smart-indentation + (or indent-offset (py-guess-indent-offset)) + (or indent-offset py-indent-offset)))) + ((and (not line) + (eq liep (line-end-position)) + (py--beginning-of-statement-p)) + (py-backward-statement) + (py-compute-indentation iact orig origline closing line nesting repeat indent-offset liep)) + (t (current-indentation)))) + (when py-verbose-p (message "%s" indent)) + indent)))))) + +(defun py--uncomment-intern (beg end) + (uncomment-region beg end) + (when py-uncomment-indents-p + (py-indent-region beg end))) + +(defun py-uncomment (&optional beg) + "Uncomment commented lines at point. + +If region is active, restrict uncommenting at region " + (interactive "*") + (save-excursion + (save-restriction + (when (use-region-p) + (narrow-to-region (region-beginning) (region-end))) + (let* (last + (beg (or beg (save-excursion + (while (and (py-backward-comment) (setq last (point))(prog1 (forward-line -1)(end-of-line)))) + last)))) + (and (py-forward-comment)) + (py--uncomment-intern beg (point)))))) + +(defun py-load-named-shells () + (interactive) + (dolist (ele py-known-shells) + (let ((erg (py-install-named-shells-fix-doc ele))) + (eval (fset (car (read-from-string ele)) (car + (read-from-string (concat "(lambda (&optional dedicated args) \"Start a `" erg "' interpreter. +Optional DEDICATED: with \\\\[universal-argument] start in a new +dedicated shell. +Optional ARGS overriding `py-" ele "-command-args'. + +Calls `py-shell' +\" + (interactive \"p\") (py-shell dedicated args nil \""ele"\"))"))))))) + (when (functionp (car (read-from-string (car-safe py-known-shells)))) + (when py-verbose-p (message "py-load-named-shells: %s" "installed named-shells")))) + +;; (py-load-named-shells) + +(defun py-load-file (file-name) + "Load a Python file FILE-NAME into the Python process. + +If the file has extension `.py' import or reload it as a module. +Treating it as a module keeps the global namespace clean, provides +function location information for debugging, and supports users of +module-qualified names." + (interactive "f") + (py--execute-file-base file-name (get-buffer-process (get-buffer (py-shell))))) + +;; Hooks +;; arrange to kill temp files when Emacs exists + +(when py--warn-tmp-files-left-p + (add-hook 'python-mode-hook 'py--warn-tmp-files-left)) + +(defun py-guess-pdb-path () + "If py-pdb-path isn't set, find location of pdb.py. " + (interactive) + (let ((ele (split-string (shell-command-to-string "whereis python"))) + erg) + (while (or (not erg)(string= "" erg)) + (when (and (string-match "^/" (car ele)) (not (string-match "/man" (car ele)))) + (setq erg (shell-command-to-string (concat "find " (car ele) " -type f -name \"pdb.py\"")))) + (setq ele (cdr ele))) + (if erg + (message "%s" erg) + (message "%s" "pdb.py not found, please customize `py-pdb-path'")) + erg)) + +(if py-mode-output-map + nil + (setq py-mode-output-map (make-sparse-keymap)) + (define-key py-mode-output-map [button2] 'py-mouseto-exception) + (define-key py-mode-output-map "\C-c\C-c" 'py-goto-exception) + ;; TBD: Disable all self-inserting keys. This is bogus, we should + ;; really implement this as *Python Output* buffer being read-only + (mapc #' (lambda (key) + (define-key py-mode-output-map key + #'(lambda () (interactive) (beep)))) + (where-is-internal 'self-insert-command))) + +(defun py-toggle-comment-auto-fill (&optional arg) + "Toggles comment-auto-fill mode" + (interactive "P") + (if (or (and arg (< 0 (prefix-numeric-value arg))) + (and (boundp 'py-comment-auto-fill-p)(not py-comment-auto-fill-p))) + (progn + (set (make-local-variable 'py-comment-auto-fill-p) t) + (setq fill-column py-comment-fill-column) + (auto-fill-mode 1)) + (set (make-local-variable 'py-comment-auto-fill-p) nil) + (auto-fill-mode -1))) + +(defun py-comment-auto-fill-on () + (interactive) + (py-toggle-comment-auto-fill 1)) + +(defun py-comment-auto-fill-off () + (interactive) + (py-toggle-comment-auto-fill -1)) + +(defun py--set-auto-fill-values () + "Internal use by `py--run-auto-fill-timer'" + (let ((pps (parse-partial-sexp (point-min) (point)))) + (cond ((and (nth 4 pps)(numberp py-comment-fill-column)) + (setq fill-column py-comment-fill-column)) + ((and (nth 3 pps)(numberp py-docstring-fill-column)) + (setq fill-column py-docstring-fill-column)) + (t (setq fill-column py-fill-column-orig))))) + +(defun py--run-auto-fill-timer () + "Set fill-column to values according to environment. + +`py-docstring-fill-column' resp. to `py-comment-fill-column'." + (when py-auto-fill-mode + (unless py-autofill-timer + (setq py-autofill-timer + (run-with-idle-timer + py-autofill-timer-delay t + 'py--set-auto-fill-values))))) + +;; unconditional Hooks +;; (orgstruct-mode 1) + +(defun py-complete-auto () + "Auto-complete function using py-complete. " + ;; disable company + ;; (when company-mode (company-mode)) + (let ((modified (buffer-chars-modified-tick))) + ;; don't try completion if buffer wasn't modified + (unless (eq modified py-complete-last-modified) + (if py-auto-completion-mode-p + (if (string= "*PythonCompletions*" (buffer-name (current-buffer))) + (sit-for 0.1 t) + (if + (eq py-auto-completion-buffer (current-buffer)) + ;; not after whitespace, TAB or newline + (unless (member (char-before) (list 32 9 10)) + (py-complete) + (setq py-complete-last-modified (buffer-chars-modified-tick))) + (setq py-auto-completion-mode-p nil + py-auto-completion-buffer nil) + (cancel-timer py--auto-complete-timer))))))) + +;; End-of- p + +;; Opens +(defun py--statement-opens-block-p (&optional regexp) + "Return position if the current statement opens a block +in stricter or wider sense. + +For stricter sense specify regexp. " + (let* ((regexp (or regexp py-block-or-clause-re)) + (erg (py--statement-opens-base regexp))) + erg)) + +(defun py--statement-opens-base (regexp) + (let ((orig (point)) + erg) + (save-excursion + (back-to-indentation) + (py-forward-statement) + (py-backward-statement) + (when (and + (<= (line-beginning-position) orig)(looking-back "^[ \t]*" (line-beginning-position))(looking-at regexp)) + (setq erg (point)))) + erg)) + +(defun py--statement-opens-clause-p () + "Return position if the current statement opens block or clause. " + (py--statement-opens-base py-clause-re)) + +(defun py--statement-opens-block-or-clause-p () + "Return position if the current statement opens block or clause. " + (py--statement-opens-base py-block-or-clause-re)) + +(defun py--statement-opens-class-p () + "If the statement opens a functions or class. + +Return `t', nil otherwise. " + (py--statement-opens-base py-class-re)) + +(defun py--statement-opens-def-p () + "If the statement opens a functions or class. +Return `t', nil otherwise. " + (py--statement-opens-base py-def-re)) + +(defun py--statement-opens-def-or-class-p () + "If the statement opens a functions or class definition. +Return `t', nil otherwise. " + (py--statement-opens-base py-def-or-class-re)) + +(defun py--down-top-level (&optional regexp) + "Go to the end of a top-level form. + +When already at end, go to EOB." + (end-of-line) + (while (and (py--forward-regexp (or regexp "^[[:graph:]]")) + (save-excursion + (beginning-of-line) + (or + (looking-at py-clause-re) + (looking-at comment-start))))) + (beginning-of-line) + (and (looking-at regexp) (point))) + +(defun py--end-of-paragraph (regexp) + (let* ((regexp (if (symbolp regexp) (symbol-value regexp) + regexp))) + (while (and (not (eobp)) (re-search-forward regexp nil 'move 1) (nth 8 (parse-partial-sexp (point-min) (point))))))) + +(defun py--look-downward-for-beginning (regexp) + "When above any beginning of FORM, search downward. " + (let* ((orig (point)) + (erg orig) + pps) + (while (and (not (eobp)) (re-search-forward regexp nil t 1) (setq erg (match-beginning 0)) (setq pps (parse-partial-sexp (point-min) (point))) + (or (nth 8 pps) (nth 1 pps)))) + (cond ((not (or (nth 8 pps) (nth 1 pps) (or (looking-at comment-start)))) + (when (ignore-errors (< orig erg)) + erg))))) + +(defun py-look-downward-for-clause (&optional ind orig regexp) + "If beginning of other clause exists downward in current block. + +If succesful return position. " + (interactive) + (unless (eobp) + (let ((ind (or ind + (save-excursion + (py-backward-statement) + (if (py--statement-opens-block-p) + (current-indentation) + (- (current-indentation) py-indent-offset))))) + (orig (or orig (point))) + (regexp (or regexp py-extended-block-or-clause-re)) + erg) + (end-of-line) + (when (re-search-forward regexp nil t 1) + (when (nth 8 (parse-partial-sexp (point-min) (point))) + (while (and (re-search-forward regexp nil t 1) + (nth 8 (parse-partial-sexp (point-min) (point)))))) + ;; (setq last (point)) + (back-to-indentation) + (unless (and (looking-at py-clause-re) + (not (nth 8 (parse-partial-sexp (point-min) (point)))) (eq (current-indentation) ind)) + (progn (setq ind (current-indentation)) + (while (and (py-forward-statement-bol)(not (looking-at py-clause-re))(<= ind (current-indentation))))) + (if (and (looking-at py-clause-re) + (not (nth 8 (parse-partial-sexp (point-min) (point)))) + (< orig (point))) + (setq erg (point)) + (goto-char orig)))) + erg))) + +(defun py-current-defun () + "Go to the outermost method or class definition in current scope. + +Python value for `add-log-current-defun-function'. +This tells add-log.el how to find the current function/method/variable. +Returns name of class or methods definition, if found, nil otherwise. + +See customizable variables `py-current-defun-show' and `py-current-defun-delay'." + (interactive) + (save-restriction + (widen) + (save-excursion + (let ((erg (when (py-backward-def-or-class) + (forward-word 1) + (skip-chars-forward " \t") + (prin1-to-string (symbol-at-point))))) + (when (and erg py-current-defun-show) + (push-mark (point) t t) (skip-chars-forward "^ (") + (exchange-point-and-mark) + (sit-for py-current-defun-delay t)) + erg)))) + +(defun py--join-words-wrapping (words separator prefix line-length) + (let ((lines ()) + (current-line prefix)) + (while words + (let* ((word (car words)) + (maybe-line (concat current-line word separator))) + (if (> (length maybe-line) line-length) + (setq lines (cons (substring current-line 0 -1) lines) + current-line (concat prefix word separator " ")) + (setq current-line (concat maybe-line " ")))) + (setq words (cdr words))) + (setq lines (cons (substring current-line 0 (- 0 (length separator) 1)) lines)) + (mapconcat 'identity (nreverse lines) "\n"))) + +(defun py-sort-imports () + "Sort multiline imports. + +Put point inside the parentheses of a multiline import and hit +\\[py-sort-imports] to sort the imports lexicographically" + (interactive) + (save-excursion + (let ((open-paren (ignore-errors (save-excursion (progn (up-list -1) (point))))) + (close-paren (ignore-errors (save-excursion (progn (up-list 1) (point))))) + sorted-imports) + (when (and open-paren close-paren) + (goto-char (1+ open-paren)) + (skip-chars-forward " \n\t") + (setq sorted-imports + (sort + (delete-dups + (split-string (buffer-substring + (point) + (save-excursion (goto-char (1- close-paren)) + (skip-chars-backward " \n\t") + (point))) + ", *\\(\n *\\)?")) + ;; XXX Should this sort case insensitively? + 'string-lessp)) + ;; Remove empty strings. + (delete-region open-paren close-paren) + (goto-char open-paren) + (insert "(\n") + (insert (py--join-words-wrapping (remove "" sorted-imports) "," " " 78)) + (insert ")"))))) + +(defun py--in-literal (&optional lim) + "Return non-nil if point is in a Python literal (a comment or string). +Optional argument LIM indicates the beginning of the containing form, +i.e. the limit on how far back to scan." + (let* ((lim (or lim (point-min))) + (state (parse-partial-sexp lim (point)))) + (cond + ((nth 3 state) 'string) + ((nth 4 state) 'comment)))) + +(defconst py-help-address "python-mode@python.org" + "List dealing with usage and developing python-mode. + +Also accepts submission of bug reports, whilst a ticket at +http://launchpad.net/python-mode +is preferable for that. ") + +;; Utilities + +(defun py-install-local-shells (&optional local) + "Builds Python-shell commands from executable found in LOCAL. + +If LOCAL is empty, shell-command `find' searches beneath current directory. +Eval resulting buffer to install it, see customizable `py-extensions'. " + (interactive) + (let* ((local-dir (if local + (expand-file-name local) + (read-from-minibuffer "Virtualenv directory: " default-directory))) + (path-separator (if (string-match "/" local-dir) + "/" + "\\" t)) + (shells (split-string (shell-command-to-string (concat "find " local-dir " -maxdepth 9 -type f -executable -name \"*python\"")))) + prefix end orig curexe aktpath) + (set-buffer (get-buffer-create py-extensions)) + (erase-buffer) + (dolist (elt shells) + (setq prefix "") + (setq curexe (substring elt (1+ (string-match "/[^/]+$" elt)))) + (setq aktpath (substring elt 0 (1+ (string-match "/[^/]+$" elt)))) + (dolist (prf (split-string aktpath (regexp-quote path-separator))) + (unless (string= "" prf) + (setq prefix (concat prefix (substring prf 0 1))))) + (setq orig (point)) + (insert py-shell-template) + (setq end (point)) + (goto-char orig) + (when (re-search-forward "\\" end t 1) + (replace-match (concat prefix "-" (substring elt (1+ (save-match-data (string-match "/[^/]+$" elt)))))t)) + (goto-char orig) + (while (search-forward "DOCNAME" end t 1) + (replace-match (if (string= "ipython" curexe) + "IPython" + (capitalize curexe)) t)) + (goto-char orig) + (when (search-forward "FULLNAME" end t 1) + (replace-match elt t)) + (goto-char (point-max))) + (emacs-lisp-mode) + (if (file-readable-p (concat py-install-directory "/" py-extensions)) + (find-file (concat py-install-directory "/" py-extensions))))) + +(defun py--until-found (search-string liste) + "Search liste for search-string until found. " + (let ((liste liste) element) + (while liste + (if (member search-string (car liste)) + (setq element (car liste) liste nil)) + (setq liste (cdr liste))) + (when element + (while (and element (not (numberp element))) + (if (member search-string (car element)) + (setq element (car element)) + (setq element (cdr element)))) + element))) + +(defun py--report-end-marker (process) + ;; (message "py--report-end-marker in %s" (current-buffer)) + (if (derived-mode-p 'comint-mode) + (if (bound-and-true-p comint-last-prompt) + (car-safe comint-last-prompt) + (dotimes (_ 3) (when (not (bound-and-true-p comint-last-prompt)))(sit-for 1 t)) + (and (bound-and-true-p comint-last-prompt) + (car-safe comint-last-prompt))) + (if (markerp (process-mark process)) + (process-mark process) + (progn + (dotimes (_ 3) (when (not (markerp (process-mark process)))(sit-for 1 t))) + (process-mark process))))) + +(defun py-which-def-or-class (&optional orig) + "Returns concatenated `def' and `class' names. + +In hierarchical order, if cursor is inside. + +Returns \"???\" otherwise +Used by variable `which-func-functions' " + (interactive) + (let* ((orig (or orig (point))) + (backindent 99999) + (re py-def-or-class-re + ;; (concat py-def-or-class-re "\\([[:alnum:]_]+\\)") + ) + erg forward indent backward limit) + (if + (and (looking-at re) + (not (nth 8 (parse-partial-sexp (point-min) (point))))) + (progn + (setq erg (list (match-string-no-properties 2))) + (setq backindent (current-indentation))) + ;; maybe inside a definition's symbol + (or (eolp) (and (looking-at "[[:alnum:]]")(forward-word 1)))) + (if + (and (not (and erg (eq 0 (current-indentation)))) + (setq limit (py-backward-top-level)) + (looking-at re)) + (progn + (push (match-string-no-properties 2) erg) + (setq indent (current-indentation))) + (goto-char orig) + (while (and + (re-search-backward py-def-or-class-re limit t 1) + (< (current-indentation) backindent) + (setq backindent (current-indentation)) + (setq backward (point)) + (or (< 0 (current-indentation)) + (nth 8 (parse-partial-sexp (point-min) (point)))))) + (when (and backward + (goto-char backward) + (looking-at re)) + (push (match-string-no-properties 2) erg) + (setq indent (current-indentation)))) + ;; (goto-char orig)) + (if erg + (progn + (end-of-line) + (while (and (re-search-forward py-def-or-class-re nil t 1) + (<= (point) orig) + (< indent (current-indentation)) + (or + (nth 8 (parse-partial-sexp (point-min) (point))) + (setq forward (point))))) + (if forward + (progn + (goto-char forward) + (save-excursion + (back-to-indentation) + (and (looking-at re) + (setq erg (list (car erg) (match-string-no-properties 2))) + ;; (< (py-forward-def-or-class) orig) + ;; if match was beyond definition, nil + ;; (setq erg nil) +))) + (goto-char orig)))) + (if erg + (if (< 1 (length erg)) + (setq erg (mapconcat 'identity erg ".")) + (setq erg (car erg))) + (setq erg "???")) + (goto-char orig) + erg)) + +(defun py--fetch-first-python-buffer () + "Returns first (I)Python-buffer found in `buffer-list'" + (let ((buli (buffer-list)) + erg) + (while (and buli (not erg)) + (if (string-match "Python" (prin1-to-string (car buli))) + (setq erg (car buli)) + (setq buli (cdr buli)))) + erg)) + +(defun py-unload-python-el () + "Unloads python-mode delivered by shipped python.el + +Removes python-skeleton forms from abbrevs. +These would interfere when inserting forms heading a block" + (interactive) + (let (done) + (when (featurep 'python) (unload-feature 'python t)) + (when (file-readable-p abbrev-file-name) + (find-file abbrev-file-name) + (goto-char (point-min)) + (while (re-search-forward "^.+python-skeleton.+$" nil t 1) + (setq done t) + (delete-region (match-beginning 0) (1+ (match-end 0)))) + (when done (write-file abbrev-file-name) + ;; now reload + (read-abbrev-file abbrev-file-name)) + (kill-buffer (file-name-nondirectory abbrev-file-name))))) + +(defmacro py--kill-buffer-unconditional (buffer) + "Kill buffer unconditional, kill buffer-process if existing. " + `(let ((proc (get-buffer-process ,buffer)) + kill-buffer-query-functions) + (ignore-errors + (and proc (kill-process proc)) + (set-buffer ,buffer) + (set-buffer-modified-p 'nil) + (kill-buffer (current-buffer))))) + +(defun py-down-top-level () + "Go to beginning of next top-level form downward. + +Returns position if successful, nil otherwise" + (interactive) + (let ((orig (point)) + erg) + (while (and (not (eobp)) + (progn (end-of-line) + (re-search-forward "^[[:alpha:]_'\"]" nil 'move 1)) + (nth 8 (parse-partial-sexp (point-min) (point))))) + (when (and (not (eobp)) (< orig (point))) + (goto-char (match-beginning 0)) + (setq erg (point))) + erg)) + +(defun py-forward-top-level-bol () + "Go to end of top-level form at point, stop at next beginning-of-line. + +Returns position successful, nil otherwise" + (interactive) + (let (erg) + (py-forward-top-level) + (unless (or (eobp) (bolp)) + (forward-line 1) + (beginning-of-line) + (setq erg (point))) + erg)) + +(defun py-down (&optional indent) + "Go to beginning one level below. + +Of compound statement or definition at point. + +Also honor a delimited form -- string or list. +Repeated call from there will behave like down-list. + +Returns position if successful, nil otherwise" + (interactive) + (let* ((orig (point)) + erg + (indent (or + indent + (if + (py--beginning-of-statement-p) + (current-indentation) + (progn + (py-backward-statement) + (current-indentation)))))) + (while (and (py-forward-statement) (py-forward-statement) (py-backward-statement) (> (current-indentation) indent))) + (cond ((= indent (current-indentation)) + (setq erg (point))) + ((< (point) orig) + (goto-char orig)) + ((and (eq (point) orig) + (progn (forward-char 1) + (skip-chars-forward "^\"'[({" (line-end-position)) + (member (char-after) (list ?\( ?\" ?\' ?\[ ?\{))) + (setq erg (point))))) + (unless erg + (goto-char orig)) + erg)) + +(defun py--thing-at-point (form &optional mark-decorators) + "Returns buffer-substring of string-argument FORM as cons. + +Text properties are stripped. +If PY-MARK-DECORATORS, `def'- and `class'-forms include decorators +If BOL is t, from beginning-of-line" + (interactive) + (let* ((begform (intern-soft (concat "py-backward-" form))) + (endform (intern-soft (concat "py-forward-" form))) + (begcheckform (intern-soft (concat "py--beginning-of-" form "-p"))) + (orig (point)) + beg end erg) + (setq beg (if + (setq beg (funcall begcheckform)) + beg + (funcall begform))) + (and mark-decorators + (and (setq erg (py-backward-decorator)) + (setq beg erg))) + (setq end (funcall endform)) + (unless end (when (< beg (point)) + (setq end (point)))) + (if (and beg end (<= beg orig) (<= orig end)) + (buffer-substring-no-properties beg end) + nil))) + +(defun py--thing-at-point-bol (form &optional mark-decorators) + (let* ((begform (intern-soft (concat "py-backward-" form "-bol"))) + (endform (intern-soft (concat "py-forward-" form "-bol"))) + (begcheckform (intern-soft (concat "py--beginning-of-" form "-bol-p"))) + beg end erg) + (setq beg (if + (setq beg (funcall begcheckform)) + beg + (funcall begform))) + (when mark-decorators + (save-excursion + (when (setq erg (py-backward-decorator)) + (setq beg erg)))) + (setq end (funcall endform)) + (unless end (when (< beg (point)) + (setq end (point)))) + (cons beg end))) + +(defun py--mark-base (form &optional mark-decorators) + "Returns boundaries of FORM, a cons. + +If PY-MARK-DECORATORS, `def'- and `class'-forms include decorators +If BOL is t, mark from beginning-of-line" + (let* ((begform (intern-soft (concat "py-backward-" form))) + (endform (intern-soft (concat "py-forward-" form))) + (begcheckform (intern-soft (concat "py--beginning-of-" form "-p"))) + (orig (point)) + beg end erg) + (setq beg (if + (setq beg (funcall begcheckform)) + beg + (funcall begform))) + (and mark-decorators + (and (setq erg (py-backward-decorator)) + (setq beg erg))) + (push-mark) + (setq end (funcall endform)) + (unless end (when (< beg (point)) + (setq end (point)))) + (if (and beg end (<= beg orig) (<= orig end)) + (progn + (cons beg end) + (exchange-point-and-mark)) + nil))) + +(defun py--mark-base-bol (form &optional mark-decorators) + (let* ((begform (intern-soft (concat "py-backward-" form "-bol"))) + (endform (intern-soft (concat "py-forward-" form "-bol"))) + (begcheckform (intern-soft (concat "py--beginning-of-" form "-bol-p"))) + beg end erg) + (if (functionp begcheckform) + (or (setq beg (funcall begcheckform)) + (if (functionp begform) + (setq beg (funcall begform)) + (error (concat "py--mark-base-bol: " begform " don't exist!" )))) + (error (concat "py--mark-base-bol: " begcheckform " don't exist!" ))) + (when mark-decorators + (save-excursion + (when (setq erg (py-backward-decorator)) + (setq beg erg)))) + (if (functionp endform) + (setq end (funcall endform)) + (error (concat "py--mark-base-bol: " endform " don't exist!" ))) + (push-mark beg t t) + (unless end (when (< beg (point)) + (setq end (point)))) + (cons beg end))) + +(defun py-mark-base (form &optional mark-decorators) + "Calls py--mark-base, returns bounds of form, a cons. " + (let* ((bounds (py--mark-base form mark-decorators)) + (beg (car bounds))) + (push-mark beg t t) + bounds)) + +(defun py-backward-same-level-intern (indent) + (while (and + (py-backward-statement) + (< indent (current-indentation) )))) + +(defun py-backward-same-level () + "Go form backward keeping indent level if possible. + +If inside a delimited form --string or list-- go to its beginning. +If not at beginning of a statement or block, go to its beginning. +If at beginning of a statement or block, +go to previous beginning of at point. +If no further element at same level, go one level up." + (interactive) + (let* ((pps (parse-partial-sexp (point-min) (point))) + (erg (cond ((nth 8 pps) (goto-char (nth 8 pps))) + ((nth 1 pps) (goto-char (nth 1 pps))) + (t (if (eq (current-column) (current-indentation)) + (py-backward-same-level-intern (current-indentation)) + (back-to-indentation) + (py-backward-same-level)))))) + erg)) + +(defun py-forward-same-level () + "Go form forward keeping indent level if possible. + +If inside a delimited form --string or list-- go to its beginning. +If not at beginning of a statement or block, go to its beginning. +If at beginning of a statement or block, go to previous beginning. +If no further element at same level, go one level up." + (interactive) + (let (erg) + (unless (py-beginning-of-statement-p) + (py-backward-statement)) + (setq erg (py-down (current-indentation))) + erg)) + +(defun py--end-of-buffer-p () + "Returns position, if cursor is at the end of buffer, nil otherwise. " + (when (eobp)(point))) + +(defun py-sectionize-region (&optional beg end) + "Markup code in region as section. + +Use current region unless optional args BEG END are delivered." + (interactive "*") + (let ((beg (or beg (region-beginning))) + (end (or (and end (copy-marker end)) (copy-marker (region-end))))) + (save-excursion + (goto-char beg) + (unless (py-empty-line-p) (split-line)) + (beginning-of-line) + (insert py-section-start) + (goto-char end) + (unless (py-empty-line-p) (newline 1)) + (insert py-section-end)))) + +(defun py-execute-section-prepare (&optional shell) + "Execute section at point. " + (save-excursion + (let ((start (when (or (py--beginning-of-section-p) + (py-backward-section)) + (forward-line 1) + (beginning-of-line) + (point)))) + (if (and start (py-forward-section)) + (progn + (beginning-of-line) + (skip-chars-backward " \t\r\n\f") + (if shell + (funcall (car (read-from-string (concat "py-execute-region-" shell))) start (point)) + (py-execute-region start (point)))) + (error "Can't see `py-section-start' resp. `py-section-end'"))))) + +(defun py--narrow-prepare (name) + "Used internally. " + (save-excursion + (let ((start (cond ((string= name "statement") + (if (py--beginning-of-statement-p) + (point) + (py-backward-statement-bol))) + ((funcall (car (read-from-string (concat "py--statement-opens-" name "-p"))))) + (t (funcall (car (read-from-string (concat "py-backward-" name "-bol")))))))) + (funcall (car (read-from-string (concat "py-forward-" name)))) + (narrow-to-region (point) start)))) + +(defun py--forms-report-result (erg &optional iact) + (let ((res (ignore-errors (buffer-substring-no-properties (car-safe erg) (cdr-safe erg))))) + (when (and res iact) + (goto-char (car-safe erg)) + (set-mark (point)) + (goto-char (cdr-safe erg))) + res)) + +(defun py-toggle-shell-fontification (msg) + "Toggles value of `py-shell-fontify-p'. " + (interactive "p") + + (if (setq py-shell-fontify-p (not py-shell-fontify-p)) + (progn + (py-shell-font-lock-turn-on)) + (py-shell-font-lock-turn-off)) + (when msg (message "py-shell-fontify-p set to: %s" py-shell-fontify-p))) + +(defun py-toggle-execute-use-temp-file () + (interactive) + (setq py--execute-use-temp-file-p (not py--execute-use-temp-file-p))) + +(defun py--close-intern (regexp) + "Core function, internal used only. " + (let ((cui (car (py--go-to-keyword regexp)))) + (message "%s" cui) + (py--end-base regexp (point)) + (forward-line 1) + (if py-close-provides-newline + (unless (py-empty-line-p) (split-line)) + (fixup-whitespace)) + (indent-to-column cui) + cui)) + +(defun py--backward-regexp-fast (regexp) + "Search backward next regexp not in string or comment. + +Return and move to match-beginning if successful" + (save-match-data + (let (last) + (while (and + (re-search-backward regexp nil 'move 1) + (setq last (match-beginning 0)) + (nth 8 (parse-partial-sexp (point-min) (point))))) + (unless (nth 8 (parse-partial-sexp (point-min) (point))) + last)))) + +(defun py-indent-and-forward (&optional indent) + "Indent current line according to mode, move one line forward. + +If optional INDENT is given, use it" + (interactive "*") + (beginning-of-line) + (when (member (char-after) (list 32 9 10 12 13)) (delete-region (point) (progn (skip-chars-forward " \t\r\n\f")(point)))) + (indent-to (or indent (py-compute-indentation))) + (if (eobp) + (newline-and-indent) + (forward-line 1)) + (back-to-indentation)) + +(defun py--indent-line-by-line (beg end) + "Indent every line until end to max reasonable extend. + +Starts from second line of region specified +BEG END deliver the boundaries of region to work within" + (goto-char beg) + (py-indent-and-forward) + ;; (forward-line 1) + (while (< (line-end-position) end) + (if (py-empty-line-p) + (forward-line 1) + (py-indent-and-forward))) + (unless (py-empty-line-p) (py-indent-and-forward))) + +(defun py-indent-region (&optional beg end no-check) + "Reindent a region delimited by BEG END. + +In case first line accepts an indent, keep the remaining +lines relative. +Otherwise lines in region get outmost indent, +same with optional argument + +In order to shift a chunk of code, start with second line. + +Optional BEG: used by tests +Optional END: used by tests +Optional NO-CHECK: used by tests +" + (interactive "*") + (or no-check (use-region-p) (error "Don't see an active region")) + (let ((end (copy-marker (or end (region-end))))) + (goto-char (or beg (region-beginning))) + (beginning-of-line) + (setq beg (point)) + (skip-chars-forward " \t\r\n\f") + (py--indent-line-by-line beg end))) + +(defun py-find-imports () + "Find top-level imports. + +Returns imports" + (interactive) + (let (imports erg) + (save-excursion + (if (eq major-mode 'comint-mode) + (progn + (re-search-backward comint-prompt-regexp nil t 1) + (goto-char (match-end 0)) + (while (re-search-forward + "import *[A-Za-z_][A-Za-z_0-9].*\\|^from +[A-Za-z_][A-Za-z_0-9.]+ +import .*" nil t) + (setq imports + (concat + imports + (replace-regexp-in-string + "[\\]\r?\n?\s*" "" + (buffer-substring-no-properties (match-beginning 0) (point))) ";"))) + (when (ignore-errors (string-match ";" imports)) + (setq imports (split-string imports ";" t)) + (dolist (ele imports) + (and (string-match "import" ele) + (if erg + (setq erg (concat erg ";" ele)) + (setq erg ele))) + (setq imports erg)))) + (goto-char (point-min)) + (while (re-search-forward + "^import *[A-Za-z_][A-Za-z_0-9].*\\|^from +[A-Za-z_][A-Za-z_0-9.]+ +import .*" nil t) + (unless (py--end-of-statement-p) + (py-forward-statement)) + (setq imports + (concat + imports + (replace-regexp-in-string + "[\\]\r*\n*\s*" "" + (buffer-substring-no-properties (match-beginning 0) (point))) ";"))))) + ;; (and imports + ;; (setq imports (replace-regexp-in-string ";$" "" imports))) + (when (and py-verbose-p (called-interactively-p 'any)) (message "%s" imports)) + imports)) + +(defun py-kill-buffer-unconditional (&optional buffer) + "Kill buffer unconditional, kill buffer-process if existing." + (interactive + (list (current-buffer))) + (let ((buffer (or (and (bufferp buffer) buffer) + (get-buffer (current-buffer)))) + proc kill-buffer-query-functions) + (if (buffer-live-p buffer) + (progn + (setq proc (get-buffer-process buffer)) + (and proc (kill-process proc)) + (set-buffer buffer) + (set-buffer-modified-p 'nil) + (kill-buffer (current-buffer))) + (message "Can't see a buffer %s" buffer)))) + +;; python-components-copy-forms + + +(defun py-copy-block () + "Copy block at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "block"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-block-or-clause () + "Copy block-or-clause at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "block-or-clause"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-buffer () + "Copy buffer at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "buffer"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-class () + "Copy class at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "class"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-clause () + "Copy clause at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "clause"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-def () + "Copy def at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "def"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-def-or-class () + "Copy def-or-class at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "def-or-class"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-expression () + "Copy expression at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "expression"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-indent () + "Copy indent at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "indent"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-line () + "Copy line at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "line"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-minor-block () + "Copy minor-block at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "minor-block"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-paragraph () + "Copy paragraph at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "paragraph"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-partial-expression () + "Copy partial-expression at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "partial-expression"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-region () + "Copy region at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "region"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-statement () + "Copy statement at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "statement"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-top-level () + "Copy top-level at point. + +Store data in kill ring, so it might yanked back." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "top-level"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-block-bol () + "Delete block bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "block"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-block-or-clause-bol () + "Delete block-or-clause bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "block-or-clause"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-buffer-bol () + "Delete buffer bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "buffer"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-class-bol () + "Delete class bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "class"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-clause-bol () + "Delete clause bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "clause"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-def-bol () + "Delete def bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "def"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-def-or-class-bol () + "Delete def-or-class bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "def-or-class"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-expression-bol () + "Delete expression bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "expression"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-indent-bol () + "Delete indent bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "indent"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-line-bol () + "Delete line bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "line"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-minor-block-bol () + "Delete minor-block bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "minor-block"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-paragraph-bol () + "Delete paragraph bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "paragraph"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-partial-expression-bol () + "Delete partial-expression bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "partial-expression"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-region-bol () + "Delete region bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "region"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-statement-bol () + "Delete statement bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "statement"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +(defun py-copy-top-level-bol () + "Delete top-level bol at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (save-excursion + (let ((erg (py--mark-base-bol "top-level"))) + (copy-region-as-kill (car erg) (cdr erg))))) + +;; python-components-delete-forms + + +(defun py-delete-block () + "Delete BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-block-or-clause () + "Delete BLOCK-OR-CLAUSE at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "block-or-clause"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-class (&optional arg) + "Delete CLASS at point until `beginning-of-line'. + +Don't store data in kill ring. +With ARG \\[universal-argument] or `py-mark-decorators' set to t, `decorators' are included." + (interactive "P") + (let* ((py-mark-decorators (or arg py-mark-decorators)) + (erg (py--mark-base "class" py-mark-decorators))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-clause () + "Delete CLAUSE at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "clause"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-def (&optional arg) + "Delete DEF at point until `beginning-of-line'. + +Don't store data in kill ring. +With ARG \\[universal-argument] or `py-mark-decorators' set to t, `decorators' are included." + (interactive "P") + (let* ((py-mark-decorators (or arg py-mark-decorators)) + (erg (py--mark-base "def" py-mark-decorators))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-def-or-class (&optional arg) + "Delete DEF-OR-CLASS at point until `beginning-of-line'. + +Don't store data in kill ring. +With ARG \\[universal-argument] or `py-mark-decorators' set to t, `decorators' are included." + (interactive "P") + (let* ((py-mark-decorators (or arg py-mark-decorators)) + (erg (py--mark-base "def-or-class" py-mark-decorators))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-elif-block () + "Delete ELIF-BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "elif-block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-else-block () + "Delete ELSE-BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "else-block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-except-block () + "Delete EXCEPT-BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "except-block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-for-block () + "Delete FOR-BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "for-block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-if-block () + "Delete IF-BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "if-block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-indent () + "Delete INDENT at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "indent"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-minor-block () + "Delete MINOR-BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "minor-block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-statement () + "Delete STATEMENT at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "statement"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-try-block () + "Delete TRY-BLOCK at point until `beginning-of-line'. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base-bol "try-block"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-comment () + "Delete COMMENT at point. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base "comment"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-line () + "Delete LINE at point. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base "line"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-paragraph () + "Delete PARAGRAPH at point. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base "paragraph"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-expression () + "Delete EXPRESSION at point. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base "expression"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-partial-expression () + "Delete PARTIAL-EXPRESSION at point. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base "partial-expression"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-section () + "Delete SECTION at point. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base "section"))) + (delete-region (car erg) (cdr erg)))) + +(defun py-delete-top-level () + "Delete TOP-LEVEL at point. + +Don't store data in kill ring." + (interactive) + (let ((erg (py--mark-base "top-level"))) + (delete-region (car erg) (cdr erg)))) + +;; python-components-mark-forms + + +(defun py-mark-comment () + "Mark comment at point. + +Return beginning and end positions of marked area, a cons." + (interactive) + (py--mark-base "comment") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) + +(defun py-mark-expression () + "Mark expression at point. + +Return beginning and end positions of marked area, a cons." + (interactive) + (py--mark-base "expression") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) + +(defun py-mark-line () + "Mark line at point. + +Return beginning and end positions of marked area, a cons." + (interactive) + (py--mark-base "line") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) + +(defun py-mark-paragraph () + "Mark paragraph at point. + +Return beginning and end positions of marked area, a cons." + (interactive) + (py--mark-base "paragraph") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) + +(defun py-mark-partial-expression () + "Mark partial-expression at point. + +Return beginning and end positions of marked area, a cons." + (interactive) + (py--mark-base "partial-expression") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) + +(defun py-mark-section () + "Mark section at point. + +Return beginning and end positions of marked area, a cons." + (interactive) + (py--mark-base "section") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) + +(defun py-mark-top-level () + "Mark top-level at point. + +Return beginning and end positions of marked area, a cons." + (interactive) + (py--mark-base "top-level") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) + +(defun py-mark-assignment () + "Mark assignment, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "assignment") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-block () + "Mark block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-block-or-clause () + "Mark block-or-clause, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "block-or-clause") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-class (&optional arg) + "Mark class, take beginning of line positions. + +With ARG \\[universal-argument] or `py-mark-decorators' set to t, decorators are marked too. +Return beginning and end positions of region, a cons." + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators))) + (py--mark-base-bol "class" py-mark-decorators)) + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-clause () + "Mark clause, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "clause") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-def (&optional arg) + "Mark def, take beginning of line positions. + +With ARG \\[universal-argument] or `py-mark-decorators' set to t, decorators are marked too. +Return beginning and end positions of region, a cons." + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators))) + (py--mark-base-bol "def" py-mark-decorators)) + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-def-or-class (&optional arg) + "Mark def-or-class, take beginning of line positions. + +With ARG \\[universal-argument] or `py-mark-decorators' set to t, decorators are marked too. +Return beginning and end positions of region, a cons." + (interactive "P") + (let ((py-mark-decorators (or arg py-mark-decorators))) + (py--mark-base-bol "def-or-class" py-mark-decorators)) + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-elif-block () + "Mark elif-block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "elif-block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-else-block () + "Mark else-block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "else-block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-except-block () + "Mark except-block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "except-block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-for-block () + "Mark for-block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "for-block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-if-block () + "Mark if-block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "if-block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-indent () + "Mark indent, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "indent") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-minor-block () + "Mark minor-block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "minor-block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-statement () + "Mark statement, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "statement") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +(defun py-mark-try-block () + "Mark try-block, take beginning of line positions. + +Return beginning and end positions of region, a cons." + (interactive) + (py--mark-base-bol "try-block") + (exchange-point-and-mark) + (cons (region-beginning) (region-end))) +;; python-components-close-forms + + +(defun py-close-block () + "Close block at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-block-re)) + +(defun py-close-class () + "Close class at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-class-re)) + +(defun py-close-clause () + "Close clause at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-clause-re)) + +(defun py-close-block-or-clause () + "Close block-or-clause at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-block-or-clause-re)) + +(defun py-close-def () + "Close def at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-def-re)) + +(defun py-close-def-or-class () + "Close def-or-class at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-def-or-class-re)) + +(defun py-close-minor-block () + "Close minor-block at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-minor-block-re)) + +(defun py-close-statement () + "Close statement at point. + +Set indent level to that of beginning of function definition. + +If final line isn't empty +and `py-close-block-provides-newline' non-nil, +insert a newline." + (interactive "*") + (py--close-intern 'py-statement-re)) + +;; python-components-kill-forms + + +(defun py-kill-comment () + "Delete comment at point. + +Stores data in kill ring" + (interactive "*") + (let ((erg (py--mark-base "comment"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-line () + "Delete line at point. + +Stores data in kill ring" + (interactive "*") + (let ((erg (py--mark-base "line"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-paragraph () + "Delete paragraph at point. + +Stores data in kill ring" + (interactive "*") + (let ((erg (py--mark-base "paragraph"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-expression () + "Delete expression at point. + +Stores data in kill ring" + (interactive "*") + (let ((erg (py--mark-base "expression"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-partial-expression () + "Delete partial-expression at point. + +Stores data in kill ring" + (interactive "*") + (let ((erg (py--mark-base "partial-expression"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-section () + "Delete section at point. + +Stores data in kill ring" + (interactive "*") + (let ((erg (py--mark-base "section"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-top-level () + "Delete top-level at point. + +Stores data in kill ring" + (interactive "*") + (let ((erg (py--mark-base "top-level"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-block () + "Delete block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-block-or-clause () + "Delete block-or-clause at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "block-or-clause"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-class () + "Delete class at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "class"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-clause () + "Delete clause at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "clause"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-def () + "Delete def at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "def"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-def-or-class () + "Delete def-or-class at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "def-or-class"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-elif-block () + "Delete elif-block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "elif-block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-else-block () + "Delete else-block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "else-block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-except-block () + "Delete except-block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "except-block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-for-block () + "Delete for-block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "for-block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-if-block () + "Delete if-block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "if-block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-indent () + "Delete indent at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "indent"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-minor-block () + "Delete minor-block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "minor-block"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-statement () + "Delete statement at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "statement"))) + (kill-region (car erg) (cdr erg)))) + +(defun py-kill-try-block () + "Delete try-block at point. + +Stores data in kill ring. Might be yanked back using `C-y'." + (interactive "*") + (let ((erg (py--mark-base-bol "try-block"))) + (kill-region (car erg) (cdr erg)))) + +;; python-components-forms-code + +(defun py-block (&optional decorators) + "When called interactively, mark Block at point. + +From a programm, return source of Block at point, a string. + +Optional arg DECORATORS: include decorators when called at def or class. +Also honors setting of `py-mark-decorators'" + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "block" (or decorators py-mark-decorators)) + (py--thing-at-point "block" (or decorators py-mark-decorators)))) + +(defun py-block-or-clause (&optional decorators) + "When called interactively, mark Block-Or-Clause at point. + +From a programm, return source of Block-Or-Clause at point, a string. + +Optional arg DECORATORS: include decorators when called at def or class. +Also honors setting of `py-mark-decorators'" + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "block-or-clause" (or decorators py-mark-decorators)) + (py--thing-at-point "block-or-clause" (or decorators py-mark-decorators)))) + +(defun py-buffer () + "When called interactively, mark Buffer at point. + +From a programm, return source of Buffer at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "buffer") + (py--thing-at-point "buffer"))) + +(defun py-class (&optional decorators) + "When called interactively, mark Class at point. + +From a programm, return source of Class at point, a string. + +Optional arg DECORATORS: include decorators when called at def or class. +Also honors setting of `py-mark-decorators'" + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "class" (or decorators py-mark-decorators)) + (py--thing-at-point "class" (or decorators py-mark-decorators)))) + +(defun py-clause () + "When called interactively, mark Clause at point. + +From a programm, return source of Clause at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "clause") + (py--thing-at-point "clause"))) + +(defun py-def (&optional decorators) + "When called interactively, mark Def at point. + +From a programm, return source of Def at point, a string. + +Optional arg DECORATORS: include decorators when called at def or class. +Also honors setting of `py-mark-decorators'" + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "def" (or decorators py-mark-decorators)) + (py--thing-at-point "def" (or decorators py-mark-decorators)))) + +(defun py-def-or-class (&optional decorators) + "When called interactively, mark Def-Or-Class at point. + +From a programm, return source of Def-Or-Class at point, a string. + +Optional arg DECORATORS: include decorators when called at def or class. +Also honors setting of `py-mark-decorators'" + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "def-or-class" (or decorators py-mark-decorators)) + (py--thing-at-point "def-or-class" (or decorators py-mark-decorators)))) + +(defun py-expression () + "When called interactively, mark Expression at point. + +From a programm, return source of Expression at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "expression") + (py--thing-at-point "expression"))) + +(defun py-indent () + "When called interactively, mark Indent at point. + +From a programm, return source of Indent at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "indent") + (py--thing-at-point "indent"))) + +(defun py-line () + "When called interactively, mark Line at point. + +From a programm, return source of Line at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "line") + (py--thing-at-point "line"))) + +(defun py-minor-block () + "When called interactively, mark Minor-Block at point. + +From a programm, return source of Minor-Block at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "minor-block") + (py--thing-at-point "minor-block"))) + +(defun py-paragraph () + "When called interactively, mark Paragraph at point. + +From a programm, return source of Paragraph at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "paragraph") + (py--thing-at-point "paragraph"))) + +(defun py-partial-expression () + "When called interactively, mark Partial-Expression at point. + +From a programm, return source of Partial-Expression at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "partial-expression") + (py--thing-at-point "partial-expression"))) + +(defun py-region () + "When called interactively, mark Region at point. + +From a programm, return source of Region at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "region") + (py--thing-at-point "region"))) + +(defun py-statement () + "When called interactively, mark Statement at point. + +From a programm, return source of Statement at point, a string." + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "statement") + (py--thing-at-point "statement"))) + +(defun py-top-level (&optional decorators) + "When called interactively, mark Top-Level at point. + +From a programm, return source of Top-Level at point, a string. + +Optional arg DECORATORS: include decorators when called at def or class. +Also honors setting of `py-mark-decorators'" + (interactive) + (if (called-interactively-p 'interactive) + (py--mark-base "top-level" (or decorators py-mark-decorators)) + (py--thing-at-point "top-level" (or decorators py-mark-decorators)))) + +;; python-components-forms-code.el ends here +;; python-components-booleans-end-forms + + +(defun py--end-of-comment-p () + "If cursor is at the end of a comment. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-comment) + (py-forward-comment) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-expression-p () + "If cursor is at the end of a expression. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-expression) + (py-forward-expression) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-line-p () + "If cursor is at the end of a line. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-line) + (py-forward-line) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-paragraph-p () + "If cursor is at the end of a paragraph. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-paragraph) + (py-forward-paragraph) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-partial-expression-p () + "If cursor is at the end of a partial-expression. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-partial-expression) + (py-forward-partial-expression) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-section-p () + "If cursor is at the end of a section. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-section) + (py-forward-section) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-top-level-p () + "If cursor is at the end of a top-level. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-top-level) + (py-forward-top-level) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-assignment-bol-p () + "If at `beginning-of-line' at the end of a assignment. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-assignment-bol) + (py-forward-assignment-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-block-bol-p () + "If at `beginning-of-line' at the end of a block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-block-bol) + (py-forward-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-block-or-clause-bol-p () + "If at `beginning-of-line' at the end of a block-or-clause. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-block-or-clause-bol) + (py-forward-block-or-clause-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-class-bol-p () + "If at `beginning-of-line' at the end of a class. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-class-bol) + (py-forward-class-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-clause-bol-p () + "If at `beginning-of-line' at the end of a clause. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-clause-bol) + (py-forward-clause-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-def-bol-p () + "If at `beginning-of-line' at the end of a def. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-def-bol) + (py-forward-def-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-def-or-class-bol-p () + "If at `beginning-of-line' at the end of a def-or-class. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-def-or-class-bol) + (py-forward-def-or-class-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-elif-block-bol-p () + "If at `beginning-of-line' at the end of a elif-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-elif-block-bol) + (py-forward-elif-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-else-block-bol-p () + "If at `beginning-of-line' at the end of a else-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-else-block-bol) + (py-forward-else-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-except-block-bol-p () + "If at `beginning-of-line' at the end of a except-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-except-block-bol) + (py-forward-except-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-for-block-bol-p () + "If at `beginning-of-line' at the end of a for-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-for-block-bol) + (py-forward-for-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-if-block-bol-p () + "If at `beginning-of-line' at the end of a if-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-if-block-bol) + (py-forward-if-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-indent-bol-p () + "If at `beginning-of-line' at the end of a indent. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-indent-bol) + (py-forward-indent-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-minor-block-bol-p () + "If at `beginning-of-line' at the end of a minor-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-minor-block-bol) + (py-forward-minor-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-try-block-bol-p () + "If at `beginning-of-line' at the end of a try-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-try-block-bol) + (py-forward-try-block-bol) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-assignment-p () + "If cursor is at the end of a assignment. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-assignment) + (py-forward-assignment) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-block-p () + "If cursor is at the end of a block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-block) + (py-forward-block) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-block-or-clause-p () + "If cursor is at the end of a block-or-clause. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-block-or-clause) + (py-forward-block-or-clause) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-class-p () + "If cursor is at the end of a class. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-class) + (py-forward-class) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-clause-p () + "If cursor is at the end of a clause. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-clause) + (py-forward-clause) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-def-p () + "If cursor is at the end of a def. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-def) + (py-forward-def) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-def-or-class-p () + "If cursor is at the end of a def-or-class. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-def-or-class) + (py-forward-def-or-class) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-elif-block-p () + "If cursor is at the end of a elif-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-elif-block) + (py-forward-elif-block) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-else-block-p () + "If cursor is at the end of a else-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-else-block) + (py-forward-else-block) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-except-block-p () + "If cursor is at the end of a except-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-except-block) + (py-forward-except-block) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-for-block-p () + "If cursor is at the end of a for-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-for-block) + (py-forward-for-block) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-if-block-p () + "If cursor is at the end of a if-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-if-block) + (py-forward-if-block) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-indent-p () + "If cursor is at the end of a indent. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-indent) + (py-forward-indent) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-minor-block-p () + "If cursor is at the end of a minor-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-minor-block) + (py-forward-minor-block) + (when (eq orig (point)) + orig)))) + +(defun py--end-of-try-block-p () + "If cursor is at the end of a try-block. +Return position, nil otherwise." + (let ((orig (point))) + (save-excursion + (py-backward-try-block) + (py-forward-try-block) + (when (eq orig (point)) + orig)))) + +;; python-components-exec-forms + +;; Execute forms at point + +(defun py-execute-try-block () + "Send try-block at point to Python default interpreter." + (interactive) + (let ((beg (prog1 + (or (py--beginning-of-try-block-p) + (save-excursion + (py-backward-try-block))))) + (end (save-excursion + (py-forward-try-block)))) + (py-execute-region beg end))) + +(defun py-execute-if-block () + "Send if-block at point to Python default interpreter." + (interactive) + (let ((beg (prog1 + (or (py--beginning-of-if-block-p) + (save-excursion + (py-backward-if-block))))) + (end (save-excursion + (py-forward-if-block)))) + (py-execute-region beg end))) + +(defun py-execute-for-block () + "Send for-block at point to Python default interpreter." + (interactive) + (let ((beg (prog1 + (or (py--beginning-of-for-block-p) + (save-excursion + (py-backward-for-block))))) + (end (save-excursion + (py-forward-for-block)))) + (py-execute-region beg end))) + +;; python-components-switches + +;; Smart indentation +(defun py-toggle-smart-indentation (&optional arg) + "Toggle `py-smart-indentation' - on with positiv ARG. + +Returns value of `py-smart-indentation' switched to." + (interactive) + (let ((arg (or arg (if py-smart-indentation -1 1)))) + (if (< 0 arg) + (progn + (setq py-smart-indentation t) + (py-guess-indent-offset)) + (setq py-smart-indentation nil) + (setq py-indent-offset (default-value 'py-indent-offset))) + (when (called-interactively-p 'any) (message "py-smart-indentation: %s" py-smart-indentation)) + py-smart-indentation)) + +(defun py-smart-indentation-on (&optional arg) + "Toggle`py-smart-indentation' - on with positive ARG. + +Returns value of `py-smart-indentation'." + (interactive "p") + (let ((arg (or arg 1))) + (py-toggle-smart-indentation arg)) + (when (called-interactively-p 'any) (message "py-smart-indentation: %s" py-smart-indentation)) + py-smart-indentation) + +(defun py-smart-indentation-off (&optional arg) + "Toggle `py-smart-indentation' according to ARG. + +Returns value of `py-smart-indentation'." + (interactive "p") + (let ((arg (if arg (- arg) -1))) + (py-toggle-smart-indentation arg)) + (when (called-interactively-p 'any) (message "py-smart-indentation: %s" py-smart-indentation)) + py-smart-indentation) + +(defun py-toggle-sexp-function () + "Opens customization." + (interactive) + (customize-variable 'py-sexp-function)) + +;; Autopair mode +;; py-autopair-mode forms +(defun py-toggle-autopair-mode () + "If `py-autopair-mode' should be on or off. + + Returns value of `py-autopair-mode' switched to." + (interactive) + (and (py-autopair-check) + (setq py-autopair-mode (autopair-mode (if autopair-mode 0 1))))) + +(defun py-autopair-mode-on () + "Make sure, py-autopair-mode' is on. + +Returns value of `py-autopair-mode'." + (interactive) + (and (py-autopair-check) + (setq py-autopair-mode (autopair-mode 1)))) + +(defun py-autopair-mode-off () + "Make sure, py-autopair-mode' is off. + +Returns value of `py-autopair-mode'." + (interactive) + (setq py-autopair-mode (autopair-mode 0))) + +;; py-switch-buffers-on-execute-p forms +(defun py-toggle-switch-buffers-on-execute-p (&optional arg) + "Toggle `py-switch-buffers-on-execute-p' according to ARG. + + Returns value of `py-switch-buffers-on-execute-p' switched to." + (interactive) + (let ((arg (or arg (if py-switch-buffers-on-execute-p -1 1)))) + (if (< 0 arg) + (setq py-switch-buffers-on-execute-p t) + (setq py-switch-buffers-on-execute-p nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-switch-buffers-on-execute-p: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p)) + +(defun py-switch-buffers-on-execute-p-on (&optional arg) + "Toggle `py-switch-buffers-on-execute-p' according to ARG. + +Returns value of `py-switch-buffers-on-execute-p'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-switch-buffers-on-execute-p arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-switch-buffers-on-execute-p: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p) + +(defun py-switch-buffers-on-execute-p-off () + "Make sure, `py-switch-buffers-on-execute-p' is off. + +Returns value of `py-switch-buffers-on-execute-p'." + (interactive) + (py-toggle-switch-buffers-on-execute-p -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-switch-buffers-on-execute-p: %s" py-switch-buffers-on-execute-p)) + py-switch-buffers-on-execute-p) + +;; py-split-window-on-execute forms +(defun py-toggle-split-window-on-execute (&optional arg) + "Toggle `py-split-window-on-execute' according to ARG. + + Returns value of `py-split-window-on-execute' switched to." + (interactive) + (let ((arg (or arg (if py-split-window-on-execute -1 1)))) + (if (< 0 arg) + (setq py-split-window-on-execute t) + (setq py-split-window-on-execute nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-split-window-on-execute: %s" py-split-window-on-execute)) + py-split-window-on-execute)) + +(defun py-split-window-on-execute-on (&optional arg) + "Toggle `py-split-window-on-execute' according to ARG. + +Returns value of `py-split-window-on-execute'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-split-window-on-execute arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-split-window-on-execute: %s" py-split-window-on-execute)) + py-split-window-on-execute) + +(defun py-split-window-on-execute-off () + "Make sure, `py-split-window-on-execute' is off. + +Returns value of `py-split-window-on-execute'." + (interactive) + (py-toggle-split-window-on-execute -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-split-window-on-execute: %s" py-split-window-on-execute)) + py-split-window-on-execute) + +;; py-fontify-shell-buffer-p forms +(defun py-toggle-fontify-shell-buffer-p (&optional arg) + "Toggle `py-fontify-shell-buffer-p' according to ARG. + + Returns value of `py-fontify-shell-buffer-p' switched to." + (interactive) + (let ((arg (or arg (if py-fontify-shell-buffer-p -1 1)))) + (if (< 0 arg) + (progn + (setq py-fontify-shell-buffer-p t) + (set (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords nil nil nil nil + (font-lock-syntactic-keywords + . py-font-lock-syntactic-keywords))) + (unless (looking-at comint-prompt-regexp) + (when (re-search-backward comint-prompt-regexp nil t 1) + (font-lock-fontify-region (line-beginning-position) (point-max))))) + (setq py-fontify-shell-buffer-p nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-fontify-shell-buffer-p: %s" py-fontify-shell-buffer-p)) + py-fontify-shell-buffer-p)) + +(defun py-fontify-shell-buffer-p-on (&optional arg) + "Toggle `py-fontify-shell-buffer-p' according to ARG. + +Returns value of `py-fontify-shell-buffer-p'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-fontify-shell-buffer-p arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-fontify-shell-buffer-p: %s" py-fontify-shell-buffer-p)) + py-fontify-shell-buffer-p) + +(defun py-fontify-shell-buffer-p-off () + "Make sure, `py-fontify-shell-buffer-p' is off. + +Returns value of `py-fontify-shell-buffer-p'." + (interactive) + (py-toggle-fontify-shell-buffer-p -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-fontify-shell-buffer-p: %s" py-fontify-shell-buffer-p)) + py-fontify-shell-buffer-p) + +;; python-mode-v5-behavior-p forms +(defun py-toggle-python-mode-v5-behavior-p (&optional arg) + "Toggle `python-mode-v5-behavior-p' according to ARG. + + Returns value of `python-mode-v5-behavior-p' switched to." + (interactive) + (let ((arg (or arg (if python-mode-v5-behavior-p -1 1)))) + (if (< 0 arg) + (setq python-mode-v5-behavior-p t) + (setq python-mode-v5-behavior-p nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "python-mode-v5-behavior-p: %s" python-mode-v5-behavior-p)) + python-mode-v5-behavior-p)) + +(defun py-python-mode-v5-behavior-p-on (&optional arg) + "To `python-mode-v5-behavior-p' according to ARG. + +Returns value of `python-mode-v5-behavior-p'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-python-mode-v5-behavior-p arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "python-mode-v5-behavior-p: %s" python-mode-v5-behavior-p)) + python-mode-v5-behavior-p) + +(defun py-python-mode-v5-behavior-p-off () + "Make sure, `python-mode-v5-behavior-p' is off. + +Returns value of `python-mode-v5-behavior-p'." + (interactive) + (py-toggle-python-mode-v5-behavior-p -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "python-mode-v5-behavior-p: %s" python-mode-v5-behavior-p)) + python-mode-v5-behavior-p) + +;; py-jump-on-exception forms +(defun py-toggle-jump-on-exception (&optional arg) + "Toggle `py-jump-on-exception' according to ARG. + + Returns value of `py-jump-on-exception' switched to." + (interactive) + (let ((arg (or arg (if py-jump-on-exception -1 1)))) + (if (< 0 arg) + (setq py-jump-on-exception t) + (setq py-jump-on-exception nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-jump-on-exception: %s" py-jump-on-exception)) + py-jump-on-exception)) + +(defun py-jump-on-exception-on (&optional arg) + "Toggle py-jump-on-exception' according to ARG. + +Returns value of `py-jump-on-exception'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-jump-on-exception arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-jump-on-exception: %s" py-jump-on-exception)) + py-jump-on-exception) + +(defun py-jump-on-exception-off () + "Make sure, `py-jump-on-exception' is off. + +Returns value of `py-jump-on-exception'." + (interactive) + (py-toggle-jump-on-exception -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-jump-on-exception: %s" py-jump-on-exception)) + py-jump-on-exception) + +;; py-use-current-dir-when-execute-p forms +(defun py-toggle-use-current-dir-when-execute-p (&optional arg) + "Toggle `py-use-current-dir-when-execute-p' according to ARG. + + Returns value of `py-use-current-dir-when-execute-p' switched to." + (interactive) + (let ((arg (or arg (if py-use-current-dir-when-execute-p -1 1)))) + (if (< 0 arg) + (setq py-use-current-dir-when-execute-p t) + (setq py-use-current-dir-when-execute-p nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-use-current-dir-when-execute-p: %s" py-use-current-dir-when-execute-p)) + py-use-current-dir-when-execute-p)) + +(defun py-use-current-dir-when-execute-p-on (&optional arg) + "Toggle py-use-current-dir-when-execute-p' according to ARG. + +Returns value of `py-use-current-dir-when-execute-p'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-use-current-dir-when-execute-p arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-use-current-dir-when-execute-p: %s" py-use-current-dir-when-execute-p)) + py-use-current-dir-when-execute-p) + +(defun py-use-current-dir-when-execute-p-off () + "Make sure, `py-use-current-dir-when-execute-p' is off. + +Returns value of `py-use-current-dir-when-execute-p'." + (interactive) + (py-toggle-use-current-dir-when-execute-p -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-use-current-dir-when-execute-p: %s" py-use-current-dir-when-execute-p)) + py-use-current-dir-when-execute-p) + +;; py-electric-comment-p forms +(defun py-toggle-electric-comment-p (&optional arg) + "Toggle `py-electric-comment-p' according to ARG. + + Returns value of `py-electric-comment-p' switched to." + (interactive) + (let ((arg (or arg (if py-electric-comment-p -1 1)))) + (if (< 0 arg) + (setq py-electric-comment-p t) + (setq py-electric-comment-p nil)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-electric-comment-p: %s" py-electric-comment-p)) + py-electric-comment-p)) + +(defun py-electric-comment-p-on (&optional arg) + "Toggle py-electric-comment-p' according to ARG. + +Returns value of `py-electric-comment-p'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-electric-comment-p arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-electric-comment-p: %s" py-electric-comment-p)) + py-electric-comment-p) + +(defun py-electric-comment-p-off () + "Make sure, `py-electric-comment-p' is off. + +Returns value of `py-electric-comment-p'." + (interactive) + (py-toggle-electric-comment-p -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-electric-comment-p: %s" py-electric-comment-p)) + py-electric-comment-p) + +;; py-underscore-word-syntax-p forms +(defun py-toggle-underscore-word-syntax-p (&optional arg) + "Toggle `py-underscore-word-syntax-p' according to ARG. + + Returns value of `py-underscore-word-syntax-p' switched to." + (interactive) + (let ((arg (or arg (if py-underscore-word-syntax-p -1 1)))) + (if (< 0 arg) + (progn + (setq py-underscore-word-syntax-p t) + (modify-syntax-entry ?\_ "w" python-mode-syntax-table)) + (setq py-underscore-word-syntax-p nil) + (modify-syntax-entry ?\_ "_" python-mode-syntax-table)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-underscore-word-syntax-p: %s" py-underscore-word-syntax-p)) + py-underscore-word-syntax-p)) + +(defun py-underscore-word-syntax-p-on (&optional arg) + "Toggle py-underscore-word-syntax-p' according to ARG. + +Returns value of `py-underscore-word-syntax-p'." + (interactive) + (let ((arg (or arg 1))) + (py-toggle-underscore-word-syntax-p arg)) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-underscore-word-syntax-p: %s" py-underscore-word-syntax-p)) + py-underscore-word-syntax-p) + +(defun py-underscore-word-syntax-p-off () + "Make sure, `py-underscore-word-syntax-p' is off. + +Returns value of `py-underscore-word-syntax-p'." + (interactive) + (py-toggle-underscore-word-syntax-p -1) + (when (or py-verbose-p (called-interactively-p 'any)) (message "py-underscore-word-syntax-p: %s" py-underscore-word-syntax-p)) + py-underscore-word-syntax-p) + +;; py-toggle-underscore-word-syntax-p must be known already +;; circular: py-toggle-underscore-word-syntax-p sets and calls it +(defcustom py-underscore-word-syntax-p t + "If underscore chars should be of `syntax-class' word. + +I.e. not of `symbol'. + +Underscores in word-class like `forward-word' travel the indentifiers. +Default is t. + +See bug report at launchpad, lp:940812" + :type 'boolean + :tag "py-underscore-word-syntax-p" + :group 'python-mode + :set (lambda (symbol value) + (set-default symbol value) + (py-toggle-underscore-word-syntax-p (if value 1 0)))) + +;; python-components-edit + +(defun py-insert-default-shebang () + "Insert in buffer shebang of installed default Python." + (interactive "*") + (let* ((erg (if py-edit-only-p + py-shell-name + (executable-find py-shell-name))) + (sheb (concat "#! " erg))) + (insert sheb))) + +(defun py--top-level-form-p () + "Return non-nil, if line start with a top level form." + (save-excursion + (beginning-of-line) + (unless + ;; in string + (nth 3 (parse-partial-sexp (point-min) (point))) + (and (eq (current-indentation) 0) + (looking-at "[[:alpha:]_]+") + ;; (or (looking-at py-def-or-class-re) + ;; (looking-at py-block-or-clause-re) + ;; (looking-at py-assignment-re)) + )))) + +(defun py-indent-line-outmost (&optional arg) + "Indent the current line to the outmost reasonable indent. + +With optional \\[universal-argument] ARG, unconditionally insert an indent of +`py-indent-offset' length." + (interactive "*P") + (cond + ((eq 4 (prefix-numeric-value arg)) + (if indent-tabs-mode + (insert (make-string 1 9)) + (insert (make-string py-indent-offset 32)))) + ;; + (t + (let* ((need (py-compute-indentation (point))) + (cui (current-indentation)) + (cuc (current-column))) + (if (and (eq need cui) + (not (eq cuc cui))) + (back-to-indentation) + (beginning-of-line) + (delete-horizontal-space) + (indent-to need)))))) + +(defun py--re-indent-line () + "Re-indent the current line." + (beginning-of-line) + (delete-region (point) + (progn (skip-chars-forward " \t\r\n\f") + (point))) + (indent-to (py-compute-indentation))) + +;; TODO: the following function can fall into an infinite loop. +;; See https://gitlab.com/python-mode-devs/python-mode/-/issues/99 +(defun py--indent-fix-region-intern (beg end) + "Used when `py-tab-indents-region-p' is non-nil. + +Requires BEG, END as the boundery of region" + (save-excursion + (save-restriction + (beginning-of-line) + (narrow-to-region beg end) + (goto-char beg) + (let ((end (copy-marker end))) + (forward-line 1) + (narrow-to-region (line-beginning-position) end) + (py--re-indent-line) + (while (< (line-end-position) end) + (forward-line 1) + (py--re-indent-line)))))) + +(defun py-indent-current-line (need) + "Indent current line to NEED." + (beginning-of-line) + (delete-horizontal-space) + (indent-to need)) + +;; TODO: Add docstring. +;; What is the intent of the this utility function? +;; What is the purpose of each argument? +(defun py--indent-line-intern (need cui indent col &optional beg end region dedent) + (let (erg) + (if py-tab-indent + (progn + (and py-tab-indents-region-p region + (py--indent-fix-region-intern beg end)) + (cond + ((bolp) + (if (and py-tab-shifts-region-p region) + (while (< (current-indentation) need) + (py-shift-region-right 1)) + (beginning-of-line) + (delete-horizontal-space) + (indent-to need))) + ;; + ((< need cui) + (if (and py-tab-shifts-region-p region) + (progn + (when (eq (point) (region-end)) + (exchange-point-and-mark)) + (while (< 0 (current-indentation)) + (py-shift-region-left 1))) + (beginning-of-line) + (delete-horizontal-space) + (indent-to need))) + ;; + ((eq need cui) + (if (or dedent + (eq this-command last-command) + (eq this-command 'py-indent-line)) + (if (and py-tab-shifts-region-p region) + (while (and (goto-char beg) (< 0 (current-indentation))) + (py-shift-region-left 1)) + (beginning-of-line) + (delete-horizontal-space) + (if (<= (line-beginning-position) (+ (point) (- col cui))) + (forward-char (- col cui)) + (beginning-of-line))))) + ;; + ((< cui need) + (if (and py-tab-shifts-region-p region) + (py-shift-region-right 1) + (beginning-of-line) + (delete-horizontal-space) + ;; indent one indent only if goal < need + (setq erg (+ (* (/ cui indent) indent) indent)) + (if (< need erg) + (indent-to need) + (indent-to erg)) + (forward-char (- col cui)))) + ;; + (t + (if (and py-tab-shifts-region-p region) + (while (< (current-indentation) need) + (py-shift-region-right 1)) + (beginning-of-line) + (delete-horizontal-space) + (indent-to need) + (back-to-indentation) + (if (<= (line-beginning-position) (+ (point) (- col cui))) + (forward-char (- col cui)) + (beginning-of-line)))))) + (insert-tab)))) + +(defun py--indent-line-or-region-base (beg end region cui need arg this-indent-offset col &optional dedent) + (cond ((eq 4 (prefix-numeric-value arg)) + (if (and (eq cui (current-indentation)) + (<= need cui)) + (if indent-tabs-mode (insert "\t")(insert (make-string py-indent-offset 32))) + (beginning-of-line) + (delete-horizontal-space) + (indent-to (+ need py-indent-offset)))) + ((not (eq 1 (prefix-numeric-value arg))) + (py-smart-indentation-off) + (py--indent-line-intern need cui this-indent-offset col beg end region dedent)) + (t (py--indent-line-intern need cui this-indent-offset col beg end region dedent)))) + +(defun py--calculate-indent-backwards (cui indent-offset) + "Return the next reasonable indent lower than current indentation. + +Requires current indent as CUI +Requires current indent-offset as INDENT-OFFSET" + (if (< 0 (% cui py-indent-offset)) + ;; not correctly indented at all + (/ cui indent-offset) + (- cui indent-offset))) + +(defun py-indent-line (&optional arg dedent) + "Indent the current line according ARG. + +When called interactivly with \\[universal-argument], +ignore dedenting rules for block closing statements +\(e.g. return, raise, break, continue, pass) + +An optional \\[universal-argument] followed by a numeric argument +neither 1 nor 4 will switch off `py-smart-indentation' for this execution. +This permits to correct allowed but unwanted indents. Similar to +`py-toggle-smart-indentation' resp. `py-smart-indentation-off' followed by TAB. + +OUTMOST-ONLY stops circling possible indent. + +When `py-tab-shifts-region-p' is t, not just the current line, +but the region is shiftet that way. + +If `py-tab-indents-region-p' is t and first TAB doesn't shift +--as indent is at outmost reasonable--, `indent-region' is called. + +Optional arg DEDENT: force dedent. + +\\[quoted-insert] TAB inserts a literal TAB-character." + (interactive "P") + (unless (eq this-command last-command) + (setq py-already-guessed-indent-offset nil)) + (let ((orig (copy-marker (point))) + ;; TAB-leaves-point-in-the-wrong-lp-1178453-test + (region (use-region-p)) + cui + outmost + col + beg + end + need + this-indent-offset) + (and region + (setq beg (region-beginning)) + (setq end (region-end)) + (goto-char beg)) + (setq cui (current-indentation)) + (setq col (current-column)) + (setq this-indent-offset + (cond ((and py-smart-indentation (not (eq this-command last-command))) + (py-guess-indent-offset)) + ((and py-smart-indentation (eq this-command last-command) py-already-guessed-indent-offset) + py-already-guessed-indent-offset) + (t (default-value 'py-indent-offset)))) + (setq outmost (py-compute-indentation nil nil nil nil nil nil nil this-indent-offset)) + ;; now choose the indent + (unless (and (not dedent)(not (eq this-command last-command))(eq outmost (current-indentation))) + (setq need + (cond ((eq this-command last-command) + (if (bolp) + ;; jump forward to max indent + outmost + (py--calculate-indent-backwards cui this-indent-offset))) + ;; (py--calculate-indent-backwards cui this-indent-offset))))) + (t + outmost + ))) + (py--indent-line-or-region-base beg end region cui need arg this-indent-offset col dedent) + (and region (or py-tab-shifts-region-p + py-tab-indents-region-p) + (not (eq (point) orig)) + (exchange-point-and-mark)) + (current-indentation)))) + +(defun py--delete-trailing-whitespace (orig) + "Delete trailing whitespace. + +Either `py-newline-delete-trailing-whitespace-p' +or ` +py-trailing-whitespace-smart-delete-p' must be t. + +Start from position ORIG" + (when (or py-newline-delete-trailing-whitespace-p py-trailing-whitespace-smart-delete-p) + (let ((pos (copy-marker (point)))) + (save-excursion + (goto-char orig) + (if (py-empty-line-p) + (if (py---emacs-version-greater-23) + (delete-trailing-whitespace (line-beginning-position) pos) + (save-restriction + (narrow-to-region (line-beginning-position) pos) + (delete-trailing-whitespace))) + (skip-chars-backward " \t") + (if (py---emacs-version-greater-23) + (delete-trailing-whitespace (line-beginning-position) pos) + (save-restriction + (narrow-to-region (point) pos) + (delete-trailing-whitespace)))))))) + +(defun py-newline-and-indent () + "Add a newline and indent to outmost reasonable indent. +When indent is set back manually, this is honoured in following lines." + (interactive "*") + (let* ((orig (point)) + ;; lp:1280982, deliberatly dedented by user + (this-dedent + (when + ;; (and (or (eq 10 (char-after))(eobp))(looking-back "^[ \t]*" (line-beginning-position))) + (looking-back "^[ \t]+" (line-beginning-position)) + (current-column))) + erg) + (newline 1) + (py--delete-trailing-whitespace orig) + (setq erg + (cond (this-dedent + (indent-to-column this-dedent)) + ((and py-empty-line-closes-p (or (eq this-command last-command)(py--after-empty-line))) + (indent-to-column (save-excursion (py-backward-statement)(- (current-indentation) py-indent-offset)))) + (t + (fixup-whitespace) + (indent-to-column (py-compute-indentation))))) + erg)) + +(defun py-newline-and-dedent () + "Add a newline and indent to one level below current. +Returns column." + (interactive "*") + (let ((cui (current-indentation))) + (newline 1) + (when (< 0 cui) + (indent-to (- (py-compute-indentation) py-indent-offset))))) + +(defun py-toggle-indent-tabs-mode () + "Toggle `indent-tabs-mode'. + +Returns value of `indent-tabs-mode' switched to." + (interactive) + (when + (setq indent-tabs-mode (not indent-tabs-mode)) + (setq tab-width py-indent-offset)) + (when (and py-verbose-p (called-interactively-p 'any)) (message "indent-tabs-mode %s py-indent-offset %s" indent-tabs-mode py-indent-offset)) + indent-tabs-mode) + +(defun py-indent-tabs-mode (arg) + "With positive ARG switch `indent-tabs-mode' on. + +With negative ARG switch `indent-tabs-mode' off. +Returns value of `indent-tabs-mode' switched to. + +If IACT is provided, message result" + (interactive "p") + (if (< 0 arg) + (progn + (setq indent-tabs-mode t) + (setq tab-width py-indent-offset)) + (setq indent-tabs-mode nil)) + (when (and py-verbose-p (called-interactively-p 'any)) (message "indent-tabs-mode %s py-indent-offset %s" indent-tabs-mode py-indent-offset)) + indent-tabs-mode) + +(defun py-indent-tabs-mode-on (arg) + "Switch `indent-tabs-mode' according to ARG." + (interactive "p") + (py-indent-tabs-mode (abs arg))) + +(defun py-indent-tabs-mode-off (arg) + "Switch `indent-tabs-mode' according to ARG." + (interactive "p") + (py-indent-tabs-mode (- (abs arg)))) + +;; Guess indent offset + +(defun py--comment-indent-function () + "Python version of `comment-indent-function'." + ;; This is required when filladapt is turned off. Without it, when + ;; filladapt is not used, comments which start in column zero + ;; cascade one character to the right + (save-excursion + (beginning-of-line) + (let ((eol (line-end-position))) + (and comment-start-skip + (re-search-forward comment-start-skip eol t) + (setq eol (match-beginning 0))) + (goto-char eol) + (skip-chars-backward " \t") + (max comment-column (+ (current-column) (if (bolp) 0 1)))))) + +;; ; + +;; Declarations start +(defun py--bounds-of-declarations () + "Bounds of consecutive multitude of assigments resp. statements around point. + +Indented same level, which don't open blocks. +Typically declarations resp. initialisations of variables following +a class or function definition. +See also `py--bounds-of-statements'" + (let* ((orig-indent (progn + (back-to-indentation) + (unless (py--beginning-of-statement-p) + (py-backward-statement)) + (unless (py--beginning-of-block-p) + (current-indentation)))) + (orig (point)) + last beg end) + (when orig-indent + (setq beg (line-beginning-position)) + ;; look upward first + (while (and + (progn + (unless (py--beginning-of-statement-p) + (py-backward-statement)) + (line-beginning-position)) + (py-backward-statement) + (not (py--beginning-of-block-p)) + (eq (current-indentation) orig-indent)) + (setq beg (line-beginning-position))) + (goto-char orig) + (while (and (setq last (line-end-position)) + (setq end (py-down-statement)) + (not (py--beginning-of-block-p)) + (eq (py-indentation-of-statement) orig-indent))) + (setq end last) + (goto-char beg) + (if (and beg end) + (progn + (cons beg end)) + nil)))) + +(defun py-backward-declarations () + "Got to the beginning of assigments resp. statements. + +Move in current level which don't open blocks." + (interactive) + (let* ((bounds (py--bounds-of-declarations)) + (erg (car bounds))) + (when erg (goto-char erg)) + erg)) + +(defun py-forward-declarations () + "Got to the end of assigments resp. statements. + +Move in current level which don't open blocks." + (interactive) + (let* ((bounds (py--bounds-of-declarations)) + (erg (cdr bounds))) + (when erg (goto-char erg)) + erg)) + +(defun py-declarations () + "Forms in current level. + +Forms don't open blocks or start with a keyword. + +See also `py-statements'." + (interactive) + (let* ((bounds (py--bounds-of-declarations)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (goto-char beg) + (push-mark) + (goto-char end) + (kill-new (buffer-substring-no-properties beg end)) + (exchange-point-and-mark)))) + +(defun py-kill-declarations () + "Delete variables declared in current level. + +Store deleted variables in `kill-ring'" + (interactive "*") + (let* ((bounds (py--bounds-of-declarations)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (goto-char beg) + (push-mark) + (goto-char end) + (kill-new (buffer-substring-no-properties beg end)) + (delete-region beg end)))) +;; Declarations end + +;; Statements start +(defun py--bounds-of-statements () + "Bounds of consecutive multitude of statements around point. + +Indented same level, which don't open blocks." + (interactive) + (let* ((orig-indent (progn + (back-to-indentation) + (unless (py--beginning-of-statement-p) + (py-backward-statement)) + (unless (py--beginning-of-block-p) + (current-indentation)))) + (orig (point)) + last beg end) + (when orig-indent + (setq beg (point)) + (while (and (setq last beg) + (setq beg + (when (py-backward-statement) + (line-beginning-position))) + ;; backward-statement shouldn't stop in string + ;; (not (py-in-string-p)) + (not (py--beginning-of-block-p)) + (eq (current-indentation) orig-indent))) + (setq beg last) + (goto-char orig) + (setq end (line-end-position)) + (while (and (setq last (py--end-of-statement-position)) + (setq end (py-down-statement)) + (not (py--beginning-of-block-p)) + ;; (not (looking-at py-keywords)) + ;; (not (looking-at "pdb\.")) + ;; (not (py-in-string-p)) + (eq (py-indentation-of-statement) orig-indent))) + (setq end last) + (goto-char orig) + (if (and beg end) + (progn + (when (called-interactively-p 'any) (message "%s %s" beg end)) + (cons beg end)) + nil)))) + +(defun py-backward-statements () + "Got to the beginning of statements in current level which don't open blocks." + (interactive) + (let* ((bounds (py--bounds-of-statements)) + (erg (car bounds))) + (when erg (goto-char erg)) + erg)) + +(defun py-forward-statements () + "Got to the end of statements in current level which don't open blocks." + (interactive) + (let* ((bounds (py--bounds-of-statements)) + (erg (cdr bounds))) + (when erg (goto-char erg)) + erg)) + +(defun py-statements () + "Copy and mark simple statements level. + +These statements don't open blocks. + +More general than `py-declarations'." + (interactive) + (let* ((bounds (py--bounds-of-statements)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (goto-char beg) + (push-mark) + (goto-char end) + (kill-new (buffer-substring-no-properties beg end)) + (exchange-point-and-mark)))) + +(defun py-kill-statements () + "Delete statements declared in current level. + +Store deleted statements in `kill-ring'" + (interactive "*") + (let* ((bounds (py--bounds-of-statements)) + (beg (car bounds)) + (end (cdr bounds))) + (when (and beg end) + (kill-new (buffer-substring-no-properties beg end)) + (delete-region beg end)))) + +(defun py-insert-super () + "Insert a function \"super()\" from current environment. + +As example given in Python v3.1 documentation » The Python Standard Library » + +class C(B): + def method(self, arg): + super().method(arg) # This does the same thing as: + # super(C, self).method(arg) + +Returns the string inserted." + (interactive "*") + (let* ((orig (point)) + (funcname (progn + (py-backward-def) + (when (looking-at (concat py-def-re " *\\([^(]+\\) *(\\(?:[^),]*\\),? *\\([^)]*\\))")) + (match-string-no-properties 2)))) + (args (match-string-no-properties 3)) + (ver (py-which-python)) + classname erg) + (if (< ver 3) + (progn + (py-backward-class) + (when (looking-at (concat py-class-re " *\\([^( ]+\\)")) + (setq classname (match-string-no-properties 2))) + (goto-char orig) + (setq erg (concat "super(" classname ", self)." funcname "(" args ")")) + ;; super(C, self).method(arg)" + (insert erg)) + (goto-char orig) + (setq erg (concat "super()." funcname "(" args ")")) + (insert erg)) + erg)) + +;; Comments +(defun py-delete-comments-in-def-or-class () + "Delete all commented lines in def-or-class at point." + (interactive "*") + (save-excursion + (let ((beg (py--beginning-of-def-or-class-position)) + (end (py--end-of-def-or-class-position))) + (and beg end (py--delete-comments-intern beg end))))) + +(defun py-delete-comments-in-class () + "Delete all commented lines in class at point." + (interactive "*") + (save-excursion + (let ((beg (py--beginning-of-class-position)) + (end (py--end-of-class-position))) + (and beg end (py--delete-comments-intern beg end))))) + +(defun py-delete-comments-in-block () + "Delete all commented lines in block at point." + (interactive "*") + (save-excursion + (let ((beg (py--beginning-of-block-position)) + (end (py--end-of-block-position))) + (and beg end (py--delete-comments-intern beg end))))) + +(defun py-delete-comments-in-region (beg end) + "Delete all commented lines in region delimited by BEG END." + (interactive "r*") + (save-excursion + (py--delete-comments-intern beg end))) + +(defun py--delete-comments-intern (beg end) + (save-restriction + (narrow-to-region beg end) + (goto-char beg) + (while (and (< (line-end-position) end) (not (eobp))) + (beginning-of-line) + (if (looking-at (concat "[ \t]*" comment-start)) + (delete-region (point) (1+ (line-end-position))) + (forward-line 1))))) + +;; Edit docstring +(defun py--edit-set-vars () + (save-excursion + (let ((py--editbeg (when (use-region-p) (region-beginning))) + (py--editend (when (use-region-p) (region-end))) + (pps (parse-partial-sexp (point-min) (point)))) + (when (nth 3 pps) + (setq py--editbeg (or py--editbeg (progn (goto-char (nth 8 pps)) + (skip-chars-forward (char-to-string (char-after)))(push-mark) (point)))) + (setq py--editend (or py--editend + (progn (goto-char (nth 8 pps)) + (forward-sexp) + (skip-chars-backward (char-to-string (char-before))) + (point))))) + (cons (copy-marker py--editbeg) (copy-marker py--editend))))) + +(defun py--write-edit () + "When edit is finished, write docstring back to orginal buffer." + (interactive) + (goto-char (point-min)) + (while (re-search-forward "[\"']" nil t 1) + (or (py-escaped-p) + (replace-match (concat "\\\\" (match-string-no-properties 0))))) + (jump-to-register py--edit-register) + ;; (py-restore-window-configuration) + (delete-region py--docbeg py--docend) + (insert-buffer-substring py-edit-buffer)) + +(defun py-edit--intern (buffer-name mode &optional beg end prefix suffix action) + "Edit string or active region in `python-mode'. + +arg BUFFER-NAME: a string. +arg MODE: which buffer-mode used in edit-buffer" + (interactive "*") + (save-excursion + (save-restriction + (window-configuration-to-register py--edit-register) + (setq py--oldbuf (current-buffer)) + (let* ((orig (point)) + (bounds (or (and beg end)(py--edit-set-vars))) + relpos editstrg + erg) + (setq py--docbeg (or beg (car bounds))) + (setq py--docend (or end (cdr bounds))) + ;; store relative position in editstrg + (setq relpos (1+ (- orig py--docbeg))) + (setq editstrg (buffer-substring py--docbeg py--docend)) + (set-buffer (get-buffer-create buffer-name)) + (erase-buffer) + (switch-to-buffer (current-buffer)) + (when prefix (insert prefix)) + (insert editstrg) + (when suffix (insert suffix)) + (funcall mode) + (when action + (setq erg (funcall action)) + (erase-buffer) + (insert erg)) + (local-set-key [(control c) (control c)] 'py--write-edit) + (goto-char relpos) + (message "%s" "Type C-c C-c writes contents back"))))) + +(defun py-edit-docstring () + "Edit docstring or active region in `python-mode'." + (interactive "*") + (py-edit--intern "Edit docstring" 'python-mode)) + +(defun py-unpretty-assignment () + "Revoke prettyprint, write assignment in a shortest way." + (interactive "*") + (save-excursion + (let* ((beg (py-beginning-of-assignment)) + (end (copy-marker (py-forward-assignment))) + last) + (goto-char beg) + (while (and (not (eobp))(re-search-forward "^\\([ \t]*\\)\[\]\"'{}]" end t 1) (setq last (copy-marker (point)))) + (save-excursion (goto-char (match-end 1)) + (when (eq (current-column) (current-indentation)) (delete-region (point) (progn (skip-chars-backward " \t\r\n\f") (point))))) + (when last (goto-char last)))))) + +(defun py--prettyprint-assignment-intern (beg end name buffer) + (let ((proc (get-buffer-process buffer)) + erg) + ;; (py-send-string "import pprint" proc nil t) + (py-fast-send-string "import json" proc buffer) + ;; send the dict/assigment + (py-fast-send-string (buffer-substring-no-properties beg end) proc buffer) + ;; do pretty-print + ;; print(json.dumps(neudict4, indent=4)) + (setq erg (py-fast-send-string (concat "print(json.dumps("name", indent=5))") proc buffer t)) + (goto-char beg) + (skip-chars-forward "^{") + (delete-region (point) (progn (forward-sexp) (point))) + (insert erg))) + +(defun py-prettyprint-assignment () + "Prettyprint assignment in `python-mode'." + (interactive "*") + (window-configuration-to-register py--windows-config-register) + (save-excursion + (let* ((beg (py-beginning-of-assignment)) + (name (py-expression)) + (end (py-forward-assignment)) + (proc-buf (py-shell nil nil "Fast Intern Utility Re-Use"))) + (py--prettyprint-assignment-intern beg end name proc-buf))) + (py-restore-window-configuration)) + +;; python-components-named-shells + +(defun ipython (&optional argprompt args buffer fast exception-buffer split) + "Start an IPython interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "ipython" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +;; (defun ipython2.7 (&optional argprompt args buffer fast exception-buffer split) +;; "Start an IPython2.7 interpreter. + +;; With optional \\[universal-argument] get a new dedicated shell." +;; (interactive "p") +;; (py-shell argprompt args nil "ipython2.7" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +(defun ipython3 (&optional argprompt args buffer fast exception-buffer split) + "Start an IPython3 interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "ipython3" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +(defun jython (&optional argprompt args buffer fast exception-buffer split) + "Start an Jython interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "jython" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +(defun python (&optional argprompt args buffer fast exception-buffer split) + "Start an Python interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "python" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +(defun python2 (&optional argprompt args buffer fast exception-buffer split) + "Start an Python2 interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "python2" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +(defun python3 (&optional argprompt args buffer fast exception-buffer split) + "Start an Python3 interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "python3" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +(defun pypy (&optional argprompt args buffer fast exception-buffer split) + "Start an Pypy interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "pypy" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +(defun isympy3 (&optional argprompt args buffer fast exception-buffer split) + "Start an Pypy interpreter. + +With optional \\[universal-argument] get a new dedicated shell." + (interactive "p") + (py-shell argprompt args nil "isympy3" buffer fast exception-buffer split (unless argprompt (eq 1 (prefix-numeric-value argprompt))))) + +;; python-components-font-lock +;; (require 'python) + +;; (defconst rx--builtin-symbols +;; (append '(nonl not-newline any anychar anything unmatchable +;; bol eol line-start line-end +;; bos eos string-start string-end +;; bow eow word-start word-end +;; symbol-start symbol-end +;; point word-boundary not-word-boundary not-wordchar) +;; (mapcar #'car rx--char-classes)) +;; "List of built-in rx variable-like symbols.") + +;; (defconst rx--builtin-forms +;; '(seq sequence : and or | any in char not-char not intersection +;; repeat = >= ** +;; zero-or-more 0+ * +;; one-or-more 1+ + +;; zero-or-one opt optional \? +;; *? +? \?? +;; minimal-match maximal-match +;; group submatch group-n submatch-n backref +;; syntax not-syntax category +;; literal eval regexp regex) +;; "List of built-in rx function-like symbols.") + +;; (defconst rx--builtin-names +;; (append rx--builtin-forms rx--builtin-symbols) +;; "List of built-in rx names. These cannot be redefined by the user.") + +;; (defun rx--make-binding (name tail) +;; "Make a definitions entry out of TAIL. +;; TAIL is on the form ([ARGLIST] DEFINITION)." +;; (unless (symbolp name) +;; (error "Bad `rx' definition name: %S" name)) +;; ;; FIXME: Consider using a hash table or symbol property, for speed. +;; (when (memq name rx--builtin-names) +;; (error "Cannot redefine built-in rx name `%s'" name)) +;; (pcase tail +;; (`(,def) +;; (list def)) +;; (`(,args ,def) +;; (unless (and (listp args) (rx--every #'symbolp args)) +;; (error "Bad argument list for `rx' definition %s: %S" name args)) +;; (list args def)) +;; (_ (error "Bad `rx' definition of %s: %S" name tail)))) + +;; (defun rx--make-named-binding (bindspec) +;; "Make a definitions entry out of BINDSPEC. +;; BINDSPEC is on the form (NAME [ARGLIST] DEFINITION)." +;; (unless (consp bindspec) +;; (error "Bad `rx-let' binding: %S" bindspec)) +;; (cons (car bindspec) +;; (rx--make-binding (car bindspec) (cdr bindspec)))) + +;; ;;;###autoload +;; (defmacro rx-let (bindings &rest body) +;; "Evaluate BODY with local BINDINGS for `rx'. +;; BINDINGS is an unevaluated list of bindings each on the form +;; (NAME [(ARGS...)] RX). +;; They are bound lexically and are available in `rx' expressions in +;; BODY only. + +;; For bindings without an ARGS list, NAME is defined as an alias +;; for the `rx' expression RX. Where ARGS is supplied, NAME is +;; defined as an `rx' form with ARGS as argument list. The +;; parameters are bound from the values in the (NAME ...) form and +;; are substituted in RX. ARGS can contain `&rest' parameters, +;; whose values are spliced into RX where the parameter name occurs. + +;; Any previous definitions with the same names are shadowed during +;; the expansion of BODY only. +;; For local extensions to `rx-to-string', use `rx-let-eval'. +;; To make global rx extensions, use `rx-define'. +;; For more details, see Info node `(elisp) Extending Rx'. + +;; \(fn BINDINGS BODY...)" +;; (declare (indent 1) (debug (sexp body))) +;; (let ((prev-locals (cdr (assq :rx-locals macroexpand-all-environment))) +;; (new-locals (mapcar #'rx--make-named-binding bindings))) +;; (macroexpand-all (cons 'progn body) +;; (cons (cons :rx-locals (append new-locals prev-locals)) +;; macroexpand-all-environment)))) + +(defmacro py-rx (&rest regexps) + "Python mode specialized rx macro. +This variant of `rx' supports common Python named REGEXPS." + `(rx-let ((block-start (seq symbol-start + (or "def" "class" "if" "elif" "else" "try" + "except" "finally" "for" "while" "with" + ;; Python 3.10+ PEP634 + "match" "case" + ;; Python 3.5+ PEP492 + (and "async" (+ space) + (or "def" "for" "with"))) + symbol-end)) + (dedenter (seq symbol-start + (or "elif" "else" "except" "finally") + symbol-end)) + (block-ender (seq symbol-start + (or + "break" "continue" "pass" "raise" "return") + symbol-end)) + (decorator (seq line-start (* space) ?@ (any letter ?_) + (* (any word ?_)))) + (defun (seq symbol-start + (or "def" "class" + ;; Python 3.5+ PEP492 + (and "async" (+ space) "def")) + symbol-end)) + (if-name-main (seq line-start "if" (+ space) "__name__" + (+ space) "==" (+ space) + (any ?' ?\") "__main__" (any ?' ?\") + (* space) ?:)) + (symbol-name (seq (any letter ?_) (* (any word ?_)))) + (assignment-target (seq (? ?*) + (* symbol-name ?.) symbol-name + (? ?\[ (+ (not ?\])) ?\]))) + (grouped-assignment-target (seq (? ?*) + (* symbol-name ?.) (group symbol-name) + (? ?\[ (+ (not ?\])) ?\]))) + (open-paren (or "{" "[" "(")) + (close-paren (or "}" "]" ")")) + (simple-operator (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?= ?%)) + (not-simple-operator (not (or simple-operator ?\n))) + (operator (or "==" ">=" "is" "not" + "**" "//" "<<" ">>" "<=" "!=" + "+" "-" "/" "&" "^" "~" "|" "*" "<" ">" + "=" "%")) + (assignment-operator (or "+=" "-=" "*=" "/=" "//=" "%=" "**=" + ">>=" "<<=" "&=" "^=" "|=" + "=")) + (string-delimiter (seq + ;; Match even number of backslashes. + (or (not (any ?\\ ?\' ?\")) point + ;; Quotes might be preceded by an + ;; escaped quote. + (and (or (not (any ?\\)) point) ?\\ + (* ?\\ ?\\) (any ?\' ?\"))) + (* ?\\ ?\\) + ;; Match single or triple quotes of any kind. + (group (or "\"\"\"" "\"" "'''" "'")))) + (coding-cookie (seq line-start ?# (* space) + (or + ;; # coding= + (: "coding" (or ?: ?=) (* space) + (group-n 1 (+ (or word ?-)))) + ;; # -*- coding: -*- + (: "-*-" (* space) "coding:" (* space) + (group-n 1 (+ (or word ?-))) + (* space) "-*-") + ;; # vim: set fileencoding= : + (: "vim:" (* space) "set" (+ space) + "fileencoding" (* space) ?= (* space) + (group-n 1 (+ (or word ?-))) + (* space) ":"))))) + (rx ,@regexps))) + +(defun py-font-lock-assignment-matcher (regexp) + "Font lock matcher for assignments based on REGEXP. +Search for next occurrence if REGEXP matched within a `paren' +context (to avoid, e.g., default values for arguments or passing +arguments by name being treated as assignments) or is followed by +an '=' sign (to avoid '==' being treated as an assignment. Set +point to the position one character before the end of the +occurrence found so that subsequent searches can detect the '=' +sign in chained assignment." + (lambda (limit) + (cl-loop while (re-search-forward regexp limit t) + unless (or + ;; (python-syntax-context 'paren) + (nth 1 (parse-partial-sexp (point-min) (point))) + (equal (char-after) ?=)) + return (progn (backward-char) t)))) + +(defconst python-font-lock-keywords + ;; Keywords + `(,(rx symbol-start + (or + "if" "and" "del" "not" "while" "as" "elif" "global" + "or" "async with" "with" "assert" "else" "pass" "yield" "break" + "exec" "in" "continue" "finally" "is" "except" "raise" + "return" "async for" "for" "lambda" "await" "match" "case") + symbol-end) + (,(rx symbol-start (or "async def" "def" "class") symbol-end) . py-def-class-face) + (,(rx symbol-start (or "import" "from") symbol-end) . py-import-from-face) + (,(rx symbol-start (or "try" "if") symbol-end) . py-try-if-face) + ;; functions + (,(rx symbol-start "def" (1+ space) (group (seq (any letter ?_) (* (any word ?_))))) + ;; (1 font-lock-function-name-face)) + (1 py-def-face)) + (,(rx symbol-start "async def" (1+ space) (group (seq (any letter ?_) (* (any word ?_))))) + ;; (1 font-lock-function-name-face)) + (1 py-def-face)) + ;; classes + (,(rx symbol-start (group "class") (1+ space) (group (seq (any letter ?_) (* (any word ?_))))) + (1 py-def-class-face) (2 py-class-name-face)) + (,(rx symbol-start + (or"Ellipsis" "True" "False" "None" "__debug__" "NotImplemented") symbol-end) . py-pseudo-keyword-face) + ;; Decorators. + (,(rx line-start (* (any " \t")) (group "@" (1+ (or word ?_)) + (0+ "." (1+ (or word ?_))))) + (1 py-decorators-face)) + (,(rx symbol-start (or "cls" "self") + symbol-end) . py-object-reference-face) + + ;; Exceptions + (,(rx word-start + (or "ArithmeticError" "AssertionError" "AttributeError" + "BaseException" "BufferError" "BytesWarning" "DeprecationWarning" + "EOFError" "EnvironmentError" "Exception" "FloatingPointError" + "FutureWarning" "GeneratorExit" "IOError" "ImportError" + "ImportWarning" "IndentationError" "IndexError" "KeyError" + "KeyboardInterrupt" "LookupError" "MemoryError" "NameError" "NoResultFound" + "NotImplementedError" "OSError" "OverflowError" + "PendingDeprecationWarning" "ReferenceError" "RuntimeError" + "RuntimeWarning" "StandardError" "StopIteration" "SyntaxError" + "SyntaxWarning" "SystemError" "SystemExit" "TabError" "TypeError" + "UnboundLocalError" "UnicodeDecodeError" "UnicodeEncodeError" + "UnicodeError" "UnicodeTranslateError" "UnicodeWarning" + "UserWarning" "ValueError" "Warning" "ZeroDivisionError" + ;; OSError subclasses + "BlockIOError" "ChildProcessError" "ConnectionError" + "BrokenPipError" "ConnectionAbortedError" + "ConnectionRefusedError" "ConnectionResetError" + "FileExistsError" "FileNotFoundError" "InterruptedError" + "IsADirectoryError" "NotADirectoryError" "PermissionError" + "ProcessLookupError" "TimeoutError") + word-end) . py-exception-name-face) + ;; Builtins + (,(rx + (or space line-start (not (any "."))) + symbol-start + (group (or "_" "__doc__" "__import__" "__name__" "__package__" "abs" "all" + "any" "apply" "basestring" "bin" "bool" "buffer" "bytearray" + "bytes" "callable" "chr" "classmethod" "cmp" "coerce" "compile" + "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" + "execfile" "filter" "float" "format" "frozenset" + "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" + "int" "intern" "isinstance" "issubclass" "iter" "len" "list" + "locals" "long" "map" "max" "min" "next" "object" "oct" "open" + "ord" "pow" "property" "range" "raw_input" "reduce" + "reload" "repr" "reversed" "round" "set" "setattr" "slice" + "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" + "unichr" "unicode" "vars" "xrange" "zip")) symbol-end) . (1 py-builtins-face)) + ;; #104, GNU bug 44568 font lock of assignments with type hints + ;; ("\\([._[:word:]]+\\)\\(?:\\[[^]]+]\\)?[[:space:]]*\\(?:\\(?:\\*\\*\\|//\\|<<\\|>>\\|[%&*+/|^-]\\)?=\\)" + ;; (1 py-variable-name-face nil nil)) + ;; https://emacs.stackexchange.com/questions/55184/ + ;; how-to-highlight-in-different-colors-for-variables-inside-fstring-on-python-mo + ;; + ;; this is the full string. + ;; group 1 is the quote type and a closing quote is matched + ;; group 2 is the string part + ("f\\(['\"]\\{1,3\\}\\)\\([^\\1]+?\\)\\1" + ;; these are the {keywords} + ("{[^}]*?}" + ;; Pre-match form + (progn (goto-char (match-beginning 0)) (match-end 0)) + ;; Post-match form + (goto-char (match-end 0)) + ;; face for this match + ;; (0 font-lock-variable-name-face t))) + (0 py-variable-name-face t))) + ;; assignment + ;; a, b, c = (1, 2, 3) + ;; a, *b, c = range(10) + ;; inst.a, inst.b, inst.c = 'foo', 'bar', 'baz' + ;; (a, b, *c, d) = x, *y = 5, 6, 7, 8, 9 + (,(py-font-lock-assignment-matcher + (py-rx line-start (* space) (? (or "[" "(")) + grouped-assignment-target (* space) ?, (* space) + (* assignment-target (* space) ?, (* space)) + (? assignment-target (* space)) + (? ?, (* space)) + (? (or ")" "]") (* space)) + (group assignment-operator))) + (1 py-variable-name-face) + (,(py-rx grouped-assignment-target) + (progn + (goto-char (match-end 1)) ; go back after the first symbol + (match-beginning 2)) ; limit the search until the assignment + nil + (1 py-variable-name-face))) + ( + ;; "(closure (t) (limit) (let ((re \"\\(?:self\\)*\\([._[:word:]]+\\)[[:space:]]*\\(?:,[[:space:]]*[._[:word:]]+[[:space:]]*\\)*\\(?:%=\\|&=\\|\\*\\(?:\\*?=\\)\\|\\+=\\|-=\\|/\\(?:/?=\\)\\|\\(?:<<\\|>>\\|[|^]\\)=\\|[:=]\\)\") (res nil)) (while (and (setq res (re-search-forward re limit t)) (goto-char (match-end 1)) (nth 1 (parse-partial-sexp (point-min) (point))))) res))" . (1 py-variable-name-face nil nil) + + ,(lambda (limit) + (let ((re (rx (* "self")(group (+ (any word ?. ?_))) (* space) + (* ?, (* space) (+ (any word ?. ?_)) (* space)) + (or ":" "=" "+=" "-=" "*=" "/=" "//=" "%=" "**=" ">>=" "<<=" "&=" "^=" "|="))) + (res nil)) + (while (and (setq res (re-search-forward re limit t)) + (goto-char (match-end 1)) + (nth 1 (parse-partial-sexp (point-min) (point))) + ;; (python-syntax-context 'paren) + )) + res)) + . (1 py-variable-name-face nil nil)) + + + ;; Numbers + ;; (,(rx symbol-start (or (1+ digit) (1+ hex-digit)) symbol-end) . py-number-face) + ("\\_<[[:digit:]]+\\_>" . py-number-face)) + ;; ,(rx symbol-start (1+ digit) symbol-end) + + "Keywords matching font-lock") + +;; python-components-menu +(defun py-define-menu (map) + (easy-menu-define py-menu map "Py" + `("Python" + ("Interpreter" + ["Ipython" ipython + :help " `ipython' +Start an IPython interpreter."] + + ["Ipython2\.7" ipython2\.7 + :help " `ipython2\.7'"] + + ["Ipython3" ipython3 + :help " `ipython3' +Start an IPython3 interpreter."] + + ["Jython" jython + :help " `jython' +Start an Jython interpreter."] + + ["Python" python + :help " `python' +Start an Python interpreter."] + + ["Python2" python2 + :help " `python2' +Start an Python2 interpreter."] + + ["Python3" python3 + :help " `python3' +Start an Python3 interpreter."] + ["SymPy" isympy3 + :help " `isympy3' +Start an SymPy interpreter."]) + + ("Edit" + ("Shift" + ("Shift right" + ["Shift block right" py-shift-block-right + :help " `py-shift-block-right' +Indent block by COUNT spaces."] + + ["Shift block or clause right" py-shift-block-or-clause-right + :help " `py-shift-block-or-clause-right' +Indent block-or-clause by COUNT spaces."] + + ["Shift class right" py-shift-class-right + :help " `py-shift-class-right' +Indent class by COUNT spaces."] + + ["Shift clause right" py-shift-clause-right + :help " `py-shift-clause-right' +Indent clause by COUNT spaces."] + + ["Shift comment right" py-shift-comment-right + :help " `py-shift-comment-right' +Indent comment by COUNT spaces."] + + ["Shift def right" py-shift-def-right + :help " `py-shift-def-right' +Indent def by COUNT spaces."] + + ["Shift def or class right" py-shift-def-or-class-right + :help " `py-shift-def-or-class-right' +Indent def-or-class by COUNT spaces."] + + ["Shift indent right" py-shift-indent-right + :help " `py-shift-indent-right' +Indent indent by COUNT spaces."] + + ["Shift minor block right" py-shift-minor-block-right + :help " `py-shift-minor-block-right' +Indent minor-block by COUNT spaces."] + + ["Shift paragraph right" py-shift-paragraph-right + :help " `py-shift-paragraph-right' +Indent paragraph by COUNT spaces."] + + ["Shift region right" py-shift-region-right + :help " `py-shift-region-right' +Indent region by COUNT spaces."] + + ["Shift statement right" py-shift-statement-right + :help " `py-shift-statement-right' +Indent statement by COUNT spaces."] + + ["Shift top level right" py-shift-top-level-right + :help " `py-shift-top-level-right' +Indent top-level by COUNT spaces."]) + ("Shift left" + ["Shift block left" py-shift-block-left + :help " `py-shift-block-left' +Dedent block by COUNT spaces."] + + ["Shift block or clause left" py-shift-block-or-clause-left + :help " `py-shift-block-or-clause-left' +Dedent block-or-clause by COUNT spaces."] + + ["Shift class left" py-shift-class-left + :help " `py-shift-class-left' +Dedent class by COUNT spaces."] + + ["Shift clause left" py-shift-clause-left + :help " `py-shift-clause-left' +Dedent clause by COUNT spaces."] + + ["Shift comment left" py-shift-comment-left + :help " `py-shift-comment-left' +Dedent comment by COUNT spaces."] + + ["Shift def left" py-shift-def-left + :help " `py-shift-def-left' +Dedent def by COUNT spaces."] + + ["Shift def or class left" py-shift-def-or-class-left + :help " `py-shift-def-or-class-left' +Dedent def-or-class by COUNT spaces."] + + ["Shift indent left" py-shift-indent-left + :help " `py-shift-indent-left' +Dedent indent by COUNT spaces."] + + ["Shift minor block left" py-shift-minor-block-left + :help " `py-shift-minor-block-left' +Dedent minor-block by COUNT spaces."] + + ["Shift paragraph left" py-shift-paragraph-left + :help " `py-shift-paragraph-left' +Dedent paragraph by COUNT spaces."] + + ["Shift region left" py-shift-region-left + :help " `py-shift-region-left' +Dedent region by COUNT spaces."] + + ["Shift statement left" py-shift-statement-left + :help " `py-shift-statement-left' +Dedent statement by COUNT spaces."])) + ("Mark" + ["Mark block" py-mark-block + :help " `py-mark-block' +Mark block, take beginning of line positions."] + + ["Mark block or clause" py-mark-block-or-clause + :help " `py-mark-block-or-clause' +Mark block-or-clause, take beginning of line positions."] + + ["Mark class" py-mark-class + :help " `py-mark-class' +Mark class, take beginning of line positions."] + + ["Mark clause" py-mark-clause + :help " `py-mark-clause' +Mark clause, take beginning of line positions."] + + ["Mark comment" py-mark-comment + :help " `py-mark-comment' +Mark comment at point."] + + ["Mark def" py-mark-def + :help " `py-mark-def' +Mark def, take beginning of line positions."] + + ["Mark def or class" py-mark-def-or-class + :help " `py-mark-def-or-class' +Mark def-or-class, take beginning of line positions."] + + ["Mark expression" py-mark-expression + :help " `py-mark-expression' +Mark expression at point."] + + ["Mark except block" py-mark-except-block + :help " `py-mark-except-block' +Mark except-block, take beginning of line positions."] + + ["Mark if block" py-mark-if-block + :help " `py-mark-if-block' +Mark if-block, take beginning of line positions."] + + ["Mark indent" py-mark-indent + :help " `py-mark-indent' +Mark indent, take beginning of line positions."] + + ["Mark line" py-mark-line + :help " `py-mark-line' +Mark line at point."] + + ["Mark minor block" py-mark-minor-block + :help " `py-mark-minor-block' +Mark minor-block, take beginning of line positions."] + + ["Mark partial expression" py-mark-partial-expression + :help " `py-mark-partial-expression' +Mark partial-expression at point."] + + ["Mark paragraph" py-mark-paragraph + :help " `py-mark-paragraph' +Mark paragraph at point."] + + ["Mark section" py-mark-section + :help " `py-mark-section' +Mark section at point."] + + ["Mark statement" py-mark-statement + :help " `py-mark-statement' +Mark statement, take beginning of line positions."] + + ["Mark top level" py-mark-top-level + :help " `py-mark-top-level' +Mark top-level, take beginning of line positions."] + + ["Mark try block" py-mark-try-block + :help " `py-mark-try-block' +Mark try-block, take beginning of line positions."]) + ("Copy" + ["Copy block" py-copy-block + :help " `py-copy-block' +Copy block at point."] + + ["Copy block or clause" py-copy-block-or-clause + :help " `py-copy-block-or-clause' +Copy block-or-clause at point."] + + ["Copy class" py-copy-class + :help " `py-copy-class' +Copy class at point."] + + ["Copy clause" py-copy-clause + :help " `py-copy-clause' +Copy clause at point."] + + ["Copy comment" py-copy-comment + :help " `py-copy-comment'"] + + ["Copy def" py-copy-def + :help " `py-copy-def' +Copy def at point."] + + ["Copy def or class" py-copy-def-or-class + :help " `py-copy-def-or-class' +Copy def-or-class at point."] + + ["Copy expression" py-copy-expression + :help " `py-copy-expression' +Copy expression at point."] + + ["Copy except block" py-copy-except-block + :help " `py-copy-except-block'"] + + ["Copy if block" py-copy-if-block + :help " `py-copy-if-block'"] + + ["Copy indent" py-copy-indent + :help " `py-copy-indent' +Copy indent at point."] + + ["Copy line" py-copy-line + :help " `py-copy-line' +Copy line at point."] + + ["Copy minor block" py-copy-minor-block + :help " `py-copy-minor-block' +Copy minor-block at point."] + + ["Copy partial expression" py-copy-partial-expression + :help " `py-copy-partial-expression' +Copy partial-expression at point."] + + ["Copy paragraph" py-copy-paragraph + :help " `py-copy-paragraph' +Copy paragraph at point."] + + ["Copy section" py-copy-section + :help " `py-copy-section'"] + + ["Copy statement" py-copy-statement + :help " `py-copy-statement' +Copy statement at point."] + + ["Copy top level" py-copy-top-level + :help " `py-copy-top-level' +Copy top-level at point."]) + ("Kill" + ["Kill block" py-kill-block + :help " `py-kill-block' +Delete block at point."] + + ["Kill block or clause" py-kill-block-or-clause + :help " `py-kill-block-or-clause' +Delete block-or-clause at point."] + + ["Kill class" py-kill-class + :help " `py-kill-class' +Delete class at point."] + + ["Kill clause" py-kill-clause + :help " `py-kill-clause' +Delete clause at point."] + + ["Kill comment" py-kill-comment + :help " `py-kill-comment' +Delete comment at point."] + + ["Kill def" py-kill-def + :help " `py-kill-def' +Delete def at point."] + + ["Kill def or class" py-kill-def-or-class + :help " `py-kill-def-or-class' +Delete def-or-class at point."] + + ["Kill expression" py-kill-expression + :help " `py-kill-expression' +Delete expression at point."] + + ["Kill except block" py-kill-except-block + :help " `py-kill-except-block' +Delete except-block at point."] + + ["Kill if block" py-kill-if-block + :help " `py-kill-if-block' +Delete if-block at point."] + + ["Kill indent" py-kill-indent + :help " `py-kill-indent' +Delete indent at point."] + + ["Kill line" py-kill-line + :help " `py-kill-line' +Delete line at point."] + + ["Kill minor block" py-kill-minor-block + :help " `py-kill-minor-block' +Delete minor-block at point."] + + ["Kill partial expression" py-kill-partial-expression + :help " `py-kill-partial-expression' +Delete partial-expression at point."] + + ["Kill paragraph" py-kill-paragraph + :help " `py-kill-paragraph' +Delete paragraph at point."] + + ["Kill section" py-kill-section + :help " `py-kill-section' +Delete section at point."] + + ["Kill statement" py-kill-statement + :help " `py-kill-statement' +Delete statement at point."] + + ["Kill top level" py-kill-top-level + :help " `py-kill-top-level' +Delete top-level at point."] + + ["Kill try block" py-kill-try-block + :help " `py-kill-try-block' +Delete try-block at point."]) + ("Delete" + ["Delete block" py-delete-block + :help " `py-delete-block' +Delete BLOCK at point until beginning-of-line."] + + ["Delete block or clause" py-delete-block-or-clause + :help " `py-delete-block-or-clause' +Delete BLOCK-OR-CLAUSE at point until beginning-of-line."] + + ["Delete class" py-delete-class + :help " `py-delete-class' +Delete CLASS at point until beginning-of-line."] + + ["Delete clause" py-delete-clause + :help " `py-delete-clause' +Delete CLAUSE at point until beginning-of-line."] + + ["Delete comment" py-delete-comment + :help " `py-delete-comment' +Delete COMMENT at point."] + + ["Delete def" py-delete-def + :help " `py-delete-def' +Delete DEF at point until beginning-of-line."] + + ["Delete def or class" py-delete-def-or-class + :help " `py-delete-def-or-class' +Delete DEF-OR-CLASS at point until beginning-of-line."] + + ["Delete expression" py-delete-expression + :help " `py-delete-expression' +Delete EXPRESSION at point."] + + ["Delete except block" py-delete-except-block + :help " `py-delete-except-block' +Delete EXCEPT-BLOCK at point until beginning-of-line."] + + ["Delete if block" py-delete-if-block + :help " `py-delete-if-block' +Delete IF-BLOCK at point until beginning-of-line."] + + ["Delete indent" py-delete-indent + :help " `py-delete-indent' +Delete INDENT at point until beginning-of-line."] + + ["Delete line" py-delete-line + :help " `py-delete-line' +Delete LINE at point."] + + ["Delete minor block" py-delete-minor-block + :help " `py-delete-minor-block' +Delete MINOR-BLOCK at point until beginning-of-line."] + + ["Delete partial expression" py-delete-partial-expression + :help " `py-delete-partial-expression' +Delete PARTIAL-EXPRESSION at point."] + + ["Delete paragraph" py-delete-paragraph + :help " `py-delete-paragraph' +Delete PARAGRAPH at point."] + + ["Delete section" py-delete-section + :help " `py-delete-section' +Delete SECTION at point."] + + ["Delete statement" py-delete-statement + :help " `py-delete-statement' +Delete STATEMENT at point until beginning-of-line."] + + ["Delete top level" py-delete-top-level + :help " `py-delete-top-level' +Delete TOP-LEVEL at point."] + + ["Delete try block" py-delete-try-block + :help " `py-delete-try-block' +Delete TRY-BLOCK at point until beginning-of-line."]) + ("Comment" + ["Comment block" py-comment-block + :help " `py-comment-block' +Comments block at point."] + + ["Comment block or clause" py-comment-block-or-clause + :help " `py-comment-block-or-clause' +Comments block-or-clause at point."] + + ["Comment class" py-comment-class + :help " `py-comment-class' +Comments class at point."] + + ["Comment clause" py-comment-clause + :help " `py-comment-clause' +Comments clause at point."] + + ["Comment def" py-comment-def + :help " `py-comment-def' +Comments def at point."] + + ["Comment def or class" py-comment-def-or-class + :help " `py-comment-def-or-class' +Comments def-or-class at point."] + + ["Comment indent" py-comment-indent + :help " `py-comment-indent' +Comments indent at point."] + + ["Comment minor block" py-comment-minor-block + :help " `py-comment-minor-block' +Comments minor-block at point."] + + ["Comment section" py-comment-section + :help " `py-comment-section' +Comments section at point."] + + ["Comment statement" py-comment-statement + :help " `py-comment-statement' +Comments statement at point."] + + ["Comment top level" py-comment-top-level + :help " `py-comment-top-level' +Comments top-level at point."])) + ("Move" + ("Backward" + + ["Backward def or class" py-backward-def-or-class + :help " `py-backward-def-or-class' +Go to beginning of def-or-class."] + + ["Backward class" py-backward-class + :help " `py-backward-class' +Go to beginning of class."] + + ["Backward def" py-backward-def + :help " `py-backward-def' +Go to beginning of def."] + + ["Backward block" py-backward-block + :help " `py-backward-block' +Go to beginning of `block'."] + + ["Backward statement" py-backward-statement + :help " `py-backward-statement' +Go to the initial line of a simple statement."] + + ["Backward indent" py-backward-indent + :help " `py-backward-indent' +Go to the beginning of a section of equal indent."] + + ["Backward top level" py-backward-top-level + :help " `py-backward-top-level' +Go up to beginning of statments until level of indentation is null."] + + ("Other" + ["Backward section" py-backward-section + :help " `py-backward-section' +Go to next section start upward in buffer."] + + ["Backward expression" py-backward-expression + :help " `py-backward-expression'"] + + ["Backward partial expression" py-backward-partial-expression + :help " `py-backward-partial-expression'"] + + ["Backward assignment" py-backward-assignment + :help " `py-backward-assignment'"] + + ["Backward block or clause" py-backward-block-or-clause + :help " `py-backward-block-or-clause' +Go to beginning of `block-or-clause'."] + + ["Backward clause" py-backward-clause + :help " `py-backward-clause' +Go to beginning of `clause'."] + + ["Backward elif block" py-backward-elif-block + :help " `py-backward-elif-block' +Go to beginning of `elif-block'."] + + ["Backward else block" py-backward-else-block + :help " `py-backward-else-block' +Go to beginning of `else-block'."] + + ["Backward except block" py-backward-except-block + :help " `py-backward-except-block' +Go to beginning of `except-block'."] + + ["Backward if block" py-backward-if-block + :help " `py-backward-if-block' +Go to beginning of `if-block'."] + + ["Backward minor block" py-backward-minor-block + :help " `py-backward-minor-block' +Go to beginning of `minor-block'."] + + ["Backward try block" py-backward-try-block + :help " `py-backward-try-block' +Go to beginning of `try-block'."])) + ("Forward" + ["Forward def or class" py-forward-def-or-class + :help " `py-forward-def-or-class' +Go to end of def-or-class."] + + ["Forward class" py-forward-class + :help " `py-forward-class' +Go to end of class."] + + ["Forward def" py-forward-def + :help " `py-forward-def' +Go to end of def."] + + ["Forward block" py-forward-block + :help " `py-forward-block' +Go to end of block."] + + ["Forward statement" py-forward-statement + :help " `py-forward-statement' +Go to the last char of current statement."] + + ["Forward indent" py-forward-indent + :help " `py-forward-indent' +Go to the end of a section of equal indentation."] + + ["Forward top level" py-forward-top-level + :help " `py-forward-top-level' +Go to end of top-level form at point."] + + ("Other" + ["Forward section" py-forward-section + :help " `py-forward-section' +Go to next section end downward in buffer."] + + ["Forward expression" py-forward-expression + :help " `py-forward-expression'"] + + ["Forward partial expression" py-forward-partial-expression + :help " `py-forward-partial-expression'"] + + ["Forward assignment" py-forward-assignment + :help " `py-forward-assignment'"] + + ["Forward block or clause" py-forward-block-or-clause + :help " `py-forward-block-or-clause' +Go to end of block-or-clause."] + + ["Forward clause" py-forward-clause + :help " `py-forward-clause' +Go to end of clause."] + + ["Forward for block" py-forward-for-block + :help " `py-forward-for-block' +Go to end of for-block."] + + ["Forward elif block" py-forward-elif-block + :help " `py-forward-elif-block' +Go to end of elif-block."] + + ["Forward else block" py-forward-else-block + :help " `py-forward-else-block' +Go to end of else-block."] + + ["Forward except block" py-forward-except-block + :help " `py-forward-except-block' +Go to end of except-block."] + + ["Forward if block" py-forward-if-block + :help " `py-forward-if-block' +Go to end of if-block."] + + ["Forward minor block" py-forward-minor-block + :help " `py-forward-minor-block' +Go to end of minor-block."] + ["Forward try block" py-forward-try-block + :help " `py-forward-try-block' +Go to end of try-block."])) + ("BOL-forms" + ("Backward" + ["Backward block bol" py-backward-block-bol + :help " `py-backward-block-bol' +Go to beginning of `block', go to BOL."] + + ["Backward block or clause bol" py-backward-block-or-clause-bol + :help " `py-backward-block-or-clause-bol' +Go to beginning of `block-or-clause', go to BOL."] + + ["Backward class bol" py-backward-class-bol + :help " `py-backward-class-bol' +Go to beginning of class, go to BOL."] + + ["Backward clause bol" py-backward-clause-bol + :help " `py-backward-clause-bol' +Go to beginning of `clause', go to BOL."] + + ["Backward def bol" py-backward-def-bol + :help " `py-backward-def-bol' +Go to beginning of def, go to BOL."] + + ["Backward def or class bol" py-backward-def-or-class-bol + :help " `py-backward-def-or-class-bol' +Go to beginning of def-or-class, go to BOL."] + + ["Backward elif block bol" py-backward-elif-block-bol + :help " `py-backward-elif-block-bol' +Go to beginning of `elif-block', go to BOL."] + + ["Backward else block bol" py-backward-else-block-bol + :help " `py-backward-else-block-bol' +Go to beginning of `else-block', go to BOL."] + + ["Backward except block bol" py-backward-except-block-bol + :help " `py-backward-except-block-bol' +Go to beginning of `except-block', go to BOL."] + + ["Backward expression bol" py-backward-expression-bol + :help " `py-backward-expression-bol'"] + + ["Backward for block bol" py-backward-for-block-bol + :help " `py-backward-for-block-bol' +Go to beginning of `for-block', go to BOL."] + + ["Backward if block bol" py-backward-if-block-bol + :help " `py-backward-if-block-bol' +Go to beginning of `if-block', go to BOL."] + + ["Backward indent bol" py-backward-indent-bol + :help " `py-backward-indent-bol' +Go to the beginning of line of a section of equal indent."] + + ["Backward minor block bol" py-backward-minor-block-bol + :help " `py-backward-minor-block-bol' +Go to beginning of `minor-block', go to BOL."] + + ["Backward partial expression bol" py-backward-partial-expression-bol + :help " `py-backward-partial-expression-bol'"] + + ["Backward section bol" py-backward-section-bol + :help " `py-backward-section-bol'"] + + ["Backward statement bol" py-backward-statement-bol + :help " `py-backward-statement-bol' +Goto beginning of line where statement starts."] + + ["Backward try block bol" py-backward-try-block-bol + :help " `py-backward-try-block-bol' +Go to beginning of `try-block', go to BOL."]) + ("Forward" + ["Forward block bol" py-forward-block-bol + :help " `py-forward-block-bol' +Goto beginning of line following end of block."] + + ["Forward block or clause bol" py-forward-block-or-clause-bol + :help " `py-forward-block-or-clause-bol' +Goto beginning of line following end of block-or-clause."] + + ["Forward class bol" py-forward-class-bol + :help " `py-forward-class-bol' +Goto beginning of line following end of class."] + + ["Forward clause bol" py-forward-clause-bol + :help " `py-forward-clause-bol' +Goto beginning of line following end of clause."] + + ["Forward def bol" py-forward-def-bol + :help " `py-forward-def-bol' +Goto beginning of line following end of def."] + + ["Forward def or class bol" py-forward-def-or-class-bol + :help " `py-forward-def-or-class-bol' +Goto beginning of line following end of def-or-class."] + + ["Forward elif block bol" py-forward-elif-block-bol + :help " `py-forward-elif-block-bol' +Goto beginning of line following end of elif-block."] + + ["Forward else block bol" py-forward-else-block-bol + :help " `py-forward-else-block-bol' +Goto beginning of line following end of else-block."] + + ["Forward except block bol" py-forward-except-block-bol + :help " `py-forward-except-block-bol' +Goto beginning of line following end of except-block."] + + ["Forward expression bol" py-forward-expression-bol + :help " `py-forward-expression-bol'"] + + ["Forward for block bol" py-forward-for-block-bol + :help " `py-forward-for-block-bol' +Goto beginning of line following end of for-block."] + + ["Forward if block bol" py-forward-if-block-bol + :help " `py-forward-if-block-bol' +Goto beginning of line following end of if-block."] + + ["Forward indent bol" py-forward-indent-bol + :help " `py-forward-indent-bol' +Go to beginning of line following of a section of equal indentation."] + + ["Forward minor block bol" py-forward-minor-block-bol + :help " `py-forward-minor-block-bol' +Goto beginning of line following end of minor-block."] + + ["Forward partial expression bol" py-forward-partial-expression-bol + :help " `py-forward-partial-expression-bol'"] + + ["Forward section bol" py-forward-section-bol + :help " `py-forward-section-bol'"] + + ["Forward statement bol" py-forward-statement-bol + :help " `py-forward-statement-bol' +Go to the beginning-of-line following current statement."] + + ["Forward top level bol" py-forward-top-level-bol + :help " `py-forward-top-level-bol' +Go to end of top-level form at point, stop at next beginning-of-line."] + + ["Forward try block bol" py-forward-try-block-bol + :help " `py-forward-try-block-bol' +Goto beginning of line following end of try-block."])) + ("Up/Down" + ["Up" py-up + :help " `py-up' +Go up or to beginning of form if inside."] + + ["Down" py-down + :help " `py-down' +Go to beginning one level below of compound statement or definition at point."])) + ("Send" + ["Execute block" py-execute-block + :help " `py-execute-block' +Send block at point to interpreter."] + + ["Execute block or clause" py-execute-block-or-clause + :help " `py-execute-block-or-clause' +Send block-or-clause at point to interpreter."] + + ["Execute buffer" py-execute-buffer + :help " `py-execute-buffer' +:around advice: `ad-Advice-py-execute-buffer'"] + + ["Execute class" py-execute-class + :help " `py-execute-class' +Send class at point to interpreter."] + + ["Execute clause" py-execute-clause + :help " `py-execute-clause' +Send clause at point to interpreter."] + + ["Execute def" py-execute-def + :help " `py-execute-def' +Send def at point to interpreter."] + + ["Execute def or class" py-execute-def-or-class + :help " `py-execute-def-or-class' +Send def-or-class at point to interpreter."] + + ["Execute expression" py-execute-expression + :help " `py-execute-expression' +Send expression at point to interpreter."] + + ["Execute indent" py-execute-indent + :help " `py-execute-indent' +Send indent at point to interpreter."] + + ["Execute line" py-execute-line + :help " `py-execute-line' +Send line at point to interpreter."] + + ["Execute minor block" py-execute-minor-block + :help " `py-execute-minor-block' +Send minor-block at point to interpreter."] + + ["Execute paragraph" py-execute-paragraph + :help " `py-execute-paragraph' +Send paragraph at point to interpreter."] + + ["Execute partial expression" py-execute-partial-expression + :help " `py-execute-partial-expression' +Send partial-expression at point to interpreter."] + + ["Execute region" py-execute-region + :help " `py-execute-region' +Send region at point to interpreter."] + + ["Execute statement" py-execute-statement + :help " `py-execute-statement' +Send statement at point to interpreter."] + + ["Execute top level" py-execute-top-level + :help " `py-execute-top-level' +Send top-level at point to interpreter."] + ("Other" + ("IPython" + ["Execute block ipython" py-execute-block-ipython + :help " `py-execute-block-ipython' +Send block at point to IPython interpreter."] + + ["Execute block or clause ipython" py-execute-block-or-clause-ipython + :help " `py-execute-block-or-clause-ipython' +Send block-or-clause at point to IPython interpreter."] + + ["Execute buffer ipython" py-execute-buffer-ipython + :help " `py-execute-buffer-ipython' +Send buffer at point to IPython interpreter."] + + ["Execute class ipython" py-execute-class-ipython + :help " `py-execute-class-ipython' +Send class at point to IPython interpreter."] + + ["Execute clause ipython" py-execute-clause-ipython + :help " `py-execute-clause-ipython' +Send clause at point to IPython interpreter."] + + ["Execute def ipython" py-execute-def-ipython + :help " `py-execute-def-ipython' +Send def at point to IPython interpreter."] + + ["Execute def or class ipython" py-execute-def-or-class-ipython + :help " `py-execute-def-or-class-ipython' +Send def-or-class at point to IPython interpreter."] + + ["Execute expression ipython" py-execute-expression-ipython + :help " `py-execute-expression-ipython' +Send expression at point to IPython interpreter."] + + ["Execute indent ipython" py-execute-indent-ipython + :help " `py-execute-indent-ipython' +Send indent at point to IPython interpreter."] + + ["Execute line ipython" py-execute-line-ipython + :help " `py-execute-line-ipython' +Send line at point to IPython interpreter."] + + ["Execute minor block ipython" py-execute-minor-block-ipython + :help " `py-execute-minor-block-ipython' +Send minor-block at point to IPython interpreter."] + + ["Execute paragraph ipython" py-execute-paragraph-ipython + :help " `py-execute-paragraph-ipython' +Send paragraph at point to IPython interpreter."] + + ["Execute partial expression ipython" py-execute-partial-expression-ipython + :help " `py-execute-partial-expression-ipython' +Send partial-expression at point to IPython interpreter."] + + ["Execute region ipython" py-execute-region-ipython + :help " `py-execute-region-ipython' +Send region at point to IPython interpreter."] + + ["Execute statement ipython" py-execute-statement-ipython + :help " `py-execute-statement-ipython' +Send statement at point to IPython interpreter."] + + ["Execute top level ipython" py-execute-top-level-ipython + :help " `py-execute-top-level-ipython' +Send top-level at point to IPython interpreter."]) + ("IPython2" + ["Execute block ipython2" py-execute-block-ipython2 + :help " `py-execute-block-ipython2'"] + + ["Execute block or clause ipython2" py-execute-block-or-clause-ipython2 + :help " `py-execute-block-or-clause-ipython2'"] + + ["Execute buffer ipython2" py-execute-buffer-ipython2 + :help " `py-execute-buffer-ipython2'"] + + ["Execute class ipython2" py-execute-class-ipython2 + :help " `py-execute-class-ipython2'"] + + ["Execute clause ipython2" py-execute-clause-ipython2 + :help " `py-execute-clause-ipython2'"] + + ["Execute def ipython2" py-execute-def-ipython2 + :help " `py-execute-def-ipython2'"] + + ["Execute def or class ipython2" py-execute-def-or-class-ipython2 + :help " `py-execute-def-or-class-ipython2'"] + + ["Execute expression ipython2" py-execute-expression-ipython2 + :help " `py-execute-expression-ipython2'"] + + ["Execute indent ipython2" py-execute-indent-ipython2 + :help " `py-execute-indent-ipython2'"] + + ["Execute line ipython2" py-execute-line-ipython2 + :help " `py-execute-line-ipython2'"] + + ["Execute minor block ipython2" py-execute-minor-block-ipython2 + :help " `py-execute-minor-block-ipython2'"] + + ["Execute paragraph ipython2" py-execute-paragraph-ipython2 + :help " `py-execute-paragraph-ipython2'"] + + ["Execute partial expression ipython2" py-execute-partial-expression-ipython2 + :help " `py-execute-partial-expression-ipython2'"] + + ["Execute region ipython2" py-execute-region-ipython2 + :help " `py-execute-region-ipython2'"] + + ["Execute statement ipython2" py-execute-statement-ipython2 + :help " `py-execute-statement-ipython2'"] + + ["Execute top level ipython2" py-execute-top-level-ipython2 + :help " `py-execute-top-level-ipython2'"]) + ("IPython3" + ["Execute block ipython3" py-execute-block-ipython3 + :help " `py-execute-block-ipython3' +Send block at point to IPython interpreter."] + + ["Execute block or clause ipython3" py-execute-block-or-clause-ipython3 + :help " `py-execute-block-or-clause-ipython3' +Send block-or-clause at point to IPython interpreter."] + + ["Execute buffer ipython3" py-execute-buffer-ipython3 + :help " `py-execute-buffer-ipython3' +Send buffer at point to IPython interpreter."] + + ["Execute class ipython3" py-execute-class-ipython3 + :help " `py-execute-class-ipython3' +Send class at point to IPython interpreter."] + + ["Execute clause ipython3" py-execute-clause-ipython3 + :help " `py-execute-clause-ipython3' +Send clause at point to IPython interpreter."] + + ["Execute def ipython3" py-execute-def-ipython3 + :help " `py-execute-def-ipython3' +Send def at point to IPython interpreter."] + + ["Execute def or class ipython3" py-execute-def-or-class-ipython3 + :help " `py-execute-def-or-class-ipython3' +Send def-or-class at point to IPython interpreter."] + + ["Execute expression ipython3" py-execute-expression-ipython3 + :help " `py-execute-expression-ipython3' +Send expression at point to IPython interpreter."] + + ["Execute indent ipython3" py-execute-indent-ipython3 + :help " `py-execute-indent-ipython3' +Send indent at point to IPython interpreter."] + + ["Execute line ipython3" py-execute-line-ipython3 + :help " `py-execute-line-ipython3' +Send line at point to IPython interpreter."] + + ["Execute minor block ipython3" py-execute-minor-block-ipython3 + :help " `py-execute-minor-block-ipython3' +Send minor-block at point to IPython interpreter."] + + ["Execute paragraph ipython3" py-execute-paragraph-ipython3 + :help " `py-execute-paragraph-ipython3' +Send paragraph at point to IPython interpreter."] + + ["Execute partial expression ipython3" py-execute-partial-expression-ipython3 + :help " `py-execute-partial-expression-ipython3' +Send partial-expression at point to IPython interpreter."] + + ["Execute region ipython3" py-execute-region-ipython3 + :help " `py-execute-region-ipython3' +Send region at point to IPython interpreter."] + + ["Execute statement ipython3" py-execute-statement-ipython3 + :help " `py-execute-statement-ipython3' +Send statement at point to IPython interpreter."] + + ["Execute top level ipython3" py-execute-top-level-ipython3 + :help " `py-execute-top-level-ipython3' +Send top-level at point to IPython interpreter."]) + ("Jython" + ["Execute block jython" py-execute-block-jython + :help " `py-execute-block-jython' +Send block at point to Jython interpreter."] + + ["Execute block or clause jython" py-execute-block-or-clause-jython + :help " `py-execute-block-or-clause-jython' +Send block-or-clause at point to Jython interpreter."] + + ["Execute buffer jython" py-execute-buffer-jython + :help " `py-execute-buffer-jython' +Send buffer at point to Jython interpreter."] + + ["Execute class jython" py-execute-class-jython + :help " `py-execute-class-jython' +Send class at point to Jython interpreter."] + + ["Execute clause jython" py-execute-clause-jython + :help " `py-execute-clause-jython' +Send clause at point to Jython interpreter."] + + ["Execute def jython" py-execute-def-jython + :help " `py-execute-def-jython' +Send def at point to Jython interpreter."] + + ["Execute def or class jython" py-execute-def-or-class-jython + :help " `py-execute-def-or-class-jython' +Send def-or-class at point to Jython interpreter."] + + ["Execute expression jython" py-execute-expression-jython + :help " `py-execute-expression-jython' +Send expression at point to Jython interpreter."] + + ["Execute indent jython" py-execute-indent-jython + :help " `py-execute-indent-jython' +Send indent at point to Jython interpreter."] + + ["Execute line jython" py-execute-line-jython + :help " `py-execute-line-jython' +Send line at point to Jython interpreter."] + + ["Execute minor block jython" py-execute-minor-block-jython + :help " `py-execute-minor-block-jython' +Send minor-block at point to Jython interpreter."] + + ["Execute paragraph jython" py-execute-paragraph-jython + :help " `py-execute-paragraph-jython' +Send paragraph at point to Jython interpreter."] + + ["Execute partial expression jython" py-execute-partial-expression-jython + :help " `py-execute-partial-expression-jython' +Send partial-expression at point to Jython interpreter."] + + ["Execute region jython" py-execute-region-jython + :help " `py-execute-region-jython' +Send region at point to Jython interpreter."] + + ["Execute statement jython" py-execute-statement-jython + :help " `py-execute-statement-jython' +Send statement at point to Jython interpreter."] + + ["Execute top level jython" py-execute-top-level-jython + :help " `py-execute-top-level-jython' +Send top-level at point to Jython interpreter."]) + ("Python" + ["Execute block python" py-execute-block-python + :help " `py-execute-block-python' +Send block at point to default interpreter."] + + ["Execute block or clause python" py-execute-block-or-clause-python + :help " `py-execute-block-or-clause-python' +Send block-or-clause at point to default interpreter."] + + ["Execute buffer python" py-execute-buffer-python + :help " `py-execute-buffer-python' +Send buffer at point to default interpreter."] + + ["Execute class python" py-execute-class-python + :help " `py-execute-class-python' +Send class at point to default interpreter."] + + ["Execute clause python" py-execute-clause-python + :help " `py-execute-clause-python' +Send clause at point to default interpreter."] + + ["Execute def python" py-execute-def-python + :help " `py-execute-def-python' +Send def at point to default interpreter."] + + ["Execute def or class python" py-execute-def-or-class-python + :help " `py-execute-def-or-class-python' +Send def-or-class at point to default interpreter."] + + ["Execute expression python" py-execute-expression-python + :help " `py-execute-expression-python' +Send expression at point to default interpreter."] + + ["Execute indent python" py-execute-indent-python + :help " `py-execute-indent-python' +Send indent at point to default interpreter."] + + ["Execute line python" py-execute-line-python + :help " `py-execute-line-python' +Send line at point to default interpreter."] + + ["Execute minor block python" py-execute-minor-block-python + :help " `py-execute-minor-block-python' +Send minor-block at point to default interpreter."] + + ["Execute paragraph python" py-execute-paragraph-python + :help " `py-execute-paragraph-python' +Send paragraph at point to default interpreter."] + + ["Execute partial expression python" py-execute-partial-expression-python + :help " `py-execute-partial-expression-python' +Send partial-expression at point to default interpreter."] + + ["Execute region python" py-execute-region-python + :help " `py-execute-region-python' +Send region at point to default interpreter."] + + ["Execute statement python" py-execute-statement-python + :help " `py-execute-statement-python' +Send statement at point to default interpreter."] + + ["Execute top level python" py-execute-top-level-python + :help " `py-execute-top-level-python' +Send top-level at point to default interpreter."]) + ("Python2" + ["Execute block python2" py-execute-block-python2 + :help " `py-execute-block-python2' +Send block at point to Python2 interpreter."] + + ["Execute block or clause python2" py-execute-block-or-clause-python2 + :help " `py-execute-block-or-clause-python2' +Send block-or-clause at point to Python2 interpreter."] + + ["Execute buffer python2" py-execute-buffer-python2 + :help " `py-execute-buffer-python2' +Send buffer at point to Python2 interpreter."] + + ["Execute class python2" py-execute-class-python2 + :help " `py-execute-class-python2' +Send class at point to Python2 interpreter."] + + ["Execute clause python2" py-execute-clause-python2 + :help " `py-execute-clause-python2' +Send clause at point to Python2 interpreter."] + + ["Execute def python2" py-execute-def-python2 + :help " `py-execute-def-python2' +Send def at point to Python2 interpreter."] + + ["Execute def or class python2" py-execute-def-or-class-python2 + :help " `py-execute-def-or-class-python2' +Send def-or-class at point to Python2 interpreter."] + + ["Execute expression python2" py-execute-expression-python2 + :help " `py-execute-expression-python2' +Send expression at point to Python2 interpreter."] + + ["Execute indent python2" py-execute-indent-python2 + :help " `py-execute-indent-python2' +Send indent at point to Python2 interpreter."] + + ["Execute line python2" py-execute-line-python2 + :help " `py-execute-line-python2' +Send line at point to Python2 interpreter."] + + ["Execute minor block python2" py-execute-minor-block-python2 + :help " `py-execute-minor-block-python2' +Send minor-block at point to Python2 interpreter."] + + ["Execute paragraph python2" py-execute-paragraph-python2 + :help " `py-execute-paragraph-python2' +Send paragraph at point to Python2 interpreter."] + + ["Execute partial expression python2" py-execute-partial-expression-python2 + :help " `py-execute-partial-expression-python2' +Send partial-expression at point to Python2 interpreter."] + + ["Execute region python2" py-execute-region-python2 + :help " `py-execute-region-python2' +Send region at point to Python2 interpreter."] + + ["Execute statement python2" py-execute-statement-python2 + :help " `py-execute-statement-python2' +Send statement at point to Python2 interpreter."] + + ["Execute top level python2" py-execute-top-level-python2 + :help " `py-execute-top-level-python2' +Send top-level at point to Python2 interpreter."]) + ("Python3" + ["Execute block python3" py-execute-block-python3 + :help " `py-execute-block-python3' +Send block at point to Python3 interpreter."] + + ["Execute block or clause python3" py-execute-block-or-clause-python3 + :help " `py-execute-block-or-clause-python3' +Send block-or-clause at point to Python3 interpreter."] + + ["Execute buffer python3" py-execute-buffer-python3 + :help " `py-execute-buffer-python3' +Send buffer at point to Python3 interpreter."] + + ["Execute class python3" py-execute-class-python3 + :help " `py-execute-class-python3' +Send class at point to Python3 interpreter."] + + ["Execute clause python3" py-execute-clause-python3 + :help " `py-execute-clause-python3' +Send clause at point to Python3 interpreter."] + + ["Execute def python3" py-execute-def-python3 + :help " `py-execute-def-python3' +Send def at point to Python3 interpreter."] + + ["Execute def or class python3" py-execute-def-or-class-python3 + :help " `py-execute-def-or-class-python3' +Send def-or-class at point to Python3 interpreter."] + + ["Execute expression python3" py-execute-expression-python3 + :help " `py-execute-expression-python3' +Send expression at point to Python3 interpreter."] + + ["Execute indent python3" py-execute-indent-python3 + :help " `py-execute-indent-python3' +Send indent at point to Python3 interpreter."] + + ["Execute line python3" py-execute-line-python3 + :help " `py-execute-line-python3' +Send line at point to Python3 interpreter."] + + ["Execute minor block python3" py-execute-minor-block-python3 + :help " `py-execute-minor-block-python3' +Send minor-block at point to Python3 interpreter."] + + ["Execute paragraph python3" py-execute-paragraph-python3 + :help " `py-execute-paragraph-python3' +Send paragraph at point to Python3 interpreter."] + + ["Execute partial expression python3" py-execute-partial-expression-python3 + :help " `py-execute-partial-expression-python3' +Send partial-expression at point to Python3 interpreter."] + + ["Execute region python3" py-execute-region-python3 + :help " `py-execute-region-python3' +Send region at point to Python3 interpreter."] + + ["Execute statement python3" py-execute-statement-python3 + :help " `py-execute-statement-python3' +Send statement at point to Python3 interpreter."] + + ["Execute top level python3" py-execute-top-level-python3 + :help " `py-execute-top-level-python3' +Send top-level at point to Python3 interpreter."]) + ("Ignoring defaults " + :help "`M-x py-execute-statement- TAB' for example list commands ignoring defaults + + of `py-switch-buffers-on-execute-p' and `py-split-window-on-execute'"))) + ("Hide-Show" + ("Hide" + ["Hide block" py-hide-block + :help " `py-hide-block' +Hide block at point."] + + ["Hide top level" py-hide-top-level + :help " `py-hide-top-level' +Hide top-level at point."] + + ["Hide def" py-hide-def + :help " `py-hide-def' +Hide def at point."] + + ["Hide def or class" py-hide-def-or-class + :help " `py-hide-def-or-class' +Hide def-or-class at point."] + + ["Hide statement" py-hide-statement + :help " `py-hide-statement' +Hide statement at point."] + + ["Hide class" py-hide-class + :help " `py-hide-class' +Hide class at point."] + + ["Hide clause" py-hide-clause + :help " `py-hide-clause' +Hide clause at point."] + + ["Hide block or clause" py-hide-block-or-clause + :help " `py-hide-block-or-clause' +Hide block-or-clause at point."] + + ["Hide comment" py-hide-comment + :help " `py-hide-comment' +Hide comment at point."] + + ["Hide indent" py-hide-indent + :help " `py-hide-indent' +Hide indent at point."] + + ["Hide expression" py-hide-expression + :help " `py-hide-expression' +Hide expression at point."] + + ["Hide line" py-hide-line + :help " `py-hide-line' +Hide line at point."] + + ["Hide for-block" py-hide-for-block + :help " `py-hide-for-block' +Hide for-block at point."] + + ["Hide if-block" py-hide-if-block + :help " `py-hide-if-block' +Hide if-block at point."] + + ["Hide elif-block" py-hide-elif-block + :help " `py-hide-elif-block' +Hide elif-block at point."] + + ["Hide else-block" py-hide-else-block + :help " `py-hide-else-block' +Hide else-block at point."] + + ["Hide except-block" py-hide-except-block + :help " `py-hide-except-block' +Hide except-block at point."] + + ["Hide minor-block" py-hide-minor-block + :help " `py-hide-minor-block' +Hide minor-block at point."] + + ["Hide paragraph" py-hide-paragraph + :help " `py-hide-paragraph' +Hide paragraph at point."] + + ["Hide partial expression" py-hide-partial-expression + :help " `py-hide-partial-expression' +Hide partial-expression at point."] + + ["Hide section" py-hide-section + :help " `py-hide-section' +Hide section at point."]) + ("Show" + ["Show all" py-show-all + :help " `py-show-all' +Show all in buffer."] + + ["Show" py-show + :help " `py-show' +Show hidden code at point."])) + ("Fast process" + ["Execute block fast" py-execute-block-fast + :help " `py-execute-block-fast' +Process block at point by a Python interpreter."] + + ["Execute block or clause fast" py-execute-block-or-clause-fast + :help " `py-execute-block-or-clause-fast' +Process block-or-clause at point by a Python interpreter."] + + ["Execute class fast" py-execute-class-fast + :help " `py-execute-class-fast' +Process class at point by a Python interpreter."] + + ["Execute clause fast" py-execute-clause-fast + :help " `py-execute-clause-fast' +Process clause at point by a Python interpreter."] + + ["Execute def fast" py-execute-def-fast + :help " `py-execute-def-fast' +Process def at point by a Python interpreter."] + + ["Execute def or class fast" py-execute-def-or-class-fast + :help " `py-execute-def-or-class-fast' +Process def-or-class at point by a Python interpreter."] + + ["Execute expression fast" py-execute-expression-fast + :help " `py-execute-expression-fast' +Process expression at point by a Python interpreter."] + + ["Execute partial expression fast" py-execute-partial-expression-fast + :help " `py-execute-partial-expression-fast' +Process partial-expression at point by a Python interpreter."] + + ["Execute region fast" py-execute-region-fast + :help " `py-execute-region-fast'"] + + ["Execute statement fast" py-execute-statement-fast + :help " `py-execute-statement-fast' +Process statement at point by a Python interpreter."] + + ["Execute string fast" py-execute-string-fast + :help " `py-execute-string-fast'"] + + ["Execute top level fast" py-execute-top-level-fast + :help " `py-execute-top-level-fast' +Process top-level at point by a Python interpreter."]) + ("Virtualenv" + ["Virtualenv activate" virtualenv-activate + :help " `virtualenv-activate' +Activate the virtualenv located in DIR"] + + ["Virtualenv deactivate" virtualenv-deactivate + :help " `virtualenv-deactivate' +Deactivate the current virtual enviroment"] + + ["Virtualenv p" virtualenv-p + :help " `virtualenv-p' +Check if a directory is a virtualenv"] + + ["Virtualenv workon" virtualenv-workon + :help " `virtualenv-workon' +Issue a virtualenvwrapper-like virtualenv-workon command"]) + + ["Execute import or reload" py-execute-import-or-reload + :help " `py-execute-import-or-reload' +Import the current buffer’s file in a Python interpreter."] + ("Help" + ["Find definition" py-find-definition + :help " `py-find-definition' +Find source of definition of SYMBOL."] + + ["Help at point" py-help-at-point + :help " `py-help-at-point' +Print help on symbol at point."] + + ["Info lookup symbol" py-info-lookup-symbol + :help " `py-info-lookup-symbol'"] + + ["Symbol at point" py-symbol-at-point + :help " `py-symbol-at-point' +Return the current Python symbol."]) + ("Debugger" + ["Execute statement pdb" py-execute-statement-pdb + :help " `py-execute-statement-pdb' +Execute statement running pdb."] + + ["Pdb" pdb + :help " `pdb' +Run pdb on program FILE in buffer `*gud-FILE*'."]) + ("Checks" + ["Pychecker run" py-pychecker-run + :help " `py-pychecker-run' +*Run pychecker (default on the file currently visited)."] + ("Pylint" + ["Pylint run" py-pylint-run + :help " `py-pylint-run' +*Run pylint (default on the file currently visited)."] + + ["Pylint help" py-pylint-help + :help " `py-pylint-help' +Display Pylint command line help messages."] + + ["Pylint flymake mode" pylint-flymake-mode + :help " `pylint-flymake-mode' +Toggle `pylint' `flymake-mode'."]) + ("Pep8" + ["Pep8 run" py-pep8-run + :help " `py-pep8-run' +*Run pep8, check formatting - default on the file currently visited."] + + ["Pep8 help" py-pep8-help + :help " `py-pep8-help' +Display pep8 command line help messages."] + + ["Pep8 flymake mode" pep8-flymake-mode + :help " `pep8-flymake-mode' +Toggle `pep8’ `flymake-mode'."]) + ("Pyflakes" + ["Pyflakes run" py-pyflakes-run + :help " `py-pyflakes-run' +*Run pyflakes (default on the file currently visited)."] + + ["Pyflakes help" py-pyflakes-help + :help " `py-pyflakes-help' +Display Pyflakes command line help messages."] + + ["Pyflakes flymake mode" pyflakes-flymake-mode + :help " `pyflakes-flymake-mode' +Toggle `pyflakes' `flymake-mode'."]) + ("Flake8" + ["Flake8 run" py-flake8-run + :help " `py-flake8-run' +Flake8 is a wrapper around these tools:"] + + ["Flake8 help" py-flake8-help + :help " `py-flake8-help' +Display flake8 command line help messages."] + ("Pyflakes-pep8" + ["Pyflakes pep8 run" py-pyflakes-pep8-run + :help " `py-pyflakes-pep8-run'"] + + ["Pyflakes pep8 help" py-pyflakes-pep8-help + :help " `py-pyflakes-pep8-help'"] + + ["Pyflakes pep8 flymake mode" pyflakes-pep8-flymake-mode + :help " `pyflakes-pep8-flymake-mode'"]))) + ("Customize" + + ["Python-mode customize group" (customize-group 'python-mode) + :help "Open the customization buffer for Python mode"] + ("Switches" + :help "Toggle useful modes" + ("Interpreter" + + ["Shell prompt read only" + (setq py-shell-prompt-read-only + (not py-shell-prompt-read-only)) + :help "If non-nil, the python prompt is read only. Setting this variable will only effect new shells.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-shell-prompt-read-only] + + ["Remove cwd from path" + (setq py-remove-cwd-from-path + (not py-remove-cwd-from-path)) + :help "Whether to allow loading of Python modules from the current directory. +If this is non-nil, Emacs removes '' from sys.path when starting +a Python process. This is the default, for security +reasons, as it is easy for the Python process to be started +without the user's realization (e.g. to perform completion).Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-remove-cwd-from-path] + + ["Honor IPYTHONDIR " + (setq py-honor-IPYTHONDIR-p + (not py-honor-IPYTHONDIR-p)) + :help "When non-nil ipython-history file is constructed by \$IPYTHONDIR +followed by "/history". Default is nil. + +Otherwise value of py-ipython-history is used. Use `M-x customize-variable' to set it permanently" +:style toggle :selected py-honor-IPYTHONDIR-p] + + ["Honor PYTHONHISTORY " + (setq py-honor-PYTHONHISTORY-p + (not py-honor-PYTHONHISTORY-p)) + :help "When non-nil python-history file is set by \$PYTHONHISTORY +Default is nil. + +Otherwise value of py-python-history is used. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-honor-PYTHONHISTORY-p] + + ["Enforce py-shell-name" force-py-shell-name-p-on + :help "Enforce customized default `py-shell-name' should upon execution. "] + + ["Don't enforce default interpreter" force-py-shell-name-p-off + :help "Make execute commands guess interpreter from environment"] + ) + + ("Execute" + + ["Fast process" py-fast-process-p + :help " `py-fast-process-p' + +Use `py-fast-process'\. + +Commands prefixed \"py-fast-...\" suitable for large output + +See: large output makes Emacs freeze, lp:1253907 + +Output-buffer is not in comint-mode" + :style toggle :selected py-fast-process-p] + + ["Python mode v5 behavior" + (setq python-mode-v5-behavior-p + (not python-mode-v5-behavior-p)) + :help "Execute region through `shell-command-on-region' as +v5 did it - lp:990079. This might fail with certain chars - see UnicodeEncodeError lp:550661 + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected python-mode-v5-behavior-p] + + ["Force shell name " + (setq py-force-py-shell-name-p + (not py-force-py-shell-name-p)) + :help "When `t', execution with kind of Python specified in `py-shell-name' is enforced, possibly shebang doesn't take precedence. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-force-py-shell-name-p] + + ["Execute \"if name == main\" blocks p" + (setq py-if-name-main-permission-p + (not py-if-name-main-permission-p)) + :help " `py-if-name-main-permission-p' + +Allow execution of code inside blocks delimited by +if __name__ == '__main__' + +Default is non-nil. " + :style toggle :selected py-if-name-main-permission-p] + + ["Ask about save" + (setq py-ask-about-save + (not py-ask-about-save)) + :help "If not nil, ask about which buffers to save before executing some code. +Otherwise, all modified buffers are saved without asking.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-ask-about-save] + + ["Store result" + (setq py-store-result-p + (not py-store-result-p)) + :help " `py-store-result-p' + +When non-nil, put resulting string of `py-execute-...' into kill-ring, so it might be yanked. " + :style toggle :selected py-store-result-p] + + ["Prompt on changed " + (setq py-prompt-on-changed-p + (not py-prompt-on-changed-p)) + :help "When called interactively, ask for save before a changed buffer is sent to interpreter. + +Default is `t'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-prompt-on-changed-p] + + ["Dedicated process " + (setq py-dedicated-process-p + (not py-dedicated-process-p)) + :help "If commands executing code use a dedicated shell. + +Default is nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-dedicated-process-p] + + ["Execute without temporary file" + (setq py-execute-no-temp-p + (not py-execute-no-temp-p)) + :help " `py-execute-no-temp-p' +Seems Emacs-24.3 provided a way executing stuff without temporary files. +In experimental state yet " + :style toggle :selected py-execute-no-temp-p] + + ["Warn tmp files left " + (setq py--warn-tmp-files-left-p + (not py--warn-tmp-files-left-p)) + :help "Messages a warning, when `py-temp-directory' contains files susceptible being left by previous Python-mode sessions. See also lp:987534 Use `M-x customize-variable' to set it permanently" + :style toggle :selected py--warn-tmp-files-left-p]) + + ("Edit" + + ("Completion" + + ["Set Pymacs-based complete keymap " + (setq py-set-complete-keymap-p + (not py-set-complete-keymap-p)) + :help "If `py-complete-initialize', which sets up enviroment for Pymacs based py-complete, should load it's keys into `python-mode-map' + +Default is nil. +See also resp. edit `py-complete-set-keymap' Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-set-complete-keymap-p] + + ["Indent no completion " + (setq py-indent-no-completion-p + (not py-indent-no-completion-p)) + :help "If completion function should indent when no completion found. Default is `t' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-indent-no-completion-p] + + ["Company pycomplete " + (setq py-company-pycomplete-p + (not py-company-pycomplete-p)) + :help "Load company-pycomplete stuff. Default is nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-company-pycomplete-p]) + + ("Filling" + + ("Docstring styles" + :help "Switch docstring-style" + + ["Nil" py-set-nil-docstring-style + :help " `py-set-nil-docstring-style' + +Set py-docstring-style to nil, format string normally. "] + + ["pep-257-nn" py-set-pep-257-nn-docstring-style + :help " `py-set-pep-257-nn-docstring-style' + +Set py-docstring-style to 'pep-257-nn "] + + ["pep-257" py-set-pep-257-docstring-style + :help " `py-set-pep-257-docstring-style' + +Set py-docstring-style to 'pep-257 "] + + ["django" py-set-django-docstring-style + :help " `py-set-django-docstring-style' + +Set py-docstring-style to 'django "] + + ["onetwo" py-set-onetwo-docstring-style + :help " `py-set-onetwo-docstring-style' + +Set py-docstring-style to 'onetwo "] + + ["symmetric" py-set-symmetric-docstring-style + :help " `py-set-symmetric-docstring-style' + +Set py-docstring-style to 'symmetric "]) + + ["Auto-fill mode" + (setq py-auto-fill-mode + (not py-auto-fill-mode)) + :help "Fill according to `py-docstring-fill-column' and `py-comment-fill-column' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-auto-fill-mode]) + + ["Use current dir when execute" + (setq py-use-current-dir-when-execute-p + (not py-use-current-dir-when-execute-p)) + :help " `py-toggle-use-current-dir-when-execute-p' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-current-dir-when-execute-p] + + ("Indent" + ("TAB related" + + ["indent-tabs-mode" + (setq indent-tabs-mode + (not indent-tabs-mode)) + :help "Indentation can insert tabs if this is non-nil. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected indent-tabs-mode] + + ["Tab indent" + (setq py-tab-indent + (not py-tab-indent)) + :help "Non-nil means TAB in Python mode calls `py-indent-line'.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-indent] + + ["Tab shifts region " + (setq py-tab-shifts-region-p + (not py-tab-shifts-region-p)) + :help "If `t', TAB will indent/cycle the region, not just the current line. + +Default is nil +See also `py-tab-indents-region-p' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-shifts-region-p] + + ["Tab indents region " + (setq py-tab-indents-region-p + (not py-tab-indents-region-p)) + :help "When `t' and first TAB doesn't shift, indent-region is called. + +Default is nil +See also `py-tab-shifts-region-p' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-indents-region-p]) + + ["Close at start column" + (setq py-closing-list-dedents-bos + (not py-closing-list-dedents-bos)) + :help "When non-nil, indent list's closing delimiter like start-column. + +It will be lined up under the first character of + the line that starts the multi-line construct, as in: + +my_list = \[ + 1, 2, 3, + 4, 5, 6,] + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-closing-list-dedents-bos] + + ["Closing list keeps space" + (setq py-closing-list-keeps-space + (not py-closing-list-keeps-space)) + :help "If non-nil, closing parenthesis dedents onto column of opening plus `py-closing-list-space', default is nil Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-closing-list-keeps-space] + + ["Closing list space" + (setq py-closing-list-space + (not py-closing-list-space)) + :help "Number of chars, closing parenthesis outdent from opening, default is 1 Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-closing-list-space] + + ["Tab shifts region " + (setq py-tab-shifts-region-p + (not py-tab-shifts-region-p)) + :help "If `t', TAB will indent/cycle the region, not just the current line. + +Default is nil +See also `py-tab-indents-region-p'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-shifts-region-p] + + ["Lhs inbound indent" + (setq py-lhs-inbound-indent + (not py-lhs-inbound-indent)) + :help "When line starts a multiline-assignment: How many colums indent should be more than opening bracket, brace or parenthesis. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-lhs-inbound-indent] + + ["Continuation offset" + (setq py-continuation-offset + (not py-continuation-offset)) + :help "With numeric ARG different from 1 py-continuation-offset is set to that value; returns py-continuation-offset. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-continuation-offset] + + ["Electric colon" + (setq py-electric-colon-active-p + (not py-electric-colon-active-p)) + :help " `py-electric-colon-active-p' + +`py-electric-colon' feature. Default is `nil'. See lp:837065 for discussions. " + :style toggle :selected py-electric-colon-active-p] + + ["Electric colon at beginning of block only" + (setq py-electric-colon-bobl-only + (not py-electric-colon-bobl-only)) + :help "When inserting a colon, do not indent lines unless at beginning of block. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-colon-bobl-only] + + ["Electric yank active " + (setq py-electric-yank-active-p + (not py-electric-yank-active-p)) + :help " When non-nil, `yank' will be followed by an `indent-according-to-mode'. + +Default is nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-yank-active-p] + + + + ["Trailing whitespace smart delete " + (setq py-trailing-whitespace-smart-delete-p + (not py-trailing-whitespace-smart-delete-p)) + :help "Default is nil. When t, python-mode calls + (add-hook 'before-save-hook 'delete-trailing-whitespace nil 'local) + +Also commands may delete trailing whitespace by the way. +When editing other peoples code, this may produce a larger diff than expected Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-trailing-whitespace-smart-delete-p] + + ["Newline delete trailing whitespace " + (setq py-newline-delete-trailing-whitespace-p + (not py-newline-delete-trailing-whitespace-p)) + :help "Delete trailing whitespace maybe left by `py-newline-and-indent'. + +Default is `t'. See lp:1100892 Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-newline-delete-trailing-whitespace-p] + + ["Dedent keep relative column" + (setq py-dedent-keep-relative-column + (not py-dedent-keep-relative-column)) + :help "If point should follow dedent or kind of electric move to end of line. Default is t - keep relative position. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-dedent-keep-relative-column] + + ["Indent comment " + (setq py-indent-comments + (not py-indent-comments)) + :help "If comments should be indented like code. Default is `nil'. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-indent-comments] + + ["Uncomment indents " + (setq py-uncomment-indents-p + (not py-uncomment-indents-p)) + :help "When non-nil, after uncomment indent lines. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-uncomment-indents-p] + + ["Indent honors inline comment" + (setq py-indent-honors-inline-comment + (not py-indent-honors-inline-comment)) + :help "If non-nil, indents to column of inlined comment start. +Default is nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-indent-honors-inline-comment] + + ["Kill empty line" + (setq py-kill-empty-line + (not py-kill-empty-line)) + :help "If t, py-indent-forward-line kills empty lines. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-kill-empty-line] + + ("Smart indentation" + :help "Toggle py-smart-indentation' + +Use `M-x customize-variable' to set it permanently" + + ["Toggle py-smart-indentation" py-toggle-smart-indentation + :help "Toggles py-smart-indentation + +Use `M-x customize-variable' to set it permanently"] + + ["py-smart-indentation on" py-smart-indentation-on + :help "Switches py-smart-indentation on + +Use `M-x customize-variable' to set it permanently"] + + ["py-smart-indentation off" py-smart-indentation-off + :help "Switches py-smart-indentation off + +Use `M-x customize-variable' to set it permanently"]) + + ["Beep if tab change" + (setq py-beep-if-tab-change + (not py-beep-if-tab-change)) + :help "Ring the bell if `tab-width' is changed. +If a comment of the form + + # vi:set tabsize=: + +is found before the first code line when the file is entered, and the +current value of (the general Emacs variable) `tab-width' does not +equal , `tab-width' is set to , a message saying so is +displayed in the echo area, and if `py-beep-if-tab-change' is non-nil +the Emacs bell is also rung as a warning.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-beep-if-tab-change] + + ["Electric comment " + (setq py-electric-comment-p + (not py-electric-comment-p)) + :help "If \"#\" should call `py-electric-comment'. Default is `nil'. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-comment-p] + + ["Electric comment add space " + (setq py-electric-comment-add-space-p + (not py-electric-comment-add-space-p)) + :help "If py-electric-comment should add a space. Default is `nil'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-comment-add-space-p] + + ["Empty line closes " + (setq py-empty-line-closes-p + (not py-empty-line-closes-p)) + :help "When non-nil, dedent after empty line following block + +if True: + print(\"Part of the if-statement\") + +print(\"Not part of the if-statement\") + +Default is nil + +If non-nil, a C-j from empty line dedents. +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-empty-line-closes-p]) + ["Defun use top level " + (setq py-defun-use-top-level-p + (not py-defun-use-top-level-p)) + :help "When non-nil, keys C-M-a, C-M-e address top-level form. + +Beginning- end-of-defun forms use +commands `py-backward-top-level', `py-forward-top-level' + +mark-defun marks top-level form at point etc. " + :style toggle :selected py-defun-use-top-level-p] + + ["Close provides newline" + (setq py-close-provides-newline + (not py-close-provides-newline)) + :help "If a newline is inserted, when line after block isn't empty. Default is non-nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-close-provides-newline] + + ["Block comment prefix " + (setq py-block-comment-prefix-p + (not py-block-comment-prefix-p)) + :help "If py-comment inserts py-block-comment-prefix. + +Default is tUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-block-comment-prefix-p]) + + ("Display" + + ("Index" + + ["Imenu create index " + (setq py--imenu-create-index-p + (not py--imenu-create-index-p)) + :help "Non-nil means Python mode creates and displays an index menu of functions and global variables. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py--imenu-create-index-p] + + ["Imenu show method args " + (setq py-imenu-show-method-args-p + (not py-imenu-show-method-args-p)) + :help "Controls echoing of arguments of functions & methods in the Imenu buffer. +When non-nil, arguments are printed.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-imenu-show-method-args-p] + ["Switch index-function" py-switch-imenu-index-function + :help "`py-switch-imenu-index-function' +Switch between `py--imenu-create-index' from 5.1 series and `py--imenu-create-index-new'."]) + + ("Fontification" + + ["Mark decorators" + (setq py-mark-decorators + (not py-mark-decorators)) + :help "If py-mark-def-or-class functions should mark decorators too. Default is `nil'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-mark-decorators] + + ["Fontify shell buffer " + (setq py-fontify-shell-buffer-p + (not py-fontify-shell-buffer-p)) + :help "If code in Python shell should be highlighted as in script buffer. + +Default is nil. + +If `t', related vars like `comment-start' will be set too. +Seems convenient when playing with stuff in IPython shell +Might not be TRT when a lot of output arrives Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-fontify-shell-buffer-p] + + ["Use font lock doc face " + (setq py-use-font-lock-doc-face-p + (not py-use-font-lock-doc-face-p)) + :help "If documention string inside of def or class get `font-lock-doc-face'. + +`font-lock-doc-face' inherits `font-lock-string-face'. + +Call M-x `customize-face' in order to have a visible effect. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-font-lock-doc-face-p]) + + ["Switch buffers on execute" + (setq py-switch-buffers-on-execute-p + (not py-switch-buffers-on-execute-p)) + :help "When non-nil switch to the Python output buffer. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-switch-buffers-on-execute-p] + + ["Split windows on execute" + (setq py-split-window-on-execute + (not py-split-window-on-execute)) + :help "When non-nil split windows. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-split-window-on-execute] + + ["Keep windows configuration" + (setq py-keep-windows-configuration + (not py-keep-windows-configuration)) + :help "If a windows is splitted displaying results, this is directed by variable `py-split-window-on-execute'\. Also setting `py-switch-buffers-on-execute-p' affects window-configuration\. While commonly a screen splitted into source and Python-shell buffer is assumed, user may want to keep a different config\. + +Setting `py-keep-windows-configuration' to `t' will restore windows-config regardless of settings mentioned above\. However, if an error occurs, it's displayed\. + +To suppres window-changes due to error-signaling also: M-x customize-variable RET. Set `py-keep-4windows-configuration' onto 'force + +Default is nil Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-keep-windows-configuration] + + ["Which split windows on execute function" + (progn + (if (eq 'split-window-vertically py-split-windows-on-execute-function) + (setq py-split-windows-on-execute-function'split-window-horizontally) + (setq py-split-windows-on-execute-function 'split-window-vertically)) + (message "py-split-windows-on-execute-function set to: %s" py-split-windows-on-execute-function)) + + :help "If `split-window-vertically' or `...-horizontally'. Use `M-x customize-variable' RET `py-split-windows-on-execute-function' RET to set it permanently" + :style toggle :selected py-split-windows-on-execute-function] + + ["Modeline display full path " + (setq py-modeline-display-full-path-p + (not py-modeline-display-full-path-p)) + :help "If the full PATH/TO/PYTHON should be displayed in shell modeline. + +Default is nil. Note: when `py-shell-name' is specified with path, it's shown as an acronym in buffer-name already. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-modeline-display-full-path-p] + + ["Modeline acronym display home " + (setq py-modeline-acronym-display-home-p + (not py-modeline-acronym-display-home-p)) + :help "If the modeline acronym should contain chars indicating the home-directory. + +Default is nil Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-modeline-acronym-display-home-p] + + ["Hide show hide docstrings" + (setq py-hide-show-hide-docstrings + (not py-hide-show-hide-docstrings)) + :help "Controls if doc strings can be hidden by hide-showUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-hide-show-hide-docstrings] + + ["Hide comments when hiding all" + (setq py-hide-comments-when-hiding-all + (not py-hide-comments-when-hiding-all)) + :help "Hide the comments too when you do `hs-hide-all'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-hide-comments-when-hiding-all] + + ["Max help buffer " + (setq py-max-help-buffer-p + (not py-max-help-buffer-p)) + :help "If \"\*Python-Help\*\"-buffer should appear as the only visible. + +Default is nil. In help-buffer, \"q\" will close it. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-max-help-buffer-p] + + ["Current defun show" + (setq py-current-defun-show + (not py-current-defun-show)) + :help "If `py-current-defun' should jump to the definition, highlight it while waiting PY-WHICH-FUNC-DELAY seconds, before returning to previous position. + +Default is `t'.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-current-defun-show] + + ["Match paren mode" + (setq py-match-paren-mode + (not py-match-paren-mode)) + :help "Non-nil means, cursor will jump to beginning or end of a block. +This vice versa, to beginning first. +Sets `py-match-paren-key' in python-mode-map. +Customize `py-match-paren-key' which key to use. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-match-paren-mode]) + + ("Debug" + + ["py-debug-p" + (setq py-debug-p + (not py-debug-p)) + :help "When non-nil, keep resp\. store information useful for debugging\. + +Temporary files are not deleted\. Other functions might implement +some logging etc\. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-debug-p] + + ["Pdbtrack do tracking " + (setq py-pdbtrack-do-tracking-p + (not py-pdbtrack-do-tracking-p)) + :help "Controls whether the pdbtrack feature is enabled or not. +When non-nil, pdbtrack is enabled in all comint-based buffers, +e.g. shell buffers and the \*Python\* buffer. When using pdb to debug a +Python program, pdbtrack notices the pdb prompt and displays the +source file and line that the program is stopped at, much the same way +as gud-mode does for debugging C programs with gdb.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-pdbtrack-do-tracking-p] + + ["Jump on exception" + (setq py-jump-on-exception + (not py-jump-on-exception)) + :help "Jump to innermost exception frame in Python output buffer. +When this variable is non-nil and an exception occurs when running +Python code synchronously in a subprocess, jump immediately to the +source code of the innermost traceback frame. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-jump-on-exception] + + ["Highlight error in source " + (setq py-highlight-error-source-p + (not py-highlight-error-source-p)) + :help "Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-highlight-error-source-p]) + + ("Other" + + ("Directory" + + ["Guess install directory " + (setq py-guess-py-install-directory-p + (not py-guess-py-install-directory-p)) + :help "If in cases, `py-install-directory' isn't set, `py-set-load-path'should guess it from `buffer-file-name'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-guess-py-install-directory-p] + + ["Use local default" + (setq py-use-local-default + (not py-use-local-default)) + :help "If `t', py-shell will use `py-shell-local-path' instead +of default Python. + +Making switch between several virtualenv's easier, + `python-mode' should deliver an installer, so named-shells pointing to virtualenv's will be available. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-local-default] + + ["Use current dir when execute " + (setq py-use-current-dir-when-execute-p + (not py-use-current-dir-when-execute-p)) + :help "When `t', current directory is used by Python-shell for output of `py-execute-buffer' and related commands. + +See also `py-execute-directory'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-current-dir-when-execute-p] + + ["Keep shell dir when execute " + (setq py-keep-shell-dir-when-execute-p + (not py-keep-shell-dir-when-execute-p)) + :help "Don't change Python shell's current working directory when sending code. + +See also `py-execute-directory'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-keep-shell-dir-when-execute-p] + + ["Fileless buffer use default directory " + (setq py-fileless-buffer-use-default-directory-p + (not py-fileless-buffer-use-default-directory-p)) + :help "When `py-use-current-dir-when-execute-p' is non-nil and no buffer-file exists, value of `default-directory' sets current working directory of Python output shellUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-fileless-buffer-use-default-directory-p]) + + ("Underscore word syntax" + :help "Toggle `py-underscore-word-syntax-p'" + + ["Toggle underscore word syntax" py-toggle-underscore-word-syntax-p + :help " `py-toggle-underscore-word-syntax-p' + +If `py-underscore-word-syntax-p' should be on or off. + + Returns value of `py-underscore-word-syntax-p' switched to. . + +Use `M-x customize-variable' to set it permanently"] + + ["Underscore word syntax on" py-underscore-word-syntax-p-on + :help " `py-underscore-word-syntax-p-on' + +Make sure, py-underscore-word-syntax-p' is on. + +Returns value of `py-underscore-word-syntax-p'. . + +Use `M-x customize-variable' to set it permanently"] + + ["Underscore word syntax off" py-underscore-word-syntax-p-off + :help " `py-underscore-word-syntax-p-off' + +Make sure, `py-underscore-word-syntax-p' is off. + +Returns value of `py-underscore-word-syntax-p'. . + +Use `M-x customize-variable' to set it permanently"]) + + ["Load pymacs " + (setq py-load-pymacs-p + (not py-load-pymacs-p)) + :help "If Pymacs related stuff should be loaded. + +Default is nil. + +Pymacs has been written by François Pinard and many others. +See original source: http://pymacs.progiciels-bpi.caUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-load-pymacs-p] + + ["Verbose " + (setq py-verbose-p + (not py-verbose-p)) + :help "If functions should report results. + +Default is nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-verbose-p] + ;; ["No session mode " + ;; (setq py-no-session-p + ;; (not py-no-session-p)) + ;; :help "If shell should be in session-mode. + + ;; Default is nil. Use `M-x customize-variable' to set it permanently" + ;; :style toggle :selected py-no-session-p] + + ["Empty comment line separates paragraph " + (setq py-empty-comment-line-separates-paragraph-p + (not py-empty-comment-line-separates-paragraph-p)) + :help "Consider paragraph start/end lines with nothing inside but comment sign. + +Default is non-nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-empty-comment-line-separates-paragraph-p] + + ["Org cycle " + (setq py-org-cycle-p + (not py-org-cycle-p)) + :help "When non-nil, command `org-cycle' is available at shift-TAB, + +Default is nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-org-cycle-p] + + ["Set pager cat" + (setq py-set-pager-cat-p + (not py-set-pager-cat-p)) + :help "If the shell environment variable \$PAGER should set to `cat'. + +If `t', use `C-c C-r' to jump to beginning of output. Then scroll normally. + +Avoids lp:783828, \"Terminal not fully functional\", for help('COMMAND') in python-shell + +When non-nil, imports module `os' Use `M-x customize-variable' to +set it permanently" + :style toggle :selected py-set-pager-cat-p] + + ["Edit only " + (setq py-edit-only-p + (not py-edit-only-p)) + :help "When `t' `python-mode' will not take resort nor check for installed Python executables. Default is nil. + +See bug report at launchpad, lp:944093. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-edit-only-p]))) + ("Other" + ["Boolswitch" py-boolswitch + :help " `py-boolswitch' +Edit the assignment of a boolean variable, revert them."] + + ["Empty out list backward" py-empty-out-list-backward + :help " `py-empty-out-list-backward' +Deletes all elements from list before point."] + + ["Kill buffer unconditional" py-kill-buffer-unconditional + :help " `py-kill-buffer-unconditional' +Kill buffer unconditional, kill buffer-process if existing."] + + ["Remove overlays at point" py-remove-overlays-at-point + :help " `py-remove-overlays-at-point' +Remove overlays as set when `py-highlight-error-source-p' is non-nil."] + ("Electric" + ["Complete electric comma" py-complete-electric-comma + :help " `py-complete-electric-comma'"] + + ["Complete electric lparen" py-complete-electric-lparen + :help " `py-complete-electric-lparen'"] + + ["Electric backspace" py-electric-backspace + :help " `py-electric-backspace' +Delete preceding character or level of indentation."] + + ["Electric colon" py-electric-colon + :help " `py-electric-colon' +Insert a colon and indent accordingly."] + + ["Electric comment" py-electric-comment + :help " `py-electric-comment' +Insert a comment. If starting a comment, indent accordingly."] + + ["Electric delete" py-electric-delete + :help " `py-electric-delete' +Delete following character or levels of whitespace."] + + ["Electric yank" py-electric-yank + :help " `py-electric-yank' +Perform command `yank' followed by an `indent-according-to-mode'"] + + ["Hungry delete backwards" py-hungry-delete-backwards + :help " `py-hungry-delete-backwards' +Delete the preceding character or all preceding whitespace"] + + ["Hungry delete forward" py-hungry-delete-forward + :help " `py-hungry-delete-forward' +Delete the following character or all following whitespace"]) + ("Filling" + ["Py docstring style" py-docstring-style + :help " `py-docstring-style'"] + + ["Py fill comment" py-fill-comment + :help " `py-fill-comment'"] + + ["Py fill paragraph" py-fill-paragraph + :help " `py-fill-paragraph'"] + + ["Py fill string" py-fill-string + :help " `py-fill-string'"] + + ["Py fill string django" py-fill-string-django + :help " `py-fill-string-django'"] + + ["Py fill string onetwo" py-fill-string-onetwo + :help " `py-fill-string-onetwo'"] + + ["Py fill string pep 257" py-fill-string-pep-257 + :help " `py-fill-string-pep-257'"] + + ["Py fill string pep 257 nn" py-fill-string-pep-257-nn + :help " `py-fill-string-pep-257-nn'"] + + ["Py fill string symmetric" py-fill-string-symmetric + :help " `py-fill-string-symmetric'"]) + ("Abbrevs" :help "see also `py-add-abbrev'" + :filter (lambda (&rest junk) + (abbrev-table-menu python-mode-abbrev-table))) + + ["Add abbrev" py-add-abbrev + :help " `py-add-abbrev' +Defines python-mode specific abbrev for last expressions before point."] + ("Completion" + ["Py indent or complete" py-indent-or-complete + :help " `py-indent-or-complete'"] + + ["Py shell complete" py-shell-complete + :help " `py-shell-complete'"] + + ["Py complete" py-complete + :help " `py-complete'"]) + + ["Find function" py-find-function + :help " `py-find-function' +Find source of definition of SYMBOL."]))) + map) + +;; python-components-map + +(defvar py-use-menu-p t + "If the menu should be loaded. + +Default is t") + +(defvar py-menu nil + "Make a dynamically bound variable `py-menu'.") + + +(setq python-mode-map + (let ((map (make-sparse-keymap))) + ;; electric keys + (define-key map [(:)] 'py-electric-colon) + (define-key map [(\#)] 'py-electric-comment) + (define-key map [(delete)] 'py-electric-delete) + (define-key map [(backspace)] 'py-electric-backspace) + (define-key map [(control backspace)] 'py-hungry-delete-backwards) + (define-key map [(control c) (delete)] 'py-hungry-delete-forward) + ;; (define-key map [(control y)] 'py-electric-yank) + ;; moving point + (define-key map [(control c) (control p)] 'py-backward-statement) + (define-key map [(control c) (control n)] 'py-forward-statement) + (define-key map [(control c) (control u)] 'py-backward-block) + (define-key map [(control c) (control q)] 'py-forward-block) + (define-key map [(control meta a)] 'py-backward-def-or-class) + (define-key map [(control meta e)] 'py-forward-def-or-class) + ;; (define-key map [(meta i)] 'py-indent-forward-line) + ;; (define-key map [(control j)] 'py-newline-and-indent) + (define-key map (kbd "C-j") 'newline) + ;; Most Pythoneers expect RET `py-newline-and-indent' + ;; which is default of var py-return-key’ + (define-key map (kbd "RET") py-return-key) + ;; (define-key map (kbd "RET") 'newline) + ;; (define-key map (kbd "RET") 'py-newline-and-dedent) + (define-key map [(super backspace)] 'py-dedent) + ;; (define-key map [(control return)] 'py-newline-and-dedent) + ;; indentation level modifiers + (define-key map [(control c) (control l)] 'py-shift-left) + (define-key map [(control c) (control r)] 'py-shift-right) + (define-key map [(control c) (<)] 'py-shift-left) + (define-key map [(control c) (>)] 'py-shift-right) + ;; (define-key map [(control c) (tab)] 'py-indent-region) + (define-key map (kbd "C-c TAB") 'py-indent-region) + (define-key map [(control c) (:)] 'py-guess-indent-offset) + ;; subprocess commands + (define-key map [(control c) (control c)] 'py-execute-buffer) + (define-key map [(control c) (control m)] 'py-execute-import-or-reload) + (define-key map [(control c) (control s)] 'py-execute-string) + (define-key map [(control c) (|)] 'py-execute-region) + (define-key map [(control meta x)] 'py-execute-def-or-class) + (define-key map [(control c) (!)] 'py-shell) + (define-key map [(control c) (control t)] 'py-toggle-shell) + (define-key map [(control meta h)] 'py-mark-def-or-class) + (define-key map [(control c) (control k)] 'py-mark-block-or-clause) + (define-key map [(control c) (.)] 'py-expression) + ;; Miscellaneous + ;; (define-key map [(super q)] 'py-copy-statement) + (define-key map [(control c) (control d)] 'py-pdbtrack-toggle-stack-tracking) + (define-key map [(control c) (control f)] 'py-sort-imports) + (define-key map [(control c) (\#)] 'py-comment-region) + (define-key map [(control c) (\?)] 'py-describe-mode) + (define-key map [(control c) (control e)] 'py-help-at-point) + (define-key map [(control c) (-)] 'py-up-exception) + (define-key map [(control c) (=)] 'py-down-exception) + (define-key map [(control x) (n) (d)] 'py-narrow-to-def-or-class) + ;; information + (define-key map [(control c) (control b)] 'py-submit-bug-report) + (define-key map [(control c) (control v)] 'py-version) + (define-key map [(control c) (control w)] 'py-pychecker-run) + ;; (define-key map (kbd "TAB") 'py-indent-line) + (define-key map (kbd "TAB") 'py-indent-or-complete) + ;; (if py-complete-function + ;; (progn + ;; (define-key map [(meta tab)] py-complete-function) + ;; (define-key map [(esc) (tab)] py-complete-function)) + ;; (define-key map [(meta tab)] 'py-shell-complete) + ;; (define-key map [(esc) (tab)] 'py-shell-complete)) + (substitute-key-definition 'complete-symbol 'completion-at-point + map global-map) + (substitute-key-definition 'backward-up-list 'py-up + map global-map) + (substitute-key-definition 'down-list 'py-down + map global-map) + (when py-use-menu-p + (setq map (py-define-menu map))) + map)) + +(defvar py-python-shell-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "RET") 'comint-send-input) + (define-key map [(control c)(-)] 'py-up-exception) + (define-key map [(control c)(=)] 'py-down-exception) + (define-key map (kbd "TAB") 'py-indent-or-complete) + (define-key map [(meta tab)] 'py-shell-complete) + (define-key map [(control c)(!)] 'py-shell) + (define-key map [(control c)(control t)] 'py-toggle-shell) + ;; electric keys + ;; (define-key map [(:)] 'py-electric-colon) + ;; (define-key map [(\#)] 'py-electric-comment) + ;; (define-key map [(delete)] 'py-electric-delete) + ;; (define-key map [(backspace)] 'py-electric-backspace) + ;; (define-key map [(control backspace)] 'py-hungry-delete-backwards) + ;; (define-key map [(control c) (delete)] 'py-hungry-delete-forward) + ;; (define-key map [(control y)] 'py-electric-yank) + ;; moving point + (define-key map [(control c)(control p)] 'py-backward-statement) + (define-key map [(control c)(control n)] 'py-forward-statement) + (define-key map [(control c)(control u)] 'py-backward-block) + (define-key map [(control c)(control q)] 'py-forward-block) + (define-key map [(control meta a)] 'py-backward-def-or-class) + (define-key map [(control meta e)] 'py-forward-def-or-class) + (define-key map [(control j)] 'py-newline-and-indent) + (define-key map [(super backspace)] 'py-dedent) + ;; (define-key map [(control return)] 'py-newline-and-dedent) + ;; indentation level modifiers + (define-key map [(control c)(control l)] 'comint-dynamic-list-input-ring) + (define-key map [(control c)(control r)] 'comint-previous-prompt) + (define-key map [(control c)(<)] 'py-shift-left) + (define-key map [(control c)(>)] 'py-shift-right) + (define-key map [(control c)(tab)] 'py-indent-region) + (define-key map [(control c)(:)] 'py-guess-indent-offset) + ;; subprocess commands + (define-key map [(control meta h)] 'py-mark-def-or-class) + (define-key map [(control c)(control k)] 'py-mark-block-or-clause) + (define-key map [(control c)(.)] 'py-expression) + ;; Miscellaneous + ;; (define-key map [(super q)] 'py-copy-statement) + (define-key map [(control c)(control d)] 'py-pdbtrack-toggle-stack-tracking) + (define-key map [(control c)(\#)] 'py-comment-region) + (define-key map [(control c)(\?)] 'py-describe-mode) + (define-key map [(control c)(control e)] 'py-help-at-point) + (define-key map [(control x) (n) (d)] 'py-narrow-to-def-or-class) + ;; information + (define-key map [(control c)(control b)] 'py-submit-bug-report) + (define-key map [(control c)(control v)] 'py-version) + (define-key map [(control c)(control w)] 'py-pychecker-run) + (substitute-key-definition 'complete-symbol 'completion-at-point + map global-map) + (substitute-key-definition 'backward-up-list 'py-up + map global-map) + (substitute-key-definition 'down-list 'py-down + map global-map) + map) + "Used inside a Python-shell.") + +(defvar py-ipython-shell-mode-map py-python-shell-mode-map + "Copy `py-python-shell-mode-map' here.") + +(defvar py-shell-map py-python-shell-mode-map) + +;; python-components-shell-menu + +(and (ignore-errors (require 'easymenu) t) + ;; (easy-menu-define py-menu map "Python Tools" + ;; `("PyTools" + (easy-menu-define + py-shell-menu py-python-shell-mode-map "Py-Shell Mode menu" + `("Py-Shell" + ("Edit" + ("Shift" + ("Shift right" + ["Shift block right" py-shift-block-right + :help " `py-shift-block-right' +Indent block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift block or clause right" py-shift-block-or-clause-right + :help " `py-shift-block-or-clause-right' +Indent block-or-clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift class right" py-shift-class-right + :help " `py-shift-class-right' +Indent class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift clause right" py-shift-clause-right + :help " `py-shift-clause-right' +Indent clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift comment right" py-shift-comment-right + :help " `py-shift-comment-right'"] + + ["Shift def right" py-shift-def-right + :help " `py-shift-def-right' +Indent def by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift def or class right" py-shift-def-or-class-right + :help " `py-shift-def-or-class-right' +Indent def-or-class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift minor block right" py-shift-minor-block-right + :help " `py-shift-minor-block-right' +Indent minor-block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached. +A minor block is started by a `for', `if', `try' or `with'."] + + ["Shift paragraph right" py-shift-paragraph-right + :help " `py-shift-paragraph-right' +Indent paragraph by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift region right" py-shift-region-right + :help " `py-shift-region-right' +Indent region according to `py-indent-offset' by COUNT times. + +If no region is active, current line is indented. +Returns indentation reached."] + + ["Shift statement right" py-shift-statement-right + :help " `py-shift-statement-right' +Indent statement by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift top level right" py-shift-top-level-right + :help " `py-shift-top-level-right'"] + ) + ("Shift left" + ["Shift block left" py-shift-block-left + :help " `py-shift-block-left' +Dedent block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift block or clause left" py-shift-block-or-clause-left + :help " `py-shift-block-or-clause-left' +Dedent block-or-clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift class left" py-shift-class-left + :help " `py-shift-class-left' +Dedent class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift clause left" py-shift-clause-left + :help " `py-shift-clause-left' +Dedent clause by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift comment left" py-shift-comment-left + :help " `py-shift-comment-left'"] + + ["Shift def left" py-shift-def-left + :help " `py-shift-def-left' +Dedent def by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift def or class left" py-shift-def-or-class-left + :help " `py-shift-def-or-class-left' +Dedent def-or-class by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift minor block left" py-shift-minor-block-left + :help " `py-shift-minor-block-left' +Dedent minor-block by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached. +A minor block is started by a `for', `if', `try' or `with'."] + + ["Shift paragraph left" py-shift-paragraph-left + :help " `py-shift-paragraph-left' +Dedent paragraph by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + + ["Shift region left" py-shift-region-left + :help " `py-shift-region-left' +Dedent region according to `py-indent-offset' by COUNT times. + +If no region is active, current line is dedented. +Returns indentation reached."] + + ["Shift statement left" py-shift-statement-left + :help " `py-shift-statement-left' +Dedent statement by COUNT spaces. + +COUNT defaults to `py-indent-offset', +use [universal-argument] to specify a different value. + +Returns outmost indentation reached."] + )) + ("Mark" + ["Mark block" py-mark-block + :help " `py-mark-block' +Mark block at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark block or clause" py-mark-block-or-clause + :help " `py-mark-block-or-clause' +Mark block-or-clause at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark class" py-mark-class + :help " `py-mark-class' +Mark class at point. + +With C-u or `py-mark-decorators' set to `t', decorators are marked too. +Returns beginning and end positions of marked area, a cons."] + + ["Mark clause" py-mark-clause + :help " `py-mark-clause' +Mark clause at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark comment" py-mark-comment + :help " `py-mark-comment' +Mark comment at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark def" py-mark-def + :help " `py-mark-def' +Mark def at point. + +With C-u or `py-mark-decorators' set to `t', decorators are marked too. +Returns beginning and end positions of marked area, a cons."] + + ["Mark def or class" py-mark-def-or-class + :help " `py-mark-def-or-class' +Mark def-or-class at point. + +With C-u or `py-mark-decorators' set to `t', decorators are marked too. +Returns beginning and end positions of marked area, a cons."] + + ["Mark expression" py-mark-expression + :help " `py-mark-expression' +Mark expression at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark line" py-mark-line + :help " `py-mark-line' +Mark line at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark minor block" py-mark-minor-block + :help " `py-mark-minor-block' +Mark minor-block at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark paragraph" py-mark-paragraph + :help " `py-mark-paragraph' +Mark paragraph at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark partial expression" py-mark-partial-expression + :help " `py-mark-partial-expression' +Mark partial-expression at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark statement" py-mark-statement + :help " `py-mark-statement' +Mark statement at point. + +Returns beginning and end positions of marked area, a cons."] + + ["Mark top level" py-mark-top-level + :help " `py-mark-top-level' +Mark top-level at point. + +Returns beginning and end positions of marked area, a cons."] + ) + ("Copy" + ["Copy block" py-copy-block + :help " `py-copy-block' +Copy block at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy block or clause" py-copy-block-or-clause + :help " `py-copy-block-or-clause' +Copy block-or-clause at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy class" py-copy-class + :help " `py-copy-class' +Copy class at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy clause" py-copy-clause + :help " `py-copy-clause' +Copy clause at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy comment" py-copy-comment + :help " `py-copy-comment'"] + + ["Copy def" py-copy-def + :help " `py-copy-def' +Copy def at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy def or class" py-copy-def-or-class + :help " `py-copy-def-or-class' +Copy def-or-class at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy expression" py-copy-expression + :help " `py-copy-expression' +Copy expression at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy line" py-copy-line + :help " `py-copy-line'"] + + ["Copy minor block" py-copy-minor-block + :help " `py-copy-minor-block' +Copy minor-block at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy paragraph" py-copy-paragraph + :help " `py-copy-paragraph'"] + + ["Copy partial expression" py-copy-partial-expression + :help " `py-copy-partial-expression' +Copy partial-expression at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy statement" py-copy-statement + :help " `py-copy-statement' +Copy statement at point. + +Store data in kill ring, so it might yanked back."] + + ["Copy top level" py-copy-top-level + :help " `py-copy-top-level' +Copy top-level at point. + +Store data in kill ring, so it might yanked back."] + ) + ("Kill" + ["Kill block" py-kill-block + :help " `py-kill-block' +Delete `block' at point. + +Stores data in kill ring"] + + ["Kill block or clause" py-kill-block-or-clause + :help " `py-kill-block-or-clause' +Delete `block-or-clause' at point. + +Stores data in kill ring"] + + ["Kill class" py-kill-class + :help " `py-kill-class' +Delete `class' at point. + +Stores data in kill ring"] + + ["Kill clause" py-kill-clause + :help " `py-kill-clause' +Delete `clause' at point. + +Stores data in kill ring"] + + ["Kill comment" py-kill-comment + :help " `py-kill-comment'"] + + ["Kill def" py-kill-def + :help " `py-kill-def' +Delete `def' at point. + +Stores data in kill ring"] + + ["Kill def or class" py-kill-def-or-class + :help " `py-kill-def-or-class' +Delete `def-or-class' at point. + +Stores data in kill ring"] + + ["Kill expression" py-kill-expression + :help " `py-kill-expression' +Delete `expression' at point. + +Stores data in kill ring"] + + ["Kill line" py-kill-line + :help " `py-kill-line'"] + + ["Kill minor block" py-kill-minor-block + :help " `py-kill-minor-block' +Delete `minor-block' at point. + +Stores data in kill ring"] + + ["Kill paragraph" py-kill-paragraph + :help " `py-kill-paragraph'"] + + ["Kill partial expression" py-kill-partial-expression + :help " `py-kill-partial-expression' +Delete `partial-expression' at point. + +Stores data in kill ring"] + + ["Kill statement" py-kill-statement + :help " `py-kill-statement' +Delete `statement' at point. + +Stores data in kill ring"] + + ["Kill top level" py-kill-top-level + :help " `py-kill-top-level' +Delete `top-level' at point. + +Stores data in kill ring"] + ) + ("Delete" + ["Delete block" py-delete-block + :help " `py-delete-block' +Delete BLOCK at point. + +Don't store data in kill ring."] + + ["Delete block or clause" py-delete-block-or-clause + :help " `py-delete-block-or-clause' +Delete BLOCK-OR-CLAUSE at point. + +Don't store data in kill ring."] + + ["Delete class" py-delete-class + :help " `py-delete-class' +Delete CLASS at point. + +Don't store data in kill ring. +With C-u or `py-mark-decorators' set to `t', `decorators' are included."] + + ["Delete clause" py-delete-clause + :help " `py-delete-clause' +Delete CLAUSE at point. + +Don't store data in kill ring."] + + ["Delete comment" py-delete-comment + :help " `py-delete-comment'"] + + ["Delete def" py-delete-def + :help " `py-delete-def' +Delete DEF at point. + +Don't store data in kill ring. +With C-u or `py-mark-decorators' set to `t', `decorators' are included."] + + ["Delete def or class" py-delete-def-or-class + :help " `py-delete-def-or-class' +Delete DEF-OR-CLASS at point. + +Don't store data in kill ring. +With C-u or `py-mark-decorators' set to `t', `decorators' are included."] + + ["Delete expression" py-delete-expression + :help " `py-delete-expression' +Delete EXPRESSION at point. + +Don't store data in kill ring."] + + ["Delete line" py-delete-line + :help " `py-delete-line'"] + + ["Delete minor block" py-delete-minor-block + :help " `py-delete-minor-block' +Delete MINOR-BLOCK at point. + +Don't store data in kill ring."] + + ["Delete paragraph" py-delete-paragraph + :help " `py-delete-paragraph'"] + + ["Delete partial expression" py-delete-partial-expression + :help " `py-delete-partial-expression' +Delete PARTIAL-EXPRESSION at point. + +Don't store data in kill ring."] + + ["Delete statement" py-delete-statement + :help " `py-delete-statement' +Delete STATEMENT at point. + +Don't store data in kill ring."] + + ["Delete top level" py-delete-top-level + :help " `py-delete-top-level' +Delete TOP-LEVEL at point. + +Don't store data in kill ring."] + ) + ("Comment" + ["Comment block" py-comment-block + :help " `py-comment-block' +Comments block at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is `t', +the default"] + + ["Comment block or clause" py-comment-block-or-clause + :help " `py-comment-block-or-clause' +Comments block-or-clause at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is `t', +the default"] + + ["Comment class" py-comment-class + :help " `py-comment-class' +Comments class at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is `t', +the default"] + + ["Comment clause" py-comment-clause + :help " `py-comment-clause' +Comments clause at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is `t', +the default"] + + ["Comment def" py-comment-def + :help " `py-comment-def' +Comments def at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is `t', +the default"] + + ["Comment def or class" py-comment-def-or-class + :help " `py-comment-def-or-class' +Comments def-or-class at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is `t', +the default"] + + ["Comment statement" py-comment-statement + :help " `py-comment-statement' +Comments statement at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is `t', +the default"] + )) + ("Move" + ("Backward" + ["Beginning of block" py-beginning-of-block + :help " `py-beginning-of-block' +Go to beginning block, skip whitespace at BOL. + +Returns beginning of block if successful, nil otherwise"] + + ["Beginning of block or clause" py-beginning-of-block-or-clause + :help " `py-beginning-of-block-or-clause' +Go to beginning block-or-clause, skip whitespace at BOL. + +Returns beginning of block-or-clause if successful, nil otherwise"] + + ["Beginning of class" py-beginning-of-class + :help " `py-beginning-of-class' +Go to beginning class, skip whitespace at BOL. + +Returns beginning of class if successful, nil otherwise + +When `py-mark-decorators' is non-nil, decorators are considered too."] + + ["Beginning of clause" py-beginning-of-clause + :help " `py-beginning-of-clause' +Go to beginning clause, skip whitespace at BOL. + +Returns beginning of clause if successful, nil otherwise"] + + ["Beginning of def" py-beginning-of-def + :help " `py-beginning-of-def' +Go to beginning def, skip whitespace at BOL. + +Returns beginning of def if successful, nil otherwise + +When `py-mark-decorators' is non-nil, decorators are considered too."] + + ["Beginning of def or class" py-backward-def-or-class + :help " `py-backward-def-or-class' +Go to beginning def-or-class, skip whitespace at BOL. + +Returns beginning of def-or-class if successful, nil otherwise + +When `py-mark-decorators' is non-nil, decorators are considered too."] + + ["Beginning of elif block" py-beginning-of-elif-block + :help " `py-beginning-of-elif-block' +Go to beginning elif-block, skip whitespace at BOL. + +Returns beginning of elif-block if successful, nil otherwise"] + + ["Beginning of else block" py-beginning-of-else-block + :help " `py-beginning-of-else-block' +Go to beginning else-block, skip whitespace at BOL. + +Returns beginning of else-block if successful, nil otherwise"] + + ["Beginning of except block" py-beginning-of-except-block + :help " `py-beginning-of-except-block' +Go to beginning except-block, skip whitespace at BOL. + +Returns beginning of except-block if successful, nil otherwise"] + + ["Beginning of expression" py-beginning-of-expression + :help " `py-beginning-of-expression' +Go to the beginning of a compound python expression. + +With numeric ARG do it that many times. + +A a compound python expression might be concatenated by \".\" operator, thus composed by minor python expressions. + +If already at the beginning or before a expression, go to next expression in buffer upwards + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference +Operators however are left aside resp. limit py-expression designed for edit-purposes."] + + ["Beginning of if block" py-beginning-of-if-block + :help " `py-beginning-of-if-block' +Go to beginning if-block, skip whitespace at BOL. + +Returns beginning of if-block if successful, nil otherwise"] + + ["Beginning of partial expression" py-backward-partial-expression + :help " `py-backward-partial-expression'"] + + ["Beginning of statement" py-backward-statement + :help " `py-backward-statement' +Go to the initial line of a simple statement. + +For beginning of compound statement use py-beginning-of-block. +For beginning of clause py-beginning-of-clause."] + + ["Beginning of top level" py-backward-top-level + :help " `py-backward-top-level' +Go up to beginning of statments until level of indentation is null. + +Returns position if successful, nil otherwise"] + + ["Beginning of try block" py-beginning-of-try-block + :help " `py-beginning-of-try-block' +Go to beginning try-block, skip whitespace at BOL. + +Returns beginning of try-block if successful, nil otherwise"] + ) + ("Forward" + ["End of block" py-forward-block + :help " `py-forward-block' +Go to end of block. + +Returns end of block if successful, nil otherwise"] + + ["End of block or clause" py-forward-block-or-clause + :help " `py-forward-block-or-clause' +Go to end of block-or-clause. + +Returns end of block-or-clause if successful, nil otherwise"] + + ["End of class" py-forward-class + :help " `py-forward-class' +Go to end of class. + +Returns end of class if successful, nil otherwise"] + + ["End of clause" py-forward-clause + :help " `py-forward-clause' +Go to end of clause. + +Returns end of clause if successful, nil otherwise"] + + ["End of def" py-forward-def + :help " `py-forward-def' +Go to end of def. + +Returns end of def if successful, nil otherwise"] + + ["End of def or class" py-forward-def-or-class + :help " `py-forward-def-or-class' +Go to end of def-or-class. + +Returns end of def-or-class if successful, nil otherwise"] + + ["End of elif block" py-forward-elif-block + :help " `py-forward-elif-block' +Go to end of elif-block. + +Returns end of elif-block if successful, nil otherwise"] + + ["End of else block" py-forward-else-block + :help " `py-forward-else-block' +Go to end of else-block. + +Returns end of else-block if successful, nil otherwise"] + + ["End of except block" py-forward-except-block + :help " `py-forward-except-block' +Go to end of except-block. + +Returns end of except-block if successful, nil otherwise"] + + ["End of expression" py-forward-expression + :help " `py-forward-expression' +Go to the end of a compound python expression. + +With numeric ARG do it that many times. + +A a compound python expression might be concatenated by \".\" operator, thus composed by minor python expressions. + +Expression here is conceived as the syntactical component of a statement in Python. See http://docs.python.org/reference + +Operators however are left aside resp. limit py-expression designed for edit-purposes."] + + ["End of if block" py-forward-if-block + :help " `py-forward-if-block' +Go to end of if-block. + +Returns end of if-block if successful, nil otherwise"] + + ["End of partial expression" py-forward-partial-expression + :help " `py-forward-partial-expression'"] + + ["End of statement" py-forward-statement + :help " `py-forward-statement' +Go to the last char of current statement. + +Optional argument REPEAT, the number of loops done already, is checked for py-max-specpdl-size error. Avoid eternal loops due to missing string delimters etc."] + + ["End of top level" py-forward-top-level + :help " `py-forward-top-level' +Go to end of top-level form at point. + +Returns position if successful, nil otherwise"] + + ["End of try block" py-forward-try-block + :help " `py-forward-try-block' +Go to end of try-block. + +Returns end of try-block if successful, nil otherwise"] + ) + ("BOL-forms" + ("Backward" + ["Beginning of block bol" py-beginning-of-block-bol + :help " `py-beginning-of-block-bol' +Go to beginning block, go to BOL. + +Returns beginning of block if successful, nil otherwise"] + + ["Beginning of block or clause bol" py-beginning-of-block-or-clause-bol + :help " `py-beginning-of-block-or-clause-bol' +Go to beginning block-or-clause, go to BOL. + +Returns beginning of block-or-clause if successful, nil otherwise"] + + ["Beginning of class bol" py-beginning-of-class-bol + :help " `py-beginning-of-class-bol' +Go to beginning class, go to BOL. + +Returns beginning of class if successful, nil otherwise + +When `py-mark-decorators' is non-nil, decorators are considered too."] + + ["Beginning of clause bol" py-beginning-of-clause-bol + :help " `py-beginning-of-clause-bol' +Go to beginning clause, go to BOL. + +Returns beginning of clause if successful, nil otherwise"] + + ["Beginning of def bol" py-beginning-of-def-bol + :help " `py-beginning-of-def-bol' +Go to beginning def, go to BOL. + +Returns beginning of def if successful, nil otherwise + +When `py-mark-decorators' is non-nil, decorators are considered too."] + + ["Beginning of def or class bol" py-backward-def-or-class-bol + :help " `py-backward-def-or-class-bol' +Go to beginning def-or-class, go to BOL. + +Returns beginning of def-or-class if successful, nil otherwise + +When `py-mark-decorators' is non-nil, decorators are considered too."] + + ["Beginning of elif block bol" py-beginning-of-elif-block-bol + :help " `py-beginning-of-elif-block-bol' +Go to beginning elif-block, go to BOL. + +Returns beginning of elif-block if successful, nil otherwise"] + + ["Beginning of else block bol" py-beginning-of-else-block-bol + :help " `py-beginning-of-else-block-bol' +Go to beginning else-block, go to BOL. + +Returns beginning of else-block if successful, nil otherwise"] + + ["Beginning of except block bol" py-beginning-of-except-block-bol + :help " `py-beginning-of-except-block-bol' +Go to beginning except-block, go to BOL. + +Returns beginning of except-block if successful, nil otherwise"] + + ["Beginning of expression bol" py-beginning-of-expression-bol + :help " `py-beginning-of-expression-bol'"] + + ["Beginning of if block bol" py-beginning-of-if-block-bol + :help " `py-beginning-of-if-block-bol' +Go to beginning if-block, go to BOL. + +Returns beginning of if-block if successful, nil otherwise"] + + ["Beginning of partial expression bol" py-backward-partial-expression-bol + :help " `py-backward-partial-expression-bol'"] + + ["Beginning of statement bol" py-backward-statement-bol + :help " `py-backward-statement-bol' +Goto beginning of line where statement starts. + Returns position reached, if successful, nil otherwise. + +See also `py-up-statement': up from current definition to next beginning of statement above."] + + ["Beginning of try block bol" py-beginning-of-try-block-bol + :help " `py-beginning-of-try-block-bol' +Go to beginning try-block, go to BOL. + +Returns beginning of try-block if successful, nil otherwise"] + ) + ("Forward" + ["End of block bol" py-forward-block-bol + :help " `py-forward-block-bol' +Goto beginning of line following end of block. + Returns position reached, if successful, nil otherwise. + +See also `py-down-block': down from current definition to next beginning of block below."] + + ["End of block or clause bol" py-forward-block-or-clause-bol + :help " `py-forward-block-or-clause-bol' +Goto beginning of line following end of block-or-clause. + Returns position reached, if successful, nil otherwise. + +See also `py-down-block-or-clause': down from current definition to next beginning of block-or-clause below."] + + ["End of class bol" py-forward-class-bol + :help " `py-forward-class-bol' +Goto beginning of line following end of class. + Returns position reached, if successful, nil otherwise. + +See also `py-down-class': down from current definition to next beginning of class below."] + + ["End of clause bol" py-forward-clause-bol + :help " `py-forward-clause-bol' +Goto beginning of line following end of clause. + Returns position reached, if successful, nil otherwise. + +See also `py-down-clause': down from current definition to next beginning of clause below."] + + ["End of def bol" py-forward-def-bol + :help " `py-forward-def-bol' +Goto beginning of line following end of def. + Returns position reached, if successful, nil otherwise. + +See also `py-down-def': down from current definition to next beginning of def below."] + + ["End of def or class bol" py-forward-def-or-class-bol + :help " `py-forward-def-or-class-bol' +Goto beginning of line following end of def-or-class. + Returns position reached, if successful, nil otherwise. + +See also `py-down-def-or-class': down from current definition to next beginning of def-or-class below."] + + ["End of elif block bol" py-forward-elif-block-bol + :help " `py-forward-elif-block-bol' +Goto beginning of line following end of elif-block. + Returns position reached, if successful, nil otherwise. + +See also `py-down-elif-block': down from current definition to next beginning of elif-block below."] + + ["End of else block bol" py-forward-else-block-bol + :help " `py-forward-else-block-bol' +Goto beginning of line following end of else-block. + Returns position reached, if successful, nil otherwise. + +See also `py-down-else-block': down from current definition to next beginning of else-block below."] + + ["End of except block bol" py-forward-except-block-bol + :help " `py-forward-except-block-bol' +Goto beginning of line following end of except-block. + Returns position reached, if successful, nil otherwise. + +See also `py-down-except-block': down from current definition to next beginning of except-block below."] + + ["End of expression bol" py-forward-expression-bol + :help " `py-forward-expression-bol'"] + + ["End of if block bol" py-forward-if-block-bol + :help " `py-forward-if-block-bol' +Goto beginning of line following end of if-block. + Returns position reached, if successful, nil otherwise. + +See also `py-down-if-block': down from current definition to next beginning of if-block below."] + + ["End of partial expression bol" py-forward-partial-expression-bol + :help " `py-forward-partial-expression-bol'"] + + ["End of statement bol" py-forward-statement-bol + :help " `py-forward-statement-bol' +Go to the beginning-of-line following current statement."] + + ["End of top level bol" py-forward-top-level-bol + :help " `py-forward-top-level-bol' +Go to end of top-level form at point, stop at next beginning-of-line. + +Returns position successful, nil otherwise"] + + ["End of try block bol" py-forward-try-block-bol + :help " `py-forward-try-block-bol' +Goto beginning of line following end of try-block. + Returns position reached, if successful, nil otherwise. + +See also `py-down-try-block': down from current definition to next beginning of try-block below."] + )) + ("Up/Down" + ["Up" py-up + :help " `py-up' +Go up or to beginning of form if inside. + +If inside a delimited form --string or list-- go to its beginning. +If not at beginning of a statement or block, go to its beginning. +If at beginning of a statement or block, go to beginning one level above of compound statement or definition at point."] + + ["Down" py-down + :help " `py-down' +Go to beginning one level below of compound statement or definition at point. + +If no statement or block below, but a delimited form --string or list-- go to its beginning. Repeated call from there will behave like down-list. + +Returns position if successful, nil otherwise"] + )) + ("Hide-Show" + ("Hide" + ["Hide region" py-hide-region + :help " `py-hide-region' +Hide active region."] + + ["Hide statement" py-hide-statement + :help " `py-hide-statement' +Hide statement at point."] + + ["Hide block" py-hide-block + :help " `py-hide-block' +Hide block at point."] + + ["Hide clause" py-hide-clause + :help " `py-hide-clause' +Hide clause at point."] + + ["Hide block or clause" py-hide-block-or-clause + :help " `py-hide-block-or-clause' +Hide block-or-clause at point."] + + ["Hide def" py-hide-def + :help " `py-hide-def' +Hide def at point."] + + ["Hide class" py-hide-class + :help " `py-hide-class' +Hide class at point."] + + ["Hide expression" py-hide-expression + :help " `py-hide-expression' +Hide expression at point."] + + ["Hide partial expression" py-hide-partial-expression + :help " `py-hide-partial-expression' +Hide partial-expression at point."] + + ["Hide line" py-hide-line + :help " `py-hide-line' +Hide line at point."] + + ["Hide top level" py-hide-top-level + :help " `py-hide-top-level' +Hide top-level at point."] + ) + ("Show" + ["Show" py-show + :help " `py-show' +Un-hide at point."] + + ["Show all" py-show-all + :help " `py-show-all' +Un-hide all in buffer."] + )) + ("Virtualenv" + ["Virtualenv activate" virtualenv-activate + :help " `virtualenv-activate' +Activate the virtualenv located in DIR"] + + ["Virtualenv deactivate" virtualenv-deactivate + :help " `virtualenv-deactivate' +Deactivate the current virtual enviroment"] + + ["Virtualenv p" virtualenv-p + :help " `virtualenv-p' +Check if a directory is a virtualenv"] + + ["Virtualenv workon" virtualenv-workon + :help " `virtualenv-workon' +Issue a virtualenvwrapper-like virtualenv-workon command"] + ) + ("Help" + ["Find definition" py-find-definition + :help " `py-find-definition' +Find source of definition of SYMBOL. + +Interactively, prompt for SYMBOL."] + + ["Help at point" py-help-at-point + :help " `py-help-at-point' +Print help on symbol at point. + +If symbol is defined in current buffer, jump to it's definition +Optional C-u used for debugging, will prevent deletion of temp file."] + + ["Info lookup symbol" py-info-lookup-symbol + :help " `py-info-lookup-symbol'"] + + ["Symbol at point" py-symbol-at-point + :help " `py-symbol-at-point' +Return the current Python symbol."] + ) + ("Customize" + + ["Python-mode customize group" (customize-group 'python-mode) + :help "Open the customization buffer for Python mode"] + ("Switches" + :help "Toggle useful modes" + ("Interpreter" + + ["Shell prompt read only" + (setq py-shell-prompt-read-only + (not py-shell-prompt-read-only)) + :help "If non-nil, the python prompt is read only. Setting this variable will only effect new shells.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-shell-prompt-read-only] + + ["Remove cwd from path" + (setq py-remove-cwd-from-path + (not py-remove-cwd-from-path)) + :help "Whether to allow loading of Python modules from the current directory. +If this is non-nil, Emacs removes '' from sys.path when starting +a Python process. This is the default, for security +reasons, as it is easy for the Python process to be started +without the user's realization (e.g. to perform completion).Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-remove-cwd-from-path] + + ["Honor IPYTHONDIR " + (setq py-honor-IPYTHONDIR-p + (not py-honor-IPYTHONDIR-p)) + :help "When non-nil ipython-history file is constructed by \$IPYTHONDIR +followed by "/history". Default is nil. + +Otherwise value of py-ipython-history is used. Use `M-x customize-variable' to set it permanently" +:style toggle :selected py-honor-IPYTHONDIR-p] + + ["Honor PYTHONHISTORY " + (setq py-honor-PYTHONHISTORY-p + (not py-honor-PYTHONHISTORY-p)) + :help "When non-nil python-history file is set by \$PYTHONHISTORY +Default is nil. + +Otherwise value of py-python-history is used. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-honor-PYTHONHISTORY-p] + + ["Enforce py-shell-name" force-py-shell-name-p-on + :help "Enforce customized default `py-shell-name' should upon execution. "] + + ["Don't enforce default interpreter" force-py-shell-name-p-off + :help "Make execute commands guess interpreter from environment"] + + ) + + ("Execute" + + ["Fast process" py-fast-process-p + :help " `py-fast-process-p' + +Use `py-fast-process'\. + +Commands prefixed \"py-fast-...\" suitable for large output + +See: large output makes Emacs freeze, lp:1253907 + +Output-buffer is not in comint-mode" + :style toggle :selected py-fast-process-p] + + ["Python mode v5 behavior" + (setq python-mode-v5-behavior-p + (not python-mode-v5-behavior-p)) + :help "Execute region through `shell-command-on-region' as +v5 did it - lp:990079. This might fail with certain chars - see UnicodeEncodeError lp:550661 + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected python-mode-v5-behavior-p] + + ["Force shell name " + (setq py-force-py-shell-name-p + (not py-force-py-shell-name-p)) + :help "When `t', execution with kind of Python specified in `py-shell-name' is enforced, possibly shebang doesn't take precedence. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-force-py-shell-name-p] + + ["Execute \"if name == main\" blocks p" + (setq py-if-name-main-permission-p + (not py-if-name-main-permission-p)) + :help " `py-if-name-main-permission-p' + +Allow execution of code inside blocks delimited by +if __name__ == '__main__' + +Default is non-nil. " + :style toggle :selected py-if-name-main-permission-p] + + ["Ask about save" + (setq py-ask-about-save + (not py-ask-about-save)) + :help "If not nil, ask about which buffers to save before executing some code. +Otherwise, all modified buffers are saved without asking.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-ask-about-save] + + ["Store result" + (setq py-store-result-p + (not py-store-result-p)) + :help " `py-store-result-p' + +When non-nil, put resulting string of `py-execute-...' into kill-ring, so it might be yanked. " + :style toggle :selected py-store-result-p] + + ["Prompt on changed " + (setq py-prompt-on-changed-p + (not py-prompt-on-changed-p)) + :help "When called interactively, ask for save before a changed buffer is sent to interpreter. + +Default is `t'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-prompt-on-changed-p] + + ["Dedicated process " + (setq py-dedicated-process-p + (not py-dedicated-process-p)) + :help "If commands executing code use a dedicated shell. + +Default is nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-dedicated-process-p] + + ["Execute without temporary file" + (setq py-execute-no-temp-p + (not py-execute-no-temp-p)) + :help " `py-execute-no-temp-p' +Seems Emacs-24.3 provided a way executing stuff without temporary files. +In experimental state yet " + :style toggle :selected py-execute-no-temp-p] + + ["Warn tmp files left " + (setq py--warn-tmp-files-left-p + (not py--warn-tmp-files-left-p)) + :help "Messages a warning, when `py-temp-directory' contains files susceptible being left by previous Python-mode sessions. See also lp:987534 Use `M-x customize-variable' to set it permanently" + :style toggle :selected py--warn-tmp-files-left-p]) + + ("Edit" + + ("Completion" + + ["Set Pymacs-based complete keymap " + (setq py-set-complete-keymap-p + (not py-set-complete-keymap-p)) + :help "If `py-complete-initialize', which sets up enviroment for Pymacs based py-complete, should load it's keys into `python-mode-map' + +Default is nil. +See also resp. edit `py-complete-set-keymap' Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-set-complete-keymap-p] + + ["Indent no completion " + (setq py-indent-no-completion-p + (not py-indent-no-completion-p)) + :help "If completion function should indent when no completion found. Default is `t' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-indent-no-completion-p] + + ["Company pycomplete " + (setq py-company-pycomplete-p + (not py-company-pycomplete-p)) + :help "Load company-pycomplete stuff. Default is nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-company-pycomplete-p]) + + ("Filling" + + ("Docstring styles" + :help "Switch docstring-style" + + ["Nil" py-set-nil-docstring-style + :help " `py-set-nil-docstring-style' + +Set py-docstring-style to nil, format string normally. "] + + ["pep-257-nn" py-set-pep-257-nn-docstring-style + :help " `py-set-pep-257-nn-docstring-style' + +Set py-docstring-style to 'pep-257-nn "] + + ["pep-257" py-set-pep-257-docstring-style + :help " `py-set-pep-257-docstring-style' + +Set py-docstring-style to 'pep-257 "] + + ["django" py-set-django-docstring-style + :help " `py-set-django-docstring-style' + +Set py-docstring-style to 'django "] + + ["onetwo" py-set-onetwo-docstring-style + :help " `py-set-onetwo-docstring-style' + +Set py-docstring-style to 'onetwo "] + + ["symmetric" py-set-symmetric-docstring-style + :help " `py-set-symmetric-docstring-style' + +Set py-docstring-style to 'symmetric "]) + + ["Auto-fill mode" + (setq py-auto-fill-mode + (not py-auto-fill-mode)) + :help "Fill according to `py-docstring-fill-column' and `py-comment-fill-column' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-auto-fill-mode]) + + ["Use current dir when execute" + (setq py-use-current-dir-when-execute-p + (not py-use-current-dir-when-execute-p)) + :help " `py-toggle-use-current-dir-when-execute-p' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-current-dir-when-execute-p] + + ("Indent" + ("TAB related" + + ["indent-tabs-mode" + (setq indent-tabs-mode + (not indent-tabs-mode)) + :help "Indentation can insert tabs if this is non-nil. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected indent-tabs-mode] + + ["Tab indent" + (setq py-tab-indent + (not py-tab-indent)) + :help "Non-nil means TAB in Python mode calls `py-indent-line'.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-indent] + + ["Tab shifts region " + (setq py-tab-shifts-region-p + (not py-tab-shifts-region-p)) + :help "If `t', TAB will indent/cycle the region, not just the current line. + +Default is nil +See also `py-tab-indents-region-p' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-shifts-region-p] + + ["Tab indents region " + (setq py-tab-indents-region-p + (not py-tab-indents-region-p)) + :help "When `t' and first TAB doesn't shift, indent-region is called. + +Default is nil +See also `py-tab-shifts-region-p' + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-indents-region-p]) + + ["Close at start column" + (setq py-closing-list-dedents-bos + (not py-closing-list-dedents-bos)) + :help "When non-nil, indent list's closing delimiter like start-column. + +It will be lined up under the first character of + the line that starts the multi-line construct, as in: + +my_list = \[ + 1, 2, 3, + 4, 5, 6, +] + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-closing-list-dedents-bos] + + ["Closing list keeps space" + (setq py-closing-list-keeps-space + (not py-closing-list-keeps-space)) + :help "If non-nil, closing parenthesis dedents onto column of opening plus `py-closing-list-space', default is nil Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-closing-list-keeps-space] + + ["Closing list space" + (setq py-closing-list-space + (not py-closing-list-space)) + :help "Number of chars, closing parenthesis outdent from opening, default is 1 Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-closing-list-space] + + ["Tab shifts region " + (setq py-tab-shifts-region-p + (not py-tab-shifts-region-p)) + :help "If `t', TAB will indent/cycle the region, not just the current line. + +Default is nil +See also `py-tab-indents-region-p'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-tab-shifts-region-p] + + ["Lhs inbound indent" + (setq py-lhs-inbound-indent + (not py-lhs-inbound-indent)) + :help "When line starts a multiline-assignment: How many colums indent should be more than opening bracket, brace or parenthesis. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-lhs-inbound-indent] + + ["Continuation offset" + (setq py-continuation-offset + (not py-continuation-offset)) + :help "With numeric ARG different from 1 py-continuation-offset is set to that value; returns py-continuation-offset. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-continuation-offset] + + ["Electric colon" + (setq py-electric-colon-active-p + (not py-electric-colon-active-p)) + :help " `py-electric-colon-active-p' + +`py-electric-colon' feature. Default is `nil'. See lp:837065 for discussions. " + :style toggle :selected py-electric-colon-active-p] + + ["Electric colon at beginning of block only" + (setq py-electric-colon-bobl-only + (not py-electric-colon-bobl-only)) + :help "When inserting a colon, do not indent lines unless at beginning of block. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-colon-bobl-only] + + ["Electric yank active " + (setq py-electric-yank-active-p + (not py-electric-yank-active-p)) + :help " When non-nil, `yank' will be followed by an `indent-according-to-mode'. + +Default is nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-yank-active-p] + + ["Trailing whitespace smart delete " + (setq py-trailing-whitespace-smart-delete-p + (not py-trailing-whitespace-smart-delete-p)) + :help "Default is nil. When t, python-mode calls + (add-hook 'before-save-hook 'delete-trailing-whitespace nil 'local) + +Also commands may delete trailing whitespace by the way. +When editing other peoples code, this may produce a larger diff than expected Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-trailing-whitespace-smart-delete-p] + + ["Newline delete trailing whitespace " + (setq py-newline-delete-trailing-whitespace-p + (not py-newline-delete-trailing-whitespace-p)) + :help "Delete trailing whitespace maybe left by `py-newline-and-indent'. + +Default is `t'. See lp:1100892 Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-newline-delete-trailing-whitespace-p] + + ["Dedent keep relative column" + (setq py-dedent-keep-relative-column + (not py-dedent-keep-relative-column)) + :help "If point should follow dedent or kind of electric move to end of line. Default is t - keep relative position. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-dedent-keep-relative-column] + +;; ["Indent paren spanned multilines " +;; (setq py-indent-paren-spanned-multilines-p +;; (not py-indent-paren-spanned-multilines-p)) +;; :help "If non-nil, indents elements of list a value of `py-indent-offset' to first element: + +;; def foo(): +;; if (foo && +;; baz): +;; bar() + +;; Default lines up with first element: + +;; def foo(): +;; if (foo && +;; baz): +;; bar() +;; Use `M-x customize-variable' to set it permanently" +;; :style toggle :selected py-indent-paren-spanned-multilines-p] + + ;; ["Indent honors multiline listing" + ;; (setq py-indent-honors-multiline-listing + ;; (not py-indent-honors-multiline-listing)) + ;; :help "If `t', indents to 1\+ column of opening delimiter. If `nil', indent adds one level to the beginning of statement. Default is `nil'. Use `M-x customize-variable' to set it permanently" + ;; :style toggle :selected py-indent-honors-multiline-listing] + + ["Indent comment " + (setq py-indent-comments + (not py-indent-comments)) + :help "If comments should be indented like code. Default is `nil'. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-indent-comments] + + ["Uncomment indents " + (setq py-uncomment-indents-p + (not py-uncomment-indents-p)) + :help "When non-nil, after uncomment indent lines. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-uncomment-indents-p] + + ["Indent honors inline comment" + (setq py-indent-honors-inline-comment + (not py-indent-honors-inline-comment)) + :help "If non-nil, indents to column of inlined comment start. +Default is nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-indent-honors-inline-comment] + + ["Kill empty line" + (setq py-kill-empty-line + (not py-kill-empty-line)) + :help "If t, py-indent-forward-line kills empty lines. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-kill-empty-line] + + ("Smart indentation" + :help "Toggle py-smart-indentation' + +Use `M-x customize-variable' to set it permanently" + + ["Toggle py-smart-indentation" py-toggle-smart-indentation + :help "Toggles py-smart-indentation + +Use `M-x customize-variable' to set it permanently"] + + ["py-smart-indentation on" py-smart-indentation-on + :help "Switches py-smart-indentation on + +Use `M-x customize-variable' to set it permanently"] + + ["py-smart-indentation off" py-smart-indentation-off + :help "Switches py-smart-indentation off + +Use `M-x customize-variable' to set it permanently"]) + + ["Beep if tab change" + (setq py-beep-if-tab-change + (not py-beep-if-tab-change)) + :help "Ring the bell if `tab-width' is changed. +If a comment of the form + + # vi:set tabsize=: + +is found before the first code line when the file is entered, and the +current value of (the general Emacs variable) `tab-width' does not +equal , `tab-width' is set to , a message saying so is +displayed in the echo area, and if `py-beep-if-tab-change' is non-nil +the Emacs bell is also rung as a warning.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-beep-if-tab-change] + + ["Electric comment " + (setq py-electric-comment-p + (not py-electric-comment-p)) + :help "If \"#\" should call `py-electric-comment'. Default is `nil'. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-comment-p] + + ["Electric comment add space " + (setq py-electric-comment-add-space-p + (not py-electric-comment-add-space-p)) + :help "If py-electric-comment should add a space. Default is `nil'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-electric-comment-add-space-p] + + ["Empty line closes " + (setq py-empty-line-closes-p + (not py-empty-line-closes-p)) + :help "When non-nil, dedent after empty line following block + +if True: + print(\"Part of the if-statement\") + +print(\"Not part of the if-statement\") + +Default is nil + +If non-nil, a C-j from empty line dedents. +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-empty-line-closes-p]) + ["Defun use top level " + (setq py-defun-use-top-level-p + (not py-defun-use-top-level-p)) + :help "When non-nil, keys C-M-a, C-M-e address top-level form. + +Beginning- end-of-defun forms use +commands `py-backward-top-level', `py-forward-top-level' + +mark-defun marks top-level form at point etc. " + :style toggle :selected py-defun-use-top-level-p] + + ["Close provides newline" + (setq py-close-provides-newline + (not py-close-provides-newline)) + :help "If a newline is inserted, when line after block isn't empty. Default is non-nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-close-provides-newline] + + ["Block comment prefix " + (setq py-block-comment-prefix-p + (not py-block-comment-prefix-p)) + :help "If py-comment inserts py-block-comment-prefix. + +Default is tUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-block-comment-prefix-p]) + + ("Display" + + ("Index" + + ["Imenu create index " + (setq py--imenu-create-index-p + (not py--imenu-create-index-p)) + :help "Non-nil means Python mode creates and displays an index menu of functions and global variables. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py--imenu-create-index-p] + + ["Imenu show method args " + (setq py-imenu-show-method-args-p + (not py-imenu-show-method-args-p)) + :help "Controls echoing of arguments of functions & methods in the Imenu buffer. +When non-nil, arguments are printed.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-imenu-show-method-args-p] + ["Switch index-function" py-switch-imenu-index-function + :help "`py-switch-imenu-index-function' +Switch between `py--imenu-create-index' from 5.1 series and `py--imenu-create-index-new'."]) + + ("Fontification" + + ["Mark decorators" + (setq py-mark-decorators + (not py-mark-decorators)) + :help "If py-mark-def-or-class functions should mark decorators too. Default is `nil'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-mark-decorators] + + ["Fontify shell buffer " + (setq py-fontify-shell-buffer-p + (not py-fontify-shell-buffer-p)) + :help "If code in Python shell should be highlighted as in script buffer. + +Default is nil. + +If `t', related vars like `comment-start' will be set too. +Seems convenient when playing with stuff in IPython shell +Might not be TRT when a lot of output arrives Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-fontify-shell-buffer-p] + + ["Use font lock doc face " + (setq py-use-font-lock-doc-face-p + (not py-use-font-lock-doc-face-p)) + :help "If documention string inside of def or class get `font-lock-doc-face'. + +`font-lock-doc-face' inherits `font-lock-string-face'. + +Call M-x `customize-face' in order to have a visible effect. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-font-lock-doc-face-p]) + + ["Switch buffers on execute" + (setq py-switch-buffers-on-execute-p + (not py-switch-buffers-on-execute-p)) + :help "When non-nil switch to the Python output buffer. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-switch-buffers-on-execute-p] + + ["Split windows on execute" + (setq py-split-window-on-execute + (not py-split-window-on-execute)) + :help "When non-nil split windows. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-split-window-on-execute] + + ["Keep windows configuration" + (setq py-keep-windows-configuration + (not py-keep-windows-configuration)) + :help "If a windows is splitted displaying results, this is directed by variable `py-split-window-on-execute'\. Also setting `py-switch-buffers-on-execute-p' affects window-configuration\. While commonly a screen splitted into source and Python-shell buffer is assumed, user may want to keep a different config\. + +Setting `py-keep-windows-configuration' to `t' will restore windows-config regardless of settings mentioned above\. However, if an error occurs, it's displayed\. + +To suppres window-changes due to error-signaling also: M-x customize-variable RET. Set `py-keep-4windows-configuration' onto 'force + +Default is nil Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-keep-windows-configuration] + + ["Which split windows on execute function" + (progn + (if (eq 'split-window-vertically py-split-windows-on-execute-function) + (setq py-split-windows-on-execute-function'split-window-horizontally) + (setq py-split-windows-on-execute-function 'split-window-vertically)) + (message "py-split-windows-on-execute-function set to: %s" py-split-windows-on-execute-function)) + + :help "If `split-window-vertically' or `...-horizontally'. Use `M-x customize-variable' RET `py-split-windows-on-execute-function' RET to set it permanently" + :style toggle :selected py-split-windows-on-execute-function] + + ["Modeline display full path " + (setq py-modeline-display-full-path-p + (not py-modeline-display-full-path-p)) + :help "If the full PATH/TO/PYTHON should be displayed in shell modeline. + +Default is nil. Note: when `py-shell-name' is specified with path, it's shown as an acronym in buffer-name already. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-modeline-display-full-path-p] + + ["Modeline acronym display home " + (setq py-modeline-acronym-display-home-p + (not py-modeline-acronym-display-home-p)) + :help "If the modeline acronym should contain chars indicating the home-directory. + +Default is nil Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-modeline-acronym-display-home-p] + + ["Hide show hide docstrings" + (setq py-hide-show-hide-docstrings + (not py-hide-show-hide-docstrings)) + :help "Controls if doc strings can be hidden by hide-showUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-hide-show-hide-docstrings] + + ["Hide comments when hiding all" + (setq py-hide-comments-when-hiding-all + (not py-hide-comments-when-hiding-all)) + :help "Hide the comments too when you do `hs-hide-all'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-hide-comments-when-hiding-all] + + ["Max help buffer " + (setq py-max-help-buffer-p + (not py-max-help-buffer-p)) + :help "If \"\*Python-Help\*\"-buffer should appear as the only visible. + +Default is nil. In help-buffer, \"q\" will close it. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-max-help-buffer-p] + + ["Current defun show" + (setq py-current-defun-show + (not py-current-defun-show)) + :help "If `py-current-defun' should jump to the definition, highlight it while waiting PY-WHICH-FUNC-DELAY seconds, before returning to previous position. + +Default is `t'.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-current-defun-show] + + ["Match paren mode" + (setq py-match-paren-mode + (not py-match-paren-mode)) + :help "Non-nil means, cursor will jump to beginning or end of a block. +This vice versa, to beginning first. +Sets `py-match-paren-key' in python-mode-map. +Customize `py-match-paren-key' which key to use. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-match-paren-mode]) + + ("Debug" + + ["py-debug-p" + (setq py-debug-p + (not py-debug-p)) + :help "When non-nil, keep resp\. store information useful for debugging\. + +Temporary files are not deleted\. Other functions might implement +some logging etc\. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-debug-p] + + ["Pdbtrack do tracking " + (setq py-pdbtrack-do-tracking-p + (not py-pdbtrack-do-tracking-p)) + :help "Controls whether the pdbtrack feature is enabled or not. +When non-nil, pdbtrack is enabled in all comint-based buffers, +e.g. shell buffers and the \*Python\* buffer. When using pdb to debug a +Python program, pdbtrack notices the pdb prompt and displays the +source file and line that the program is stopped at, much the same way +as gud-mode does for debugging C programs with gdb.Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-pdbtrack-do-tracking-p] + + ["Jump on exception" + (setq py-jump-on-exception + (not py-jump-on-exception)) + :help "Jump to innermost exception frame in Python output buffer. +When this variable is non-nil and an exception occurs when running +Python code synchronously in a subprocess, jump immediately to the +source code of the innermost traceback frame. + +Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-jump-on-exception] + + ["Highlight error in source " + (setq py-highlight-error-source-p + (not py-highlight-error-source-p)) + :help "Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-highlight-error-source-p]) + + ("Other" + + ("Directory" + + ["Guess install directory " + (setq py-guess-py-install-directory-p + (not py-guess-py-install-directory-p)) + :help "If in cases, `py-install-directory' isn't set, `py-set-load-path'should guess it from `buffer-file-name'. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-guess-py-install-directory-p] + + ["Use local default" + (setq py-use-local-default + (not py-use-local-default)) + :help "If `t', py-shell will use `py-shell-local-path' instead +of default Python. + +Making switch between several virtualenv's easier, + `python-mode' should deliver an installer, so named-shells pointing to virtualenv's will be available. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-local-default] + + ["Use current dir when execute " + (setq py-use-current-dir-when-execute-p + (not py-use-current-dir-when-execute-p)) + :help "When `t', current directory is used by Python-shell for output of `py-execute-buffer' and related commands. + +See also `py-execute-directory'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-use-current-dir-when-execute-p] + + ["Keep shell dir when execute " + (setq py-keep-shell-dir-when-execute-p + (not py-keep-shell-dir-when-execute-p)) + :help "Don't change Python shell's current working directory when sending code. + +See also `py-execute-directory'Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-keep-shell-dir-when-execute-p] + + ["Fileless buffer use default directory " + (setq py-fileless-buffer-use-default-directory-p + (not py-fileless-buffer-use-default-directory-p)) + :help "When `py-use-current-dir-when-execute-p' is non-nil and no buffer-file exists, value of `default-directory' sets current working directory of Python output shellUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-fileless-buffer-use-default-directory-p]) + + ("Underscore word syntax" + :help "Toggle `py-underscore-word-syntax-p'" + + ["Toggle underscore word syntax" py-toggle-underscore-word-syntax-p + :help " `py-toggle-underscore-word-syntax-p' + +If `py-underscore-word-syntax-p' should be on or off. + + Returns value of `py-underscore-word-syntax-p' switched to. . + +Use `M-x customize-variable' to set it permanently"] + + ["Underscore word syntax on" py-underscore-word-syntax-p-on + :help " `py-underscore-word-syntax-p-on' + +Make sure, py-underscore-word-syntax-p' is on. + +Returns value of `py-underscore-word-syntax-p'. . + +Use `M-x customize-variable' to set it permanently"] + + ["Underscore word syntax off" py-underscore-word-syntax-p-off + :help " `py-underscore-word-syntax-p-off' + +Make sure, `py-underscore-word-syntax-p' is off. + +Returns value of `py-underscore-word-syntax-p'. . + +Use `M-x customize-variable' to set it permanently"]) + + ["Load pymacs " + (setq py-load-pymacs-p + (not py-load-pymacs-p)) + :help "If Pymacs related stuff should be loaded. + +Default is nil. + +Pymacs has been written by François Pinard and many others. +See original source: http://pymacs.progiciels-bpi.caUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-load-pymacs-p] + + ["Verbose " + (setq py-verbose-p + (not py-verbose-p)) + :help "If functions should report results. + +Default is nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-verbose-p] + + ["Empty comment line separates paragraph " + (setq py-empty-comment-line-separates-paragraph-p + (not py-empty-comment-line-separates-paragraph-p)) + :help "Consider paragraph start/end lines with nothing inside but comment sign. + +Default is non-nilUse `M-x customize-variable' to set it permanently" + :style toggle :selected py-empty-comment-line-separates-paragraph-p] + + ["Org cycle " + (setq py-org-cycle-p + (not py-org-cycle-p)) + :help "When non-nil, command `org-cycle' is available at shift-TAB, + +Default is nil. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-org-cycle-p] + + ["Set pager cat" + (setq py-set-pager-cat-p + (not py-set-pager-cat-p)) + :help "If the shell environment variable \$PAGER should set to `cat'. + +If `t', use `C-c C-r' to jump to beginning of output. Then scroll normally. + +Avoids lp:783828, \"Terminal not fully functional\", for help('COMMAND') in python-shell + +When non-nil, imports module `os' Use `M-x customize-variable' to +set it permanently" + :style toggle :selected py-set-pager-cat-p] + + ["Edit only " + (setq py-edit-only-p + (not py-edit-only-p)) + :help "When `t' `python-mode' will not take resort nor check for installed Python executables. Default is nil. + +See bug report at launchpad, lp:944093. Use `M-x customize-variable' to set it permanently" + :style toggle :selected py-edit-only-p]))) + ("Other" + ["Boolswitch" py-boolswitch + :help " `py-boolswitch' +Edit the assignment of a boolean variable, revert them. + +I.e. switch it from \"True\" to \"False\" and vice versa"] + + ["Empty out list backward" py-empty-out-list-backward + :help " `py-empty-out-list-backward' +Deletes all elements from list before point."] + + ["Kill buffer unconditional" py-kill-buffer-unconditional + :help " `py-kill-buffer-unconditional' +Kill buffer unconditional, kill buffer-process if existing."] + + ["Remove overlays at point" py-remove-overlays-at-point + :help " `py-remove-overlays-at-point' +Remove overlays as set when `py-highlight-error-source-p' is non-nil."] + ("Electric" + ["Complete electric comma" py-complete-electric-comma + :help " `py-complete-electric-comma'"] + + ["Complete electric lparen" py-complete-electric-lparen + :help " `py-complete-electric-lparen'"] + + ["Electric backspace" py-electric-backspace + :help " `py-electric-backspace' +Delete preceding character or level of indentation. + +With ARG do that ARG times. +Returns column reached."] + + ["Electric colon" py-electric-colon + :help " `py-electric-colon' +Insert a colon and indent accordingly. + +If a numeric argument ARG is provided, that many colons are inserted +non-electrically. + +Electric behavior is inhibited inside a string or +comment or by universal prefix C-u. + +Switched by `py-electric-colon-active-p', default is nil +See also `py-electric-colon-greedy-p'"] + + ["Electric comment" py-electric-comment + :help " `py-electric-comment' +Insert a comment. If starting a comment, indent accordingly. + +If a numeric argument ARG is provided, that many \"#\" are inserted +non-electrically. +With C-u \"#\" electric behavior is inhibited inside a string or comment."] + + ["Electric delete" py-electric-delete + :help " `py-electric-delete' +Delete following character or levels of whitespace. + +With ARG do that ARG times."] + + ["Electric yank" py-electric-yank + :help " `py-electric-yank' +Perform command `yank' followed by an `indent-according-to-mode'"] + + ["Hungry delete backwards" py-hungry-delete-backwards + :help " `py-hungry-delete-backwards' +Delete the preceding character or all preceding whitespace +back to the previous non-whitespace character. +See also C-c ."] + + ["Hungry delete forward" py-hungry-delete-forward + :help " `py-hungry-delete-forward' +Delete the following character or all following whitespace +up to the next non-whitespace character. +See also C-c ."] + ) + ("Abbrevs" :help "see also `py-add-abbrev'" + :filter (lambda (&rest junk) + (abbrev-table-menu python-mode-abbrev-table)) ) + + ["Add abbrev" py-add-abbrev + :help " `py-add-abbrev' +Defines python-mode specific abbrev for last expressions before point. +Argument is how many `py-partial-expression's form the expansion; or zero means the region is the expansion. + +Reads the abbreviation in the minibuffer; with numeric arg it displays a proposal for an abbrev. +Proposal is composed from the initial character(s) of the +expansion. + +Don't use this function in a Lisp program; use `define-abbrev' instead."] + ("Completion" + ["Py indent or complete" py-indent-or-complete + :help " `py-indent-or-complete'"] + + ["Py shell complete" py-shell-complete + :help " `py-shell-complete'"] + + ["Py complete" py-complete + :help " `py-complete'"] + ))))) + +;; python-components-complete + +(defun py--shell-completion-get-completions (input process completion-code) + "Retrieve available completions for INPUT using PROCESS. +Argument COMPLETION-CODE is the python code used to get +completions on the current context." + (let ((erg + (py-send-string-no-output (format completion-code input) process))) + (if (and erg (> (length erg) 2)) + (setq erg (split-string erg "^'\\|^\"\\|;\\|'$\\|\"$" t)) + (and py-verbose-p (message "py--shell-completion-get-completions: %s" "Don't see a completion"))) + erg)) + +;; post-command-hook +;; caused insert-file-contents error lp:1293172 +(defun py--after-change-function (end) + "Restore window-confiuration after completion. + +Takes END" + (when + (and (or + (eq this-command 'completion-at-point) + (eq this-command 'choose-completion) + (eq this-command 'choose-completion) + (eq this-command 'py-shell-complete) + (and (or + (eq last-command 'completion-at-point) + (eq last-command 'choose-completion) + (eq last-command 'choose-completion) + (eq last-command 'py-shell-complete)) + (eq this-command 'self-insert-command)))) + (py-restore-window-configuration) + ) + + (goto-char end)) + +(defun py--shell-insert-completion-maybe (completion input) + (cond ((eq completion t) + (and py-verbose-p (message "py--shell-do-completion-at-point %s" "`t' is returned, not completion. Might be a bug."))) + ((null completion) + (and py-verbose-p (message "py--shell-do-completion-at-point %s" "Don't see a completion"))) + ((and completion + (or (and (listp completion) + (string= input (car completion))) + (and (stringp completion) + (string= input completion))))) + ((and completion (stringp completion)(or (string= input completion) (string= "''" completion)))) + ((and completion (stringp completion)) + (progn (delete-char (- (length input))) + (insert completion))) + (t (py--try-completion input completion))) + ) + +(defun py--shell-do-completion-at-point (process imports input exception-buffer code) + "Do completion at point for PROCESS. + +Takes PROCESS IMPORTS INPUT EXCEPTION-BUFFER CODE" + (when imports + (py-execute-string imports process)) + (sit-for 0.1 t) + (let* ((completion + (py--shell-completion-get-completions + input process code))) + (set-buffer exception-buffer) + (when completion + (py--shell-insert-completion-maybe completion input)))) + +(defun py--complete-base (shell word imports buffer) + (let* ((proc (or + ;; completing inside a shell + (get-buffer-process buffer) + (and (comint-check-proc shell) + (get-process shell)) + (prog1 + (get-buffer-process (py-shell nil nil nil shell)) + (sit-for py-new-shell-delay t)))) + ;; (buffer (process-buffer proc)) + (code (if (string-match "[Ii][Pp]ython*" shell) + (py-set-ipython-completion-command-string shell) + py-shell-module-completion-code))) + (py--shell-do-completion-at-point proc imports word buffer code))) + +(defun py--try-completion-intern (input completion buffer) + (with-current-buffer buffer + (let ((erg nil)) + (and (setq erg (try-completion input completion)) + (sit-for 0.1) + (looking-back input (line-beginning-position)) + (not (string= input erg)) + (setq erg (completion-in-region (match-beginning 0) (match-end 0) completion))))) + ;; (set-window-configuration py-last-window-configuration) + ) + +(defun py--try-completion (input completion) + "Repeat `try-completion' as long as match are found. + +Interal used. Takes INPUT COMPLETION" + (let ((erg nil) + (newlist nil)) + (unless (py--try-completion-intern input completion (current-buffer)) + (dolist (elt completion) + (unless (string= erg elt) + (push elt newlist))) + (if (< 1 (length newlist)) + (with-output-to-temp-buffer py-python-completions + (display-completion-list + (all-completions input (or newlist completion)))))))) + +(defun py--fast-completion-get-completions (input process completion-code buffer) + "Retrieve available completions for INPUT using PROCESS. +Argument COMPLETION-CODE is the python code used to get +completions on the current context." + (let ((completions + (py-fast-send-string + (format completion-code input) process buffer t))) + (when (> (length completions) 2) + (split-string completions "^'\\|^\"\\|;\\|'$\\|\"$" t)))) + +(defun py--fast--do-completion-at-point (process imports input code buffer) + "Do completion at point for PROCESS." + ;; send setup-code + (let (py-store-result-p) + (when imports + ;; (message "%s" imports) + (py-fast-send-string imports process buffer nil t))) + (let* ((completion + (py--fast-completion-get-completions input process code buffer))) + (sit-for 0.1) + (cond ((eq completion t) + (and py-verbose-p (message "py--fast--do-completion-at-point %s" "`t' is returned, not completion. Might be a bug."))) + ((null completion) + (and py-verbose-p (message "py--fast--do-completion-at-point %s" "Don't see a completion")) + (set-window-configuration py-last-window-configuration)) + ((and completion + (or (and (listp completion) + (string= input (car completion))) + (and (stringp completion) + (string= input completion)))) + (set-window-configuration py-last-window-configuration)) + ((and completion (stringp completion) (not (string= input completion))) + (progn (delete-char (- (length input))) + (insert completion) + ;; (move-marker orig (point)) + ;; minibuffer.el expects a list + )) + (t (py--try-completion input completion))))) + +(defun py--fast-complete-base (shell word imports) + (let* (py-split-window-on-execute py-switch-buffers-on-execute-p + (shell (or shell py-shell-name)) + (buffer (py-shell nil nil nil shell nil t)) + (proc (get-buffer-process buffer)) + (code (if (string-match "[Ii][Pp]ython*" shell) + (py-set-ipython-completion-command-string shell) + py-shell-module-completion-code))) + (py--python-send-completion-setup-code buffer) + (py--fast--do-completion-at-point proc imports word code buffer))) + +(defun py-shell-complete (&optional shell beg end word fast imports) + (interactive) + (let* ((exception-buffer (current-buffer)) + (pps (parse-partial-sexp + (or + (ignore-errors (cdr-safe comint-last-prompt)) + (ignore-errors comint-last-prompt) + (line-beginning-position)) + (point))) + (in-string (when (nth 3 pps) (nth 8 pps))) + (beg + (save-excursion + (or beg + (and in-string + ;; possible completion of filenames + (progn + (goto-char in-string) + (and + (save-excursion + (skip-chars-backward "^ \t\r\n\f") (looking-at "open"))) + + (skip-chars-forward "\"'") (point))) + (progn (and (eq (char-before) ?\()(forward-char -1)) + (skip-chars-backward "a-zA-Z0-9_.'") (point))))) + (end (or end (point))) + (word (or word (buffer-substring-no-properties beg end))) + (ausdruck (and (string-match "^/" word) (setq word (substring-no-properties word 1))(concat "\"" word "*\""))) + ;; when in string, assume looking for filename + (filenames (and in-string ausdruck + (list (replace-regexp-in-string "\n" "" (shell-command-to-string (concat "find / -maxdepth 1 -name " ausdruck)))))) + (imports (or imports (py-find-imports))) + py-fontify-shell-buffer-p erg) + (cond (fast (py--fast-complete-base shell word imports)) + ((and in-string filenames) + (when (setq erg (try-completion (concat "/" word) filenames)) + (delete-region beg end) + (insert erg))) + (t (py--complete-base shell word imports exception-buffer))) + nil)) + +(defun py-fast-complete (&optional shell word imports) + "Complete word before point, if any. + +Use `py-fast-process' " + (interactive "*") + (window-configuration-to-register py--windows-config-register) + (setq py-last-window-configuration + (current-window-configuration)) + (py-shell-complete shell nil nil word 1 imports) + (py-restore-window-configuration) + ) + +(defun py-indent-or-complete () + "Complete or indent depending on the context. + +If cursor is at end of a symbol, try to complete +Otherwise call `py-indent-line' + +If `(use-region-p)' returns t, indent region. +Use `C-q TAB' to insert a literally TAB-character + +In `python-mode' `py-complete-function' is called, +in (I)Python shell-modes `py-shell-complete'" + (interactive "*") + (window-configuration-to-register py--windows-config-register) + ;; (setq py-last-window-configuration + ;; (current-window-configuration)) + (cond ((use-region-p) + (when py-debug-p (message "py-indent-or-complete: %s" "calling `use-region-p'-clause")) + (py-indent-region (region-beginning) (region-end))) + ((or (bolp) + (member (char-before) (list 9 10 12 13 32 ?: ?\) ?\] ?\})) + (not (looking-at "[ \t]*$"))) + (py-indent-line)) + ((comint-check-proc (current-buffer)) + ;; (let* ((shell (process-name (get-buffer-process (current-buffer))))) + (ignore-errors (completion-at-point))) + (t + (when py-debug-p (message "py-indent-or-complete: %s" "calling `t'-clause")) + ;; (py-fast-complete) + (completion-at-point)))) + +;; python-components-pdb + +(defun py-execute-statement-pdb () + "Execute statement running pdb." + (interactive) + (let ((py-python-command-args "-i -m pdb")) + (py-execute-statement))) + +(defun py-execute-region-pdb (beg end) + "Takes region between BEG END." + (interactive "r") + (let ((py-python-command-args "-i -m pdb")) + (py-execute-region beg end))) + +(defun py-pdb-execute-statement () + "Execute statement running pdb." + (interactive) + (let ((stm (progn (py-statement) (car kill-ring)))) + (py-execute-string (concat "import pdb;pdb.run('" stm "')")))) + +(defun py-pdb-help () + "Print generic pdb.help() message." + (interactive) + (py-execute-string "import pdb;pdb.help()")) + +;; https://stackoverflow.com/questions/6980749/simpler-way-to-put-pdb-breakpoints-in-python-code +;; breakpoint at line 3 +;; avoid inserting pdb.set_trace() + +;; python -m pdb -c "b 3" -c c your_script.py + +(defun py-pdb-break-at-current-line (&optional line) + "Set breakpoint at current line. + +Optional LINE FILE CONDITION" + (interactive "p") + (let ((line (number-to-string (or line (py-count-lines))))) + (py-execute-string (concat "import pdb;pdb.break('" line "')")))) + +(defun py--pdb-versioned () + "Guess existing pdb version from `py-shell-name'. + +Return \"pdb[VERSION]\" if executable found, just \"pdb\" otherwise" + (interactive) + (let ((erg (when (string-match "[23]" py-shell-name) + ;; versions-part + (substring py-shell-name (string-match "[23]" py-shell-name))))) + (if erg + (cond ((executable-find (concat "pdb" erg)) + (concat "pdb" erg)) + ((and (string-match "\\." erg) + (executable-find (concat "pdb" (substring erg 0 (string-match "\\." erg))))) + (concat "pdb" (substring erg 0 (string-match "\\." erg))))) + "pdb"))) + +(defun py-pdb (command-line) + "Run pdb on program FILE in buffer `*gud-FILE*'. +The directory containing FILE becomes the initial working directory +and source-file directory for your debugger. + +At GNU Linux required pdb version should be detected by `py--pdb-version' +at Windows configure `py-python-ms-pdb-command' + +lp:963253 +Argument COMMAND-LINE TBD." + (interactive + (progn + (require 'gud) + (list (gud-query-cmdline + (if (or (eq system-type 'ms-dos)(eq system-type 'windows-nt)) + (car (read-from-string py-python-ms-pdb-command)) + ;; sys.version_info[0] + ;; (car (read-from-string (py--pdb-version))) + 'pdb) + (py--buffer-filename-remote-maybe))))) + (pdb command-line)) + +(defun py--pdb-current-executable () + "When `py-pdb-executable' is set, return it. + +Otherwise return resuslt from `executable-find'" + (or py-pdb-executable + (executable-find "pdb"))) + +(defun py-update-gud-pdb-history () + "Put pdb file name at the head of `gud-pdb-history'. + +If pdb is called at a Python buffer." + (interactive) + (let* (;; PATH/TO/pdb + (first (cond ((and gud-pdb-history (ignore-errors (car gud-pdb-history))) + (replace-regexp-in-string "^\\([^ ]+\\) +.+$" "\\1" (car gud-pdb-history))) + (py-pdb-executable + py-pdb-executable) + ((or (eq system-type 'ms-dos)(eq system-type 'windows-nt)) + ;; lp:963253 + "c:/python27/python\ -i\ c:/python27/Lib/pdb.py") + (t + (py--pdb-current-executable)))) + ;; file to debug + (second (cond ((not (ignore-errors + (py--buffer-filename-remote-maybe))) + (error "%s" "Buffer must be saved first.")) + ((py--buffer-filename-remote-maybe)) + (t (and gud-pdb-history (stringp (car gud-pdb-history)) (replace-regexp-in-string "^\\([^ ]+\\) +\\(.+\\)$" "\\2" (car gud-pdb-history)))))) + (erg (and first second (concat first " " second)))) + (when erg + (push erg gud-pdb-history)))) + +(defadvice pdb (before gud-query-cmdline activate) + "Provide a better default command line when called interactively." + (interactive + (list (gud-query-cmdline py-pdb-path + ;; (file-name-nondirectory buffer-file-name) + (file-name-nondirectory (py--buffer-filename-remote-maybe)) )))) + +;; tbreak [ ([filename:]lineno | function) [, condition] ] +;; Same arguments as break, but sets a temporary breakpoint: it +;; is automatically deleted when first hit. + +;; python -m pdb -c "b 3" -c c your_script.py + +(defun py-pdb-tbreak () + "Insert a temporary break." + (interactive) + (let ( + (py-python-command-args '("-i -c \"b 30\" -c c \"eyp.py\"")) + (py-python3-command-args '("-i -c \"b 30\" -c c \"eyp.py\"")) + ) + (py-execute-buffer))) + + + +(defun py--pdbtrack-overlay-arrow (activation) + "Activate or de arrow at beginning-of-line in current buffer." + ;; This was derived/simplified from edebug-overlay-arrow + (cond (activation + (setq overlay-arrow-position (make-marker)) + (setq overlay-arrow-string "=>") + (set-marker overlay-arrow-position (line-beginning-position) (current-buffer)) + (setq py-pdbtrack-is-tracking-p t)) + (overlay-arrow-position + (setq overlay-arrow-position nil) + (setq py-pdbtrack-is-tracking-p nil)))) + +(defun py--pdbtrack-track-stack-file (text) + "Show the file indicated by the pdb stack entry line, in a separate window. + +Activity is disabled if the buffer-local variable +`py-pdbtrack-do-tracking-p' is nil. + +We depend on the pdb input prompt matching `py-pdbtrack-input-prompt' +at the beginning of the line. + +If the traceback target file path is invalid, we look for the most +recently visited python-mode buffer which either has the name of the +current function \(or class) or which defines the function \(or +class). This is to provide for remote scripts, eg, Zope's 'Script +\(Python)' - put a _copy_ of the script in a buffer named for the +script, and set to python-mode, and pdbtrack will find it.)" + ;; Instead of trying to piece things together from partial text + ;; (which can be almost useless depending on Emacs version), we + ;; monitor to the point where we have the next pdb prompt, and then + ;; check all text from comint-last-input-end to process-mark. + ;; + ;; Also, we're very conservative about clearing the overlay arrow, + ;; to minimize residue. This means, for instance, that executing + ;; other pdb commands wipe out the highlight. You can always do a + ;; 'where' (aka 'w') command to reveal the overlay arrow. + (let* ((origbuf (current-buffer)) + (currproc (get-buffer-process origbuf))) + + (if (not (and currproc py-pdbtrack-do-tracking-p)) + (py--pdbtrack-overlay-arrow nil) + + (let* ((procmark (process-mark currproc)) + (block (buffer-substring (max comint-last-input-end + (- procmark + py-pdbtrack-track-range)) + procmark)) + target target_fname target_lineno target_buffer) + + (if (not (string-match (concat py-pdbtrack-input-prompt "$") block)) + (py--pdbtrack-overlay-arrow nil) + + (setq target (py--pdbtrack-get-source-buffer block)) + + (if (stringp target) + (message "pdbtrack: %s" target) + + (setq target_lineno (car target)) + (setq target_buffer (cadr target)) + (setq target_fname + (py--buffer-filename-remote-maybe target_buffer)) + (switch-to-buffer-other-window target_buffer) + (goto-char (point-min)) + (forward-line (1- target_lineno)) + (message "pdbtrack: line %s, file %s" target_lineno target_fname) + (py--pdbtrack-overlay-arrow t) + (pop-to-buffer origbuf t))))))) + +(defun py--pdbtrack-map-filename (filename) + + (let + ((replacement-val (assoc-default + filename py-pdbtrack-filename-mapping + (lambda (mapkey path) + (string-match + (concat "^" (regexp-quote mapkey)) + path))) + )) + (if (not (eq replacement-val nil)) + (replace-match replacement-val 't 't filename) + filename))) + +(defun py--pdbtrack-get-source-buffer (block) + "Return line number and buffer of code indicated by block's traceback text. + +We look first to visit the file indicated in the trace. + +Failing that, we look for the most recently visited python-mode buffer +with the same name or having the named function. + +If we're unable find the source code we return a string describing the +problem as best as we can determine." + + (if (and (not (string-match py-pdbtrack-stack-entry-regexp block)) + ;; pydb integration still to be done + ;; (not (string-match py-pydbtrack-stack-entry-regexp block)) + ) + (prog1 + "Traceback cue not found" + (message "Block: %s" block)) + (let* ((remote-prefix (or (file-remote-p default-directory) "")) + (filename (concat remote-prefix + (match-string + py-pdbtrack-marker-regexp-file-group block))) + (lineno (string-to-number (match-string + py-pdbtrack-marker-regexp-line-group + block))) + (funcname (match-string py-pdbtrack-marker-regexp-funcname-group + block)) + funcbuffer) + + (cond ((string= filename "") + (format "(Skipping empty filename)")) + + ((file-exists-p filename) + (list lineno (find-file-noselect filename))) + + ((file-exists-p (py--pdbtrack-map-filename filename)) + (list lineno (find-file-noselect (py--pdbtrack-map-filename filename)))) + + ((setq funcbuffer (py--pdbtrack-grub-for-buffer funcname lineno)) + (if (string-match "/Script (Python)$" filename) + ;; Add in number of lines for leading '##' comments: + (setq lineno + (+ lineno + (save-excursion + (with-current-buffer funcbuffer + (count-lines + (point-min) + (max (point-min) + (string-match "^\\([^#]\\|#[^#]\\|#$\\)" + (buffer-substring (point-min) + (point-max)))))))))) + (list lineno funcbuffer)) + + ((= (elt filename 0) ?\<) + (format "(Non-file source: '%s')" filename)) + + (t (format "Not found: %s(), %s" funcname filename)))))) + +(defun py--pdbtrack-grub-for-buffer (funcname lineno) + "Find most recent buffer itself named or having function funcname. + +We walk the buffer-list history for python-mode buffers that are +named for funcname or define a function funcname." + (let ((buffers (buffer-list)) + buf + got) + (while (and buffers (not got)) + (setq buf (car buffers) + buffers (cdr buffers)) + (if (and (save-excursion + (with-current-buffer buf + (string= major-mode "python-mode"))) + (or (string-match funcname (buffer-name buf)) + (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+" + funcname "\\s-*(") + (save-excursion + (with-current-buffer buf + (buffer-substring (point-min) + (point-max))))))) + (setq got buf))) + got)) + +;; pdbtrack functions +(defun py-pdbtrack-set-tracked-buffer (file-name) + "Set the buffer for FILE-NAME as the tracked buffer. +Internally it uses the `py-pdbtrack-tracked-buffer' variable. +Returns the tracked buffer." + (let* ((file-name-prospect (concat (file-remote-p default-directory) + file-name)) + (file-buffer (get-file-buffer file-name-prospect))) + (if file-buffer + (setq py-pdbtrack-tracked-buffer file-buffer) + (cond + ((file-exists-p file-name-prospect) + (setq file-buffer (find-file-noselect file-name-prospect))) + ((and (not (equal file-name file-name-prospect)) + (file-exists-p file-name)) + ;; Fallback to a locally available copy of the file. + (setq file-buffer (find-file-noselect file-name-prospect)))) + (when (not (member file-buffer py-pdbtrack-buffers-to-kill)) + (add-to-list 'py-pdbtrack-buffers-to-kill file-buffer))) + file-buffer)) + +(defun py-pdbtrack-toggle-stack-tracking (arg) + "Set variable `py-pdbtrack-do-tracking-p'. " + (interactive "P") + ;; (if (not (get-buffer-process (current-buffer))) + ;; (error "No process associated with buffer '%s'" (current-buffer))) + + ;; missing or 0 is toggle, >0 turn on, <0 turn off + (cond ((not arg) + (setq py-pdbtrack-do-tracking-p (not py-pdbtrack-do-tracking-p))) + ((zerop (prefix-numeric-value arg)) + (setq py-pdbtrack-do-tracking-p nil)) + ((> (prefix-numeric-value arg) 0) + (setq py-pdbtrack-do-tracking-p t))) + ;; (if py-pdbtrack-do-tracking-p + ;; (progn + ;; (add-hook 'comint-output-filter-functions 'py--pdbtrack-track-stack-file t) + ;; (remove-hook 'comint-output-filter-functions 'python-pdbtrack-track-stack-file t)) + ;; (remove-hook 'comint-output-filter-functions 'py--pdbtrack-track-stack-file t) + ;; ) + (message "%sabled Python's pdbtrack" + (if py-pdbtrack-do-tracking-p "En" "Dis"))) + +(defun turn-on-pdbtrack () + (interactive) + (py-pdbtrack-toggle-stack-tracking 1)) + +(defun turn-off-pdbtrack () + (interactive) + (py-pdbtrack-toggle-stack-tracking 0)) + + + +(if pdb-track-stack-from-shell-p + (add-hook 'comint-output-filter-functions 'py--pdbtrack-track-stack-file t) + (remove-hook 'comint-output-filter-functions 'py--pdbtrack-track-stack-file t)) + + +(defun py-pdbtrack-comint-output-filter-function (output) + "Move overlay arrow to current pdb line in tracked buffer. +Argument OUTPUT is a string with the output from the comint process." + (when (and pdb-track-stack-from-shell-p (not (string= output ""))) + (let* ((full-output (ansi-color-filter-apply + (buffer-substring comint-last-input-end (point-max)))) + (line-number) + (file-name + (with-temp-buffer + (insert full-output) + ;; When the debugger encounters a pdb.set_trace() + ;; command, it prints a single stack frame. Sometimes + ;; it prints a bit of extra information about the + ;; arguments of the present function. When ipdb + ;; encounters an exception, it prints the _entire_ stack + ;; trace. To handle all of these cases, we want to find + ;; the _last_ stack frame printed in the most recent + ;; batch of output, then jump to the corresponding + ;; file/line number. + (goto-char (point-max)) + (when (re-search-backward py-pdbtrack-stacktrace-info-regexp nil t) + (setq line-number (string-to-number + (match-string-no-properties 2))) + (match-string-no-properties 1))))) + (if (and file-name line-number) + (let* ((tracked-buffer + (py-pdbtrack-set-tracked-buffer file-name)) + (shell-buffer (current-buffer)) + (tracked-buffer-window (get-buffer-window tracked-buffer)) + (tracked-buffer-line-pos)) + (with-current-buffer tracked-buffer + (set (make-local-variable 'overlay-arrow-string) "=>") + (set (make-local-variable 'overlay-arrow-position) (make-marker)) + (setq tracked-buffer-line-pos (progn + (goto-char (point-min)) + (forward-line (1- line-number)) + (point-marker))) + (when tracked-buffer-window + (set-window-point + tracked-buffer-window tracked-buffer-line-pos)) + (set-marker overlay-arrow-position tracked-buffer-line-pos)) + (pop-to-buffer tracked-buffer) + (switch-to-buffer-other-window shell-buffer)) + (when py-pdbtrack-tracked-buffer + (with-current-buffer py-pdbtrack-tracked-buffer + (set-marker overlay-arrow-position nil)) + (mapc #'(lambda (buffer) + (ignore-errors (kill-buffer buffer))) + py-pdbtrack-buffers-to-kill) + (setq py-pdbtrack-tracked-buffer nil + py-pdbtrack-buffers-to-kill nil))))) + output) + +;; python-components-pdbtrack + + +;; python-components-help + +;; Info-look functionality. +(require 'info-look) +(eval-when-compile (require 'info)) + +(defun py-info-lookup-symbol () + "Call `info-lookup-symbol'. + +Sends help if stuff is missing." + (interactive) + (if (functionp 'pydoc-info-add-help) + (call-interactively 'info-lookup-symbol) + (message "pydoc-info-add-help not found. Please check INSTALL-INFO-FILES"))) + +(info-lookup-add-help + :mode 'python-mode + :regexp "[[:alnum:]_]+" + :doc-spec +'(("(python)Index" nil ""))) + +(defun python-after-info-look () + "Set up info-look for Python. + +Tries to take account of versioned Python Info files, e.g. Debian's +python2.5-ref.info.gz. +Used with `eval-after-load'." + (let* ((version (let ((s (shell-command-to-string (concat py-python-command + " -V")))) + (string-match "^Python \\([0-9]+\\.[0-9]+\\>\\)" s) + (match-string 1 s))) + ;; Whether info files have a Python version suffix, e.g. in Debian. + (versioned + (with-temp-buffer + (Info-mode) + ;; First look for Info files corresponding to the version + ;; of the interpreter we're running. + (condition-case () + ;; Don't use `info' because it would pop-up a *info* buffer. + (progn + (Info-goto-node (format "(python%s-lib)Miscellaneous Index" + version)) + t) + (error + ;; Otherwise see if we actually have an un-versioned one. + (condition-case () + (progn + (Info-goto-node + (format "(python%s-lib)Miscellaneous Index" version)) + nil) + (error + ;; Otherwise look for any versioned Info file. + (condition-case () + (let (found) + (dolist (dir (or Info-directory-list + Info-default-directory-list)) + (unless found + (let ((file (car (file-expand-wildcards + (expand-file-name "python*-lib*" + dir))))) + (if (and file + (string-match + "\\\\)-" + file)) + (setq version (match-string 1 file) + found t))))) + found) + (error))))))))) + (info-lookup-maybe-add-help + :mode 'python-mode + :regexp "[[:alnum:]_]+" + :doc-spec + ;; Fixme: Can this reasonably be made specific to indices with + ;; different rules? Is the order of indices optimal? + ;; (Miscellaneous in -ref first prefers lookup of keywords, for + ;; instance.) + (if versioned + ;; The empty prefix just gets us highlighted terms. + `((,(concat "(python" version "-ref)Miscellaneous Index")) + (,(concat "(python" version "-ref)Module Index")) + (,(concat "(python" version "-ref)Function-Method-Variable Index")) + (,(concat "(python" version "-ref)Class-Exception-Object Index")) + (,(concat "(python" version "-lib)Module Index")) + (,(concat "(python" version "-lib)Class-Exception-Object Index")) + (,(concat "(python" version "-lib)Function-Method-Variable Index")) + (,(concat "(python" version "-lib)Miscellaneous Index"))) + '(("(python-ref)Miscellaneous Index") + ("(python-ref)Module Index") + ("(python-ref)Function-Method-Variable Index") + ("(python-ref)Class-Exception-Object Index") + ("(python-lib)Module Index") + ("(python-lib)Class-Exception-Object Index") + ("(python-lib)Function-Method-Variable Index") + ("(python-lib)Miscellaneous Index")))))) + +;; (if (featurep 'info-look) +;; (python-after-info-look)) + +;; (eval-after-load "info-look" '(python-after-info-look)) + +;; ; + +(defun py-fetch-docu () + "Lookup in current buffer for the doku for the symbol at point. + +Useful for newly defined symbol, not known to python yet." + (interactive) + (let* ((symb (prin1-to-string (symbol-at-point))) + erg) + (save-restriction + (widen) + (goto-char (point-min)) + (when (re-search-forward (concat py-def-or-class-re " *" symb) nil (quote move) 1) + (forward-line 1) + (when (looking-at "[ \t]*\"\"\"\\|[ \t]*'''\\|[ \t]*'[^]+\\|[ \t]*\"[^\"]+") + (goto-char (match-end 0)) + (setq erg (buffer-substring-no-properties (match-beginning 0) (re-search-forward "\"\"\"\\|'''" nil 'move))) + (when erg + (set-buffer (get-buffer-create "*Python-Help*")) + (erase-buffer) + ;; (when (called-interactively-p 'interactive) + ;; (switch-to-buffer (current-buffer))) + (insert erg))))))) + +(defun py-info-current-defun (&optional include-type) + "Return name of surrounding function. + +Use Python compatible dotted expression syntax +Optional argument INCLUDE-TYPE indicates to include the type of the defun. +This function is compatible to be used as +`add-log-current-defun-function' since it returns nil if point is +not inside a defun." + (interactive) + (let ((names '()) + (min-indent) + (first-run t)) + (save-restriction + (widen) + (save-excursion + (goto-char (line-end-position)) + (forward-comment -9999) + (setq min-indent (current-indentation)) + (while (py-backward-def-or-class) + (when (or (< (current-indentation) min-indent) + first-run) + (setq first-run nil) + (setq min-indent (current-indentation)) + (looking-at py-def-or-class-re) + (setq names (cons + (if (not include-type) + (match-string-no-properties 1) + (mapconcat 'identity + (split-string + (match-string-no-properties 0)) " ")) + names)))))) + (when names + (mapconcat (lambda (strg) strg) names ".")))) + +(defalias 'py-describe-symbol 'py-help-at-point) +(defun py--help-at-point-intern (sym orig) + (let* ((origfile (py--buffer-filename-remote-maybe)) + (cmd (py-find-imports)) + (oldbuf (current-buffer)) + ) + (when (not py-remove-cwd-from-path) + (setq cmd (concat cmd "import sys\n" + "sys.path.insert(0, '" + (file-name-directory origfile) "')\n"))) + ;; (setq cmd (concat cmd "pydoc.help('" sym "')\n")) + (py-execute-string (concat cmd "help('" sym "')\n") nil t nil orig nil nil nil nil nil nil oldbuf t) + (display-buffer oldbuf))) + ;; (with-help-window "Hilfe" (insert py-result)))) + +(defun py-help-at-point () + "Print help on symbol at point. + +If symbol is defined in current buffer, jump to it's definition" + (interactive) + (let* ((orig (point)) + (beg (and (use-region-p) (region-beginning))) + (end (and (use-region-p) (region-end))) + (symbol + (or (and beg end + (buffer-substring-no-properties beg end)) + ;; (thing-at-point 'symbol t) + (py-symbol-at-point)))) + (and symbol (unless (string= "" symbol) + (py--help-at-point-intern symbol orig)) + ;; (py--shell-manage-windows buffer exception-buffer split (or interactivep switch)) + ))) + +(defun py--dump-help-string (str) + (with-output-to-temp-buffer "*Help*" + (let ((locals (buffer-local-variables)) + funckind funcname func funcdoc + (start 0) mstart end + keys) + (while (string-match "^%\\([vc]\\):\\(.+\\)\n" str start) + (setq mstart (match-beginning 0) end (match-end 0) + funckind (substring str (match-beginning 1) (match-end 1)) + funcname (substring str (match-beginning 2) (match-end 2)) + func (intern funcname)) + (princ (substitute-command-keys (substring str start mstart))) + (cond + ((equal funckind "c") ; command + (setq funcdoc (documentation func) + keys (concat + "Key(s): " + (mapconcat 'key-description + (where-is-internal func python-mode-map) + ", ")))) + ((equal funckind "v") ; variable + (setq funcdoc (documentation-property func 'variable-documentation) + keys (if (assq func locals) + (concat + "Local/Global values: " + (prin1-to-string (symbol-value func)) + " / " + (prin1-to-string (default-value func))) + (concat + "Value: " + (prin1-to-string (symbol-value func)))))) + (t ; unexpected + (error "Error in py--dump-help-string, tag %s" funckind))) + (princ (format "\n-> %s:\t%s\t%s\n\n" + (if (equal funckind "c") "Command" "Variable") + funcname keys)) + (princ funcdoc) + (terpri) + (setq start end)) + (princ (substitute-command-keys (substring str start))) + ;; (and comint-vars-p (py-report-comint-variable-setting)) + ) + (if (featurep 'xemacs) (print-help-return-message) + (help-print-return-message)))) + +(defun py-describe-mode () + "Dump long form of `python-mode' docs." + (interactive) + (py--dump-help-string "Major mode for editing Python files. +Knows about Python indentation, tokens, comments and continuation lines. +Paragraphs are separated by blank lines only. + +Major sections below begin with the string `@'; specific function and +variable docs begin with ->. + +@EXECUTING PYTHON CODE + +\\[py-execute-import-or-reload]\timports or reloads the file in the Python interpreter +\\[py-execute-buffer]\tsends the entire buffer to the Python interpreter +\\[py-execute-region]\tsends the current region +\\[py-execute-def-or-class]\tsends the current function or class definition +\\[py-execute-string]\tsends an arbitrary string +\\[py-shell]\tstarts a Python interpreter window; this will be used by +\tsubsequent Python execution commands +%c:py-execute-import-or-reload +%c:py-execute-buffer +%c:py-execute-region +%c:py-execute-def-or-class +%c:py-execute-string +%c:py-shell + +@VARIABLES + +py-install-directory\twherefrom `python-mode' looks for extensions +py-indent-offset\tindentation increment +py-block-comment-prefix\tcomment string used by comment-region + +py-shell-name\tshell command to invoke Python interpreter +py-temp-directory\tdirectory used for temp files (if needed) + +py-beep-if-tab-change\tring the bell if tab-width is changed +%v:py-install-directory +%v:py-indent-offset +%v:py-block-comment-prefix +%v:py-shell-name +%v:py-temp-directory +%v:py-beep-if-tab-change + +@KINDS OF LINES + +Each physical line in the file is either a `continuation line' (the +preceding line ends with a backslash that's not part of a comment, or +the paren/bracket/brace nesting level at the start of the line is +non-zero, or both) or an `initial line' (everything else). + +An initial line is in turn a `blank line' (contains nothing except +possibly blanks or tabs), a `comment line' (leftmost non-blank +character is `#’), or a ‘code line' (everything else). + +Comment Lines + +Although all comment lines are treated alike by Python, Python mode +recognizes two kinds that act differently with respect to indentation. + +An `indenting comment line' is a comment line with a blank, tab or +nothing after the initial `#'. The indentation commands (see below) +treat these exactly as if they were code lines: a line following an +indenting comment line will be indented like the comment line. All +other comment lines (those with a non-whitespace character immediately +following the initial `#’) are ‘non-indenting comment lines', and +their indentation is ignored by the indentation commands. + +Indenting comment lines are by far the usual case, and should be used +whenever possible. Non-indenting comment lines are useful in cases +like these: + +\ta = b # a very wordy single-line comment that ends up being +\t #... continued onto another line + +\tif a == b: +##\t\tprint 'panic!' # old code we've `commented out' +\t\treturn a + +Since the `#...’ and ‘##' comment lines have a non-whitespace +character following the initial `#', Python mode ignores them when +computing the proper indentation for the next line. + +Continuation Lines and Statements + +The `python-mode' commands generally work on statements instead of on +individual lines, where a `statement' is a comment or blank line, or a +code line and all of its following continuation lines (if any) +considered as a single logical unit. The commands in this mode +generally (when it makes sense) automatically move to the start of the +statement containing point, even if point happens to be in the middle +of some continuation line. + +@INDENTATION + +Primarily for entering new code: +\t\\[indent-for-tab-command]\t indent line appropriately +\t\\[py-newline-and-indent]\t insert newline, then indent +\t\\[py-electric-backspace]\t reduce indentation, or delete single character + +Primarily for reindenting existing code: +\t\\[py-guess-indent-offset]\t guess py-indent-offset from file content; change locally +\t\\[universal-argument] \\[py-guess-indent-offset]\t ditto, but change globally + +\t\\[py-indent-region]\t reindent region to match its context +\t\\[py-shift-left]\t shift line or region left by py-indent-offset +\t\\[py-shift-right]\t shift line or region right by py-indent-offset + +Unlike most programming languages, Python uses indentation, and only +indentation, to specify block structure. Hence the indentation supplied +automatically by `python-mode' is just an educated guess: only you know +the block structure you intend, so only you can supply correct +indentation. + +The \\[indent-for-tab-command] and \\[py-newline-and-indent] keys try to suggest plausible indentation, based on +the indentation of preceding statements. E.g., assuming +py-indent-offset is 4, after you enter +\tif a > 0: \\[py-newline-and-indent] +the cursor will be moved to the position of the `_' (_ is not a +character in the file, it's just used here to indicate the location of +the cursor): +\tif a > 0: +\t _ +If you then enter `c = d' \\[py-newline-and-indent], the cursor will move +to +\tif a > 0: +\t c = d +\t _ +`python-mode' cannot know whether that's what you intended, or whether +\tif a > 0: +\t c = d +\t_ +was your intent. In general, `python-mode' either reproduces the +indentation of the (closest code or indenting-comment) preceding +statement, or adds an extra py-indent-offset blanks if the preceding +statement has `:' as its last significant (non-whitespace and non- +comment) character. If the suggested indentation is too much, use +\\[py-electric-backspace] to reduce it. + +Continuation lines are given extra indentation. If you don't like the +suggested indentation, change it to something you do like, and Python- +mode will strive to indent later lines of the statement in the same way. + +If a line is a continuation line by virtue of being in an unclosed +paren/bracket/brace structure (`list', for short), the suggested +indentation depends on whether the current line contains the first item +in the list. If it does, it's indented py-indent-offset columns beyond +the indentation of the line containing the open bracket. If you don't +like that, change it by hand. The remaining items in the list will mimic +whatever indentation you give to the first item. + +If a line is a continuation line because the line preceding it ends with +a backslash, the third and following lines of the statement inherit their +indentation from the line preceding them. The indentation of the second +line in the statement depends on the form of the first (base) line: if +the base line is an assignment statement with anything more interesting +than the backslash following the leftmost assigning `=', the second line +is indented two columns beyond that `='. Else it's indented to two +columns beyond the leftmost solid chunk of non-whitespace characters on +the base line. + +Warning: indent-region should not normally be used! It calls \\[indent-for-tab-command] +repeatedly, and as explained above, \\[indent-for-tab-command] can't guess the block +structure you intend. +%c:indent-for-tab-command +%c:py-newline-and-indent +%c:py-electric-backspace + +The next function may be handy when editing code you didn't write: +%c:py-guess-indent-offset + +The remaining `indent' functions apply to a region of Python code. They +assume the block structure (equals indentation, in Python) of the region +is correct, and alter the indentation in various ways while preserving +the block structure: +%c:py-indent-region +%c:py-shift-left +%c:py-shift-right + +@MARKING & MANIPULATING REGIONS OF CODE + +\\[py-mark-block]\t mark block of lines +\\[py-mark-def-or-class]\t mark smallest enclosing def +\\[universal-argument] \\[py-mark-def-or-class]\t mark smallest enclosing class +\\[comment-region]\t comment out region of code +\\[universal-argument] \\[comment-region]\t uncomment region of code +%c:py-mark-block +%c:py-mark-def-or-class +%c:comment-region + +@MOVING POINT + +\\[py-previous-statement]\t move to statement preceding point +\\[py-next-statement]\t move to statement following point +\\[py-goto-block-up]\t move up to start of current block +\\[py-backward-def-or-class]\t move to start of def +\\[universal-argument] \\[py-backward-def-or-class]\t move to start of class +\\[py-forward-def-or-class]\t move to end of def +\\[universal-argument] \\[py-forward-def-or-class]\t move to end of class + +The first two move to one statement beyond the statement that contains +point. A numeric prefix argument tells them to move that many +statements instead. Blank lines, comment lines, and continuation lines +do not count as `statements' for these commands. So, e.g., you can go +to the first code statement in a file by entering +\t\\[beginning-of-buffer]\t to move to the top of the file +\t\\[py-next-statement]\t to skip over initial comments and blank lines +Or do \\[py-previous-statement] with a huge prefix argument. +%c:py-previous-statement +%c:py-next-statement +%c:py-goto-block-up +%c:py-backward-def-or-class +%c:py-forward-def-or-class + +@LITTLE-KNOWN EMACS COMMANDS PARTICULARLY USEFUL IN PYTHON MODE + +\\[indent-new-comment-line] is handy for entering a multi-line comment. + +\\[set-selective-display] with a `small' prefix arg is ideally suited for viewing the +overall class and def structure of a module. + +`\\[back-to-indentation]' moves point to a line's first non-blank character. + +`\\[indent-relative]' is handy for creating odd indentation. + +@OTHER EMACS HINTS + +If you don't like the default value of a variable, change its value to +whatever you do like by putting a `setq' line in your .emacs file. +E.g., to set the indentation increment to 4, put this line in your +.emacs: +\t(setq py-indent-offset 4) +To see the value of a variable, do `\\[describe-variable]' and enter the variable +name at the prompt. + +When entering a key sequence like `C-c C-n', it is not necessary to +release the CONTROL key after doing the `C-c' part -- it suffices to +press the CONTROL key, press and release `c' (while still holding down +CONTROL), press and release `n' (while still holding down CONTROL), & +then release CONTROL. + +Entering Python mode calls with no arguments the value of the variable +`python-mode-hook', if that value exists and is not nil; for backward +compatibility it also tries `py-mode-hook'; see the ‘Hooks' section of +the Elisp manual for details. + +Obscure: When python-mode is first loaded, it looks for all bindings +to newline-and-indent in the global keymap, and shadows them with +local bindings to py-newline-and-indent.")) + +;; (require 'info-look) +;; The info-look package does not always provide this function (it +;; appears this is the case with XEmacs 21.1) +(when (fboundp 'info-lookup-maybe-add-help) + (info-lookup-maybe-add-help + :mode 'python-mode + :regexp "[a-zA-Z0-9_]+" + :doc-spec '(("(python-lib)Module Index") + ("(python-lib)Class-Exception-Object Index") + ("(python-lib)Function-Method-Variable Index") + ("(python-lib)Miscellaneous Index")))) + +(defun py--find-definition-in-source (sourcefile symbol) + (called-interactively-p 'any) (message "sourcefile: %s" sourcefile) + (when (find-file sourcefile) + (goto-char (point-min)) + (when + (or (re-search-forward (concat py-def-or-class-re symbol) nil t 1) + (progn + ;; maybe a variable definition? + (goto-char (point-min)) + (re-search-forward (concat "^.+ " symbol) nil t 1))) + (push-mark) + (goto-char (match-beginning 0)) + (exchange-point-and-mark)))) + +;; Find function stuff, lifted from python.el +(defalias 'py-find-function 'py-find-definition) +(defun py--find-definition-question-type (symbol imports) + (let (erg) + (cond ((setq erg (py-execute-string (concat "import inspect;inspect.isbuiltin(\"" symbol "\")")))) + (t (setq erg (py-execute-string (concat imports "import inspect;inspect.getmodule(\"" symbol "\")"))))) + erg)) + +(defun py-find-definition (&optional symbol) + "Find source of definition of SYMBOL. + +Interactively, prompt for SYMBOL." + (interactive) + ;; (set-register 98888888 (list (current-window-configuration) (point-marker))) + (let* (;; end + ;; (last-window-configuration + ;; (current-window-configuration)) + (orig (point)) + ;; (exception-buffer (current-buffer)) + (imports (py-find-imports)) + (symbol-raw (or symbol (with-syntax-table py-dotted-expression-syntax-table + (current-word)))) + ;; (enable-recursive-minibuffers t) + (symbol (if (called-interactively-p 'interactive) + (read-string (format "Find location of (default %s): " symbol-raw) + symbol-raw nil symbol-raw) + symbol-raw)) + (local (progn (goto-char (point-min)) (re-search-forward (concat "^[ \t]*" "\\(def\\|class\\)" "[ \t]" symbol) orig t)))) + ;; ismethod(), isclass(), isfunction() or isbuiltin() + ;; ismethod isclass isfunction isbuiltin) + (if local + (progn + (goto-char orig) + (split-window-vertically) + (other-buffer) + (goto-char local) + (beginning-of-line) + (push-mark) + (message "%s" (current-buffer)) + (exchange-point-and-mark)) + (with-help-window (help-buffer) + (princ (py--find-definition-question-type symbol imports)))))) + +(defun py-update-imports () + "Return imports. + +Imports done are displayed in message buffer." + (interactive) + (save-excursion + (let ((orig (point)) + (erg (py-find-imports))) + (goto-char orig) + erg))) + +;; Code-Checker +;; pep8 +(defalias 'pep8 'py-pep8-run) +(defun py-pep8-run (command) + "*Run pep8 using COMMAND, check formatting. +Default on the file currently visited." + (interactive + (let ((default + (if (py--buffer-filename-remote-maybe) + (format "%s %s %s" py-pep8-command + (mapconcat 'identity py-pep8-command-args " ") + (py--buffer-filename-remote-maybe)) + (format "%s %s" py-pep8-command + (mapconcat 'identity py-pep8-command-args " ")))) + (last (when py-pep8-history + (let* ((lastcmd (car py-pep8-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (py--buffer-filename-remote-maybe) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pep8 like this: " + (if last + last + default) + 'py-pep8-history) + (read-string "Run pep8 like this: " + (if last + last + default) + 'py-pep8-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defun py-pep8-help () + "Display pep8 command line help messages." + (interactive) + (set-buffer (get-buffer-create "*pep8-Help*")) + (erase-buffer) + (shell-command "pep8 --help" "*pep8-Help*")) + +;; Pylint +(defalias 'pylint 'py-pylint-run) +(defun py-pylint-run (command) + "Run pylint from COMMAND. + +Default on the file currently visited. + +For help see \\[pylint-help] resp. \\[pylint-long-help]. +Home-page: http://www.logilab.org/project/pylint" + (interactive + (let ((default (format "%s %s %s" py-pylint-command + (mapconcat 'identity py-pylint-command-args " ") + (py--buffer-filename-remote-maybe))) + (last (and py-pylint-history (car py-pylint-history)))) + (list (funcall (if (fboundp 'read-shell-command) + 'read-shell-command 'read-string) + "Run pylint like this: " + (or default last) + 'py-pylint-history)))) + (save-some-buffers (not py-ask-about-save)) + (set-buffer (get-buffer-create "*Pylint*")) + (erase-buffer) + (unless (file-readable-p (car (cddr (split-string command)))) + (message "Warning: %s" "pylint needs a file")) + (shell-command command "*Pylint*")) + +(defalias 'pylint-help 'py-pylint-help) +(defun py-pylint-help () + "Display Pylint command line help messages. + +Let's have this until more Emacs-like help is prepared" + (interactive) + (set-buffer (get-buffer-create "*Pylint-Help*")) + (erase-buffer) + (shell-command "pylint --long-help" "*Pylint-Help*")) + +(defalias 'pylint-doku 'py-pylint-doku) +(defun py-pylint-doku () + "Display Pylint Documentation. + +Calls `pylint --full-documentation'" + (interactive) + (set-buffer (get-buffer-create "*Pylint-Documentation*")) + (erase-buffer) + (shell-command "pylint --full-documentation" "*Pylint-Documentation*")) + +;; Pyflakes +(defalias 'pyflakes 'py-pyflakes-run) +(defun py-pyflakes-run (command) + "*Run pyflakes on COMMAND. + +Default on the file currently visited. + +For help see \\[pyflakes-help] resp. \\[pyflakes-long-help]. +Home-page: http://www.logilab.org/project/pyflakes" + (interactive + (let ((default + (if (py--buffer-filename-remote-maybe) + (format "%s %s %s" py-pyflakes-command + (mapconcat 'identity py-pyflakes-command-args " ") + (py--buffer-filename-remote-maybe)) + (format "%s %s" py-pyflakes-command + (mapconcat 'identity py-pyflakes-command-args " ")))) + (last (when py-pyflakes-history + (let* ((lastcmd (car py-pyflakes-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (py--buffer-filename-remote-maybe) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pyflakes like this: " + (if last + last + default) + 'py-pyflakes-history) + (read-string "Run pyflakes like this: " + (if last + last + default) + 'py-pyflakes-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defalias 'pyflakes-help 'py-pyflakes-help) +(defun py-pyflakes-help () + "Display Pyflakes command line help messages." + (interactive) + ;; (set-buffer (get-buffer-create "*Pyflakes-Help*")) + ;; (erase-buffer) + (with-help-window "*Pyflakes-Help*" + (with-current-buffer standard-output + (insert " pyflakes [file-or-directory ...] + + Pyflakes is a simple program which checks Python + source files for errors. It is similar to + PyChecker in scope, but differs in that it does + not execute the modules to check them. This is + both safer and faster, although it does not + perform as many checks. Unlike PyLint, Pyflakes + checks only for logical errors in programs; it + does not perform any checks on style. + + All commandline arguments are checked, which + have to be either regular files or directories. + If a directory is given, every .py file within + will be checked. + + When no commandline arguments are given, data + will be read from standard input. + + The exit status is 0 when no warnings or errors + are found. When errors are found the exit status + is 2. When warnings (but no errors) are found + the exit status is 1. + +Extracted from http://manpages.ubuntu.com/manpages/natty/man1/pyflakes.1.html")))) + +;; Pyflakes-pep8 +(defalias 'pyflakespep8 'py-pyflakespep8-run) +(defun py-pyflakespep8-run (command) + "*Run COMMAND pyflakespep8, check formatting. + +Default on the file currently visited." + (interactive + (let ((default + (if (py--buffer-filename-remote-maybe) + (format "%s %s %s" py-pyflakespep8-command + (mapconcat 'identity py-pyflakespep8-command-args " ") + (py--buffer-filename-remote-maybe)) + (format "%s %s" py-pyflakespep8-command + (mapconcat 'identity py-pyflakespep8-command-args " ")))) + (last (when py-pyflakespep8-history + (let* ((lastcmd (car py-pyflakespep8-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (py--buffer-filename-remote-maybe) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pyflakespep8 like this: " + (if last + last + default) + 'py-pyflakespep8-history) + (read-string "Run pyflakespep8 like this: " + (if last + last + default) + 'py-pyflakespep8-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defun py-pyflakespep8-help () + "Display pyflakespep8 command line help messages." + (interactive) + (set-buffer (get-buffer-create "*pyflakespep8-Help*")) + (erase-buffer) + (shell-command "pyflakespep8 --help" "*pyflakespep8-Help*")) + +;; Pychecker +;; hack for GNU Emacs +;; (unless (fboundp 'read-shell-command) +;; (defalias 'read-shell-command 'read-string)) + +(defun py-pychecker-run (command) + "Run COMMAND pychecker (default on the file currently visited)." + (interactive + (let ((default + (if (py--buffer-filename-remote-maybe) + (format "%s %s %s" py-pychecker-command + py-pychecker-command-args + (py--buffer-filename-remote-maybe)) + (format "%s %s" py-pychecker-command py-pychecker-command-args))) + (last (when py-pychecker-history + (let* ((lastcmd (car py-pychecker-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (py--buffer-filename-remote-maybe) cmd)))) + (mapconcat 'identity newcmd " "))))) + + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run pychecker like this: " + (if last + last + default) + 'py-pychecker-history) + (read-string "Run pychecker like this: " + (if last + last + default) + 'py-pychecker-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +;; After `sgml-validate-command'. +(defun py-check-command (command) + "Check a Python file (default current buffer's file). +Runs COMMAND, a shell command, as if by `compile'. +See `py-check-command' for the default." + (interactive + (list (read-string "Checker command: " + (concat py-check-command " " + (let ((name (py--buffer-filename-remote-maybe))) + (if name + (file-name-nondirectory name))))))) + (require 'compile) ;To define compilation-* variables. + (save-some-buffers (not compilation-ask-about-save) nil) + (let ((compilation-error-regexp-alist py-compilation-regexp-alist) + ;; (cons '("(\\([^,]+\\), line \\([0-9]+\\))" 1) + ;; compilation-error-regexp-alist) + ) + (compilation-start command))) + +;; flake8 +(defalias 'flake8 'py-flake8-run) +(defun py-flake8-run (command) + "COMMAND Flake8 is a wrapper around these tools: +- PyFlakes + - pep8 + - Ned Batchelder's McCabe script + + It also adds features: + - files that contain this line are skipped:: + # flake8: noqa + - no-warn lines that contain a `# noqa`` comment at the end. + - a Git and a Mercurial hook. + - a McCabe complexity checker. + - extendable through ``flake8.extension`` entry points." + (interactive + (let* ((py-flake8-command + (if (string= "" py-flake8-command) + (or (executable-find "flake8") + (error "Don't see \"flake8\" on your system. +Consider \"pip install flake8\" resp. visit \"pypi.python.org\"")) + py-flake8-command)) + (default + (if (py--buffer-filename-remote-maybe) + (format "%s %s %s" py-flake8-command + py-flake8-command-args + (py--buffer-filename-remote-maybe)) + (format "%s %s" py-flake8-command + py-flake8-command-args))) + (last + (when py-flake8-history + (let* ((lastcmd (car py-flake8-history)) + (cmd (cdr (reverse (split-string lastcmd)))) + (newcmd (reverse (cons (py--buffer-filename-remote-maybe) cmd)))) + (mapconcat 'identity newcmd " "))))) + (list + (if (fboundp 'read-shell-command) + (read-shell-command "Run flake8 like this: " + ;; (if last + ;; last + default + 'py-flake8-history1) + (read-string "Run flake8 like this: " + (if last + last + default) + 'py-flake8-history))))) + (save-some-buffers (not py-ask-about-save) nil) + (if (fboundp 'compilation-start) + ;; Emacs. + (compilation-start command) + ;; XEmacs. + (when (featurep 'xemacs) + (compile-internal command "No more errors")))) + +(defun py-flake8-help () + "Display flake8 command line help messages." + (interactive) + (set-buffer (get-buffer-create "*flake8-Help*")) + (erase-buffer) + (shell-command "flake8 --help" "*flake8-Help*")) + +;; from string-strip.el --- Strip CHARS from STRING + +(defun py-nesting-level (&optional pps) + "Accepts the output of `parse-partial-sexp' - PPS." + (interactive) + (let* ((pps (or (ignore-errors (nth 0 pps)) + (if (featurep 'xemacs) + (parse-partial-sexp (point-min) (point)) + (parse-partial-sexp (point-min) (point))))) + (erg (nth 0 pps))) + (when (and py-verbose-p (called-interactively-p 'any)) (message "%s" erg)) + erg)) + +;; Flymake +(defun py-toggle-flymake-intern (name command) + "Clear flymake allowed file-name masks. + +Takes NAME COMMAND" + (unless (string-match "pyflakespep8" name) + (unless (executable-find name) + (when py-verbose-p (message "Don't see %s. Use `easy_install' %s? " name name)))) + (if (py--buffer-filename-remote-maybe) + (let* ((temp-file (if (functionp 'flymake-proc-init-create-temp-buffer-copy) + (flymake-proc-init-create-temp-buffer-copy 'flymake-create-temp-inplace) + (flymake-proc-init-create-temp-buffer-copy 'flymake-create-temp-inplace) + )) + (local-file (file-relative-name + temp-file + (file-name-directory (py--buffer-filename-remote-maybe))))) + (if (boundp 'flymake-proc-allowed-file-name-masks) + (push (car (read-from-string (concat "(\"\\.py\\'\" flymake-" name ")"))) flymake-proc-allowed-file-name-masks) + (push (car (read-from-string (concat "(\"\\.py\\'\" flymake-" name ")"))) flymake-proc-allowed-file-name-masks)) + (list command (list local-file))) + (message "%s" "flymake needs a `file-name'. Please save before calling."))) + +(defun pylint-flymake-mode () + "Toggle `pylint' `flymake-mode'." + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode 0) + (py-toggle-flymake-intern "pylint" "pylint") + (flymake-mode 1))) + +(defun pyflakes-flymake-mode () + "Toggle `pyflakes' `flymake-mode'." + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pyflakes" "pyflakes") + (flymake-mode))) + +(defun pychecker-flymake-mode () + "Toggle `pychecker' `flymake-mode'." + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pychecker" "pychecker") + (flymake-mode))) + +(defun pep8-flymake-mode () + "Toggle `pep8’ `flymake-mode'." + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pep8" "pep8") + (flymake-mode))) + +(defun pyflakespep8-flymake-mode () + "Toggle `pyflakespep8’ `flymake-mode'. + +Joint call to pyflakes and pep8 as proposed by +Keegan Carruthers-Smith" + (interactive) + (if flymake-mode + ;; switch off + (flymake-mode) + (py-toggle-flymake-intern "pyflakespep8" "pyflakespep8") + (flymake-mode))) + +(defun py-display-state-of-variables () + "Read the state of `python-mode' variables. + +Assumes vars are defined in current source buffer" + (interactive) + (save-restriction + (let (variableslist) + (goto-char (point-min)) + ;; (eval-buffer) + (while (and (not (eobp))(re-search-forward "^(defvar [[:alpha:]]\\|^(defcustom [[:alpha:]]\\|^(defconst [[:alpha:]]" nil t 1)) + (let* ((name (symbol-at-point)) + (state + (unless + (or (eq name 'py-menu) + (eq name 'python-mode-map) + (string-match "syntax-table" (prin1-to-string name))) + + (prin1-to-string (symbol-value name))))) + (if state + (push (cons (prin1-to-string name) state) variableslist) + (message "don't see a state for %s" (prin1-to-string name)))) + (forward-line 1)) + (setq variableslist (nreverse variableslist)) + (set-buffer (get-buffer-create "State-of-Python-mode-variables.org")) + (erase-buffer) + ;; org + (insert "State of python-mode variables\n\n") + (switch-to-buffer (current-buffer)) + (dolist (ele variableslist) + (if (string-match "^;;; " (car ele)) + (unless (or (string-match "^;;; Constants\\|^;;; Commentary\\|^;;; Code\\|^;;; Macro definitions\\|^;;; Customization" (car ele))) + + (insert (concat (replace-regexp-in-string "^;;; " "* " (car ele)) "\n"))) + (insert (concat "\n** "(car ele) "\n")) + (insert (concat " " (cdr ele) "\n\n"))) + ;; (richten) + (sit-for 0.01 t)) + (sit-for 0.01 t)))) + +;; common typo +(defalias 'iypthon 'ipython) +(defalias 'pyhton 'python) + +;; python-components-extensions + +(defun py-indent-forward-line (&optional arg) + "Indent and move line forward to next indentation. +Returns column of line reached. + +If `py-kill-empty-line' is non-nil, delete an empty line. + +With \\[universal argument] just indent. +" + (interactive "*P") + (let ((orig (point)) + erg) + (unless (eobp) + (if (and (py--in-comment-p)(not py-indent-comments)) + (forward-line 1) + (py-indent-line-outmost) + (unless (eq 4 (prefix-numeric-value arg)) + (if (eobp) (newline) + (progn (forward-line 1)) + (when (and py-kill-empty-line (py-empty-line-p) (not (looking-at "[ \t]*\n[[:alpha:]]")) (not (eobp))) + (delete-region (line-beginning-position) (line-end-position))))))) + (back-to-indentation) + (when (or (eq 4 (prefix-numeric-value arg)) (< orig (point))) (setq erg (current-column))) + erg)) + +(defun py-dedent-forward-line (&optional arg) + "Dedent line and move one line forward. " + (interactive "*p") + (py-dedent arg) + (if (eobp) + (newline 1) + (forward-line 1)) + (end-of-line)) + +(defun py-dedent (&optional arg) + "Dedent line according to `py-indent-offset'. + +With arg, do it that many times. +If point is between indent levels, dedent to next level. +Return indentation reached, if dedent done, nil otherwise. + +Affected by `py-dedent-keep-relative-column'. " + (interactive "*p") + (or arg (setq arg 1)) + (let ((orig (copy-marker (point))) + erg) + (dotimes (_ arg) + (let* ((cui (current-indentation)) + (remain (% cui py-indent-offset)) + (indent (* py-indent-offset (/ cui py-indent-offset)))) + (beginning-of-line) + (fixup-whitespace) + (if (< 0 remain) + (indent-to-column indent) + (indent-to-column (- cui py-indent-offset))))) + (when (< (point) orig) + (setq erg (current-column))) + (when py-dedent-keep-relative-column (goto-char orig)) + erg)) + +(defun py-class-at-point () + "Return class definition as string. " + (interactive) + (save-excursion + (let* ((beg (py-backward-class)) + (end (py-forward-class)) + (res (when (and (numberp beg)(numberp end)(< beg end)) (buffer-substring-no-properties beg end)))) + res))) + +(defun py-backward-function () + "Jump to the beginning of defun. + +Returns position. " + (interactive "p") + (py-backward-def-or-class)) + +(defun py-forward-function () + "Jump to the end of function. + +Returns position." + (interactive "p") + (py-forward-def-or-class)) + +(defun py-function-at-point () + "Return functions definition as string. " + (interactive) + (save-excursion + (let* ((beg (py-backward-function)) + (end (py-forward-function))) + (when (and (numberp beg)(numberp end)(< beg end)) (buffer-substring-no-properties beg end))))) + +;; Functions for marking regions + +(defun py-line-at-point () + "Return line as string. " + (interactive) + (let* ((beg (line-beginning-position)) + (end (line-end-position))) + (when (and (numberp beg)(numberp end)(< beg end)) (buffer-substring-no-properties beg end)))) + +(defun py-match-paren-mode (&optional arg) + "py-match-paren-mode nil oder t" + (interactive "P") + (if (or arg (not py-match-paren-mode)) + (progn + (setq py-match-paren-mode t) + (setq py-match-paren-mode nil)))) + +(defun py--match-end-finish (cui) + (let (skipped) + (unless (eq (current-column) cui) + (when (< (current-column) cui) + (setq skipped (skip-chars-forward " \t" (line-end-position))) + (setq cui (- cui skipped)) + ;; may current-column greater as needed indent? + (if (< 0 cui) + (progn + (unless (py-empty-line-p) (split-line)) + (indent-to cui)) + (forward-char cui)) + (unless (eq (char-before) 32)(insert 32)(forward-char -1)))))) + +(defun py--match-paren-forward () + (setq py--match-paren-forward-p t) + (let ((cui (current-indentation))) + (cond + ((py--beginning-of-top-level-p) + (py-forward-top-level-bol) + (py--match-end-finish cui)) + ((py--beginning-of-class-p) + (py-forward-class-bol) + (py--match-end-finish cui)) + ((py--beginning-of-def-p) + (py-forward-def-bol) + (py--match-end-finish cui)) + ((py--beginning-of-if-block-p) + (py-forward-if-block-bol) + (py--match-end-finish cui)) + ((py--beginning-of-try-block-p) + (py-forward-try-block-bol) + (py--match-end-finish cui)) + ((py--beginning-of-for-block-p) + (py-forward-for-block-bol) + (py--match-end-finish cui)) + ((py--beginning-of-block-p) + (py-forward-block-bol) + (py--match-end-finish cui)) + ((py--beginning-of-clause-p) + (py-forward-clause-bol) + (py--match-end-finish cui)) + ((py--beginning-of-statement-p) + (py-forward-statement-bol) + (py--match-end-finish cui)) + (t (py-forward-statement) + (py--match-end-finish cui))))) + +(defun py--match-paren-backward () + (setq py--match-paren-forward-p nil) + (let* ((cui (current-indentation)) + (cuc (current-column)) + (cui (min cuc cui))) + (if (eq 0 cui) + (py-backward-top-level) + (when (py-empty-line-p) (delete-region (line-beginning-position) (point))) + (py-backward-statement) + (unless (< (current-column) cuc) + (while (and (not (bobp)) + (< cui (current-column)) + (py-backward-statement))))))) + +(defun py--match-paren-blocks () + (cond + ((and (looking-back "^[ \t]*" (line-beginning-position))(if (eq last-command 'py-match-paren)(not py--match-paren-forward-p)t) + ;; (looking-at py-extended-block-or-clause-re) + (looking-at "[[:alpha:]_]")) + ;; from beginning of top-level, block, clause, statement + (py--match-paren-forward)) + (t + (py--match-paren-backward)))) + +(defun py-match-paren (&optional arg) + "If at a beginning, jump to end and vice versa. + +When called from within, go to the start. +Matches lists, but also block, statement, string and comment. " + (interactive "*P") + (if (eq 4 (prefix-numeric-value arg)) + (insert "%") + (let ((pps (parse-partial-sexp (point-min) (point)))) + (cond + ;; if inside string, go to beginning + ((nth 3 pps) + (goto-char (nth 8 pps))) + ;; if inside comment, go to beginning + ((nth 4 pps) + (py-backward-comment)) + ;; at comment start, go to end of commented section + ((and + ;; unless comment starts where jumped to some end + (not py--match-paren-forward-p) + (eq 11 (car-safe (syntax-after (point))))) + (py-forward-comment)) + ;; at string start, go to end + ((or (eq 15 (car-safe (syntax-after (point)))) + (eq 7 (car (syntax-after (point))))) + (goto-char (scan-sexps (point) 1)) + (forward-char -1)) + ;; open paren + ((eq 4 (car (syntax-after (point)))) + (goto-char (scan-sexps (point) 1)) + (forward-char -1)) + ((eq 5 (car (syntax-after (point)))) + (goto-char (scan-sexps (1+ (point)) -1))) + ((nth 1 pps) + (goto-char (nth 1 pps))) + (t + ;; Python specific blocks + (py--match-paren-blocks)))))) + +(unless (functionp 'in-string-p) + (defun in-string-p (&optional pos) + (interactive) + (let ((orig (or pos (point)))) + (save-excursion + (save-restriction + (widen) + (beginning-of-defun) + (numberp + (progn + (if (featurep 'xemacs) + (nth 3 (parse-partial-sexp (point) orig) + (nth 3 (parse-partial-sexp (point-min) (point)))))))))))) + +(defun py-documentation (w) + "Launch PyDOC on the Word at Point" + (interactive + (list (let* ((word (py-symbol-at-point)) + (input (read-string + (format "pydoc entry%s: " + (if (not word) "" (format " (default %s)" word)))))) + (if (string= input "") + (if (not word) (error "No pydoc args given") + word) ;sinon word + input)))) ;sinon input + (shell-command (concat py-shell-name " -c \"from pydoc import help;help(\'" w "\')\"") "*PYDOCS*") + (view-buffer-other-window "*PYDOCS*" t 'kill-buffer-and-window)) + +(defun pst-here () + "Kill previous \"pdb.set_trace()\" and insert it at point. " + (interactive "*") + (let ((orig (copy-marker (point)))) + (search-backward "pdb.set_trace()") + (replace-match "") + (when (py-empty-line-p) + (delete-region (line-beginning-position) (line-end-position))) + (goto-char orig) + (insert "pdb.set_trace()"))) + +(defun py-printform-insert (&optional arg strg) + "Inserts a print statement from `(car kill-ring)'. + +With optional \\[universal-argument] print as string" + (interactive "*P") + (let* ((name (py--string-strip (or strg (car kill-ring)))) + ;; guess if doublequotes or parentheses are needed + (numbered (not (eq 4 (prefix-numeric-value arg)))) + (form (if numbered + (concat "print(\"" name ": %s \" % (" name "))") + (concat "print(\"" name ": %s \" % \"" name "\")")))) + (insert form))) + +(defun py-print-formatform-insert (&optional strg) + "Inserts a print statement out of current `(car kill-ring)' by default. + +print(\"\\nfoo: {}\"\.format(foo))" + (interactive "*") + (let ((name (py--string-strip (or strg (car kill-ring))))) + (insert (concat "print(\"" name ": {}\".format(" name "))")))) + +(defun py-line-to-printform-python2 () + "Transforms the item on current in a print statement. " + (interactive "*") + (let* ((name (py-symbol-at-point)) + (form (concat "print(\"" name ": %s \" % " name ")"))) + (delete-region (line-beginning-position) (line-end-position)) + (insert form)) + (forward-line 1) + (back-to-indentation)) + +(defun py-boolswitch () + "Edit the assignment of a boolean variable, revert them. + +I.e. switch it from \"True\" to \"False\" and vice versa" + (interactive "*") + (save-excursion + (unless (py--end-of-statement-p) + (py-forward-statement)) + (backward-word) + (cond ((looking-at "True") + (replace-match "False")) + ((looking-at "False") + (replace-match "True")) + (t (message "%s" "Can't see \"True or False\" here"))))) + +;; python-components-imenu +;; Imenu definitions + +(defvar py-imenu-class-regexp + (concat ; <> + "\\(" ; + "^[ \t]*" ; newline and maybe whitespace + "\\(class[ \t]+[a-zA-Z0-9_]+\\)" ; class name + ; possibly multiple superclasses + "\\([ \t]*\\((\\([a-zA-Z0-9_,. \t\n]\\)*)\\)?\\)" + "[ \t]*:" ; and the final : + "\\)" ; >>classes<< + ) + "Regexp for Python classes for use with the Imenu package." + ) + +;; (defvar py-imenu-method-regexp +;; (concat ; <> +;; "\\(" ; +;; "^[ \t]*" ; new line and maybe whitespace +;; "\\(def[ \t]+" ; function definitions start with def +;; "\\([a-zA-Z0-9_]+\\)" ; name is here +;; ; function arguments... +;; ;; "[ \t]*(\\([-+/a-zA-Z0-9_=,\* \t\n.()\"'#]*\\))" +;; "[ \t]*(\\([^:#]*\\))" +;; "\\)" ; end of def +;; "[ \t]*:" ; and then the : +;; "\\)" ; >>methods and functions<< +;; ) +;; "Regexp for Python methods/functions for use with the Imenu package." +;; ) + +(defvar py-imenu-method-regexp + (concat ; <> + "\\(" ; + "^[ \t]*" ; new line and maybe whitespace + "\\(def[ \t]+" ; function definitions start with def + "\\([a-zA-Z0-9_]+\\)" ; name is here + ; function arguments... + ;; "[ \t]*(\\([-+/a-zA-Z0-9_=,\* \t\n.()\"'#]*\\))" + "[ \t]*(\\(.*\\))" + "\\)" ; end of def + "[ \t]*:" ; and then the : + "\\)" ; >>methods and functions<< + ) + "Regexp for Python methods/functions for use with the Imenu package.") + + + + + +(defvar py-imenu-method-no-arg-parens '(2 8) + "Indices into groups of the Python regexp for use with Imenu. + +Using these values will result in smaller Imenu lists, as arguments to +functions are not listed. + +See the variable `py-imenu-show-method-args-p' for more +information.") + +(defvar py-imenu-method-arg-parens '(2 7) + "Indices into groups of the Python regexp for use with imenu. +Using these values will result in large Imenu lists, as arguments to +functions are listed. + +See the variable `py-imenu-show-method-args-p' for more +information.") + +;; Note that in this format, this variable can still be used with the +;; imenu--generic-function. Otherwise, there is no real reason to have +;; it. +(defvar py-imenu-generic-expression + (cons + (concat + py-imenu-class-regexp + "\\|" ; or... + py-imenu-method-regexp + ) + py-imenu-method-no-arg-parens) + "Generic Python expression which may be used directly with Imenu. +Used by setting the variable `imenu-generic-expression' to this value. +Also, see the function \\[py--imenu-create-index] for a better +alternative for finding the index.") + + +(defvar py-imenu-generic-regexp nil) +(defvar py-imenu-generic-parens nil) + + +(defun py--imenu-create-index () + "Python interface function for the Imenu package. +Finds all Python classes and functions/methods. Calls function +\\[py--imenu-create-index-engine]. See that function for the details +of how this works." + (save-excursion + (setq py-imenu-generic-regexp (car py-imenu-generic-expression) + py-imenu-generic-parens (if py-imenu-show-method-args-p + py-imenu-method-arg-parens + py-imenu-method-no-arg-parens)) + (goto-char (point-min)) + ;; Warning: When the buffer has no classes or functions, this will + ;; return nil, which seems proper according to the Imenu API, but + ;; causes an error in the XEmacs port of Imenu. Sigh. + (setq index-alist (cdr (py--imenu-create-index-engine nil))))) + +(defun py--imenu-create-index-engine (&optional start-indent) + "Function for finding Imenu definitions in Python. + +Finds all definitions (classes, methods, or functions) in a Python +file for the Imenu package. + +Returns a possibly nested alist of the form + + (INDEX-NAME . INDEX-POSITION) + +The second element of the alist may be an alist, producing a nested +list as in + + (INDEX-NAME . INDEX-ALIST) + +This function should not be called directly, as it calls itself +recursively and requires some setup. Rather this is the engine for +the function \\[py--imenu-create-index-function]. + +It works recursively by looking for all definitions at the current +indention level. When it finds one, it adds it to the alist. If it +finds a definition at a greater indentation level, it removes the +previous definition from the alist. In its place it adds all +definitions found at the next indentation level. When it finds a +definition that is less indented then the current level, it returns +the alist it has created thus far. + +The optional argument START-INDENT indicates the starting indentation +at which to continue looking for Python classes, methods, or +functions. If this is not supplied, the function uses the indentation +of the first definition found." + (let (index-alist + sub-method-alist + looking-p + def-name prev-name + cur-indent def-pos + (class-paren (first py-imenu-generic-parens)) + (def-paren (second py-imenu-generic-parens))) + ;; (switch-to-buffer (current-buffer)) + (setq looking-p + (re-search-forward py-imenu-generic-regexp (point-max) t)) + (while looking-p + (save-excursion + ;; used to set def-name to this value but generic-extract-name + ;; is new to imenu-1.14. this way it still works with + ;; imenu-1.11 + ;;(imenu--generic-extract-name py-imenu-generic-parens)) + (let ((cur-paren (if (match-beginning class-paren) + class-paren def-paren))) + (setq def-name + (buffer-substring-no-properties (match-beginning cur-paren) + (match-end cur-paren)))) + (save-match-data + (py-backward-def-or-class)) + (beginning-of-line) + (setq cur-indent (current-indentation))) + ;; HACK: want to go to the next correct definition location. We + ;; explicitly list them here but it would be better to have them + ;; in a list. + (setq def-pos + (or (match-beginning class-paren) + (match-beginning def-paren))) + ;; if we don't have a starting indent level, take this one + (or start-indent + (setq start-indent cur-indent)) + ;; if we don't have class name yet, take this one + (or prev-name + (setq prev-name def-name)) + ;; what level is the next definition on? must be same, deeper + ;; or shallower indentation + (cond + ;; Skip code in comments and strings + ((py--in-literal)) + ;; at the same indent level, add it to the list... + ((= start-indent cur-indent) + (push (cons def-name def-pos) index-alist)) + ;; deeper indented expression, recurse + ((< start-indent cur-indent) + ;; the point is currently on the expression we're supposed to + ;; start on, so go back to the last expression. The recursive + ;; call will find this place again and add it to the correct + ;; list + (re-search-backward py-imenu-generic-regexp (point-min) 'move) + (setq sub-method-alist (py--imenu-create-index-engine cur-indent)) + (if sub-method-alist + ;; we put the last element on the index-alist on the start + ;; of the submethod alist so the user can still get to it. + (let* ((save-elmt (pop index-alist)) + (classname (and (string-match "^class " (car save-elmt))(replace-regexp-in-string "^class " "" (car save-elmt))))) + (if (and classname (not (string-match "^class " (caar sub-method-alist)))) + (setcar (car sub-method-alist) (concat classname "." (caar sub-method-alist)))) + (push (cons prev-name + (cons save-elmt sub-method-alist)) + index-alist)))) + (t + (setq looking-p nil) + (re-search-backward py-imenu-generic-regexp (point-min) t))) + ;; end-cond + (setq prev-name def-name) + (and looking-p + (setq looking-p + (re-search-forward py-imenu-generic-regexp + (point-max) 'move)))) + (nreverse index-alist))) + +(defun py--imenu-create-index-new (&optional beg end) + "`imenu-create-index-function' for Python. " + (interactive) + (set (make-local-variable 'imenu-max-items) py-imenu-max-items) + (let ((orig (point)) + (beg (or beg (point-min))) + (end (or end (point-max))) + index-alist vars thisend sublist classname pos name) + (goto-char beg) + (while (and (re-search-forward "^[ \t]*\\(def\\|class\\)[ \t]+\\(\\sw+\\)" end t 1)(not (nth 8 (parse-partial-sexp (point-min) (point))))) + (if (save-match-data (string= "class" (match-string-no-properties 1))) + (progn + (setq pos (match-beginning 0) + name (match-string-no-properties 2) + classname (concat "class " name) + thisend (save-match-data (py--end-of-def-or-class-position)) + sublist '()) + (while (and (re-search-forward "^[ \t]*\\(def\\|class\\)[ \t]+\\(\\sw+\\)" (or thisend end) t 1)(not (nth 8 (parse-partial-sexp (point-min) (point))))) + (let* ((pos (match-beginning 0)) + (name (match-string-no-properties 2))) + (push (cons (concat " " name) pos) sublist))) + (if classname + (progn + (setq sublist (nreverse sublist)) + (push (cons classname pos) sublist) + (push (cons classname sublist) index-alist)) + (push sublist index-alist))) + + (let ((pos (match-beginning 0)) + (name (match-string-no-properties 2))) + (push (cons name pos) index-alist)))) + ;; Look for module variables. + (goto-char (point-min)) + (while (re-search-forward "^\\(\\sw+\\)[ \t]*=" end t) + (unless (nth 8 (parse-partial-sexp (point-min) (point))) + (let ((pos (match-beginning 1)) + (name (match-string-no-properties 1))) + (push (cons name pos) vars)))) + (setq index-alist (nreverse index-alist)) + (when vars + (push (cons "Module variables" + (nreverse vars)) + index-alist)) + (goto-char orig) + index-alist)) + +;; A modified slice from python.el +(defvar py-imenu-format-item-label-function + 'py-imenu-format-item-label + "Imenu function used to format an item label. +It must be a function with two arguments: TYPE and NAME.") + +(defvar py-imenu-format-parent-item-label-function + 'py-imenu-format-parent-item-label + "Imenu function used to format a parent item label. +It must be a function with two arguments: TYPE and NAME.") + +(defvar py-imenu-format-parent-item-jump-label-function + 'py-imenu-format-parent-item-jump-label + "Imenu function used to format a parent jump item label. +It must be a function with two arguments: TYPE and NAME.") + +(defun py-imenu-format-item-label (type name) + "Return Imenu label for single node using TYPE and NAME." + (format "%s (%s)" name type)) + +(defun py-imenu-format-parent-item-label (type name) + "Return Imenu label for parent node using TYPE and NAME." + (format "%s..." (py-imenu-format-item-label type name))) + +;; overengineering? +(defun py-imenu-format-parent-item-jump-label (type _name) + "Return Imenu label for parent node jump using TYPE and NAME." + (if (string= type "class") + "*class definition*" + "*function definition*")) + +(defun py-imenu--put-parent (type name pos tree) + "Add the parent with TYPE, NAME and POS to TREE." + (let* ((label + (funcall py-imenu-format-item-label-function type name)) + ;; (jump-label + ;; (funcall py-imenu-format-parent-item-jump-label-function type name)) + (jump-label label + ;; (funcall py-imenu-format-parent-item-jump-label-function type name) + ) + ) + (if (not tree) + (cons label pos) + (cons label (cons (cons jump-label pos) tree))))) + +(defun py-imenu--build-tree (&optional min-indent prev-indent tree) + "Recursively build the tree of nested definitions of a node. +Arguments MIN-INDENT, PREV-INDENT and TREE are internal and should +not be passed explicitly unless you know what you are doing." + (setq min-indent (or min-indent 0) + prev-indent (or prev-indent py-indent-offset)) + (save-restriction + (narrow-to-region (point-min) (point)) + (let* ((pos + (progn + ;; finds a top-level class + (py-backward-def-or-class) + ;; stops behind the indented form at EOL + (py-forward-def-or-class) + ;; may find an inner def-or-class + (py-backward-def-or-class))) + type + (name (when (and pos (looking-at py-def-or-class-re)) + (let ((split (split-string (match-string-no-properties 0)))) + (setq type (car split)) + (cadr split)))) + (label (when name + (funcall py-imenu-format-item-label-function type name))) + (indent (current-indentation)) + (children-indent-limit (+ py-indent-offset min-indent))) + (cond ((not pos) + ;; Nothing found, probably near to bobp. + nil) + ((<= indent min-indent) + ;; The current indentation points that this is a parent + ;; node, add it to the tree and stop recursing. + (py-imenu--put-parent type name pos tree)) + (t + (py-imenu--build-tree + min-indent + indent + (if (<= indent children-indent-limit) + (cons (cons label pos) tree) + (cons + (py-imenu--build-tree + prev-indent indent (list (cons label pos))) + tree)))))))) + +(defun py--imenu-index () + "Return tree Imenu alist for the current Python buffer. " + (save-excursion + (goto-char (point-max)) + (let ((index) + (tree)) + (while (setq tree (py-imenu--build-tree)) + (setq index (cons tree index))) + index))) + +;; python-components-electric +(defun py-electric-colon (arg) + "Insert a colon and indent accordingly. + +If a numeric argument ARG is provided, that many colons are inserted +non-electrically. + +Electric behavior is inhibited inside a string or +comment or by universal prefix \\[universal-argument]. + +Switched by `py-electric-colon-active-p', default is nil +See also `py-electric-colon-greedy-p'" + (interactive "*P") + (cond + ((not py-electric-colon-active-p) + (self-insert-command (prefix-numeric-value arg))) + ;; + ((and py-electric-colon-bobl-only + (save-excursion + (py-backward-statement) + (not (py--beginning-of-block-p)))) + (self-insert-command (prefix-numeric-value arg))) + ;; + ((eq 4 (prefix-numeric-value arg)) + (self-insert-command 1)) + ;; + (t + (insert ":") + (unless (py-in-string-or-comment-p) + (let ((orig (copy-marker (point))) + (indent (py-compute-indentation))) + (unless (or (eq (current-indentation) indent) + (and py-electric-colon-greedy-p + (eq indent + (save-excursion + (py-backward-statement) + (current-indentation)))) + (and (looking-at py-def-or-class-re) + (< (current-indentation) indent))) + (beginning-of-line) + (delete-horizontal-space) + (indent-to indent)) + (goto-char orig)) + (when py-electric-colon-newline-and-indent-p + (py-newline-and-indent)))))) + +;; TODO: PRouleau: I would like to better understand this. +;; I don't understand the docstring. +;; What was the completion bug this is reacting to? +(defun py-electric-close (arg) + "Close completion buffer when no longer needed. + +It is it's sure, it's no longer needed, i.e. when inserting a space. + +Works around a bug in `choose-completion'." + + (interactive "*P") + (cond + ((not py-electric-close-active-p) + (self-insert-command (prefix-numeric-value arg))) + ;; + ((eq 4 (prefix-numeric-value arg)) + (self-insert-command 1)) + ;; + (t (if (called-interactively-p 'any) + (self-insert-command (prefix-numeric-value arg)) + ;; used from dont-indent-code-unnecessarily-lp-1048778-test + (insert " "))))) + +;; TODO: PRouleau: describe the electric behavior of '#'. +;; This description should be in docstring of the +;; `py-electric-comment-p' user option and be referred to here. +;; I currently don't understand what it should be and prefer not +;; having to infer it from code. +;; - From what I saw, the intent is to align the comment being +;; typed to the one on line above or at the indentation level. +;; - Is there more to it it than that? +;; - I would like to see the following added (possibly via options): +;; - When inserting the '#' follow it with a space, such that +;; comment text is separated from the leading '#' by one space, as +;; recommended in PEP-8 +;; URL https://www.python.org/dev/peps/pep-0008/#inline-comments +(defun py-electric-comment (arg) + "Insert a comment. If starting a comment, indent accordingly. + +If a numeric argument ARG is provided, that many \"#\" are inserted +non-electrically. +With \\[universal-argument] \"#\" electric behavior is inhibited inside a +string or comment." + (interactive "*P") + (if (and py-indent-comments py-electric-comment-p) + (if (ignore-errors (eq 4 (car-safe arg))) + (insert "#") + (when (and (eq last-command 'py-electric-comment) + (looking-back " " (line-beginning-position))) + (forward-char -1)) + (if (called-interactively-p 'any) + (self-insert-command (prefix-numeric-value arg)) + (insert "#")) + (let ((orig (copy-marker (point))) + (indent (py-compute-indentation))) + (unless (eq (current-indentation) indent) + (goto-char orig) + (beginning-of-line) + (delete-horizontal-space) + (indent-to indent) + (goto-char orig)) + (when py-electric-comment-add-space-p + (unless (looking-at "[ \t]") + (insert " ")))) + (setq last-command this-command)) + (self-insert-command (prefix-numeric-value arg)))) + +;; Electric deletion +(defun py-empty-out-list-backward () + "Deletes all elements from list before point." + (interactive "*") + (and (member (char-before) (list ?\) ?\] ?\})) + (let ((orig (point)) + (thischar (char-before)) + pps cn) + (forward-char -1) + (setq pps (parse-partial-sexp (point-min) (point))) + (if (and (not (nth 8 pps)) (nth 1 pps)) + (progn + (goto-char (nth 1 pps)) + (forward-char 1)) + (cond ((or (eq thischar 41)(eq thischar ?\))) + (setq cn "(")) + ((or (eq thischar 125) (eq thischar ?\})) + (setq cn "{")) + ((or (eq thischar 93)(eq thischar ?\])) + (setq cn "["))) + (skip-chars-backward (concat "^" cn))) + (delete-region (point) orig) + (insert-char thischar 1) + (forward-char -1)))) + +;; TODO: PRouleau Question: [...] + +;; - Also, the mapping for [backspace] in python-mode-map only works in +;; graphics mode, it does not work when Emacs runs in terminal mode. +;; It would be nice to have a binding that works in terminal mode too. +;; keep-one handed over form `py-electric-delete' maybe +(defun py-electric-backspace (&optional arg) + "Delete one or more of whitespace chars left from point. +Honor indentation. + +If called at whitespace below max indentation, + +Delete region when both variable `delete-active-region' and `use-region-p' +are non-nil. + +With \\[universal-argument], deactivate electric-behavior this time, +delete just one character before point. + +At no-whitespace character, delete one before point. + +" + (interactive "*P") + (unless (bobp) + (let ((backward-delete-char-untabify-method 'untabify) + indent + done) + (cond + ;; electric-pair-mode + ((and electric-pair-mode + (or + (and + (ignore-errors (eq 5 (car (syntax-after (point))))) + (ignore-errors (eq 4 (car (syntax-after (1- (point))))))) + (and + (ignore-errors (eq 7 (car (syntax-after (point))))) + (ignore-errors (eq 7 (car (syntax-after (1- (point))))))))) + (delete-char 1) + (backward-delete-char-untabify 1)) + ((eq 4 (prefix-numeric-value arg)) + (backward-delete-char-untabify 1)) + ((use-region-p) + ;; Emacs23 doesn't know that var + (if (boundp 'delete-active-region) + (delete-active-region) + (delete-region (region-beginning) (region-end)))) + ((looking-back "[[:graph:]]" (line-beginning-position)) + (backward-delete-char-untabify 1)) + ;; before code + ((looking-back "^[ \t]+" (line-beginning-position)) + (setq indent (py-compute-indentation)) + (cond ((< indent (current-indentation)) + (back-to-indentation) + (delete-region (line-beginning-position) (point)) + (indent-to indent)) + ((<= (current-column) py-indent-offset) + (delete-region (line-beginning-position) (point))) + ((eq 0 (% (current-column) py-indent-offset)) + (delete-region (point) (progn (backward-char py-indent-offset) (point)))) + (t (delete-region + (point) + (progn + ;; go backward the remainder + (backward-char (% (current-column) py-indent-offset)) + (point)))))) + ((looking-back "[[:graph:]][ \t]+" (line-beginning-position)) + ;; in the middle fixup-whitespace + (setq done (line-end-position)) + (fixup-whitespace) + ;; if just one whitespace at point, delete that one + (or (< (line-end-position) done) (delete-char 1))) + + ;; (if (< 1 (abs (skip-chars-backward " \t"))) + ;; (delete-region (point) (progn (skip-chars-forward " \t") (point))) + ;; (delete-char 1)) + + ((bolp) + (backward-delete-char 1)) + (t + (py-indent-line nil t)))))) + +(defun py-electric-delete (&optional arg) + "Delete one or more of whitespace chars right from point. +Honor indentation. + +Delete region when both variable `delete-active-region' and `use-region-p' +are non-nil. + +With \\[universal-argument], deactivate electric-behavior this time, +delete just one character at point. + +At spaces in line of code, call fixup-whitespace. +At no-whitespace char, delete one char at point. +" + (interactive "P*") + (unless (eobp) + (let* (;; py-ert-deletes-too-much-lp:1300270-dMegYd + ;; x = {'abc':'def', + ;; 'ghi':'jkl'} + (backward-delete-char-untabify-method 'untabify) + (indent (py-compute-indentation)) + ;; (delpos (+ (line-beginning-position) indent)) + ;; (line-end-pos (line-end-position)) + ;; (orig (point)) + done) + (cond + ((eq 4 (prefix-numeric-value arg)) + (delete-char 1)) + ;; delete active region if one is active + ((use-region-p) + ;; Emacs23 doesn't know that var + (if (boundp 'delete-active-region) + (delete-active-region) + (delete-region (region-beginning) (region-end)))) + ((looking-at "[[:graph:]]") + (delete-char 1)) + ((or (eolp) (looking-at "[ \t]+$")) + (cond + ((eolp) (delete-char 1)) + ((< (+ indent (line-beginning-position)) (line-end-position)) + (end-of-line) + (while (and (member (char-before) (list 9 32 ?\r)) + (< indent (current-column))) + (backward-delete-char-untabify 1))))) + (;; before code + (looking-at "[ \t]+[[:graph:]]") + ;; before indent + (if (looking-back "^[ \t]*" (line-beginning-position)) + (cond ((< indent (current-indentation)) + (back-to-indentation) + (delete-region (line-beginning-position) (point)) + (indent-to indent)) + ((< 0 (% (current-indentation) py-indent-offset)) + (back-to-indentation) + (delete-region (point) (progn (backward-char (% (current-indentation) py-indent-offset)) (point)))) + ((eq 0 (% (current-indentation) py-indent-offset)) + (back-to-indentation) + (delete-region (point) (progn (backward-char py-indent-offset) (point)))) + (t + (skip-chars-forward " \t") + (delete-region (line-beginning-position) (point)))) + ;; in the middle fixup-whitespace + (setq done (line-end-position)) + (fixup-whitespace) + ;; if just one whitespace at point, delete that one + (or (< (line-end-position) done) (delete-char 1)))) + (t (delete-char 1)))))) + +;; TODO: PRouleau: the electric yank mechanism is currently commented out. +;; Is this a feature to keep? Was it used? I can see a benefit for it. +;; Why is it currently disabled? +(defun py-electric-yank (&optional arg) + "Perform command `yank' followed by an `indent-according-to-mode'. +Pass ARG to the command `yank'." + (interactive "P") + (cond + (py-electric-yank-active-p + (yank arg) + ;; (py-indent-line) + ) + (t + (yank arg)))) + +(defun py-toggle-py-electric-colon-active () + "Toggle use of electric colon for Python code." + (interactive) + (setq py-electric-colon-active-p (not py-electric-colon-active-p)) + (when (and py-verbose-p (called-interactively-p 'interactive)) (message "py-electric-colon-active-p: %s" py-electric-colon-active-p))) + +;; TODO: PRouleau: It might be beneficial to have toggle commands for all +;; the electric behaviours, not just the electric colon. + +;; required for pending-del and delsel modes +(put 'py-electric-colon 'delete-selection t) ;delsel +(put 'py-electric-colon 'pending-delete t) ;pending-del +(put 'py-electric-backspace 'delete-selection 'supersede) ;delsel +(put 'py-electric-backspace 'pending-delete 'supersede) ;pending-del +(put 'py-electric-delete 'delete-selection 'supersede) ;delsel +(put 'py-electric-delete 'pending-delete 'supersede) ;pending-del + +;; python-components-virtualenv + +(defvar virtualenv-workon-home nil) + +(defvar virtualenv-name nil) + +(defvar virtualenv-old-path nil) + +(defvar virtualenv-old-exec-path nil) + +(if (getenv "WORKON_HOME") + (setq virtualenv-workon-home (getenv "WORKON_HOME")) + (setq virtualenv-workon-home "~/.virtualenvs")) + +;;TODO: Move to a generic UTILITY or TOOL package +(defun virtualenv-filter (predicate sequence) + "Return a list of each SEQUENCE element for which the PREDICATE is non-nil. +The order of elements in SEQUENCE is retained." + (let ((retlist '())) + (dolist (element sequence (nreverse retlist)) + (when (funcall predicate element) + (push element retlist))))) + +(defun virtualenv-append-path (dir var) + "Append DIR to a path-like variable VAR. + +For example: +>>> (virtualenv-append-path \"/usr/bin:/bin\" \"/home/test/bin\") +\"/home/test/bin:/usr/bin:/bin\"" + (concat (expand-file-name dir) + path-separator + var)) + +(defun virtualenv-add-to-path (dir) + "Add the specified DIR path element to the Emacs PATH." + (setenv "PATH" + (virtualenv-append-path dir + (getenv "PATH")))) + +(defun virtualenv-current () + "Display the current activated virtualenv." + (interactive) + (message virtualenv-name)) + +(defun virtualenv-activate (dir) + "Activate the virtualenv located in specified DIR." + (interactive "DVirtualenv Directory: ") + ;; Eventually deactivate previous virtualenv + (when virtualenv-name + (virtualenv-deactivate)) + (let ((cmd (concat "source " dir "/bin/activate\n"))) + (comint-send-string (get-process (get-buffer-process "*shell*")) cmd) + ;; Storing old variables + (setq virtualenv-old-path (getenv "PATH")) + (setq virtualenv-old-exec-path exec-path) + + (setenv "VIRTUAL_ENV" dir) + (virtualenv-add-to-path (concat (py--normalize-directory dir) "bin")) + (push (concat (py--normalize-directory dir) "bin") exec-path) + + (setq virtualenv-name dir))) + +(defun virtualenv-deactivate () + "Deactivate the current virtual environment." + (interactive) + ;; Restoring old variables + (setenv "PATH" virtualenv-old-path) + (setq exec-path virtualenv-old-exec-path) + (message (concat "Virtualenv '" virtualenv-name "' deactivated.")) + (setq virtualenv-name nil)) + +(defun virtualenv-p (dir) + "Check if a directory DIR is a virtualenv." + (file-exists-p (concat dir "/bin/activate"))) + +(defun virtualenv-workon-complete () + "Return available completions for `virtualenv-workon'." + (let + ;;Varlist + ((filelist (directory-files virtualenv-workon-home t))) + ;; Get only the basename from the list of the virtual environments + ;; paths + (mapcar + 'file-name-nondirectory + ;; Filter the directories and then the virtual environments + (virtualenv-filter 'virtualenv-p + (virtualenv-filter 'file-directory-p filelist))))) + +(defun virtualenv-workon (name) + "Issue a virtualenvwrapper-like virtualenv-workon NAME command." + (interactive (list (completing-read "Virtualenv: " + (virtualenv-workon-complete)))) + (if (getenv "WORKON_HOME") + (virtualenv-activate (concat (py--normalize-directory + (getenv "WORKON_HOME")) name)) + (virtualenv-activate (concat + (py--normalize-directory virtualenv-workon-home) + name)))) + +;; python-abbrev-propose + +(defun py-edit-abbrevs () + "Jumps to `python-mode-abbrev-table'." + (interactive) + (save-excursion + (let ((mat (abbrev-table-name local-abbrev-table))) + (prepare-abbrev-list-buffer) + (set-buffer "*Abbrevs*") + (switch-to-buffer (current-buffer)) + (goto-char (point-min)) + (search-forward (concat "(" (format "%s" mat)))))) + +(defun py--add-abbrev-propose (table type arg &optional dont-ask) + (save-excursion + (let ((orig (point)) + proposal exp name) + (while (< 0 arg) + (py-backward-partial-expression) + (when (looking-at "[[:alpha:]]") + (setq proposal (concat (downcase (match-string-no-properties 0)) proposal))) + (setq arg (1- arg))) + (setq exp (buffer-substring-no-properties (point) orig)) + (setq name + ;; ask only when interactive + (if dont-ask + proposal + (read-string (format (if exp "%s abbrev for \"%s\": " + "Undefine %s abbrev: ") + type exp) proposal))) + (set-text-properties 0 (length name) nil name) + (when (or (null exp) + (not (abbrev-expansion name table)) + (y-or-n-p (format "%s expands to \"%s\"; redefine? " + name (abbrev-expansion name table)))) + (define-abbrev table (downcase name) exp))))) + +(defun py-add-abbrev (arg) + "Defines python-mode specific abbrev." + (interactive "p") + (save-excursion + (py--add-abbrev-propose + (if only-global-abbrevs + global-abbrev-table + (or local-abbrev-table + (error "No per-mode abbrev table"))) + "Mode" arg))) + +;; python-components-paragraph + +(defun py-fill-paren (&optional justify) + "Paren fill function for `py-fill-paragraph'. +JUSTIFY should be used (if applicable) as in `fill-paragraph'." + (interactive "*P") + (save-restriction + (save-excursion + (let ((pps (parse-partial-sexp (point-min) (point)))) + (if (nth 1 pps) + (let* ((beg (copy-marker (nth 1 pps))) + (end (and beg (save-excursion (goto-char (nth 1 pps)) + (forward-list)))) + (paragraph-start "\f\\|[ \t]*$") + (paragraph-separate ",")) + (when (and beg end (narrow-to-region beg end)) + (fill-region beg end justify) + (while (not (eobp)) + (forward-line 1) + (py-indent-line) + (goto-char (line-end-position)))))))))) + +(defun py-fill-string-django (&optional justify) + "Fill docstring according to Django's coding standards style. + + \"\"\" + Process foo, return bar. + \"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\" + +See available styles at `py-fill-paragraph' or var `py-docstring-style' +" + (interactive "*P") + (py-fill-string justify 'django t)) + +(defun py-fill-string-onetwo (&optional justify) + "One newline and start and Two at end style. + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + + \"\"\" + +See available styles at `py-fill-paragraph' or var `py-docstring-style' +" + (interactive "*P") + (py-fill-string justify 'onetwo t)) + +(defun py-fill-string-pep-257 (&optional justify) + "PEP-257 with 2 newlines at end of string. + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\"Process foo, return bar. + + If processing fails throw ProcessingError. + + \"\"\" + +See available styles at `py-fill-paragraph' or var `py-docstring-style' +" + (interactive "*P") + (py-fill-string justify 'pep-257 t)) + +(defun py-fill-string-pep-257-nn (&optional justify) + "PEP-257 with 1 newline at end of string. + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\"Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\" + +See available styles at `py-fill-paragraph' or var `py-docstring-style' +" + (interactive "*P") + (py-fill-string justify 'pep-257-nn t)) + +(defun py-fill-string-symmetric (&optional justify) + "Symmetric style. + + \"\"\"Process foo, return bar.\"\"\" + + \"\"\" + Process foo, return bar. + + If processing fails throw ProcessingError. + \"\"\" + +See available styles at `py-fill-paragraph' or var `py-docstring-style' +" + (interactive "*P") + (py-fill-string justify 'symmetric t)) + +(defun py-set-nil-docstring-style () + "Set py-docstring-style to \\='nil" + (interactive) + (setq py-docstring-style 'nil) + (when (and (called-interactively-p 'any) py-verbose-p) + (message "docstring-style set to: %s" py-docstring-style))) + +(defun py-set-pep-257-nn-docstring-style () + "Set py-docstring-style to \\='pep-257-nn" + (interactive) + (setq py-docstring-style 'pep-257-nn) + (when (and (called-interactively-p 'any) py-verbose-p) + (message "docstring-style set to: %s" py-docstring-style))) + +(defun py-set-pep-257-docstring-style () + "Set py-docstring-style to \\='pep-257" + (interactive) + (setq py-docstring-style 'pep-257) + (when (and (called-interactively-p 'any) py-verbose-p) + (message "docstring-style set to: %s" py-docstring-style))) + +(defun py-set-django-docstring-style () + "Set py-docstring-style to \\='django" + (interactive) + (setq py-docstring-style 'django) + (when (and (called-interactively-p 'any) py-verbose-p) + (message "docstring-style set to: %s" py-docstring-style))) + +(defun py-set-symmetric-docstring-style () + "Set py-docstring-style to \\='symmetric" + (interactive) + (setq py-docstring-style 'symmetric) + (when (and (called-interactively-p 'any) py-verbose-p) + (message "docstring-style set to: %s" py-docstring-style))) + +(defun py-set-onetwo-docstring-style () + "Set py-docstring-style to \\='onetwo" + (interactive) + (setq py-docstring-style 'onetwo) + (when (and (called-interactively-p 'any) py-verbose-p) + (message "docstring-style set to: %s" py-docstring-style))) + +(defun py-fill-comment (&optional justify) + "Fill the comment paragraph at point" + (interactive "*P") + (let (;; Non-nil if the current line contains a comment. + has-comment + + ;; If has-comment, the appropriate fill-prefix (format "%s" r the comment. + comment-fill-prefix) + + ;; Figure out what kind of comment we are looking at. + (save-excursion + (beginning-of-line) + (cond + ;; A line with nothing but a comment on it? + ((looking-at "[ \t]*#[# \t]*") + (setq has-comment t + comment-fill-prefix (buffer-substring (match-beginning 0) + (match-end 0)))) + + ;; A line with some code, followed by a comment? Remember that the hash + ;; which starts the comment shouldn't be part of a string or character. + ((progn + (while (not (looking-at "#\\|$")) + (skip-chars-forward "^#\n\"'\\") + (cond + ((eq (char-after (point)) ?\\) (forward-char 2)) + ((memq (char-after (point)) '(?\" ?')) (forward-sexp 1)))) + (looking-at "#+[\t ]*")) + (setq has-comment t) + (setq comment-fill-prefix + (concat (make-string (current-column) ? ) + (buffer-substring (match-beginning 0) (match-end 0))))))) + + (if (not has-comment) + (fill-paragraph justify) + + ;; Narrow to include only the comment, and then fill the region. + (save-restriction + (narrow-to-region + + ;; Find the first line we should include in the region to fill. + (save-excursion + (while (and (zerop (forward-line -1)) + (looking-at "^[ \t]*#"))) + + ;; We may have gone to far. Go forward again. + (or (looking-at "^[ \t]*#") + (forward-line 1)) + (point)) + + ;; Find the beginning of the first line past the region to fill. + (save-excursion + (while (progn (forward-line 1) + (looking-at "^[ \t]*#"))) + (point))) + + ;; Lines with only hashes on them can be paragraph boundaries. + (let ((paragraph-start (concat paragraph-start "\\|[ \t#]*$")) + (paragraph-separate (concat paragraph-separate "\\|[ \t#]*$")) + (fill-prefix comment-fill-prefix)) + (fill-paragraph justify)))) + t)) + +(defun py-fill-labelled-string (beg end) + "Fill string or paragraph containing lines starting with label + +See lp:1066489 " + (interactive "r*") + (let ((end (copy-marker end)) + (last (copy-marker (point))) + this-beg) + (save-excursion + (save-restriction + ;; (narrow-to-region beg end) + (goto-char beg) + (skip-chars-forward " \t\r\n\f") + (if (looking-at py-labelled-re) + (progn + (setq this-beg (line-beginning-position)) + (goto-char (match-end 0)) + (while (and (not (eobp)) (re-search-forward py-labelled-re end t 1)(< last (match-beginning 0))(setq last (match-beginning 0))) + (save-match-data (fill-region this-beg (1- (line-beginning-position)))) + (setq this-beg (line-beginning-position)) + (goto-char (match-end 0))))))))) + +(defun py--in-or-behind-or-before-a-docstring (pps) + (interactive "*") + (save-excursion + (let* ((strg-start-pos (when (nth 3 pps) (nth 8 pps))) + (n8pps (or strg-start-pos + (when + (equal (string-to-syntax "|") + (syntax-after (point))) + (and + (< 0 (skip-chars-forward "\"'")) + (nth 3 (parse-partial-sexp (point-min) (point)))))))) + (and n8pps (py--docstring-p n8pps))))) + +(defun py--string-fence-delete-spaces (&optional start) + "Delete spaces following or preceding delimiters of string at point. " + (interactive "*") + (let ((beg (or start (nth 8 (parse-partial-sexp (point-min) (point)))))) + (save-excursion + (goto-char beg) + (skip-chars-forward "\"'rRuU") + (delete-region (point) (progn (skip-chars-forward " \t\r\n\f")(point))) + (goto-char beg) + (forward-char 1) + (skip-syntax-forward "^|") + (skip-chars-backward "\"'rRuU") + ;; (delete-region (point) (progn (skip-chars-backward " \t\r\n\f")(point))) +))) + +(defun py--skip-raw-string-front-fence () + "Skip forward chars u, U, r, R followed by string-delimiters. " + (when (member (char-after) (list ?u ?U ?r ?R)) + (forward-char 1)) + (skip-chars-forward "\'\"")) + +(defun py--fill-fix-end (thisend orig delimiters-style) + ;; Add the number of newlines indicated by the selected style + ;; at the end. + ;; (widen) + (goto-char thisend) + (skip-chars-backward "\"'\n ") + (delete-region (point) (progn (skip-chars-forward " \t\r\n\f") (point))) + (unless (eq (char-after) 10) + (and + (cdr delimiters-style) + (or (newline (cdr delimiters-style)) t))) + (py-indent-line nil t) + (goto-char orig)) + +(defun py--fill-docstring-last-line (thisend beg end multi-line-p) + (widen) + ;; (narrow-to-region thisbeg thisend) + (goto-char thisend) + (skip-chars-backward "\"'") + (delete-region (point) (progn (skip-chars-backward " \t\r\n\f")(point))) + ;; (narrow-to-region beg end) + (fill-region beg end) + (setq multi-line-p (string-match "\n" (buffer-substring-no-properties beg end))) + (when multi-line-p + ;; adjust the region to fill according to style + (goto-char end))) + +(defun py--fill-docstring-base (thisbeg thisend style multi-line-p beg end py-current-indent orig) + ;; (widen) + ;; fill-paragraph causes wrong indent, lp:1397936 + ;; (narrow-to-region thisbeg thisend) + (let ((delimiters-style + (pcase style + ;; delimiters-style is a cons cell with the form + ;; (START-NEWLINES . END-NEWLINES). When any of the sexps + ;; is NIL means to not add any newlines for start or end + ;; of docstring. See `py-docstring-style' for a + ;; graphic idea of each style. + (`django (cons 1 1)) + (`onetwo (and multi-line-p (cons 1 2))) + (`pep-257 (and multi-line-p (cons nil 2))) + (`pep-257-nn (and multi-line-p (cons nil 1))) + (`symmetric (and multi-line-p (cons 1 1)))))) + ;; (save-excursion + (when style + ;; Add the number of newlines indicated by the selected style + ;; at the start. + (goto-char thisbeg) + (py--skip-raw-string-front-fence) + (skip-chars-forward "'\"") + (when + (car delimiters-style) + (unless (or (py-empty-line-p)(eolp)) + (newline (car delimiters-style)))) + (indent-region beg end py-current-indent)) + (when multi-line-p + (goto-char thisbeg) + (py--skip-raw-string-front-fence) + (skip-chars-forward " \t\r\n\f") + (forward-line 1) + (beginning-of-line) + (unless (py-empty-line-p) (newline 1))) + (py--fill-fix-end thisend orig delimiters-style))) + +(defun py--fill-docstring-first-line (beg end) + "Refill first line after newline maybe. " + (fill-region-as-paragraph beg (line-end-position) nil t t) + (save-excursion + (end-of-line) + (unless (eobp) + (forward-line 1) + (back-to-indentation) + (unless (or (< end (point)) (py-empty-line-p)) + (newline 1) + )))) + +(defun py--fill-paragraph-in-docstring (beg) + ;; (goto-char innerbeg) + (let* ((fill-column (- fill-column (current-indentation))) + (parabeg (max beg (py--beginning-of-paragraph-position))) + (paraend (copy-marker (py--end-of-paragraph-position)))) + ;; if paragraph is a substring, take it + (goto-char parabeg) + (py--fill-docstring-first-line parabeg paraend) + (unless (or (< paraend (point))(eobp)) + (py--fill-paragraph-in-docstring (point))))) + +(defun py--fill-docstring (justify style docstring orig py-current-indent &optional beg end) + ;; Delete spaces after/before string fencge + (py--string-fence-delete-spaces beg) + (let* ((beg (or beg docstring)) + (innerbeg (copy-marker (progn (goto-char beg) (py--skip-raw-string-front-fence) (point)))) + (end (copy-marker + (or end + (progn + (goto-char innerbeg) + ;; (py--skip-raw-string-front-fence) + (skip-syntax-forward "^|") + (1+ (point)))))) + (innerend (copy-marker (progn (goto-char end)(skip-chars-backward "\\'\"") (point)))) + (multi-line-p (string-match "\n" (buffer-substring-no-properties innerbeg innerend)))) + (save-restriction + (narrow-to-region (point-min) end) + + (when (string-match (concat "^" py-labelled-re) (buffer-substring-no-properties beg end)) + (py-fill-labelled-string beg end)) + ;; (first-line-p (<= (line-beginning-position) beg) + (goto-char innerbeg) + (py--fill-paragraph-in-docstring beg)) + (py--fill-docstring-base innerbeg innerend style multi-line-p beg end py-current-indent orig))) + +(defun py-fill-string (&optional justify style docstring pps) + "String fill function for `py-fill-paragraph'. +JUSTIFY should be used (if applicable) as in `fill-paragraph'. + +Fill according to `py-docstring-style' " + (interactive "*") + (let* ((justify (or justify (if current-prefix-arg 'full t))) + (style (or style py-docstring-style)) + (pps (or pps (parse-partial-sexp (point-min) (point)))) + (indent + ;; set inside tqs + ;; (save-excursion (and (nth 3 pps) (goto-char (nth 8 pps)) (current-indentation))) + nil) + (orig (copy-marker (point))) + ;; (docstring (or docstring (py--in-or-behind-or-before-a-docstring pps))) + (docstring (cond (docstring + (if (not (number-or-marker-p docstring)) + (py--in-or-behind-or-before-a-docstring pps)) + docstring) + (t (py--in-or-behind-or-before-a-docstring pps)))) + (beg (and (nth 3 pps) (nth 8 pps))) + (tqs (progn (and beg (goto-char beg) (looking-at "\"\"\"\\|'''") (setq indent (current-column))))) + (end (copy-marker (if tqs + (or + (progn (ignore-errors (forward-sexp))(and (< orig (point)) (point))) + (goto-char orig) + (line-end-position)) + (or (progn (goto-char beg) (ignore-errors (forward-sexp))(and (< orig (point)) (point))) + (goto-char orig) + (line-end-position)))))) + (goto-char orig) + (when beg + (if docstring + (py--fill-docstring justify style docstring orig indent beg end) + (save-restriction + (if (not tqs) + (if (py-preceding-line-backslashed-p) + (progn + (setq end (copy-marker (line-end-position))) + (narrow-to-region (line-beginning-position) end) + (fill-region (line-beginning-position) end justify t) + (when (< 1 (py-count-lines)) + (py--continue-lines-region (point-min) end))) + (narrow-to-region beg end) + (fill-region beg end justify t) + (when + ;; counting in narrowed buffer + (< 1 (py-count-lines)) + (py--continue-lines-region beg end))) + (fill-region beg end justify))))))) + +(defun py--continue-lines-region (beg end) + (save-excursion + (goto-char beg) + (while (< (line-end-position) end) + (end-of-line) + (unless (py-escaped-p) (insert-and-inherit 32) (insert-and-inherit 92)) + (ignore-errors (forward-line 1))))) + +(defun py-fill-paragraph (&optional justify pps beg end tqs) + (interactive "*") + (save-excursion + (save-restriction + (window-configuration-to-register py--windows-config-register) + (let* ((pps (or pps (parse-partial-sexp (point-min) (point)))) + (docstring (unless (not py-docstring-style) (py--in-or-behind-or-before-a-docstring pps))) + (fill-column py-comment-fill-column) + (in-string (nth 3 pps))) + (cond ((or (nth 4 pps) + (and (bolp) (looking-at "[ \t]*#[# \t]*"))) + (py-fill-comment)) + (docstring + (setq fill-column py-docstring-fill-column) + (py--fill-docstring justify py-docstring-style docstring (point) + ;; current indentation + (save-excursion (and (nth 3 pps) (goto-char (nth 8 pps)) (current-indentation))))) + (t + (let* ((beg (or beg (save-excursion + (if (looking-at paragraph-start) + (point) + (backward-paragraph) + (when (looking-at paragraph-start) + (point)))) + (and (nth 3 pps) (nth 8 pps)))) + (end (or end + (when beg + (save-excursion + (or + (and in-string + (progn + (goto-char (nth 8 pps)) + (setq tqs (looking-at "\"\"\"\\|'''")) + (forward-sexp) (point))) + (progn + (forward-paragraph) + (when (looking-at paragraph-separate) + (point))))))))) + (and beg end (fill-region beg end)) + (when (and in-string (not tqs)) + (py--continue-lines-region beg end)))))) + (jump-to-register py--windows-config-register)))) + +(defun py-fill-string-or-comment () + "Serve auto-fill-mode" + (unless (< (current-column) fill-column) + (let ((pps (parse-partial-sexp (point-min) (point)))) + (if (nth 3 pps) + (py-fill-string nil nil nil pps) + ;; (py-fill-comment pps) + (do-auto-fill) + )))) + +;; python-components-section-forms + +(defun py-execute-section () + "Execute section at point." + (interactive) + (py-execute-section-prepare)) + +(defun py-execute-section-python () + "Execute section at point using python interpreter." + (interactive) + (py-execute-section-prepare "python")) + +(defun py-execute-section-python2 () + "Execute section at point using python2 interpreter." + (interactive) + (py-execute-section-prepare "python2")) + +(defun py-execute-section-python3 () + "Execute section at point using python3 interpreter." + (interactive) + (py-execute-section-prepare "python3")) + +(defun py-execute-section-ipython () + "Execute section at point using ipython interpreter." + (interactive) + (py-execute-section-prepare "ipython")) + +(defun py-execute-section-ipython2.7 () + "Execute section at point using ipython2.7 interpreter." + (interactive) + (py-execute-section-prepare "ipython2.7")) + +(defun py-execute-section-ipython3 () + "Execute section at point using ipython3 interpreter." + (interactive) + (py-execute-section-prepare "ipython3")) + +(defun py-execute-section-jython () + "Execute section at point using jython interpreter." + (interactive) + (py-execute-section-prepare "jython")) + +;; python-components-comment + + +(defun py-comment-region (beg end &optional arg) + "Like `comment-region’ but uses double hash (`#') comment starter." + (interactive "r\nP") + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start))) + (comment-region beg end arg))) + +(defun py-comment-block (&optional beg end arg) + "Comments block at point. + +Uses double hash (`#') comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-block-position))) + (end (or end (py--end-of-block-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-block-or-clause (&optional beg end arg) + "Comments block-or-clause at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-block-or-clause-position))) + (end (or end (py--end-of-block-or-clause-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-class (&optional beg end arg) + "Comments class at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-class-position))) + (end (or end (py--end-of-class-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-clause (&optional beg end arg) + "Comments clause at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-clause-position))) + (end (or end (py--end-of-clause-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-def (&optional beg end arg) + "Comments def at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-def-position))) + (end (or end (py--end-of-def-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-def-or-class (&optional beg end arg) + "Comments def-or-class at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-def-or-class-position))) + (end (or end (py--end-of-def-or-class-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-indent (&optional beg end arg) + "Comments indent at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-indent-position))) + (end (or end (py--end-of-indent-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-minor-block (&optional beg end arg) + "Comments minor-block at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-minor-block-position))) + (end (or end (py--end-of-minor-block-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-section (&optional beg end arg) + "Comments section at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-section-position))) + (end (or end (py--end-of-section-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-statement (&optional beg end arg) + "Comments statement at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-statement-position))) + (end (or end (py--end-of-statement-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + +(defun py-comment-top-level (&optional beg end arg) + "Comments top-level at point. + +Uses double hash (`#’) comment starter when `py-block-comment-prefix-p' is t, +the default" + (interactive "*") + (save-excursion + (let ((comment-start (if py-block-comment-prefix-p + py-block-comment-prefix + comment-start)) + (beg (or beg (py--beginning-of-top-level-position))) + (end (or end (py--end-of-top-level-position)))) + (goto-char beg) + (push-mark) + (goto-char end) + (comment-region beg end arg)))) + + +;; python-components-comment ends here +;; python-components-fast-forms + +;; Process forms fast + +(defun py-execute-buffer-fast (&optional shell dedicated split switch proc) + "Send accessible part of buffer to a Python interpreter. + +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SPLIT: split buffers after executing +Optional SWITCH: switch to output buffer after executing +Optional PROC: select an already running process for executing" + (interactive) + (py-execute-buffer shell dedicated t split switch proc)) + +(defun py-execute-region-fast (beg end &optional shell dedicated split switch proc) + "Send region to a Python interpreter. + +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SPLIT: split buffers after executing +Optional SWITCH: switch to output buffer after executing +Optional PROC: select an already running process for executing" + (interactive "r") + (let ((py-fast-process-p t)) + (py-execute-region beg end shell dedicated t split switch proc))) + +(defun py-execute-block-fast (&optional shell dedicated switch beg end file) + "Process block at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'block shell dedicated switch beg end file t)) + +(defun py-execute-block-or-clause-fast (&optional shell dedicated switch beg end file) + "Process block-or-clause at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'block-or-clause shell dedicated switch beg end file t)) + +(defun py-execute-class-fast (&optional shell dedicated switch beg end file) + "Process class at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'class shell dedicated switch beg end file t)) + +(defun py-execute-clause-fast (&optional shell dedicated switch beg end file) + "Process clause at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'clause shell dedicated switch beg end file t)) + +(defun py-execute-def-fast (&optional shell dedicated switch beg end file) + "Process def at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'def shell dedicated switch beg end file t)) + +(defun py-execute-def-or-class-fast (&optional shell dedicated switch beg end file) + "Process def-or-class at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'def-or-class shell dedicated switch beg end file t)) + +(defun py-execute-expression-fast (&optional shell dedicated switch beg end file) + "Process expression at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'expression shell dedicated switch beg end file t)) + +(defun py-execute-partial-expression-fast (&optional shell dedicated switch beg end file) + "Process partial-expression at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'partial-expression shell dedicated switch beg end file t)) + +(defun py-execute-section-fast (&optional shell dedicated switch beg end file) + "Process section at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'section shell dedicated switch beg end file t)) + +(defun py-execute-statement-fast (&optional shell dedicated switch beg end file) + "Process statement at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'statement shell dedicated switch beg end file t)) + +(defun py-execute-top-level-fast (&optional shell dedicated switch beg end file) + "Process top-level at point by a Python interpreter. + +Output buffer not in comint-mode, displays \"Fast\" by default +Optional SHELL: Selecte a Python-shell(VERSION) as py-shell-name +Optional DEDICATED: run in a dedicated process +Optional SWITCH: switch to output buffer after executing +Optional File: execute through running a temp-file" + (interactive) + (py--execute-prepare 'top-level shell dedicated switch beg end file t)) + +;; python-components-narrow + +(defun py-narrow-to-block () + "Narrow to block at point." + (interactive) + (py--narrow-prepare "block")) + +(defun py-narrow-to-block-or-clause () + "Narrow to block-or-clause at point." + (interactive) + (py--narrow-prepare "block-or-clause")) + +(defun py-narrow-to-class () + "Narrow to class at point." + (interactive) + (py--narrow-prepare "class")) + +(defun py-narrow-to-clause () + "Narrow to clause at point." + (interactive) + (py--narrow-prepare "clause")) + +(defun py-narrow-to-def () + "Narrow to def at point." + (interactive) + (py--narrow-prepare "def")) + +(defun py-narrow-to-def-or-class () + "Narrow to def-or-class at point." + (interactive) + (py--narrow-prepare "def-or-class")) + +(defun py-narrow-to-statement () + "Narrow to statement at point." + (interactive) + (py--narrow-prepare "statement")) + +;; python-components-hide-show + +;; (setq hs-block-start-regexp 'py-extended-block-or-clause-re) +;; (setq hs-forward-sexp-func 'py-forward-block) + +(defun py-hide-base (form &optional beg end) + "Hide visibility of existing form at point." + (hs-minor-mode 1) + (save-excursion + (let* ((form (prin1-to-string form)) + (beg (or beg (or (funcall (intern-soft (concat "py--beginning-of-" form "-p"))) + (funcall (intern-soft (concat "py-backward-" form)))))) + (end (or end (funcall (intern-soft (concat "py-forward-" form))))) + (modified (buffer-modified-p)) + (inhibit-read-only t)) + (if (and beg end) + (progn + (hs-make-overlay beg end 'code) + (set-buffer-modified-p modified)) + (error (concat "No " (format "%s" form) " at point")))))) + +(defun py-hide-show (&optional form beg end) + "Toggle visibility of existing forms at point." + (interactive) + (save-excursion + (let* ((form (prin1-to-string form)) + (beg (or beg (or (funcall (intern-soft (concat "py--beginning-of-" form "-p"))) + (funcall (intern-soft (concat "py-backward-" form)))))) + (end (or end (funcall (intern-soft (concat "py-forward-" form))))) + (modified (buffer-modified-p)) + (inhibit-read-only t)) + (if (and beg end) + (if (overlays-in beg end) + (hs-discard-overlays beg end) + (hs-make-overlay beg end 'code)) + (error (concat "No " (format "%s" form) " at point"))) + (set-buffer-modified-p modified)))) + +(defun py-show () + "Remove invisibility of existing form at point." + (interactive) + (with-silent-modifications + (save-excursion + (back-to-indentation) + (let ((end (next-overlay-change (point)))) + (hs-discard-overlays (point) end))))) + +(defun py-show-all () + "Remove invisibility of hidden forms in buffer." + (interactive) + (save-excursion + (goto-char (point-min)) + (let (end) + (while (and (not (eobp)) (setq end (next-overlay-change (point)))) + (hs-discard-overlays (point) end) + (goto-char end))))) + +(defun py-hide-region (beg end) + "Hide active region." + (interactive + (list + (and (use-region-p) (region-beginning))(and (use-region-p) (region-end)))) + (py-hide-base 'region beg end)) + +(defun py-show-region (beg end) + "Un-hide active region." + (interactive + (list + (and (use-region-p) (region-beginning))(and (use-region-p) (region-end)))) + (hs-discard-overlays beg end)) + +(defun py-hide-block () + "Hide block at point." + (interactive) + (py-hide-base 'block)) + +(defun py-hide-block-or-clause () + "Hide block-or-clause at point." + (interactive) + (py-hide-base 'block-or-clause)) + +(defun py-hide-class () + "Hide class at point." + (interactive) + (py-hide-base 'class)) + +(defun py-hide-clause () + "Hide clause at point." + (interactive) + (py-hide-base 'clause)) + +(defun py-hide-comment () + "Hide comment at point." + (interactive) + (py-hide-base 'comment)) + +(defun py-hide-def () + "Hide def at point." + (interactive) + (py-hide-base 'def)) + +(defun py-hide-def-or-class () + "Hide def-or-class at point." + (interactive) + (py-hide-base 'def-or-class)) + +(defun py-hide-elif-block () + "Hide elif-block at point." + (interactive) + (py-hide-base 'elif-block)) + +(defun py-hide-else-block () + "Hide else-block at point." + (interactive) + (py-hide-base 'else-block)) + +(defun py-hide-except-block () + "Hide except-block at point." + (interactive) + (py-hide-base 'except-block)) + +(defun py-hide-expression () + "Hide expression at point." + (interactive) + (py-hide-base 'expression)) + +(defun py-hide-for-block () + "Hide for-block at point." + (interactive) + (py-hide-base 'for-block)) + +(defun py-hide-if-block () + "Hide if-block at point." + (interactive) + (py-hide-base 'if-block)) + +(defun py-hide-indent () + "Hide indent at point." + (interactive) + (py-hide-base 'indent)) + +(defun py-hide-line () + "Hide line at point." + (interactive) + (py-hide-base 'line)) + +(defun py-hide-minor-block () + "Hide minor-block at point." + (interactive) + (py-hide-base 'minor-block)) + +(defun py-hide-paragraph () + "Hide paragraph at point." + (interactive) + (py-hide-base 'paragraph)) + +(defun py-hide-partial-expression () + "Hide partial-expression at point." + (interactive) + (py-hide-base 'partial-expression)) + +(defun py-hide-section () + "Hide section at point." + (interactive) + (py-hide-base 'section)) + +(defun py-hide-statement () + "Hide statement at point." + (interactive) + (py-hide-base 'statement)) + +(defun py-hide-top-level () + "Hide top-level at point." + (interactive) + (py-hide-base 'top-level)) + +(defun py-dynamically-hide-indent () + (interactive) + (py-show) + (py-hide-indent)) + +(defun py-dynamically-hide-further-indent (&optional arg) + (interactive "P") + (if (eq 4 (prefix-numeric-value arg)) + (py-show) + (py-show) + (py-forward-indent) + (py-hide-indent))) + +;; python-components-hide-show.el ends here +;; python-components-foot + +(defun py-shell-fontify () + "Fontifies input in shell buffer. " + ;; causes delay in fontification until next trigger + ;; (unless (or (member (char-before) (list 32 ?: ?\))) + ;; (unless (and (eq last-command 'self-insert-command) (eq (char-before) 32)) + ;; (< (abs (save-excursion (skip-chars-backward "^ \t\r\n\f"))) 2)) + (let* ((pps (parse-partial-sexp (line-beginning-position) (point))) + (start (if (and (nth 8 pps) (nth 1 pps)) + (max (nth 1 pps) (nth 8 pps)) + (or (nth 1 pps) (nth 8 pps))))) + (when (or start + (setq start (ignore-errors (cdr comint-last-prompt)))) + (let* ((input (buffer-substring-no-properties + start (point-max))) + (buffer-undo-list t) + (replacement + (save-current-buffer + (set-buffer py-shell--font-lock-buffer) + (erase-buffer) + (insert input) + ;; Ensure buffer is fontified, keeping it + ;; compatible with Emacs < 24.4. + (if (fboundp 'font-lock-ensure) + (funcall 'font-lock-ensure) + (font-lock-default-fontify-buffer)) + (buffer-substring (point-min) (point-max)))) + (replacement-length (length replacement)) + (i 0)) + ;; Inject text properties to get input fontified. + (while (not (= i replacement-length)) + (let* ((plist (text-properties-at i replacement)) + (next-change (or (next-property-change i replacement) + replacement-length)) + (plist (let ((face (plist-get plist 'face))) + (if (not face) + plist + ;; Replace FACE text properties with + ;; FONT-LOCK-FACE so input is fontified. + (plist-put plist 'face nil) + (plist-put plist 'font-lock-face face))))) + (set-text-properties + (+ start i) (+ start next-change) plist) + (setq i next-change))))))) + +(defun py-message-which-python-mode () + (if (buffer-file-name) + (if (string= "python-mode-el" (buffer-file-name)) + (message "%s" "python-mode loaded from python-mode-el") + (message "%s" "python-mode loaded from python-components-mode")) + (message "python-mode loaded from: %s" python-mode-message-string))) + +(defalias 'py-next-statement 'py-forward-statement) +;; #134, cython-mode compatibility +(defalias 'py-end-of-statement 'py-forward-statement) +(defalias 'py-beginning-of-statement 'py-backward-statement) +(defalias 'py-beginning-of-block 'py-backward-block) +(defalias 'py-end-of-block 'py-forward-block) +(defalias 'py-previous-statement 'py-backward-statement) +(defalias 'py-markup-region-as-section 'py-sectionize-region) + +(define-derived-mode py-auto-completion-mode python-mode "Pac" + "Run auto-completion" + ;; disable company + ;; (when company-mode (company-mode)) + (if py-auto-completion-mode-p + (progn + (setq py-auto-completion-mode-p nil + py-auto-completion-buffer nil) + (when (timerp py--auto-complete-timer)(cancel-timer py--auto-complete-timer))) + (setq py-auto-completion-mode-p t + py-auto-completion-buffer (current-buffer)) + (setq py--auto-complete-timer + (run-with-idle-timer + py--auto-complete-timer-delay + ;; 1 + t + #'py-complete-auto))) + (force-mode-line-update)) + +(autoload 'python-mode "python-mode" "Python Mode." t) + +(defun all-mode-setting () + (set (make-local-variable 'indent-tabs-mode) py-indent-tabs-mode) + ) + +(define-derived-mode python-mode prog-mode python-mode-modeline-display + "Major mode for editing Python files. + +To submit a report, enter `\\[py-submit-bug-report]' +from a`python-mode' buffer. +Do `\\[py-describe-mode]' for detailed documentation. +To see what version of `python-mode' you are running, +enter `\\[py-version]'. + +This mode knows about Python indentation, +tokens, comments (and continuation lines. +Paragraphs are separated by blank lines only. + +COMMANDS + +`py-shell'\tStart an interactive Python interpreter in another window +`py-execute-statement'\tSend statement at point to Python default interpreter +`py-backward-statement'\tGo to the initial line of a simple statement + +etc. + +See available commands listed in files commands-python-mode at directory doc + +VARIABLES + +`py-indent-offset' indentation increment +`py-shell-name' shell command to invoke Python interpreter +`py-split-window-on-execute' When non-nil split windows +`py-switch-buffers-on-execute-p' When non-nil switch to the Python output buffer + +\\{python-mode-map}" + :group 'python-mode + ;; load known shell listed in + ;; Local vars + (all-mode-setting) + (set (make-local-variable 'electric-indent-inhibit) nil) + (set (make-local-variable 'outline-regexp) + (concat (mapconcat 'identity + (mapcar #'(lambda (x) (concat "^\\s-*" x "\\_>")) + py-outline-mode-keywords) + "\\|"))) + (when py-font-lock-defaults-p + (if py-use-font-lock-doc-face-p + (set (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords nil nil nil nil + (font-lock-syntactic-keywords + . py-font-lock-syntactic-keywords) + (font-lock-syntactic-face-function + . py--font-lock-syntactic-face-function))) + (set (make-local-variable 'font-lock-defaults) + '(python-font-lock-keywords nil nil nil nil + (font-lock-syntactic-keywords + . py-font-lock-syntactic-keywords))))) + ;; avoid to run py-choose-shell again from `py--fix-start' + (cond ((string-match "ython3" py-python-edit-version) + (font-lock-add-keywords 'python-mode + '(("\\" . 'py-builtins-face) + ("\\" . nil)))) + (t (font-lock-add-keywords 'python-mode + '(("\\" . 'font-lock-keyword-face) + ("\\" . 'py-builtins-face))))) + (set (make-local-variable 'which-func-functions) 'py-which-def-or-class) + (set (make-local-variable 'parse-sexp-lookup-properties) t) + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'comment-start) "#") + (set (make-local-variable 'comment-start-skip) "^[ \t]*#+ *") + + (if py-empty-comment-line-separates-paragraph-p + (progn + (set (make-local-variable 'paragraph-separate) (concat "\f\\|^[\t]*$\\|^[ \t]*" comment-start "[ \t]*$\\|^[\t\f]*:[[:alpha:]]+ [[:alpha:]]+:.+$")) + (set (make-local-variable 'paragraph-start) + (concat "\f\\|^[ \t]*$\\|^[ \t]*" comment-start "[ \t]*$\\|^[ \t\f]*:[[:alpha:]]+ [[:alpha:]]+:.+$")) + (set (make-local-variable 'paragraph-separate) + (concat "\f\\|^[ \t]*$\\|^[ \t]*" comment-start "[ \t]*$\\|^[ \t\f]*:[[:alpha:]]+ [[:alpha:]]+:.+$"))) + (set (make-local-variable 'paragraph-separate) "\f\\|^[ \t]*$\\|^[\t]*#[ \t]*$\\|^[ \t\f]*:[[:alpha:]]+ [[:alpha:]]+:.+$") + (set (make-local-variable 'paragraph-start) "\f\\|^[ \t]*$\\|^[\t]*#[ \t]*$\\|^[ \t\f]*:[[:alpha:]]+ [[:alpha:]]+:.+$")) + (set (make-local-variable 'comment-column) 40) + (set (make-local-variable 'comment-indent-function) #'py--comment-indent-function) + (set (make-local-variable 'indent-region-function) 'py-indent-region) + (set (make-local-variable 'indent-line-function) 'py-indent-line) + ;; introduced to silence compiler warning, no real setting + ;; (set (make-local-variable 'hs-hide-comments-when-hiding-all) 'py-hide-comments-when-hiding-all) + (set (make-local-variable 'outline-heading-end-regexp) ":[^\n]*\n") + (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil) + (set (make-local-variable 'add-log-current-defun-function) 'py-current-defun) + (set (make-local-variable 'fill-paragraph-function) 'py-fill-paragraph) + (set (make-local-variable 'normal-auto-fill-function) 'py-fill-string-or-comment) + (set (make-local-variable 'require-final-newline) mode-require-final-newline) + (set (make-local-variable 'tab-width) py-indent-offset) + (set (make-local-variable 'electric-indent-mode) nil) + (and py-load-skeletons-p (py-load-skeletons)) + (and py-guess-py-install-directory-p (py-set-load-path)) + (and py-autopair-mode + (load-library "autopair") + (add-hook 'python-mode-hook + #'(lambda () + (setq autopair-handle-action-fns + (list #'autopair-default-handle-action + #'autopair-python-triple-quote-action)))) + (py-autopair-mode-on)) + (when (and py--imenu-create-index-p + (fboundp 'imenu-add-to-menubar) + (ignore-errors (require 'imenu))) + (setq imenu-create-index-function 'py--imenu-create-index-function) + (setq imenu--index-alist (funcall py--imenu-create-index-function)) + ;; fallback + (unless imenu--index-alist + (setq imenu--index-alist (py--imenu-create-index-new))) + ;; (message "imenu--index-alist: %s" imenu--index-alist) + (imenu-add-to-menubar "PyIndex")) + (when py-trailing-whitespace-smart-delete-p + (add-hook 'before-save-hook 'delete-trailing-whitespace nil 'local)) + ;; this should go into interactive modes + ;; (when py-pdbtrack-do-tracking-p + ;; (add-hook 'comint-output-filter-functions 'py--pdbtrack-track-stack-file)) + (py-shell-prompt-set-calculated-regexps) + (setq comint-prompt-regexp py-shell--prompt-calculated-input-regexp) + (cond + (py-complete-function + (add-hook 'completion-at-point-functions + py-complete-function)) + (py-load-pymacs-p + (add-hook 'completion-at-point-functions + 'py-complete-completion-at-point nil 'local)) + (t + (add-hook 'completion-at-point-functions + 'py-shell-complete nil 'local))) + ;; #'python-shell-completion-at-point nil 'local))) + ;; (if py-auto-complete-p + ;; (add-hook 'python-mode-hook 'py--run-completion-timer) + ;; (remove-hook 'python-mode-hook 'py--run-completion-timer)) + ;; (when py-auto-complete-p + ;; (add-hook 'python-mode-hook + ;; (lambda () + ;; (run-with-idle-timer 1 t 'py-shell-complete)))) + (if py-auto-fill-mode + (add-hook 'python-mode-hook 'py--run-auto-fill-timer) + (remove-hook 'python-mode-hook 'py--run-auto-fill-timer)) + (add-hook 'python-mode-hook + (lambda () + (setq imenu-create-index-function py--imenu-create-index-function))) + ;; caused insert-file-contents error lp:1293172 + ;; (add-hook 'after-change-functions 'py--after-change-function nil t) + (if py-defun-use-top-level-p + (progn + (set (make-local-variable 'beginning-of-defun-function) 'py-backward-top-level) + (set (make-local-variable 'end-of-defun-function) 'py-forward-top-level) + (define-key python-mode-map [(control meta a)] 'py-backward-top-level) + (define-key python-mode-map [(control meta e)] 'py-forward-top-level)) + (set (make-local-variable 'beginning-of-defun-function) 'py-backward-def-or-class) + (set (make-local-variable 'end-of-defun-function) 'py-forward-def-or-class) + (define-key python-mode-map [(control meta a)] 'py-backward-def-or-class) + (define-key python-mode-map [(control meta e)] 'py-forward-def-or-class)) + (when py-sexp-use-expression-p + (define-key python-mode-map [(control meta f)] 'py-forward-expression) + (define-key python-mode-map [(control meta b)] 'py-backward-expression)) + + (when py-hide-show-minor-mode-p (hs-minor-mode 1)) + (when py-outline-minor-mode-p (outline-minor-mode 1)) + (when (and py-debug-p (called-interactively-p 'any)) + (py-message-which-python-mode)) + (force-mode-line-update)) + +(define-derived-mode py-shell-mode comint-mode py-modeline-display + "Major mode for Python shell process. + +Variables +`py-shell-prompt-regexp', +`py-shell-prompt-output-regexp', +`py-shell-input-prompt-2-regexp', +`py-shell-fontify-p', +`py-completion-setup-code', +`py-shell-completion-string-code', +can customize this mode for different Python interpreters. + +This mode resets `comint-output-filter-functions' locally, so you +may want to re-add custom functions to it using the +`py-shell-mode-hook'. + +\(Type \\[describe-mode] in the process buffer for a list of commands.)" + (setq mode-line-process '(":%s")) + (all-mode-setting) + ;; (set (make-local-variable 'indent-tabs-mode) nil) + (set (make-local-variable 'py-shell--prompt-calculated-input-regexp) nil) + (set (make-local-variable 'py-shell--block-prompt) nil) + (set (make-local-variable 'py-shell--prompt-calculated-output-regexp) nil) + (py-shell-prompt-set-calculated-regexps) + (set (make-local-variable 'comint-prompt-read-only) t) + (set (make-local-variable 'comint-output-filter-functions) + '(ansi-color-process-output + py-comint-watch-for-first-prompt-output-filter + py-pdbtrack-comint-output-filter-function + py-comint-postoutput-scroll-to-bottom + comint-watch-for-password-prompt)) + (set (make-local-variable 'compilation-error-regexp-alist) + py-shell-compilation-regexp-alist) + (compilation-shell-minor-mode 1) + (add-hook 'completion-at-point-functions + #'py-shell-completion-at-point nil 'local) + (cond + ((string-match "^[Jj]" (process-name (get-buffer-process (current-buffer)))) + 'indent-for-tab-command) + (t + (define-key py-shell-mode-map "\t" + 'py-indent-or-complete))) + (make-local-variable 'py-pdbtrack-buffers-to-kill) + (make-local-variable 'py-shell-fast-last-output) + (set (make-local-variable 'py-shell--block-prompt) nil) + (set (make-local-variable 'py-shell--prompt-calculated-output-regexp) nil) + (py-shell-prompt-set-calculated-regexps) + (if py-shell-fontify-p + (progn + (py-shell-font-lock-turn-on)) + (py-shell-font-lock-turn-off))) + +(make-obsolete 'jpython-mode 'jython-mode nil) + +;; (push "*Python*" same-window-buffer-names) +;; (push "*IPython*" same-window-buffer-names) + +;; Python Macro File +(unless (member '("\\.py\\'" . python-mode) auto-mode-alist) + (push (cons "\\.py\\'" 'python-mode) auto-mode-alist)) + +(unless (member '("\\.pym\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pym\\'" 'python-mode) auto-mode-alist)) + +(unless (member '("\\.pyc\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pyc\\'" 'python-mode) auto-mode-alist)) + +;; Pyrex Source +(unless (member '("\\.pyx\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pyx\\'" 'python-mode) auto-mode-alist)) + +;; Python Optimized Code +(unless (member '("\\.pyo\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pyo\\'" 'python-mode) auto-mode-alist)) + +;; Pyrex Definition File +(unless (member '("\\.pxd\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pxd\\'" 'python-mode) auto-mode-alist)) + +;; Python Repository +(unless (member '("\\.pyr\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pyr\\'" 'python-mode) auto-mode-alist)) + +;; Python Stub file +;; https://www.python.org/dev/peps/pep-0484/#stub-files +(unless (member '("\\.pyi\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pyi\\'" 'python-mode) auto-mode-alist)) + +;; Python Path Configuration +(unless (member '("\\.pth\\'" . python-mode) auto-mode-alist) + (push (cons "\\.pth\\'" 'python-mode) auto-mode-alist)) + +;; Python Wheels +(unless (member '("\\.whl\\'" . python-mode) auto-mode-alist) + (push (cons "\\.whl\\'" 'python-mode) auto-mode-alist)) + +(unless (member '("!#[ ]*/.*[jp]ython[0-9.]*" . python-mode) magic-mode-alist) + (push '("!#[ \\t]*/.*[jp]ython[0-9.]*" . python-mode) magic-mode-alist)) + +;; lp:1355458, what about using `magic-mode-alist'? + +(defalias 'py-hungry-delete-forward 'c-hungry-delete-forward) +(defalias 'py-hungry-delete-backwards 'c-hungry-delete-backwards) + +;;; +(provide 'python-mode) +;;; python-mode.el ends here diff --git a/code/elpa/rust-mode-20220819.1203/rust-cargo.el b/code/elpa/rust-mode-20220819.1203/rust-cargo.el new file mode 100644 index 0000000..0c35ddb --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-cargo.el @@ -0,0 +1,126 @@ +;;; rust-cargo.el --- Support for cargo -*-lexical-binding: t-*- +;;; Commentary: + +;; This library implements support for running `cargo'. + +;;; Code: + +(require 'json) + +;;; Options + +(defcustom rust-cargo-bin "cargo" + "Path to cargo executable." + :type 'string + :group 'rust-mode) + +(defcustom rust-always-locate-project-on-open nil + "Whether to run `cargo locate-project' every time `rust-mode' is activated." + :type 'boolean + :group 'rust-mode) + +(defcustom rust-cargo-default-arguments "" + "Default arguments when running common cargo commands." + :type 'string + :group 'rust-mode) + +;;; Buffer Project + +(defvar-local rust-buffer-project nil) + +(defun rust-buffer-project () + "Get project root if possible." + (if (file-remote-p default-directory) + (rust-buffer-crate) + ;; Copy environment variables into the new buffer, since + ;; with-temp-buffer will re-use the variables' defaults, even if + ;; they have been changed in this variable using e.g. envrc-mode. + ;; See https://github.com/purcell/envrc/issues/12. + (let ((env process-environment) + (path exec-path)) + (with-temp-buffer + ;; Copy the entire environment just in case there's something we + ;; don't know we need. + (setq-local process-environment env) + ;; Set PATH so we can find cargo. + (setq-local exec-path path) + (let ((ret (call-process rust-cargo-bin nil t nil "locate-project"))) + (when (/= ret 0) + (error "`cargo locate-project' returned %s status: %s" ret (buffer-string))) + (goto-char 0) + (let ((output (json-read))) + (cdr (assoc-string "root" output)))))))) + +(defun rust-buffer-crate () + "Try to locate Cargo.toml using `locate-dominating-file'." + (let ((dir (locate-dominating-file default-directory "Cargo.toml"))) + (if dir dir default-directory))) + +(defun rust-update-buffer-project () + (setq-local rust-buffer-project (rust-buffer-project))) + +(defun rust-maybe-initialize-buffer-project () + (setq-local rust-buffer-project nil) + (when rust-always-locate-project-on-open + (rust-update-buffer-project))) + +(add-hook 'rust-mode-hook #'rust-maybe-initialize-buffer-project) + +;;; Internal + +(defun rust--compile (format-string &rest args) + (when (null rust-buffer-project) + (rust-update-buffer-project)) + (let ((default-directory + (or (and rust-buffer-project + (file-name-directory rust-buffer-project)) + default-directory))) + (compile (apply #'format format-string args)))) + +;;; Commands + +(defun rust-check () + "Compile using `cargo check`" + (interactive) + (rust--compile "%s check %s" rust-cargo-bin rust-cargo-default-arguments)) + +(defun rust-compile () + "Compile using `cargo build`" + (interactive) + (rust--compile "%s build %s" rust-cargo-bin rust-cargo-default-arguments)) + +(defun rust-compile-release () + "Compile using `cargo build --release`" + (interactive) + (rust--compile "%s build --release" rust-cargo-bin)) + +(defun rust-run () + "Run using `cargo run`" + (interactive) + (rust--compile "%s run %s" rust-cargo-bin rust-cargo-default-arguments)) + +(defun rust-run-release () + "Run using `cargo run --release`" + (interactive) + (rust--compile "%s run --release" rust-cargo-bin)) + +(defun rust-test () + "Test using `cargo test`" + (interactive) + (rust--compile "%s test %s" rust-cargo-bin rust-cargo-default-arguments)) + +(defun rust-run-clippy () + "Run `cargo clippy'." + (interactive) + (when (null rust-buffer-project) + (rust-update-buffer-project)) + (let* ((args (list rust-cargo-bin "clippy" + (concat "--manifest-path=" rust-buffer-project))) + ;; set `compile-command' temporarily so `compile' doesn't + ;; clobber the existing value + (compile-command (mapconcat #'shell-quote-argument args " "))) + (rust--compile compile-command))) + +;;; _ +(provide 'rust-cargo) +;;; rust-cargo.el ends here diff --git a/code/elpa/rust-mode-20220819.1203/rust-compile.el b/code/elpa/rust-mode-20220819.1203/rust-compile.el new file mode 100644 index 0000000..1bb3103 --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-compile.el @@ -0,0 +1,83 @@ +;;; rust-compile.el --- Compile facilities -*-lexical-binding: t-*- +;;; Commentary: + +;; This library teaches `compilation-mode' about "rustc" output. + +;;; Code: + +(require 'compile) + +;;; _ + +(defvar rustc-compilation-location + (let ((file "\\([^\n]+\\)") + (start-line "\\([0-9]+\\)") + (start-col "\\([0-9]+\\)")) + (concat "\\(" file ":" start-line ":" start-col "\\)"))) + +(defvar rustc-compilation-regexps + (let ((re (concat "^\\(?:error\\|\\(warning\\)\\|\\(note\\)\\)[^\0]+?--> " + rustc-compilation-location))) + (cons re '(4 5 6 (1 . 2) 3))) + "Specifications for matching errors in rustc invocations. +See `compilation-error-regexp-alist' for help on their format.") + +(defvar rustc-colon-compilation-regexps + (let ((re (concat "^ *::: " rustc-compilation-location))) + (cons re '(2 3 4 0 1))) + "Specifications for matching `:::` hints in rustc invocations. +See `compilation-error-regexp-alist' for help on their format.") + +(defvar rustc-refs-compilation-regexps + (let ((re "^\\([0-9]+\\)[[:space:]]*|")) + (cons re '(nil 1 nil 0 1))) + "Specifications for matching code references in rustc invocations. +See `compilation-error-regexp-alist' for help on their format.") + +;; Match test run failures and panics during compilation as +;; compilation warnings +(defvar cargo-compilation-regexps + '("', \\(\\([^:]+\\):\\([0-9]+\\)\\)" + 2 3 nil nil 1) + "Specifications for matching panics in cargo test invocations. +See `compilation-error-regexp-alist' for help on their format.") + +(defun rustc-scroll-down-after-next-error () + "In the new style error messages, the regular expression +matches on the file name (which appears after `-->`), but the +start of the error appears a few lines earlier. This hook runs +after `next-error' (\\[next-error]); it simply scrolls down a few lines in +the compilation window until the top of the error is visible." + (save-selected-window + (when (eq major-mode 'rust-mode) + (select-window (get-buffer-window next-error-last-buffer 'visible)) + (when (save-excursion + (beginning-of-line) + (looking-at " *-->")) + (let ((start-of-error + (save-excursion + (beginning-of-line) + (while (not (looking-at "^[a-z]+:\\|^[a-z]+\\[E[0-9]+\\]:")) + (forward-line -1)) + (point)))) + (set-window-start (selected-window) start-of-error)))))) + +(eval-after-load 'compile + '(progn + (add-to-list 'compilation-error-regexp-alist-alist + (cons 'rustc-refs rustc-refs-compilation-regexps)) + (add-to-list 'compilation-error-regexp-alist 'rustc-refs) + (add-to-list 'compilation-error-regexp-alist-alist + (cons 'rustc rustc-compilation-regexps)) + (add-to-list 'compilation-error-regexp-alist 'rustc) + (add-to-list 'compilation-error-regexp-alist-alist + (cons 'rustc-colon rustc-colon-compilation-regexps)) + (add-to-list 'compilation-error-regexp-alist 'rustc-colon) + (add-to-list 'compilation-error-regexp-alist-alist + (cons 'cargo cargo-compilation-regexps)) + (add-to-list 'compilation-error-regexp-alist 'cargo) + (add-hook 'next-error-hook #'rustc-scroll-down-after-next-error))) + +;;; _ +(provide 'rust-compile) +;;; rust-compile.el ends here diff --git a/code/elpa/rust-mode-20220819.1203/rust-mode-autoloads.el b/code/elpa/rust-mode-20220819.1203/rust-mode-autoloads.el new file mode 100644 index 0000000..8ccb5f8 --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-mode-autoloads.el @@ -0,0 +1,73 @@ +;;; rust-mode-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "rust-cargo" "rust-cargo.el" (0 0 0 0)) +;;; Generated autoloads from rust-cargo.el + +(register-definition-prefixes "rust-cargo" '("rust-")) + +;;;*** + +;;;### (autoloads nil "rust-compile" "rust-compile.el" (0 0 0 0)) +;;; Generated autoloads from rust-compile.el + +(register-definition-prefixes "rust-compile" '("cargo-compilation-regexps" "rustc-")) + +;;;*** + +;;;### (autoloads nil "rust-mode" "rust-mode.el" (0 0 0 0)) +;;; Generated autoloads from rust-mode.el + +(autoload 'rust-mode "rust-mode" "\ +Major mode for Rust code. + +\\{rust-mode-map} + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) + +(register-definition-prefixes "rust-mode" '("rust-")) + +;;;*** + +;;;### (autoloads nil "rust-playpen" "rust-playpen.el" (0 0 0 0)) +;;; Generated autoloads from rust-playpen.el + +(register-definition-prefixes "rust-playpen" '("rust-")) + +;;;*** + +;;;### (autoloads nil "rust-rustfmt" "rust-rustfmt.el" (0 0 0 0)) +;;; Generated autoloads from rust-rustfmt.el + +(register-definition-prefixes "rust-rustfmt" '("rust-")) + +;;;*** + +;;;### (autoloads nil "rust-utils" "rust-utils.el" (0 0 0 0)) +;;; Generated autoloads from rust-utils.el + +(autoload 'rust-dbg-wrap-or-unwrap "rust-utils" "\ +Either remove or add the dbg! macro." t nil) + +(register-definition-prefixes "rust-utils" '("rust-")) + +;;;*** + +;;;### (autoloads nil nil ("rust-mode-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; rust-mode-autoloads.el ends here diff --git a/code/elpa/rust-mode-20220819.1203/rust-mode-pkg.el b/code/elpa/rust-mode-20220819.1203/rust-mode-pkg.el new file mode 100644 index 0000000..fe05ad9 --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-mode-pkg.el @@ -0,0 +1,12 @@ +(define-package "rust-mode" "20220819.1203" "A major-mode for editing Rust source code" + '((emacs "25.1")) + :commit "894487d44c1664a9005cafd625fa99b54ff66c85" :authors + '(("Mozilla")) + :maintainer + '("Mozilla") + :keywords + '("languages") + :url "https://github.com/rust-lang/rust-mode") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/rust-mode-20220819.1203/rust-mode.el b/code/elpa/rust-mode-20220819.1203/rust-mode.el new file mode 100644 index 0000000..997f2b1 --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-mode.el @@ -0,0 +1,1632 @@ +;;; rust-mode.el --- A major-mode for editing Rust source code -*-lexical-binding: t-*- + +;; Version: 1.0.5 +;; Author: Mozilla +;; Url: https://github.com/rust-lang/rust-mode +;; Keywords: languages +;; Package-Requires: ((emacs "25.1")) + +;; This file is distributed under the terms of both the MIT license and the +;; Apache License (version 2.0). + +;;; Commentary: + +;; This package implements a major-mode for editing Rust source code. + +;;; Code: + +(eval-when-compile (require 'rx)) + +(defvar rust-load-optional-libraries t + "Whether loading `rust-mode' also loads optional libraries. +This variable might soon be remove again.") + +(when rust-load-optional-libraries + (require 'rust-cargo) + (require 'rust-compile) + (require 'rust-playpen) + (require 'rust-rustfmt)) + +(defvar electric-pair-inhibit-predicate) +(defvar electric-pair-skip-self) +(defvar electric-indent-chars) + +(defcustom rust-before-save-hook 'rust-before-save-method + "Function for formatting before save." + :type 'function + :group 'rust-mode) + +(defcustom rust-after-save-hook 'rust-after-save-method + "Default method to handle rustfmt invocation after save." + :type 'function + :group 'rust-mode) + +(defvar rust-prettify-symbols-alist + '(("&&" . ?∧) ("||" . ?∨) + ("<=" . ?≤) (">=" . ?≥) ("!=" . ?≠) + ("INFINITY" . ?∞) ("->" . ?→) ("=>" . ?⇒)) + "Alist of symbol prettifications used for `prettify-symbols-alist'.") + +;;; Customization + +(defgroup rust-mode nil + "Support for Rust code." + :link '(url-link "https://www.rust-lang.org/") + :group 'languages) + +(defcustom rust-indent-offset 4 + "Indent Rust code by this number of spaces." + :type 'integer + :group 'rust-mode + :safe #'integerp) + +(defcustom rust-indent-method-chain nil + "Indent Rust method chains, aligned by the `.' operators." + :type 'boolean + :group 'rust-mode + :safe #'booleanp) + +(defcustom rust-indent-where-clause nil + "Indent lines starting with the `where' keyword following a function or trait. +When nil, `where' will be aligned with `fn' or `trait'." + :type 'boolean + :group 'rust-mode + :safe #'booleanp) + +(defcustom rust-match-angle-brackets t + "Whether to enable angle bracket (`<' and `>') matching where appropriate." + :type 'boolean + :safe #'booleanp + :group 'rust-mode) + +(defcustom rust-indent-return-type-to-arguments t + "Indent a line starting with the `->' (RArrow) following a function, aligning +to the function arguments. When nil, `->' will be indented one level." + :type 'boolean + :group 'rust-mode + :safe #'booleanp) + +;;; Faces + +(define-obsolete-face-alias 'rust-unsafe-face + 'rust-unsafe "0.6.0") +(define-obsolete-face-alias 'rust-question-mark-face + 'rust-question-mark "0.6.0") +(define-obsolete-face-alias 'rust-builtin-formatting-macro-face + 'rust-builtin-formatting-macro "0.6.0") +(define-obsolete-face-alias 'rust-string-interpolation-face + 'rust-string-interpolation "0.6.0") + +(defface rust-unsafe + '((t :inherit font-lock-warning-face)) + "Face for the `unsafe' keyword." + :group 'rust-mode) + +(defface rust-question-mark + '((t :weight bold :inherit font-lock-builtin-face)) + "Face for the question mark operator." + :group 'rust-mode) + +(defface rust-ampersand-face + '((t :inherit default)) + "Face for the ampersand reference mark." + :group 'rust-mode) + +(defface rust-builtin-formatting-macro + '((t :inherit font-lock-builtin-face)) + "Face for builtin formatting macros (print! &c.)." + :group 'rust-mode) + +(defface rust-string-interpolation + '((t :slant italic :inherit font-lock-string-face)) + "Face for interpolating braces in builtin formatting macro strings." + :group 'rust-mode) + +;;; Syntax + +(defun rust-re-word (inner) (concat "\\<" inner "\\>")) +(defun rust-re-grab (inner) (concat "\\(" inner "\\)")) +(defun rust-re-shy (inner) (concat "\\(?:" inner "\\)")) + +(defconst rust-re-ident "[[:word:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*") +(defconst rust-re-lc-ident "[[:lower:][:multibyte:]_][[:word:][:multibyte:]_[:digit:]]*") +(defconst rust-re-uc-ident "[[:upper:]][[:word:][:multibyte:]_[:digit:]]*") +(defvar rust-re-vis + ;; pub | pub ( crate ) | pub ( self ) | pub ( super ) | pub ( in SimplePath ) + (concat + "pub" + (rust-re-shy + (concat + "[[:space:]]*([[:space:]]*" + (rust-re-shy + (concat "crate" "\\|" + "\\(?:s\\(?:elf\\|uper\\)\\)" "\\|" + ;; in SimplePath + (rust-re-shy + (concat + "in[[:space:]]+" + rust-re-ident + (rust-re-shy (concat "::" rust-re-ident)) "*")))) + "[[:space:]]*)")) + "?")) +(defconst rust-re-unsafe "unsafe") +(defconst rust-re-extern "extern") +(defconst rust-re-async-or-const "async\\|const") +(defconst rust-re-generic + (concat "<[[:space:]]*'" rust-re-ident "[[:space:]]*>")) +(defconst rust-re-union + (rx-to-string + `(seq + (or space line-start) + (group symbol-start "union" symbol-end) + (+ space) (regexp ,rust-re-ident)))) + +(defun rust-re-item-def (itype) + (concat (rust-re-word itype) + (rust-re-shy rust-re-generic) "?" + "[[:space:]]+" (rust-re-grab rust-re-ident))) + +;; TODO some of this does only make sense for `fn' (unsafe, extern...) +;; and not other items +(defun rust-re-item-def-imenu (itype) + (concat "^[[:space:]]*" + (rust-re-shy (concat rust-re-vis "[[:space:]]+")) "?" + (rust-re-shy (concat (rust-re-word "default") "[[:space:]]+")) "?" + (rust-re-shy (concat (rust-re-shy rust-re-async-or-const) "[[:space:]]+")) "?" + (rust-re-shy (concat (rust-re-word rust-re-unsafe) "[[:space:]]+")) "?" + (rust-re-shy (concat (rust-re-word rust-re-extern) "[[:space:]]+" + (rust-re-shy "\"[^\"]+\"[[:space:]]+") "?")) "?" + (rust-re-item-def itype))) + +(defvar rust-imenu-generic-expression + (append (mapcar #'(lambda (x) + (list (capitalize x) (rust-re-item-def-imenu x) 1)) + '("enum" "struct" "union" "type" "mod" "fn" "trait" "impl")) + `(("Macro" ,(rust-re-item-def-imenu "macro_rules!") 1))) + "Value for `imenu-generic-expression' in Rust mode. + +Create a hierarchical index of the item definitions in a Rust file. + +Imenu will show all the enums, structs, etc. in their own subheading. +Use idomenu (imenu with `ido-mode') for best mileage.") + +(defvar rust-mode-syntax-table + (let ((table (make-syntax-table))) + + ;; Operators + (dolist (i '(?+ ?- ?* ?/ ?% ?& ?| ?^ ?! ?< ?> ?~ ?@)) + (modify-syntax-entry i "." table)) + + ;; Strings + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?\\ "\\" table) + + ;; Angle brackets. We suppress this with syntactic propertization + ;; when needed + (modify-syntax-entry ?< "(>" table) + (modify-syntax-entry ?> ")<" table) + + ;; Comments + (modify-syntax-entry ?/ ". 124b" table) + (modify-syntax-entry ?* ". 23n" table) + (modify-syntax-entry ?\n "> b" table) + (modify-syntax-entry ?\^m "> b" table) + + table) + "Syntax definitions and helpers.") + +;;; Prettify + +(defun rust--prettify-symbols-compose-p (start end match) + "Return true iff the symbol MATCH should be composed. +See `prettify-symbols-compose-predicate'." + (and (fboundp 'prettify-symbols-default-compose-p) + (prettify-symbols-default-compose-p start end match) + ;; Make sure || is not a closure with 0 arguments and && is not + ;; a double reference. + (pcase match + ("||" (not (save-excursion + (goto-char start) + (looking-back "\\(?:\\") + "Start of a Rust item.") + +(defconst rust-re-type-or-constructor + (rx symbol-start + (group upper (0+ (any word nonascii digit "_"))) + symbol-end)) + +(defconst rust-keywords + '("as" "async" "await" + "box" "break" + "const" "continue" "crate" + "do" "dyn" + "else" "enum" "extern" "existential" + "false" "fn" "for" + "if" "impl" "in" + "let" "loop" + "match" "mod" "move" "mut" + "priv" "pub" + "ref" "return" + "self" "static" "struct" "super" + "true" "trait" "type" "try" + "use" + "virtual" + "where" "while" + "yield") + "Font-locking definitions and helpers.") + +(defconst rust-special-types + '("u8" "i8" + "u16" "i16" + "u32" "i32" + "u64" "i64" + "u128" "i128" + + "f32" "f64" + "isize" "usize" + "bool" + "str" "char")) + +(defconst rust-number-with-type + (eval-when-compile + (concat + "\\_<\\(?:0[box]?\\|[1-9]\\)[[:digit:]a-fA-F_.]*\\(?:[eE][+-]?[[:digit:]_]\\)?" + (regexp-opt '("u8" "i8" "u16" "i16" "u32" "i32" "u64" "i64" + "u128" "i128" "usize" "isize" "f32" "f64") + t) + "\\_>")) + "Regular expression matching a number with a type suffix.") + +(defvar rust-builtin-formatting-macros + '("eprint" + "eprintln" + "format" + "print" + "println") + "List of builtin Rust macros for string formatting. +This is used by `rust-font-lock-keywords'. +\(`write!' is handled separately).") + +(defvar rust-formatting-macro-opening-re + "[[:space:]\n]*[({[][[:space:]\n]*" + "Regular expression to match the opening delimiter of a Rust formatting macro.") + +(defvar rust-start-of-string-re + "\\(?:r#*\\)?\"" + "Regular expression to match the start of a Rust raw string.") + +(defun rust-path-font-lock-matcher (re-ident) + "Match occurrences of RE-IDENT followed by a double-colon. +Examples include to match names like \"foo::\" or \"Foo::\". +Does not match type annotations of the form \"foo::<\"." + `(lambda (limit) + (catch 'rust-path-font-lock-matcher + (while t + (let* ((symbol-then-colons (rx-to-string '(seq (group (regexp ,re-ident)) "::"))) + (match (re-search-forward symbol-then-colons limit t))) + (cond + ;; If we didn't find a match, there are no more occurrences + ;; of foo::, so return. + ((null match) (throw 'rust-path-font-lock-matcher nil)) + ;; If this isn't a type annotation foo::<, we've found a + ;; match, so a return it! + ((not (looking-at (rx (0+ space) "<"))) + (throw 'rust-path-font-lock-matcher match)))))))) + +(defvar rust-font-lock-keywords + (append + `( + ;; Keywords proper + (,(regexp-opt rust-keywords 'symbols) . font-lock-keyword-face) + + ;; Contextual keywords + ("\\_<\\(default\\)[[:space:]]+fn\\_>" 1 font-lock-keyword-face) + (,rust-re-union 1 font-lock-keyword-face) + + ;; Special types + (,(regexp-opt rust-special-types 'symbols) . font-lock-type-face) + + ;; The unsafe keyword + ("\\_" . 'rust-unsafe) + + ;; Attributes like `#[bar(baz)]` or `#![bar(baz)]` or `#[bar = "baz"]` + (,(rust-re-grab (concat "#\\!?\\[" rust-re-ident "[^]]*\\]")) + 1 font-lock-preprocessor-face keep) + + ;; Builtin formatting macros + (,(concat (rust-re-grab + (concat (rust-re-word (regexp-opt rust-builtin-formatting-macros)) + "!")) + rust-formatting-macro-opening-re + "\\(?:" rust-start-of-string-re "\\)?") + (1 'rust-builtin-formatting-macro) + (rust-string-interpolation-matcher + (rust-end-of-string) + nil + (0 'rust-string-interpolation t nil))) + + ;; write! macro + (,(concat (rust-re-grab (concat (rust-re-word "write\\(ln\\)?") "!")) + rust-formatting-macro-opening-re + "[[:space:]]*[^\"]+,[[:space:]]*" + rust-start-of-string-re) + (1 'rust-builtin-formatting-macro) + (rust-string-interpolation-matcher + (rust-end-of-string) + nil + (0 'rust-string-interpolation t nil))) + + ;; Syntax extension invocations like `foo!`, highlight including the ! + (,(concat (rust-re-grab (concat rust-re-ident "!")) "[({[:space:][]") + 1 font-lock-preprocessor-face) + + ;; Field names like `foo:`, highlight excluding the : + (,(concat (rust-re-grab rust-re-ident) "[[:space:]]*:[^:]") + 1 font-lock-variable-name-face) + + ;; CamelCase Means Type Or Constructor + (,rust-re-type-or-constructor 1 font-lock-type-face) + + ;; Type-inferred binding + (,(concat "\\_<\\(?:let\\s-+ref\\|let\\|ref\\|for\\)\\s-+\\(?:mut\\s-+\\)?" + (rust-re-grab rust-re-ident) + "\\_>") + 1 font-lock-variable-name-face) + + ;; Type names like `Foo::`, highlight excluding the :: + (,(rust-path-font-lock-matcher rust-re-uc-ident) 1 font-lock-type-face) + + ;; Module names like `foo::`, highlight excluding the :: + (,(rust-path-font-lock-matcher rust-re-lc-ident) 1 font-lock-constant-face) + + ;; Lifetimes like `'foo` + (,(concat "'" (rust-re-grab rust-re-ident) "[^']") 1 font-lock-variable-name-face) + + ;; Question mark operator + ("\\?" . 'rust-question-mark) + ("\\(&+\\)\\(?:'\\(?:\\<\\|_\\)\\|\\<\\|[[({:*_|]\\)" + 1 'rust-ampersand-face) + ;; Numbers with type suffix + (,rust-number-with-type 1 font-lock-type-face) + ) + + ;; Ensure we highlight `Foo` in `struct Foo` as a type. + (mapcar #'(lambda (x) + (list (rust-re-item-def (car x)) + 1 (cdr x))) + '(("enum" . font-lock-type-face) + ("struct" . font-lock-type-face) + ("union" . font-lock-type-face) + ("type" . font-lock-type-face) + ("mod" . font-lock-constant-face) + ("use" . font-lock-constant-face) + ("fn" . font-lock-function-name-face))))) + +(defun rust-end-of-string () + "Skip to the end of the current string." + (save-excursion + (skip-syntax-forward "^\"|") + (skip-syntax-forward "\"|") + (point))) + +(defun rust-looking-back-str (str) + "Return non-nil if there's a match on the text before point and STR. +Like `looking-back' but for fixed strings rather than regexps (so +that it's not so slow)." + (let ((len (length str))) + (and (> (point) len) + (equal str (buffer-substring-no-properties (- (point) len) (point)))))) + +(defun rust-looking-back-symbols (symbols) + "Return non-nil if the point is after a member of SYMBOLS. +SYMBOLS is a list of strings that represent the respective +symbols." + (save-excursion + (let* ((pt-orig (point)) + (beg-of-symbol (progn (forward-thing 'symbol -1) (point))) + (end-of-symbol (progn (forward-thing 'symbol 1) (point)))) + (and + (= end-of-symbol pt-orig) + (member (buffer-substring-no-properties beg-of-symbol pt-orig) + symbols))))) + +(defun rust-looking-back-ident () + "Non-nil if we are looking backwards at a valid rust identifier." + (let ((beg-of-symbol (save-excursion (forward-thing 'symbol -1) (point)))) + (looking-back rust-re-ident beg-of-symbol))) + +(defun rust-looking-back-macro () + "Non-nil if looking back at an ident followed by a ! + +This is stricter than rust syntax which allows a space between +the ident and the ! symbol. If this space is allowed, then we +would also need a keyword check to avoid `if !(condition)` being +seen as a macro." + (if (> (- (point) (point-min)) 1) + (save-excursion + (backward-char) + (and (= ?! (char-after)) + (rust-looking-back-ident))))) + +;;; Syntax definitions and helpers + +(defun rust-paren-level () (nth 0 (syntax-ppss))) +(defun rust-in-str () (nth 3 (syntax-ppss))) +(defun rust-in-str-or-cmnt () (nth 8 (syntax-ppss))) +(defun rust-rewind-past-str-cmnt () (goto-char (nth 8 (syntax-ppss)))) + +(defun rust-rewind-irrelevant () + (let ((continue t)) + (while continue + (let ((starting (point))) + (skip-chars-backward "[:space:]\n") + (when (rust-looking-back-str "*/") + (backward-char)) + (when (rust-in-str-or-cmnt) + (rust-rewind-past-str-cmnt)) + ;; Rewind until the point no longer moves + (setq continue (/= starting (point))))))) + +(defvar-local rust-macro-scopes nil + "Cache for the scopes calculated by `rust-macro-scope'. + +This variable can be `let' bound directly or indirectly around +`rust-macro-scope' as an optimization but should not be otherwise +set.") + +(defun rust-macro-scope (start end) + "Return the scope of macros in the buffer. + +The return value is a list of (START END) positions in the +buffer. + +If set START and END are optimizations which limit the return +value to scopes which are approximately with this range." + (save-excursion + ;; need to special case macro_rules which has unique syntax + (let ((scope nil) + (start (or start (point-min))) + (end (or end (point-max)))) + (goto-char start) + ;; if there is a start move back to the previous top level, + ;; as any macros before that must have closed by this time. + (let ((top (syntax-ppss-toplevel-pos (syntax-ppss)))) + (when top + (goto-char top))) + (while + (and + ;; The movement below may have moved us passed end, in + ;; which case search-forward will error + (< (point) end) + (search-forward "!" end t)) + (let ((pt (point))) + (cond + ;; in a string or comment is boring, move straight on + ((rust-in-str-or-cmnt)) + ;; in a normal macro, + ((and (skip-chars-forward " \t\n\r") + (memq (char-after) + '(?\[ ?\( ?\{)) + ;; Check that we have a macro declaration after. + (rust-looking-back-macro)) + (let ((start (point))) + (ignore-errors (forward-list)) + (setq scope (cons (list start (point)) scope)))) + ;; macro_rules, why, why, why did you not use macro syntax?? + ((save-excursion + ;; yuck -- last test moves point, even if it fails + (goto-char (- pt 1)) + (skip-chars-backward " \t\n\r") + (rust-looking-back-str "macro_rules")) + (save-excursion + (when (re-search-forward "[[({]" nil t) + (backward-char) + (let ((start (point))) + (ignore-errors (forward-list)) + (setq scope (cons (list start (point)) scope))))))))) + ;; Return 'empty rather than nil, to indicate a buffer with no + ;; macros at all. + (or scope 'empty)))) + +(defun rust-in-macro (&optional start end) + "Return non-nil when point is within the scope of a macro. + +If START and END are set, minimize the buffer analysis to +approximately this location as an optimization. + +Alternatively, if `rust-macro-scopes' is a list use the scope +information in this variable. This last is an optimization and +the caller is responsible for ensuring that the data in +`rust-macro-scopes' is up to date." + (when (> (rust-paren-level) 0) + (let ((scopes + (or + rust-macro-scopes + (rust-macro-scope start end)))) + ;; `rust-macro-scope' can return the symbol `empty' if the + ;; buffer has no macros at all. + (when (listp scopes) + (seq-some + (lambda (sc) + (and (>= (point) (car sc)) + (< (point) (cadr sc)))) + scopes))))) + +(defun rust-looking-at-where () + "Return T when looking at the \"where\" keyword." + (and (looking-at-p "\\bwhere\\b") + (not (rust-in-str-or-cmnt)))) + +(defun rust-rewind-to-where (&optional limit) + "Rewind the point to the closest occurrence of the \"where\" keyword. +Return T iff a where-clause was found. Does not rewind past +LIMIT when passed, otherwise only stops at the beginning of the +buffer." + (when (re-search-backward "\\bwhere\\b" limit t) + (if (rust-in-str-or-cmnt) + (rust-rewind-to-where limit) + t))) + +(defconst rust-re-pre-expression-operators "[-=!%&*/:<>[{(|.^;}]") + +(defconst rust-re-special-types (regexp-opt rust-special-types 'symbols)) + +(defun rust-align-to-expr-after-brace () + (save-excursion + (forward-char) + ;; We don't want to indent out to the open bracket if the + ;; open bracket ends the line + (when (not (looking-at "[[:blank:]]*\\(?://.*\\)?$")) + (when (looking-at "[[:space:]]") + (forward-word 1) + (backward-word 1)) + (current-column)))) + +(defun rust-rewind-to-beginning-of-current-level-expr () + (let ((current-level (rust-paren-level))) + (back-to-indentation) + (when (looking-at "->") + (rust-rewind-irrelevant) + (back-to-indentation)) + (while (> (rust-paren-level) current-level) + (backward-up-list) + (back-to-indentation)) + ;; When we're in the where clause, skip over it. First find out the start + ;; of the function and its paren level. + (let ((function-start nil) (function-level nil)) + (save-excursion + (rust-beginning-of-defun) + (back-to-indentation) + ;; Avoid using multiple-value-bind + (setq function-start (point) + function-level (rust-paren-level))) + ;; On a where clause + (when (or (rust-looking-at-where) + ;; or in one of the following lines, e.g. + ;; where A: Eq + ;; B: Hash <- on this line + (and (save-excursion + (rust-rewind-to-where function-start)) + (= current-level function-level))) + (goto-char function-start))))) + +(defun rust-align-to-method-chain () + (save-excursion + ;; for method-chain alignment to apply, we must be looking at + ;; another method call or field access or something like + ;; that. This avoids rather "eager" jumps in situations like: + ;; + ;; { + ;; something.foo() + ;; + ;; + ;; Without this check, we would wind up with the cursor under the + ;; `.`. In an older version, I had the inverse of the current + ;; check, where we checked for situations that should NOT indent, + ;; vs checking for the one situation where we SHOULD. It should be + ;; clear that this is more robust, but also I find it mildly less + ;; annoying to have to press tab again to align to a method chain + ;; than to have an over-eager indent in all other cases which must + ;; be undone via tab. + + (when (looking-at (concat "\s*\." rust-re-ident)) + (forward-line -1) + (end-of-line) + ;; Keep going up (looking for a line that could contain a method chain) + ;; while we're in a comment or on a blank line. Stop when the paren + ;; level changes. + (let ((level (rust-paren-level))) + (while (and (or (rust-in-str-or-cmnt) + ;; Only whitespace (or nothing) from the beginning to + ;; the end of the line. + (looking-back "^\s*" (point-at-bol))) + (= (rust-paren-level) level)) + (forward-line -1) + (end-of-line))) + + (let + ;; skip-dot-identifier is used to position the point at the + ;; `.` when looking at something like + ;; + ;; foo.bar + ;; ^ ^ + ;; | | + ;; | position of point + ;; returned offset + ;; + ((skip-dot-identifier + (lambda () + (when (and (rust-looking-back-ident) + (save-excursion + (forward-thing 'symbol -1) + (= ?. (char-before)))) + (forward-thing 'symbol -1) + (backward-char) + (- (current-column) rust-indent-offset))))) + (cond + ;; foo.bar(...) + ((looking-back "[)?]" (1- (point))) + (backward-list 1) + (funcall skip-dot-identifier)) + + ;; foo.bar + (t (funcall skip-dot-identifier))))))) + +(defun rust-mode-indent-line () + (interactive) + (let ((indent + (save-excursion + (back-to-indentation) + ;; Point is now at beginning of current line + (let* ((level (rust-paren-level)) + (baseline + ;; Our "baseline" is one level out from the + ;; indentation of the expression containing the + ;; innermost enclosing opening bracket. That way + ;; if we are within a block that has a different + ;; indentation than this mode would give it, we + ;; still indent the inside of it correctly relative + ;; to the outside. + (if (= 0 level) + 0 + (or + (when rust-indent-method-chain + (rust-align-to-method-chain)) + (save-excursion + (rust-rewind-irrelevant) + (backward-up-list) + (rust-rewind-to-beginning-of-current-level-expr) + (+ (current-column) rust-indent-offset)))))) + (cond + ;; Indent inside a non-raw string only if the previous line + ;; ends with a backslash that is inside the same string + ((nth 3 (syntax-ppss)) + (let* + ((string-begin-pos (nth 8 (syntax-ppss))) + (end-of-prev-line-pos + (and (not (rust--same-line-p (point) (point-min))) + (line-end-position 0)))) + (when + (and + ;; If the string begins with an "r" it's a raw string and + ;; we should not change the indentation + (/= ?r (char-after string-begin-pos)) + + ;; If we're on the first line this will be nil and the + ;; rest does not apply + end-of-prev-line-pos + + ;; The end of the previous line needs to be inside the + ;; current string... + (> end-of-prev-line-pos string-begin-pos) + + ;; ...and end with a backslash + (= ?\\ (char-before end-of-prev-line-pos))) + + ;; Indent to the same level as the previous line, or the + ;; start of the string if the previous line starts the string + (if (rust--same-line-p end-of-prev-line-pos string-begin-pos) + ;; The previous line is the start of the string. + ;; If the backslash is the only character after the + ;; string beginning, indent to the next indent + ;; level. Otherwise align with the start of the string. + (if (> (- end-of-prev-line-pos string-begin-pos) 2) + (save-excursion + (goto-char (+ 1 string-begin-pos)) + (current-column)) + baseline) + + ;; The previous line is not the start of the string, so + ;; match its indentation. + (save-excursion + (goto-char end-of-prev-line-pos) + (back-to-indentation) + (current-column)))))) + + ;; A function return type is indented to the corresponding + ;; function arguments, if -to-arguments is selected. + ((and rust-indent-return-type-to-arguments + (looking-at "->")) + (save-excursion + (backward-list) + (or (rust-align-to-expr-after-brace) + (+ baseline rust-indent-offset)))) + + ;; A closing brace is 1 level unindented + ((looking-at "[]})]") (- baseline rust-indent-offset)) + + ;; Doc comments in /** style with leading * indent to line up the *s + ((and (nth 4 (syntax-ppss)) (looking-at "*")) + (+ 1 baseline)) + + ;; When the user chose not to indent the start of the where + ;; clause, put it on the baseline. + ((and (not rust-indent-where-clause) + (rust-looking-at-where)) + baseline) + + ;; If we're in any other token-tree / sexp, then: + (t + (or + ;; If we are inside a pair of braces, with something after the + ;; open brace on the same line and ending with a comma, treat + ;; it as fields and align them. + (when (> level 0) + (save-excursion + (rust-rewind-irrelevant) + (backward-up-list) + ;; Point is now at the beginning of the containing set of braces + (rust-align-to-expr-after-brace))) + + ;; When where-clauses are spread over multiple lines, clauses + ;; should be aligned on the type parameters. In this case we + ;; take care of the second and following clauses (the ones + ;; that don't start with "where ") + (save-excursion + ;; Find the start of the function, we'll use this to limit + ;; our search for "where ". + (let ((function-start nil) (function-level nil)) + (save-excursion + ;; If we're already at the start of a function, + ;; don't go back any farther. We can easily do + ;; this by moving to the end of the line first. + (end-of-line) + (rust-beginning-of-defun) + (back-to-indentation) + ;; Avoid using multiple-value-bind + (setq function-start (point) + function-level (rust-paren-level))) + ;; When we're not on a line starting with "where ", but + ;; still on a where-clause line, go to "where " + (when (and + (not (rust-looking-at-where)) + ;; We're looking at something like "F: ..." + (looking-at (concat rust-re-ident ":")) + ;; There is a "where " somewhere after the + ;; start of the function. + (rust-rewind-to-where function-start) + ;; Make sure we're not inside the function + ;; already (e.g. initializing a struct) by + ;; checking we are the same level. + (= function-level level)) + ;; skip over "where" + (forward-char 5) + ;; Unless "where" is at the end of the line + (if (eolp) + ;; in this case the type parameters bounds are just + ;; indented once + (+ baseline rust-indent-offset) + ;; otherwise, skip over whitespace, + (skip-chars-forward "[:space:]") + ;; get the column of the type parameter and use that + ;; as indentation offset + (current-column))))) + + (progn + (back-to-indentation) + ;; Point is now at the beginning of the current line + (if (or + ;; If this line begins with "else" or "{", stay on the + ;; baseline as well (we are continuing an expression, + ;; but the "else" or "{" should align with the beginning + ;; of the expression it's in.) + ;; Or, if this line starts a comment, stay on the + ;; baseline as well. + (looking-at "\\\\|{\\|/[/*]") + + ;; If this is the start of a top-level item, + ;; stay on the baseline. + (looking-at rust-top-item-beg-re) + + (save-excursion + (rust-rewind-irrelevant) + ;; Point is now at the end of the previous line + (or + ;; If we are at the start of the buffer, no + ;; indentation is needed, so stay at baseline... + (= (point) 1) + ;; ..or if the previous line ends with any of these: + ;; { ? : ( , ; [ } + ;; then we are at the beginning of an + ;; expression, so stay on the baseline... + (looking-back "[(,:;[{}]\\|[^|]|" (- (point) 2)) + ;; or if the previous line is the end of an + ;; attribute, stay at the baseline... + (progn (rust-rewind-to-beginning-of-current-level-expr) + (looking-at "#"))))) + baseline + + ;; Otherwise, we are continuing the same expression from + ;; the previous line, so add one additional indent level + (+ baseline rust-indent-offset)))))))))) + + (when indent + ;; If we're at the beginning of the line (before or at the current + ;; indentation), jump with the indentation change. Otherwise, save the + ;; excursion so that adding the indentations will leave us at the + ;; equivalent position within the line to where we were before. + (if (<= (current-column) (current-indentation)) + (indent-line-to indent) + (save-excursion (indent-line-to indent)))))) + +(defun rust--same-line-p (pos1 pos2) + "Return non-nil if POS1 and POS2 are on the same line." + (save-excursion (= (progn (goto-char pos1) (line-end-position)) + (progn (goto-char pos2) (line-end-position))))) + +;;; Font-locking definitions and helpers + +(defun rust-next-string-interpolation (limit) + "Search forward from point for next Rust interpolation marker before LIMIT. +Set point to the end of the occurrence found, and return match beginning +and end." + (catch 'match + (save-match-data + (save-excursion + (while (search-forward "{" limit t) + (if (eql (char-after (point)) ?{) + (forward-char) + (let ((start (match-beginning 0))) + ;; According to fmt_macros::Parser::next, an opening brace + ;; must be followed by an optional argument and/or format + ;; specifier, then a closing brace. A single closing brace + ;; without a corresponding unescaped opening brace is an + ;; error. We don't need to do anything special with + ;; arguments, specifiers, or errors, so we only search for + ;; the single closing brace. + (when (search-forward "}" limit t) + (throw 'match (list start (point))))))))))) + +(defun rust-string-interpolation-matcher (limit) + "Match next Rust interpolation marker before LIMIT and set match data if found. +Returns nil if not within a Rust string." + (when (rust-in-str) + (let ((match (rust-next-string-interpolation limit))) + (when match + (set-match-data match) + (goto-char (cadr match)) + match)))) + +(defun rust-syntax-class-before-point () + (when (> (point) 1) + (syntax-class (syntax-after (1- (point)))))) + +(defun rust-rewind-qualified-ident () + (while (rust-looking-back-ident) + (backward-sexp) + (when (save-excursion (rust-rewind-irrelevant) (rust-looking-back-str "::")) + (rust-rewind-irrelevant) + (backward-char 2) + (rust-rewind-irrelevant)))) + +(defun rust-rewind-type-param-list () + (cond + ((and (rust-looking-back-str ">") (equal 5 (rust-syntax-class-before-point))) + (backward-sexp) + (rust-rewind-irrelevant)) + + ;; We need to be able to back up past the Fn(args) -> RT form as well. If + ;; we're looking back at this, we want to end up just after "Fn". + ((member (char-before) '(?\] ?\) )) + (let* ((is-paren (rust-looking-back-str ")")) + (dest (save-excursion + (backward-sexp) + (rust-rewind-irrelevant) + (or + (when (rust-looking-back-str "->") + (backward-char 2) + (rust-rewind-irrelevant) + (when (rust-looking-back-str ")") + (backward-sexp) + (point))) + (and is-paren (point)))))) + (when dest + (goto-char dest)))))) + +(defun rust-rewind-to-decl-name () + "Return the point at the beginning of the name in a declaration. +I.e. if we are before an ident that is part of a declaration that +can have a where clause, rewind back to just before the name of +the subject of that where clause and return the new point. +Otherwise return nil." + (let* ((ident-pos (point)) + (newpos (save-excursion + (rust-rewind-irrelevant) + (rust-rewind-type-param-list) + (cond + ((rust-looking-back-symbols + '("fn" "trait" "enum" "struct" "union" "impl" "type")) + ident-pos) + + ((equal 5 (rust-syntax-class-before-point)) + (backward-sexp) + (rust-rewind-to-decl-name)) + + ((looking-back "[:,'+=]" (1- (point))) + (backward-char) + (rust-rewind-to-decl-name)) + + ((rust-looking-back-str "->") + (backward-char 2) + (rust-rewind-to-decl-name)) + + ((rust-looking-back-ident) + (rust-rewind-qualified-ident) + (rust-rewind-to-decl-name)))))) + (when newpos (goto-char newpos)) + newpos)) + +(defun rust-is-in-expression-context (token) + "Return t if what comes right after the point is part of an +expression (as opposed to starting a type) by looking at what +comes before. Takes a symbol that roughly indicates what is +after the point. + +This function is used as part of `rust-is-lt-char-operator' as +part of angle bracket matching, and is not intended to be used +outside of this context." + (save-excursion + (let ((postchar (char-after))) + (rust-rewind-irrelevant) + ;; A type alias or ascription could have a type param list. Skip backwards past it. + (when (member token '(ambiguous-operator open-brace)) + (rust-rewind-type-param-list)) + (cond + + ;; Certain keywords always introduce expressions + ((rust-looking-back-symbols '("if" "while" "match" "return" "box" "in")) t) + + ;; "as" introduces a type + ((rust-looking-back-symbols '("as")) nil) + + ;; An open angle bracket never introduces expression context WITHIN the angle brackets + ((and (equal token 'open-brace) (equal postchar ?<)) nil) + + ;; An ident! followed by an open brace is a macro invocation. Consider + ;; it to be an expression. + ((and (equal token 'open-brace) (rust-looking-back-macro)) t) + + ;; In a brace context a "]" introduces an expression. + ((and (eq token 'open-brace) (rust-looking-back-str "]"))) + + ;; An identifier is right after an ending paren, bracket, angle bracket + ;; or curly brace. It's a type if the last sexp was a type. + ((and (equal token 'ident) (equal 5 (rust-syntax-class-before-point))) + (backward-sexp) + (rust-is-in-expression-context 'open-brace)) + + ;; If a "for" appears without a ; or { before it, it's part of an + ;; "impl X for y", so the y is a type. Otherwise it's + ;; introducing a loop, so the y is an expression + ((and (equal token 'ident) (rust-looking-back-symbols '("for"))) + (backward-sexp) + (rust-rewind-irrelevant) + (looking-back "[{;]" (1- (point)))) + + ((rust-looking-back-ident) + (rust-rewind-qualified-ident) + (rust-rewind-irrelevant) + (cond + ((equal token 'open-brace) + ;; We now know we have: + ;; ident [{([] + ;; where [{([] denotes either a {, ( or [. + ;; This character is bound as postchar. + (cond + ;; If postchar is a paren or square bracket, then if the + ;; brace is a type if the identifier is one + ((member postchar '(?\( ?\[ )) (rust-is-in-expression-context 'ident)) + + ;; If postchar is a curly brace, the brace can only be a type if + ;; ident2 is the name of an enum, struct or trait being declared. + ;; Note that if there is a -> before the ident then the ident would + ;; be a type but the { is not. + ((equal ?{ postchar) + (not (and (rust-rewind-to-decl-name) + (progn + (rust-rewind-irrelevant) + (rust-looking-back-symbols + '("enum" "struct" "union" "trait" "type")))))))) + + ((equal token 'ambiguous-operator) + (cond + ;; An ampersand after an ident has to be an operator rather + ;; than a & at the beginning of a ref type + ((equal postchar ?&) t) + + ;; A : followed by a type then an = introduces an + ;; expression (unless it is part of a where clause of a + ;; "type" declaration) + ((and (equal postchar ?=) + (looking-back "[^:]:" (- (point) 2)) + (not (save-excursion + (and (rust-rewind-to-decl-name) + (progn (rust-rewind-irrelevant) + (rust-looking-back-symbols '("type")))))))) + + ;; "let ident =" introduces an expression--and so does "const" and "mut" + ((and (equal postchar ?=) (rust-looking-back-symbols '("let" "const" "mut"))) t) + + ;; As a specific special case, see if this is the = in this situation: + ;; enum EnumName { Ident = + ;; In this case, this is a c-like enum and despite Ident + ;; representing a type, what comes after the = is an expression + ((and + (> (rust-paren-level) 0) + (save-excursion + (backward-up-list) + (rust-rewind-irrelevant) + (rust-rewind-type-param-list) + (and + (rust-looking-back-ident) + (progn + (rust-rewind-qualified-ident) + (rust-rewind-irrelevant) + (rust-looking-back-str "enum"))))) + t) + + ;; Otherwise the ambiguous operator is a type if the identifier is a type + ((rust-is-in-expression-context 'ident) t))) + + ((equal token 'colon) + (cond + ;; If we see a ident: not inside any braces/parens, we're at top level. + ;; There are no allowed expressions after colons there, just types. + ((<= (rust-paren-level) 0) nil) + + ;; We see ident: inside a list + ((looking-back "[{,]" (1- (point))) + (backward-up-list) + + ;; If a : appears whose surrounding paren/brackets/braces are + ;; anything other than curly braces, it can't be a field + ;; initializer and must be denoting a type. + (when (looking-at "{") + (rust-rewind-irrelevant) + (rust-rewind-type-param-list) + (when (rust-looking-back-ident) + ;; We have a context that looks like this: + ;; ident2 { [maybe paren-balanced code ending in comma] ident1: + ;; the point is sitting just after ident2, and we trying to + ;; figure out if the colon introduces an expression or a type. + ;; The answer is that ident1 is a field name, and what comes + ;; after the colon is an expression, if ident2 is an + ;; expression. + (rust-rewind-qualified-ident) + (rust-is-in-expression-context 'ident)))) + + ;; Otherwise, if the ident: appeared with anything other than , or { + ;; before it, it can't be part of a struct initializer and therefore + ;; must be denoting a type. + (t nil))))) + + ;; An operator-like character after a string is indeed an operator + ((and (equal token 'ambiguous-operator) + (member (rust-syntax-class-before-point) '(5 7 15))) t) + + ;; A colon that has something other than an identifier before it is a + ;; type ascription + ((equal token 'colon) nil) + + ;; A :: introduces a type (or module, but not an expression in any case) + ((rust-looking-back-str "::") nil) + + ((rust-looking-back-str ":") + (backward-char) + (rust-is-in-expression-context 'colon)) + + ;; A -> introduces a type + ((rust-looking-back-str "->") nil) + + ;; If we are up against the beginning of a list, or after a comma inside + ;; of one, back up out of it and check what the list itself is + ((or + (equal 4 (rust-syntax-class-before-point)) + (rust-looking-back-str ",")) + (condition-case nil + (progn + (backward-up-list) + (rust-is-in-expression-context 'open-brace)) + (scan-error nil))) + + ;; A => introduces an expression + ((rust-looking-back-str "=>") t) + + ;; A == introduces an expression + ((rust-looking-back-str "==") t) + + ;; These operators can introduce expressions or types + ((looking-back "[-+=!?&*]" (1- (point))) + (backward-char) + (rust-is-in-expression-context 'ambiguous-operator)) + + ;; These operators always introduce expressions. (Note that if this + ;; regexp finds a < it must not be an angle bracket, or it'd + ;; have been caught in the syntax-class check above instead of this.) + ((looking-back rust-re-pre-expression-operators (1- (point))) t))))) + +(defun rust-is-lt-char-operator () + "Return non-nil if the `<' sign just after point is an operator. +Otherwise, if it is an opening angle bracket, then return nil." + (let ((case-fold-search nil)) + (save-excursion + (rust-rewind-irrelevant) + ;; We are now just after the character syntactically before the <. + (cond + + ;; If we are looking back at a < that is not an angle bracket (but not + ;; two of them) then this is the second < in a bit shift operator + ((and (rust-looking-back-str "<") + (not (equal 4 (rust-syntax-class-before-point))) + (not (rust-looking-back-str "<<")))) + + ;; On the other hand, if we are after a closing paren/brace/bracket it + ;; can only be an operator, not an angle bracket. Likewise, if we are + ;; after a string it's an operator. (The string case could actually be + ;; valid in rust for character literals.) + ((member (rust-syntax-class-before-point) '(5 7 15)) t) + + ;; If we are looking back at an operator, we know that we are at + ;; the beginning of an expression, and thus it has to be an angle + ;; bracket (starting a "::" construct.) + ((looking-back rust-re-pre-expression-operators (1- (point))) nil) + + ;; If we are looking back at a keyword, it's an angle bracket + ;; unless that keyword is "self", "true" or "false" + ((rust-looking-back-symbols rust-keywords) + (rust-looking-back-symbols '("self" "true" "false"))) + + ((rust-looking-back-str "?") + (rust-is-in-expression-context 'ambiguous-operator)) + + ;; If we're looking back at an identifier, this depends on whether + ;; the identifier is part of an expression or a type + ((rust-looking-back-ident) + (backward-sexp) + (or + ;; The special types can't take type param lists, so a < after one is + ;; always an operator + (looking-at rust-re-special-types) + + (rust-is-in-expression-context 'ident))) + + ;; Otherwise, assume it's an angle bracket + )))) + +(defun rust-electric-pair-inhibit-predicate-wrap (char) + "Prevent \"matching\" with a `>' when CHAR is the less-than operator. +This wraps the default defined by `electric-pair-inhibit-predicate'." + (or + (when (= ?< char) + (save-excursion + (backward-char) + (rust-is-lt-char-operator))) + (funcall (default-value 'electric-pair-inhibit-predicate) char))) + +(defun rust-electric-pair-skip-self (char) + "Skip CHAR instead of inserting a second closing character. +This is added to the default skips defined by `electric-pair-skip-self'." + (= ?> char)) + +(defun rust-ordinary-lt-gt-p () + "Test whether the `<' or `>' at point is an ordinary operator of some kind. + +This returns t if the `<' or `>' is an ordinary operator (like +less-than) or part of one (like `->'); and nil if the character +should be considered a paired angle bracket." + (cond + ;; If matching is turned off suppress all of them + ((not rust-match-angle-brackets) t) + + ;; This is a cheap check so we do it early. + ;; Don't treat the > in -> or => as an angle bracket + ((and (= (following-char) ?>) (memq (preceding-char) '(?- ?=))) t) + + ;; We don't take < or > in strings or comments to be angle brackets + ((rust-in-str-or-cmnt) t) + + ;; Inside a macro we don't really know the syntax. Any < or > may be an + ;; angle bracket or it may not. But we know that the other braces have + ;; to balance regardless of the < and >, so if we don't treat any < or > + ;; as angle brackets it won't mess up any paren balancing. + ((rust-in-macro) t) + + ((= (following-char) ?<) + (rust-is-lt-char-operator)) + + ;; Since rust-ordinary-lt-gt-p is called only when either < or > are at the point, + ;; we know that the following char must be > in the clauses below. + + ;; If we are at top level and not in any list, it can't be a closing + ;; angle bracket + ((>= 0 (rust-paren-level)) t) + + ;; Otherwise, treat the > as a closing angle bracket if it would + ;; match an opening one + ((save-excursion + (backward-up-list) + (/= (following-char) ?<))))) + +(defun rust-mode-syntactic-face-function (state) + "Return face that distinguishes doc and normal comments in given syntax STATE." + (if (nth 3 state) + 'font-lock-string-face + (save-excursion + (goto-char (nth 8 state)) + (if (looking-at "/\\([*][*!][^*!]\\|/[/!][^/!]\\)") + 'font-lock-doc-face + 'font-lock-comment-face)))) + +(eval-and-compile + (defconst rust--char-literal-rx + (rx (seq + (group "'") + (or + (seq + "\\" + (or + (: "u{" (** 1 6 xdigit) "}") + (: "x" (= 2 xdigit)) + (any "'nrt0\"\\"))) + (not (any "'\\"))) + (group "'"))) + "A regular expression matching a character literal.")) + +(defun rust--syntax-propertize-raw-string (str-start end) + "A helper for rust-syntax-propertize. + +This will apply the appropriate string syntax to the character +from the STR-START up to the end of the raw string, or to END, +whichever comes first." + (when (save-excursion + (goto-char str-start) + (looking-at "r\\(#*\\)\\(\"\\)")) + ;; In a raw string, so try to find the end. + (let ((hashes (match-string 1))) + ;; Match \ characters at the end of the string to suppress + ;; their normal character-quote syntax. + (when (re-search-forward (concat "\\(\\\\*\\)\\(\"" hashes "\\)") end t) + (put-text-property (match-beginning 1) (match-end 1) + 'syntax-table (string-to-syntax "_")) + (put-text-property (1- (match-end 2)) (match-end 2) + 'syntax-table (string-to-syntax "|")) + (goto-char (match-end 0)))))) + +;;; Syntax Propertize + +(defun rust-syntax-propertize (start end) + "A `syntax-propertize-function' to apply properties from START to END." + ;; Cache all macro scopes as an optimization. See issue #208 + (let ((rust-macro-scopes (rust-macro-scope start end))) + (goto-char start) + (let ((str-start (rust-in-str-or-cmnt))) + (when str-start + (rust--syntax-propertize-raw-string str-start end))) + (funcall + (syntax-propertize-rules + ;; Character literals. + (rust--char-literal-rx (1 "\"") (2 "\"")) + ;; Raw strings. + ("\\(r\\)#*\"" + (0 (ignore + (goto-char (match-end 0)) + (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0)))) + (put-text-property (match-beginning 1) (match-end 1) + 'syntax-table (string-to-syntax "|")) + (rust--syntax-propertize-raw-string (match-beginning 0) end))))) + ("[<>]" + (0 (ignore + (when (save-match-data + (save-excursion + (goto-char (match-beginning 0)) + (rust-ordinary-lt-gt-p))) + (put-text-property (match-beginning 0) (match-end 0) + 'syntax-table (string-to-syntax ".")) + (goto-char (match-end 0))))))) + (point) end))) + +(defun rust-fill-prefix-for-comment-start (line-start) + "Determine what to use for `fill-prefix' based on the text at LINE-START." + (let ((result + ;; Replace /* with same number of spaces + (replace-regexp-in-string + "\\(?:/\\*+?\\)[!*]?" + (lambda (s) + ;; We want the * to line up with the first * of the + ;; comment start + (let ((offset (if (eq t + (compare-strings "/*" nil nil + s + (- (length s) 2) + (length s))) + 1 2))) + (concat (make-string (- (length s) offset) + ?\x20) "*"))) + line-start))) + ;; Make sure we've got at least one space at the end + (if (not (= (aref result (- (length result) 1)) ?\x20)) + (setq result (concat result " "))) + result)) + +(defun rust-in-comment-paragraph (body) + ;; We might move the point to fill the next comment, but we don't want it + ;; seeming to jump around on the user + (save-excursion + ;; If we're outside of a comment, with only whitespace and then a comment + ;; in front, jump to the comment and prepare to fill it. + (when (not (nth 4 (syntax-ppss))) + (beginning-of-line) + (when (looking-at (concat "[[:space:]\n]*" comment-start-skip)) + (goto-char (match-end 0)))) + + ;; We need this when we're moving the point around and then checking syntax + ;; while doing paragraph fills, because the cache it uses isn't always + ;; invalidated during this. + (syntax-ppss-flush-cache 1) + ;; If we're at the beginning of a comment paragraph with nothing but + ;; whitespace til the next line, jump to the next line so that we use the + ;; existing prefix to figure out what the new prefix should be, rather than + ;; inferring it from the comment start. + (let ((next-bol (line-beginning-position 2))) + (while (save-excursion + (end-of-line) + (syntax-ppss-flush-cache 1) + (and (nth 4 (syntax-ppss)) + (save-excursion + (beginning-of-line) + (looking-at paragraph-start)) + (looking-at "[[:space:]]*$") + (nth 4 (syntax-ppss next-bol)))) + (goto-char next-bol))) + + (syntax-ppss-flush-cache 1) + ;; If we're on the last line of a multiline-style comment that started + ;; above, back up one line so we don't mistake the * of the */ that ends + ;; the comment for a prefix. + (when (save-excursion + (and (nth 4 (syntax-ppss (line-beginning-position 1))) + (looking-at "[[:space:]]*\\*/"))) + (goto-char (line-end-position 0))) + (funcall body))) + +(defun rust-with-comment-fill-prefix (body) + (let* + ((line-string (buffer-substring-no-properties + (line-beginning-position) (line-end-position))) + (line-comment-start + (when (nth 4 (syntax-ppss)) + (cond + ;; If we're inside the comment and see a * prefix, use it + ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)" + line-string) + (match-string 1 line-string)) + ;; If we're at the start of a comment, figure out what prefix + ;; to use for the subsequent lines after it + ((string-match (concat "[[:space:]]*" comment-start-skip) line-string) + (rust-fill-prefix-for-comment-start + (match-string 0 line-string)))))) + (fill-prefix + (or line-comment-start + fill-prefix))) + (funcall body))) + +(defun rust-find-fill-prefix () + (rust-in-comment-paragraph + (lambda () + (rust-with-comment-fill-prefix + (lambda () + fill-prefix))))) + +(defun rust-fill-paragraph (&rest args) + "Special wrapping for `fill-paragraph'. +This handles multi-line comments with a * prefix on each line." + (rust-in-comment-paragraph + (lambda () + (rust-with-comment-fill-prefix + (lambda () + (let + ((fill-paragraph-function + (if (not (eq fill-paragraph-function #'rust-fill-paragraph)) + fill-paragraph-function)) + (fill-paragraph-handle-comment t)) + (apply #'fill-paragraph args) + t)))))) + +(defun rust-do-auto-fill (&rest args) + "Special wrapping for `do-auto-fill'. +This handles multi-line comments with a * prefix on each line." + (rust-with-comment-fill-prefix + (lambda () + (apply #'do-auto-fill args) + t))) + +(defun rust-fill-forward-paragraph (arg) + ;; This is to work around some funny behavior when a paragraph separator is + ;; at the very top of the file and there is a fill prefix. + (let ((fill-prefix nil)) (forward-paragraph arg))) + +(defun rust-comment-indent-new-line (&optional arg) + (rust-with-comment-fill-prefix + (lambda () (comment-indent-new-line arg)))) + +;;; Defun Motions + +(defun rust-beginning-of-defun (&optional arg) + "Move backward to the beginning of the current defun. + +With ARG, move backward multiple defuns. Negative ARG means +move forward. + +This is written mainly to be used as `beginning-of-defun-function' for Rust. +Don't move to the beginning of the line. `beginning-of-defun', +which calls this, does that afterwards." + (interactive "p") + (let* ((arg (or arg 1)) + (magnitude (abs arg)) + (sign (if (< arg 0) -1 1))) + ;; If moving forward, don't find the defun we might currently be + ;; on. + (when (< sign 0) + (end-of-line)) + (catch 'done + (dotimes (_ magnitude) + ;; Search until we find a match that is not in a string or comment. + (while (if (re-search-backward (concat "^\\(" rust-top-item-beg-re "\\)") + nil 'move sign) + (rust-in-str-or-cmnt) + ;; Did not find it. + (throw 'done nil))))) + t)) + +(defun rust-end-of-defun () + "Move forward to the next end of defun. + +With argument, do it that many times. +Negative argument -N means move back to Nth preceding end of defun. + +Assume that this is called after `beginning-of-defun'. So point is +at the beginning of the defun body. + +This is written mainly to be used as `end-of-defun-function' for Rust." + (interactive) + ;; Find the opening brace + (if (re-search-forward "[{]" nil t) + (progn + (goto-char (match-beginning 0)) + ;; Go to the closing brace + (condition-case nil + (forward-sexp) + (scan-error + ;; The parentheses are unbalanced; instead of being unable + ;; to fontify, just jump to the end of the buffer + (goto-char (point-max))))) + ;; There is no opening brace, so consider the whole buffer to be one "defun" + (goto-char (point-max)))) + +;;; _ + +(defun rust-mode-reload () + (interactive) + (unload-feature 'rust-mode) + (require 'rust-mode) + (rust-mode)) + +(provide 'rust-mode) +(require 'rust-utils) + +;;; rust-mode.el ends here diff --git a/code/elpa/rust-mode-20220819.1203/rust-playpen.el b/code/elpa/rust-mode-20220819.1203/rust-playpen.el new file mode 100644 index 0000000..1a9e583 --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-playpen.el @@ -0,0 +1,59 @@ +;;; rust-playpen.el --- Support for the Rust Playground -*- lexical-binding:t -*- +;;; Commentary: + +;; This library implements support for the Rust Playground, +;; which is hosted at https://play.rust-lang.org and developed +;; at https://github.com/integer32llc/rust-playground. + +;;; Code: + +(eval-when-compile (require 'url)) + +;;; Options + +(defcustom rust-playpen-url-format "https://play.rust-lang.org/?code=%s" + "Format string to use when submitting code to the playpen." + :type 'string + :group 'rust-mode) + +(defcustom rust-shortener-url-format "https://is.gd/create.php?format=simple&url=%s" + "Format string to use for creating the shortened link of a playpen submission." + :type 'string + :group 'rust-mode) + +;;; Commands + +(defun rust-playpen-region (begin end) + "Create a shareable URL for the region from BEGIN to END on the Rust playpen." + (interactive "r") + (let* ((data (buffer-substring begin end)) + (escaped-data (url-hexify-string data)) + (escaped-playpen-url (url-hexify-string + (format rust-playpen-url-format escaped-data)))) + (if (> (length escaped-playpen-url) 5000) + (error "encoded playpen data exceeds 5000 character limit (length %s)" + (length escaped-playpen-url)) + (let ((shortener-url (format rust-shortener-url-format escaped-playpen-url)) + (url-request-method "POST")) + (url-retrieve shortener-url + (lambda (state) + ;; filter out the headers etc. included at the + ;; start of the buffer: the relevant text + ;; (shortened url or error message) is exactly + ;; the last line. + (goto-char (point-max)) + (let ((last-line (thing-at-point 'line t)) + (err (plist-get state :error))) + (kill-buffer) + (if err + (error "failed to shorten playpen url: %s" last-line) + (message "%s" last-line))))))))) + +(defun rust-playpen-buffer () + "Create a shareable URL for the contents of the buffer on the Rust playpen." + (interactive) + (rust-playpen-region (point-min) (point-max))) + +;;; _ +(provide 'rust-playpen) +;;; rust-playpen.el ends here diff --git a/code/elpa/rust-mode-20220819.1203/rust-rustfmt.el b/code/elpa/rust-mode-20220819.1203/rust-rustfmt.el new file mode 100644 index 0000000..bb88647 --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-rustfmt.el @@ -0,0 +1,372 @@ +;;; rust-rustfmt.el --- Support for rustfmt -*- lexical-binding:t -*- +;;; Commentary: + +;; This library implements support for "rustfmt", a tool for +;; formatting Rust code according to style guidelines. + +;;; Code: +;;; Options + +(defcustom rust-format-on-save nil + "Format future rust buffers before saving using rustfmt." + :type 'boolean + :safe #'booleanp + :group 'rust-mode) + +(defcustom rust-format-show-buffer t + "Show *rustfmt* buffer if formatting detected problems." + :type 'boolean + :safe #'booleanp + :group 'rust-mode) + +(defcustom rust-format-goto-problem t + "Jump to location of first detected problem when formatting buffer." + :type 'boolean + :safe #'booleanp + :group 'rust-mode) + +(defcustom rust-rustfmt-bin "rustfmt" + "Path to rustfmt executable." + :type 'string + :group 'rust-mode) + +(defcustom rust-rustfmt-switches '("--edition" "2018") + "Arguments to pass when invoking the `rustfmt' executable." + :type '(repeat string) + :group 'rust-mode) + +;;; _ + +(defconst rust-rustfmt-buffername "*rustfmt*") + +(defun rust--format-call (buf) + "Format BUF using rustfmt." + (with-current-buffer (get-buffer-create rust-rustfmt-buffername) + (view-mode +1) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert-buffer-substring buf) + (let* ((tmpf (make-temp-file "rustfmt")) + (ret (apply #'call-process-region + (point-min) + (point-max) + rust-rustfmt-bin + t + `(t ,tmpf) + nil + rust-rustfmt-switches))) + (unwind-protect + (cond + ((zerop ret) + (if (not (string= (buffer-string) + (with-current-buffer buf (buffer-string)))) + ;; replace-buffer-contents was in emacs 26.1, but it + ;; was broken for non-ASCII strings, so we need 26.2. + (if (and (fboundp 'replace-buffer-contents) + (version<= "26.2" emacs-version)) + (with-current-buffer buf + (replace-buffer-contents rust-rustfmt-buffername)) + (copy-to-buffer buf (point-min) (point-max)))) + (kill-buffer)) + ((= ret 3) + (if (not (string= (buffer-string) + (with-current-buffer buf (buffer-string)))) + (copy-to-buffer buf (point-min) (point-max))) + (erase-buffer) + (insert-file-contents tmpf) + (rust--format-fix-rustfmt-buffer (buffer-name buf)) + (error "Rustfmt could not format some lines, see %s buffer for details" + rust-rustfmt-buffername)) + (t + (erase-buffer) + (insert-file-contents tmpf) + (rust--format-fix-rustfmt-buffer (buffer-name buf)) + (error "Rustfmt failed, see %s buffer for details" + rust-rustfmt-buffername)))) + (delete-file tmpf))))) + +;; Since we run rustfmt through stdin we get markers in the +;; output. This replaces them with the buffer name instead. +(defun rust--format-fix-rustfmt-buffer (buffer-name) + (save-match-data + (with-current-buffer (get-buffer rust-rustfmt-buffername) + (let ((inhibit-read-only t) + (fixed (format "--> %s:" buffer-name))) + (goto-char (point-min)) + (while (re-search-forward "--> \\(?:\\|stdin\\):" nil t) + (replace-match fixed)))))) + +;; If rust-mode has been configured to navigate to source of the error +;; or display it, do so -- and return true. Otherwise return nil to +;; indicate nothing was done. +(defun rust--format-error-handler () + (let ((ok nil)) + (when rust-format-show-buffer + (display-buffer (get-buffer rust-rustfmt-buffername)) + (setq ok t)) + (when rust-format-goto-problem + (rust-goto-format-problem) + (setq ok t)) + ok)) + +(defun rust-goto-format-problem () + "Jumps to problem reported by rustfmt, if any. + +In case of multiple problems cycles through them. Displays the +rustfmt complain in the echo area." + (interactive) + ;; This uses position in *rustfmt* buffer to know which is the next + ;; error to jump to, and source: line in the buffer to figure which + ;; buffer it is from. + (let ((rustfmt (get-buffer rust-rustfmt-buffername))) + (if (not rustfmt) + (message "No %s, no problems." rust-rustfmt-buffername) + (let ((target-buffer (with-current-buffer rustfmt + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "--> \\([^:]+\\):" nil t) + (match-string 1))))) + (target-point (with-current-buffer rustfmt + ;; No save-excursion, this is how we cycle through! + (let ((regex "--> [^:]+:\\([0-9]+\\):\\([0-9]+\\)")) + (when (or (re-search-forward regex nil t) + (progn (goto-char (point-min)) + (re-search-forward regex nil t))) + (cons (string-to-number (match-string 1)) + (string-to-number (match-string 2))))))) + (target-problem (with-current-buffer rustfmt + (save-excursion + (when (re-search-backward "^error:.+\n" nil t) + (forward-char (length "error: ")) + (let ((p0 (point))) + (if (re-search-forward "\nerror:.+\n" nil t) + (buffer-substring p0 (point)) + (buffer-substring p0 (point-max))))))))) + (when (and target-buffer (get-buffer target-buffer) target-point) + (switch-to-buffer target-buffer) + (goto-char (point-min)) + (forward-line (1- (car target-point))) + (forward-char (1- (cdr target-point)))) + (message target-problem))))) + +(defconst rust--format-word "\ +\\b\\(else\\|enum\\|fn\\|for\\|if\\|let\\|loop\\|\ +match\\|struct\\|union\\|unsafe\\|while\\)\\b") +(defconst rust--format-line "\\(\n\\)") + +;; Counts number of matches of regex beginning up to max-beginning, +;; leaving the point at the beginning of the last match. +(defun rust--format-count (regex max-beginning) + (let ((count 0) + save-point + beginning) + (while (and (< (point) max-beginning) + (re-search-forward regex max-beginning t)) + (setq count (1+ count)) + (setq beginning (match-beginning 1))) + ;; try one more in case max-beginning lies in the middle of a match + (setq save-point (point)) + (when (re-search-forward regex nil t) + (let ((try-beginning (match-beginning 1))) + (if (> try-beginning max-beginning) + (goto-char save-point) + (setq count (1+ count)) + (setq beginning try-beginning)))) + (when beginning (goto-char beginning)) + count)) + +;; Gets list describing pos or (point). +;; The list contains: +;; 1. the number of matches of rust--format-word, +;; 2. the number of matches of rust--format-line after that, +;; 3. the number of columns after that. +(defun rust--format-get-loc (buffer &optional pos) + (with-current-buffer buffer + (save-excursion + (let ((pos (or pos (point))) + words lines columns) + (goto-char (point-min)) + (setq words (rust--format-count rust--format-word pos)) + (setq lines (rust--format-count rust--format-line pos)) + (if (> lines 0) + (if (= (point) pos) + (setq columns -1) + (forward-char 1) + (goto-char pos) + (setq columns (current-column))) + (let ((initial-column (current-column))) + (goto-char pos) + (setq columns (- (current-column) initial-column)))) + (list words lines columns))))) + +;; Moves the point forward by count matches of regex up to max-pos, +;; and returns new max-pos making sure final position does not include another match. +(defun rust--format-forward (regex count max-pos) + (when (< (point) max-pos) + (let ((beginning (point))) + (while (> count 0) + (setq count (1- count)) + (re-search-forward regex nil t) + (setq beginning (match-beginning 1))) + (when (re-search-forward regex nil t) + (setq max-pos (min max-pos (match-beginning 1)))) + (goto-char beginning))) + max-pos) + +;; Gets the position from a location list obtained using rust--format-get-loc. +(defun rust--format-get-pos (buffer loc) + (with-current-buffer buffer + (save-excursion + (goto-char (point-min)) + (let ((max-pos (point-max)) + (words (pop loc)) + (lines (pop loc)) + (columns (pop loc))) + (setq max-pos (rust--format-forward rust--format-word words max-pos)) + (setq max-pos (rust--format-forward rust--format-line lines max-pos)) + (when (> lines 0) (forward-char)) + (let ((initial-column (current-column)) + (save-point (point))) + (move-end-of-line nil) + (when (> (current-column) (+ initial-column columns)) + (goto-char save-point) + (forward-char columns))) + (min (point) max-pos))))) + +(defun rust-format-diff-buffer () + "Show diff to current buffer from rustfmt. + +Return the created process." + (interactive) + (unless (executable-find rust-rustfmt-bin) + (error "Could not locate executable %S" rust-rustfmt-bin)) + (let* ((buffer + (with-current-buffer + (get-buffer-create "*rustfmt-diff*") + (let ((inhibit-read-only t)) + (erase-buffer)) + (current-buffer))) + (proc + (apply #'start-process + "rustfmt-diff" + buffer + rust-rustfmt-bin + "--check" + (cons (buffer-file-name) + rust-rustfmt-switches)))) + (set-process-sentinel proc #'rust-format-diff-buffer-sentinel) + proc)) + +(defun rust-format-diff-buffer-sentinel (process _e) + (when (eq 'exit (process-status process)) + (if (> (process-exit-status process) 0) + (with-current-buffer "*rustfmt-diff*" + (let ((inhibit-read-only t)) + (diff-mode)) + (pop-to-buffer (current-buffer))) + (message "rustfmt check passed.")))) + +(defun rust--format-buffer-using-replace-buffer-contents () + (condition-case err + (progn + (rust--format-call (current-buffer)) + (message "Formatted buffer with rustfmt.")) + (error + (or (rust--format-error-handler) + (signal (car err) (cdr err)))))) + +(defun rust--format-buffer-saving-position-manually () + (let* ((current (current-buffer)) + (base (or (buffer-base-buffer current) current)) + buffer-loc + window-loc) + (dolist (buffer (buffer-list)) + (when (or (eq buffer base) + (eq (buffer-base-buffer buffer) base)) + (push (list buffer + (rust--format-get-loc buffer nil)) + buffer-loc))) + (dolist (frame (frame-list)) + (dolist (window (window-list frame)) + (let ((buffer (window-buffer window))) + (when (or (eq buffer base) + (eq (buffer-base-buffer buffer) base)) + (let ((start (window-start window)) + (point (window-point window))) + (push (list window + (rust--format-get-loc buffer start) + (rust--format-get-loc buffer point)) + window-loc)))))) + (condition-case err + (unwind-protect + ;; save and restore window start position + ;; after reformatting + ;; to avoid the disturbing scrolling + (let ((w-start (window-start))) + (rust--format-call (current-buffer)) + (set-window-start (selected-window) w-start) + (message "Formatted buffer with rustfmt.")) + (dolist (loc buffer-loc) + (let* ((buffer (pop loc)) + (pos (rust--format-get-pos buffer (pop loc)))) + (with-current-buffer buffer + (goto-char pos)))) + (dolist (loc window-loc) + (let* ((window (pop loc)) + (buffer (window-buffer window)) + (start (rust--format-get-pos buffer (pop loc))) + (pos (rust--format-get-pos buffer (pop loc)))) + (unless (eq buffer current) + (set-window-start window start)) + (set-window-point window pos)))) + (error + (or (rust--format-error-handler) + (signal (car err) (cdr err))))))) + +(defun rust-format-buffer () + "Format the current buffer using rustfmt." + (interactive) + (unless (executable-find rust-rustfmt-bin) + (error "Could not locate executable \"%s\"" rust-rustfmt-bin)) + ;; If emacs version >= 26.2, we can use replace-buffer-contents to + ;; preserve location and markers in buffer, otherwise we can try to + ;; save locations as best we can, though we still lose markers. + (save-excursion + (if (version<= "26.2" emacs-version) + (rust--format-buffer-using-replace-buffer-contents) + (rust--format-buffer-saving-position-manually)))) + +(defun rust-enable-format-on-save () + "Enable formatting using rustfmt when saving buffer." + (interactive) + (setq-local rust-format-on-save t)) + +(defun rust-disable-format-on-save () + "Disable formatting using rustfmt when saving buffer." + (interactive) + (setq-local rust-format-on-save nil)) + +;;; Hooks + +(defun rust-before-save-method () + (when rust-format-on-save + (condition-case e + (rust-format-buffer) + (message (format "rust-before-save-hook: %S %S" + (car e) + (cdr e)))))) + +(defun rust-after-save-method () + (when rust-format-on-save + (if (not (executable-find rust-rustfmt-bin)) + (error "Could not locate executable \"%s\"" rust-rustfmt-bin) + (when (get-buffer rust-rustfmt-buffername) + ;; KLDUGE: re-run the error handlers -- otherwise message area + ;; would show "Wrote ..." instead of the error description. + (or (rust--format-error-handler) + (message "rustfmt detected problems, see %s for more." + rust-rustfmt-buffername)))))) + +;;; _ +(provide 'rust-rustfmt) +;;; rust-rustfmt.el ends here diff --git a/code/elpa/rust-mode-20220819.1203/rust-utils.el b/code/elpa/rust-mode-20220819.1203/rust-utils.el new file mode 100644 index 0000000..41f1ba8 --- /dev/null +++ b/code/elpa/rust-mode-20220819.1203/rust-utils.el @@ -0,0 +1,91 @@ +;;; rust-utils.el --- Various Rust utilities -*- lexical-binding:t -*- +;;; Commentary: + +;; This library implements various utilities for dealing with Rust +;; code. + +;;; Code: + +(require 'thingatpt) + +(require 'rust-mode) ; for `rust-in-str' and `rust-looking-back-str' + +;;; Promote module + +(defun rust-promote-module-into-dir () + "Promote the module file visited by the current buffer into its own directory. + +For example, if the current buffer is visiting the file `foo.rs', +then this function creates the directory `foo' and renames the +file to `foo/mod.rs'. The current buffer will be updated to +visit the new file." + (interactive) + (let ((filename (buffer-file-name))) + (if (not filename) + (message "Buffer is not visiting a file.") + (if (string-equal (file-name-nondirectory filename) "mod.rs") + (message "Won't promote a module file already named mod.rs.") + (let* ((basename (file-name-sans-extension + (file-name-nondirectory filename))) + (mod-dir (file-name-as-directory + (concat (file-name-directory filename) basename))) + (new-name (concat mod-dir "mod.rs"))) + (mkdir mod-dir t) + (rename-file filename new-name 1) + (set-visited-file-name new-name)))))) + +;;; dbg! macro + +(defun rust-insert-dbg () + "Insert the dbg! macro." + (cond ((region-active-p) + (when (< (mark) (point)) + (exchange-point-and-mark)) + (let ((old-point (point))) + (insert-parentheses) + (goto-char old-point))) + (t + (when (rust-in-str) + (up-list -1 t t)) + (insert "(") + (forward-sexp) + (insert ")") + (backward-sexp))) + (insert "dbg!")) + +;;;###autoload +(defun rust-dbg-wrap-or-unwrap () + "Either remove or add the dbg! macro." + (interactive) + (save-excursion + (if (region-active-p) + (rust-insert-dbg) + + (let ((beginning-of-symbol (ignore-errors (beginning-of-thing 'symbol)))) + (when beginning-of-symbol + (goto-char beginning-of-symbol))) + + (let ((dbg-point (save-excursion + (or (and (looking-at-p "dbg!") (+ 4 (point))) + (ignore-errors + (while (not (rust-looking-back-str "dbg!")) + (backward-up-list)) + (point)))))) + (cond (dbg-point + (goto-char dbg-point) + (delete-char -4) + (delete-pair)) + (t (rust-insert-dbg))))))) + +(defun rust-toggle-mutability () + "Toggles the mutability of the variable defined on the current line" + (interactive) + (save-excursion + (back-to-indentation) + (forward-word) + (if (string= " mut" (buffer-substring (point) (+ (point) 4))) + (delete-region (point) (+ (point) 4)) + (insert " mut")))) +;;; _ +(provide 'rust-utils) +;;; rust-utils.el ends here diff --git a/code/elpa/s-20220816.956/s-autoloads.el b/code/elpa/s-20220816.956/s-autoloads.el new file mode 100644 index 0000000..187f6d1 --- /dev/null +++ b/code/elpa/s-20220816.956/s-autoloads.el @@ -0,0 +1,22 @@ +;;; s-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "s" "s.el" (0 0 0 0)) +;;; Generated autoloads from s.el + +(register-definition-prefixes "s" '("s-")) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; s-autoloads.el ends here diff --git a/code/elpa/s-20220816.956/s-pkg.el b/code/elpa/s-20220816.956/s-pkg.el new file mode 100644 index 0000000..f35c18a --- /dev/null +++ b/code/elpa/s-20220816.956/s-pkg.el @@ -0,0 +1,2 @@ +;;; Generated package description from s.el -*- no-byte-compile: t -*- +(define-package "s" "20220816.956" "The long lost Emacs string manipulation library." 'nil :commit "07c57d3562da534d1b18a0fb21e12cd6ae6ff4d7" :authors '(("Magnar Sveen" . "magnars@gmail.com")) :maintainer '("Magnar Sveen" . "magnars@gmail.com") :keywords '("strings")) diff --git a/code/elpa/s-20220816.956/s.el b/code/elpa/s-20220816.956/s.el new file mode 100644 index 0000000..2243427 --- /dev/null +++ b/code/elpa/s-20220816.956/s.el @@ -0,0 +1,785 @@ +;;; s.el --- The long lost Emacs string manipulation library. + +;; Copyright (C) 2012-2015 Magnar Sveen + +;; Author: Magnar Sveen +;; Version: 1.12.0 +;; Package-Version: 20220816.956 +;; Package-Commit: 07c57d3562da534d1b18a0fb21e12cd6ae6ff4d7 +;; Keywords: strings + +;; 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 . + +;;; Commentary: + +;; The long lost Emacs string manipulation library. +;; +;; See documentation on https://github.com/magnars/s.el#functions + +;;; Code: + +;; Silence byte-compiler +(defvar ucs-normalize-combining-chars) ; Defined in `ucs-normalize' +(autoload 'slot-value "eieio") + +(defun s-trim-left (s) + "Remove whitespace at the beginning of S." + (declare (pure t) (side-effect-free t)) + (save-match-data + (if (string-match "\\`[ \t\n\r]+" s) + (replace-match "" t t s) + s))) + +(defun s-trim-right (s) + "Remove whitespace at the end of S." + (declare (pure t) (side-effect-free t)) + (save-match-data + (if (string-match "[ \t\n\r]+\\'" s) + (replace-match "" t t s) + s))) + +(defun s-trim (s) + "Remove whitespace at the beginning and end of S." + (declare (pure t) (side-effect-free t)) + (s-trim-left (s-trim-right s))) + +(defun s-collapse-whitespace (s) + "Convert all adjacent whitespace characters to a single space." + (declare (pure t) (side-effect-free t)) + (replace-regexp-in-string "[ \t\n\r]+" " " s)) + +(defun s-split (separator s &optional omit-nulls) + "Split S into substrings bounded by matches for regexp SEPARATOR. +If OMIT-NULLS is non-nil, zero-length substrings are omitted. + +This is a simple wrapper around the built-in `split-string'." + (declare (side-effect-free t)) + (save-match-data + (split-string s separator omit-nulls))) + +(defun s-split-up-to (separator s n &optional omit-nulls) + "Split S up to N times into substrings bounded by matches for regexp SEPARATOR. + +If OMIT-NULLS is non-nil, zero-length substrings are omitted. + +See also `s-split'." + (declare (side-effect-free t)) + (save-match-data + (let ((op 0) + (r nil)) + (with-temp-buffer + (insert s) + (setq op (goto-char (point-min))) + (while (and (re-search-forward separator nil t) + (< 0 n)) + (let ((sub (buffer-substring op (match-beginning 0)))) + (unless (and omit-nulls + (equal sub "")) + (push sub r))) + (setq op (goto-char (match-end 0))) + (setq n (1- n))) + (let ((sub (buffer-substring op (point-max)))) + (unless (and omit-nulls + (equal sub "")) + (push sub r)))) + (nreverse r)))) + +(defun s-lines (s) + "Splits S into a list of strings on newline characters." + (declare (pure t) (side-effect-free t)) + (s-split "\\(\r\n\\|[\n\r]\\)" s)) + +(defun s-join (separator strings) + "Join all the strings in STRINGS with SEPARATOR in between." + (declare (pure t) (side-effect-free t)) + (mapconcat 'identity strings separator)) + +(defun s-concat (&rest strings) + "Join all the string arguments into one string." + (declare (pure t) (side-effect-free t)) + (apply 'concat strings)) + +(defun s-prepend (prefix s) + "Concatenate PREFIX and S." + (declare (pure t) (side-effect-free t)) + (concat prefix s)) + +(defun s-append (suffix s) + "Concatenate S and SUFFIX." + (declare (pure t) (side-effect-free t)) + (concat s suffix)) + +(defun s-splice (needle n s) + "Splice NEEDLE into S at position N. +0 is the beginning of the string, -1 is the end." + (if (< n 0) + (let ((left (substring s 0 (+ 1 n (length s)))) + (right (s-right (- -1 n) s))) + (concat left needle right)) + (let ((left (s-left n s)) + (right (substring s n (length s)))) + (concat left needle right)))) + + +(defun s-repeat (num s) + "Make a string of S repeated NUM times." + (declare (pure t) (side-effect-free t)) + (let (ss) + (while (> num 0) + (setq ss (cons s ss)) + (setq num (1- num))) + (apply 'concat ss))) + +(defun s-chop-suffix (suffix s) + "Remove SUFFIX if it is at end of S." + (declare (pure t) (side-effect-free t)) + (let ((pos (- (length suffix)))) + (if (and (>= (length s) (length suffix)) + (string= suffix (substring s pos))) + (substring s 0 pos) + s))) + +(defun s-chop-suffixes (suffixes s) + "Remove SUFFIXES one by one in order, if they are at the end of S." + (declare (pure t) (side-effect-free t)) + (while suffixes + (setq s (s-chop-suffix (car suffixes) s)) + (setq suffixes (cdr suffixes))) + s) + +(defun s-chop-prefix (prefix s) + "Remove PREFIX if it is at the start of S." + (declare (pure t) (side-effect-free t)) + (let ((pos (length prefix))) + (if (and (>= (length s) (length prefix)) + (string= prefix (substring s 0 pos))) + (substring s pos) + s))) + +(defun s-chop-prefixes (prefixes s) + "Remove PREFIXES one by one in order, if they are at the start of S." + (declare (pure t) (side-effect-free t)) + (while prefixes + (setq s (s-chop-prefix (car prefixes) s)) + (setq prefixes (cdr prefixes))) + s) + +(defun s-shared-start (s1 s2) + "Returns the longest prefix S1 and S2 have in common." + (declare (pure t) (side-effect-free t)) + (let ((cmp (compare-strings s1 0 (length s1) s2 0 (length s2)))) + (if (eq cmp t) s1 (substring s1 0 (1- (abs cmp)))))) + +(defun s-shared-end (s1 s2) + "Returns the longest suffix S1 and S2 have in common." + (declare (pure t) (side-effect-free t)) + (let* ((l1 (length s1)) + (l2 (length s2)) + (search-length (min l1 l2)) + (i 0)) + (while (and (< i search-length) + (= (aref s1 (- l1 i 1)) (aref s2 (- l2 i 1)))) + (setq i (1+ i))) + ;; If I is 0, then it means that there's no common suffix between + ;; S1 and S2. + ;; + ;; However, since (substring s (- 0)) will return the whole + ;; string, `s-shared-end' should simply return the empty string + ;; when I is 0. + (if (zerop i) + "" + (substring s1 (- i))))) + +(defun s-chomp (s) + "Remove one trailing `\\n`, `\\r` or `\\r\\n` from S." + (declare (pure t) (side-effect-free t)) + (s-chop-suffixes '("\n" "\r") s)) + +(defun s-truncate (len s &optional ellipsis) + "If S is longer than LEN, cut it down and add ELLIPSIS to the end. + +The resulting string, including ellipsis, will be LEN characters +long. + +When not specified, ELLIPSIS defaults to ‘...’." + (declare (pure t) (side-effect-free t)) + (unless ellipsis + (setq ellipsis "...")) + (if (> (length s) len) + (format "%s%s" (substring s 0 (- len (length ellipsis))) ellipsis) + s)) + +(defun s-word-wrap (len s) + "If S is longer than LEN, wrap the words with newlines." + (declare (side-effect-free t)) + (save-match-data + (with-temp-buffer + (insert s) + (let ((fill-column len)) + (fill-region (point-min) (point-max))) + (buffer-substring (point-min) (point-max))))) + +(defun s-center (len s) + "If S is shorter than LEN, pad it with spaces so it is centered." + (declare (pure t) (side-effect-free t)) + (let ((extra (max 0 (- len (length s))))) + (concat + (make-string (ceiling extra 2) ?\s) + s + (make-string (floor extra 2) ?\s)))) + +(defun s-pad-left (len padding s) + "If S is shorter than LEN, pad it with PADDING on the left." + (declare (pure t) (side-effect-free t)) + (let ((extra (max 0 (- len (length s))))) + (concat (make-string extra (string-to-char padding)) + s))) + +(defun s-pad-right (len padding s) + "If S is shorter than LEN, pad it with PADDING on the right." + (declare (pure t) (side-effect-free t)) + (let ((extra (max 0 (- len (length s))))) + (concat s + (make-string extra (string-to-char padding))))) + +(defun s-left (len s) + "Returns up to the LEN first chars of S." + (declare (pure t) (side-effect-free t)) + (if (> (length s) len) + (substring s 0 len) + s)) + +(defun s-right (len s) + "Returns up to the LEN last chars of S." + (declare (pure t) (side-effect-free t)) + (let ((l (length s))) + (if (> l len) + (substring s (- l len) l) + s))) + +(defun s-chop-left (len s) + "Remove the first LEN chars from S." + (let ((l (length s))) + (if (> l len) + (substring s len l) + ""))) + +(defun s-chop-right (len s) + "Remove the last LEN chars from S." + (let ((l (length s))) + (if (> l len) + (substring s 0 (- l len)) + ""))) + +(defun s-ends-with? (suffix s &optional ignore-case) + "Does S end with SUFFIX? + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences. + +Alias: `s-suffix?'" + (declare (pure t) (side-effect-free t)) + (let ((start-pos (- (length s) (length suffix)))) + (and (>= start-pos 0) + (eq t (compare-strings suffix nil nil + s start-pos nil ignore-case))))) + +(defun s-starts-with? (prefix s &optional ignore-case) + "Does S start with PREFIX? + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences. + +Alias: `s-prefix?'. This is a simple wrapper around the built-in +`string-prefix-p'." + (declare (pure t) (side-effect-free t)) + (string-prefix-p prefix s ignore-case)) + +(defun s--truthy? (val) + (declare (pure t) (side-effect-free t)) + (not (null val))) + +(defun s-contains? (needle s &optional ignore-case) + "Does S contain NEEDLE? + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences." + (declare (pure t) (side-effect-free t)) + (let ((case-fold-search ignore-case)) + (s--truthy? (string-match-p (regexp-quote needle) s)))) + +(defun s-equals? (s1 s2) + "Is S1 equal to S2? + +This is a simple wrapper around the built-in `string-equal'." + (declare (pure t) (side-effect-free t)) + (string-equal s1 s2)) + +(defun s-less? (s1 s2) + "Is S1 less than S2? + +This is a simple wrapper around the built-in `string-lessp'." + (declare (pure t) (side-effect-free t)) + (string-lessp s1 s2)) + +(defun s-matches? (regexp s &optional start) + "Does REGEXP match S? +If START is non-nil the search starts at that index. + +This is a simple wrapper around the built-in `string-match-p'." + (declare (side-effect-free t)) + (s--truthy? (string-match-p regexp s start))) + +(defun s-blank? (s) + "Is S nil or the empty string?" + (declare (pure t) (side-effect-free t)) + (or (null s) (string= "" s))) + +(defun s-blank-str? (s) + "Is S nil or the empty string or string only contains whitespace?" + (declare (pure t) (side-effect-free t)) + (or (s-blank? s) (s-blank? (s-trim s)))) + +(defun s-present? (s) + "Is S anything but nil or the empty string?" + (declare (pure t) (side-effect-free t)) + (not (s-blank? s))) + +(defun s-presence (s) + "Return S if it's `s-present?', otherwise return nil." + (declare (pure t) (side-effect-free t)) + (and (s-present? s) s)) + +(defun s-lowercase? (s) + "Are all the letters in S in lower case?" + (declare (side-effect-free t)) + (let ((case-fold-search nil)) + (not (string-match-p "[[:upper:]]" s)))) + +(defun s-uppercase? (s) + "Are all the letters in S in upper case?" + (declare (side-effect-free t)) + (let ((case-fold-search nil)) + (not (string-match-p "[[:lower:]]" s)))) + +(defun s-mixedcase? (s) + "Are there both lower case and upper case letters in S?" + (let ((case-fold-search nil)) + (s--truthy? + (and (string-match-p "[[:lower:]]" s) + (string-match-p "[[:upper:]]" s))))) + +(defun s-capitalized? (s) + "In S, is the first letter upper case, and all other letters lower case?" + (declare (side-effect-free t)) + (let ((case-fold-search nil)) + (s--truthy? + (string-match-p "^[[:upper:]][^[:upper:]]*$" s)))) + +(defun s-numeric? (s) + "Is S a number?" + (declare (pure t) (side-effect-free t)) + (s--truthy? + (string-match-p "^[0-9]+$" s))) + +(defun s-replace (old new s) + "Replaces OLD with NEW in S." + (declare (pure t) (side-effect-free t)) + (replace-regexp-in-string (regexp-quote old) new s t t)) + +(defalias 's-replace-regexp 'replace-regexp-in-string) + +(defun s--aget (alist key) + (declare (pure t) (side-effect-free t)) + (cdr (assoc-string key alist))) + +(defun s-replace-all (replacements s) + "REPLACEMENTS is a list of cons-cells. Each `car` is replaced with `cdr` in S." + (declare (pure t) (side-effect-free t)) + (replace-regexp-in-string (regexp-opt (mapcar 'car replacements)) + (lambda (it) (s--aget replacements it)) + s t t)) + +(defun s-downcase (s) + "Convert S to lower case. + +This is a simple wrapper around the built-in `downcase'." + (declare (side-effect-free t)) + (downcase s)) + +(defun s-upcase (s) + "Convert S to upper case. + +This is a simple wrapper around the built-in `upcase'." + (declare (side-effect-free t)) + (upcase s)) + +(defun s-capitalize (s) + "Convert S first word's first character to upper and the rest to lower case." + (declare (side-effect-free t)) + (concat (upcase (substring s 0 1)) (downcase (substring s 1)))) + +(defun s-titleize (s) + "Convert in S each word's first character to upper and the rest to lower case. + +This is a simple wrapper around the built-in `capitalize'." + (declare (side-effect-free t)) + (capitalize s)) + +(defmacro s-with (s form &rest more) + "Threads S through the forms. Inserts S as the last item +in the first form, making a list of it if it is not a list +already. If there are more forms, inserts the first form as the +last item in second form, etc." + (declare (debug (form &rest [&or (function &rest form) fboundp]))) + (if (null more) + (if (listp form) + `(,(car form) ,@(cdr form) ,s) + (list form s)) + `(s-with (s-with ,s ,form) ,@more))) + +(put 's-with 'lisp-indent-function 1) + +(defun s-index-of (needle s &optional ignore-case) + "Returns first index of NEEDLE in S, or nil. + +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences." + (declare (pure t) (side-effect-free t)) + (let ((case-fold-search ignore-case)) + (string-match-p (regexp-quote needle) s))) + +(defun s-reverse (s) + "Return the reverse of S." + (declare (pure t) (side-effect-free t)) + (save-match-data + (if (multibyte-string-p s) + (let ((input (string-to-list s)) + output) + (require 'ucs-normalize) + (while input + ;; Handle entire grapheme cluster as a single unit + (let ((grapheme (list (pop input)))) + (while (memql (car input) ucs-normalize-combining-chars) + (push (pop input) grapheme)) + (setq output (nconc (nreverse grapheme) output)))) + (concat output)) + (concat (nreverse (string-to-list s)))))) + +(defun s-match-strings-all (regex string) + "Return a list of matches for REGEX in STRING. + +Each element itself is a list of matches, as per +`match-string'. Multiple matches at the same position will be +ignored after the first." + (declare (side-effect-free t)) + (save-match-data + (let ((all-strings ()) + (i 0)) + (while (and (< i (length string)) + (string-match regex string i)) + (setq i (1+ (match-beginning 0))) + (let (strings + (num-matches (/ (length (match-data)) 2)) + (match 0)) + (while (/= match num-matches) + (push (match-string match string) strings) + (setq match (1+ match))) + (push (nreverse strings) all-strings))) + (nreverse all-strings)))) + +(defun s-matched-positions-all (regexp string &optional subexp-depth) + "Return a list of matched positions for REGEXP in STRING. +SUBEXP-DEPTH is 0 by default." + (declare (side-effect-free t)) + (if (null subexp-depth) + (setq subexp-depth 0)) + (save-match-data + (let ((pos 0) result) + (while (and (string-match regexp string pos) + (< pos (length string))) + (let ((m (match-end subexp-depth))) + (push (cons (match-beginning subexp-depth) (match-end subexp-depth)) result) + (setq pos (match-end 0)))) + (nreverse result)))) + +(defun s-match (regexp s &optional start) + "When the given expression matches the string, this function returns a list +of the whole matching string and a string for each matched subexpressions. +Subexpressions that didn't match are represented by nil elements +in the list, except that non-matching subexpressions at the end +of REGEXP might not appear at all in the list. That is, the +returned list can be shorter than the number of subexpressions in +REGEXP plus one. If REGEXP did not match the returned value is +an empty list (nil). + +When START is non-nil the search will start at that index." + (declare (side-effect-free t)) + (save-match-data + (if (string-match regexp s start) + (let ((match-data-list (match-data)) + result) + (while match-data-list + (let* ((beg (car match-data-list)) + (end (cadr match-data-list)) + (subs (if (and beg end) (substring s beg end) nil))) + (setq result (cons subs result)) + (setq match-data-list + (cddr match-data-list)))) + (nreverse result))))) + +(defun s-slice-at (regexp s) + "Slices S up at every index matching REGEXP." + (declare (side-effect-free t)) + (if (s-blank? s) + (list s) + (let (ss) + (while (not (s-blank? s)) + (save-match-data + (let ((i (string-match regexp s 1))) + (if i + (setq ss (cons (substring s 0 i) ss) + s (substring s i)) + (setq ss (cons s ss) + s ""))))) + (nreverse ss)))) + +(defun s-split-words (s) + "Split S into list of words." + (declare (side-effect-free t)) + (s-split + "[^[:word:]0-9]+" + (let ((case-fold-search nil)) + (replace-regexp-in-string + "\\([[:lower:]]\\)\\([[:upper:]]\\)" "\\1 \\2" + (replace-regexp-in-string "\\([[:upper:]]\\)\\([[:upper:]][0-9[:lower:]]\\)" "\\1 \\2" s))) + t)) + +(defun s--mapcar-head (fn-head fn-rest list) + "Like MAPCAR, but applies a different function to the first element." + (if list + (cons (funcall fn-head (car list)) (mapcar fn-rest (cdr list))))) + +(defun s-lower-camel-case (s) + "Convert S to lowerCamelCase." + (declare (side-effect-free t)) + (s-join "" (s--mapcar-head 'downcase 'capitalize (s-split-words s)))) + +(defun s-upper-camel-case (s) + "Convert S to UpperCamelCase." + (declare (side-effect-free t)) + (s-join "" (mapcar 'capitalize (s-split-words s)))) + +(defun s-snake-case (s) + "Convert S to snake_case." + (declare (side-effect-free t)) + (s-join "_" (mapcar 'downcase (s-split-words s)))) + +(defun s-dashed-words (s) + "Convert S to dashed-words." + (declare (side-effect-free t)) + (s-join "-" (mapcar 'downcase (s-split-words s)))) + +(defun s-spaced-words (s) + "Convert S to spaced words." + (declare (side-effect-free t)) + (s-join " " (s-split-words s))) + +(defun s-capitalized-words (s) + "Convert S to Capitalized words." + (declare (side-effect-free t)) + (let ((words (s-split-words s))) + (s-join " " (cons (capitalize (car words)) (mapcar 'downcase (cdr words)))))) + +(defun s-titleized-words (s) + "Convert S to Titleized Words." + (declare (side-effect-free t)) + (s-join " " (mapcar 's-titleize (s-split-words s)))) + +(defun s-word-initials (s) + "Convert S to its initials." + (declare (side-effect-free t)) + (s-join "" (mapcar (lambda (ss) (substring ss 0 1)) + (s-split-words s)))) + +;; Errors for s-format +(progn + (put 's-format-resolve + 'error-conditions + '(error s-format s-format-resolve)) + (put 's-format-resolve + 'error-message + "Cannot resolve a template to values")) + +(defun s-format (template replacer &optional extra) + "Format TEMPLATE with the function REPLACER. + +REPLACER takes an argument of the format variable and optionally +an extra argument which is the EXTRA value from the call to +`s-format'. + +Several standard `s-format' helper functions are recognized and +adapted for this: + + (s-format \"${name}\" \\='gethash hash-table) + (s-format \"${name}\" \\='aget alist) + (s-format \"$0\" \\='elt sequence) + +The REPLACER function may be used to do any other kind of +transformation." + (let ((saved-match-data (match-data))) + (unwind-protect + (replace-regexp-in-string + "\\$\\({\\([^}]+\\)}\\|[0-9]+\\)" + (lambda (md) + (let ((var + (let ((m (match-string 2 md))) + (if m m + (string-to-number (match-string 1 md))))) + (replacer-match-data (match-data))) + (unwind-protect + (let ((v + (cond + ((eq replacer 'gethash) + (funcall replacer var extra)) + ((eq replacer 'aget) + (funcall 's--aget extra var)) + ((eq replacer 'elt) + (funcall replacer extra var)) + ((eq replacer 'oref) + (funcall #'slot-value extra (intern var))) + (t + (set-match-data saved-match-data) + (if extra + (funcall replacer var extra) + (funcall replacer var)))))) + (if v (format "%s" v) (signal 's-format-resolve md))) + (set-match-data replacer-match-data)))) + template + ;; Need literal to make sure it works + t t) + (set-match-data saved-match-data)))) + +(defvar s-lex-value-as-lisp nil + "If `t' interpolate lisp values as lisp. + +`s-lex-format' inserts values with (format \"%S\").") + +(defun s-lex-fmt|expand (fmt) + "Expand FMT into lisp." + (declare (side-effect-free t)) + (list 's-format fmt (quote 'aget) + (append '(list) + (mapcar + (lambda (matches) + (list + 'cons + (cadr matches) + `(format + (if s-lex-value-as-lisp "%S" "%s") + ,(intern (cadr matches))))) + (s-match-strings-all "${\\([^}]+\\)}" fmt))))) + +(defmacro s-lex-format (format-str) + "`s-format` with the current environment. + +FORMAT-STR may use the `s-format' variable reference to refer to +any variable: + + (let ((x 1)) + (s-lex-format \"x is: ${x}\")) + +The values of the variables are interpolated with \"%s\" unless +the variable `s-lex-value-as-lisp' is `t' and then they are +interpolated with \"%S\"." + (declare (debug (form))) + (s-lex-fmt|expand format-str)) + +(defun s-count-matches (regexp s &optional start end) + "Count occurrences of `regexp' in `s'. + +`start', inclusive, and `end', exclusive, delimit the part of `s' to +match. `start' and `end' are both indexed starting at 1; the initial +character in `s' is index 1. + +This function starts looking for the next match from the end of the +previous match. Hence, it ignores matches that overlap a previously +found match. To count overlapping matches, use +`s-count-matches-all'." + (declare (side-effect-free t)) + (save-match-data + (with-temp-buffer + (insert s) + (goto-char (point-min)) + (count-matches regexp (or start 1) (or end (point-max)))))) + +(defun s-count-matches-all (regexp s &optional start end) + "Count occurrences of `regexp' in `s'. + +`start', inclusive, and `end', exclusive, delimit the part of `s' to +match. `start' and `end' are both indexed starting at 1; the initial +character in `s' is index 1. + +This function starts looking for the next match from the second +character of the previous match. Hence, it counts matches that +overlap a previously found match. To ignore matches that overlap a +previously found match, use `s-count-matches'." + (declare (side-effect-free t)) + (let* ((anchored-regexp (format "^%s" regexp)) + (match-count 0) + (i 0) + (narrowed-s (substring s + (when start (1- start)) + (when end (1- end))))) + (save-match-data + (while (< i (length narrowed-s)) + (when (s-matches? anchored-regexp (substring narrowed-s i)) + (setq match-count (1+ match-count))) + (setq i (1+ i)))) + match-count)) + +(defun s-wrap (s prefix &optional suffix) + "Wrap string S with PREFIX and optionally SUFFIX. + +Return string S with PREFIX prepended. If SUFFIX is present, it +is appended, otherwise PREFIX is used as both prefix and +suffix." + (declare (pure t) (side-effect-free t)) + (concat prefix s (or suffix prefix))) + + +;;; Aliases + +(defalias 's-blank-p 's-blank?) +(defalias 's-blank-str-p 's-blank-str?) +(defalias 's-capitalized-p 's-capitalized?) +(defalias 's-contains-p 's-contains?) +(defalias 's-ends-with-p 's-ends-with?) +(defalias 's-equals-p 's-equals?) +(defalias 's-less-p 's-less?) +(defalias 's-lowercase-p 's-lowercase?) +(defalias 's-matches-p 's-matches?) +(defalias 's-mixedcase-p 's-mixedcase?) +(defalias 's-numeric-p 's-numeric?) +(defalias 's-prefix-p 's-starts-with?) +(defalias 's-prefix? 's-starts-with?) +(defalias 's-present-p 's-present?) +(defalias 's-starts-with-p 's-starts-with?) +(defalias 's-suffix-p 's-ends-with?) +(defalias 's-suffix? 's-ends-with?) +(defalias 's-uppercase-p 's-uppercase?) + + +(provide 's) +;;; s.el ends here diff --git a/code/elpa/transient-20220806.2224/dir b/code/elpa/transient-20220806.2224/dir new file mode 100644 index 0000000..d4d5540 --- /dev/null +++ b/code/elpa/transient-20220806.2224/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs misc features +* Transient: (transient). Transient Commands. diff --git a/code/elpa/transient-20220806.2224/gpl.info b/code/elpa/transient-20220806.2224/gpl.info new file mode 100644 index 0000000..094dde7 --- /dev/null +++ b/code/elpa/transient-20220806.2224/gpl.info @@ -0,0 +1,721 @@ +This is gpl.info, produced by makeinfo version 6.7 from gpl.texi. + + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies of this + license document, but changing it is not allowed. + +Preamble +======== + +The GNU General Public License is a free, copyleft license for software +and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS +==================== + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public + License. + + "Copyright" also means copyright-like laws that apply to other + kinds of works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this + License. Each licensee is addressed as "you". "Licensees" and + "recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the + work in a fashion requiring copyright permission, other than the + making of an exact copy. The resulting work is called a "modified + version" of the earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work + based on the Program. + + To "propagate" a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on + a computer or modifying a private copy. Propagation includes + copying, distribution (with or without modification), making + available to the public, and in some countries other activities as + well. + + To "convey" a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user + through a computer network, with no transfer of a copy, is not + conveying. + + An interactive user interface displays "Appropriate Legal Notices" + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to + the extent that warranties are provided), that licensees may convey + the work under this License, and how to view a copy of this + License. If the interface presents a list of user commands or + options, such as a menu, a prominent item in the list meets this + criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work + for making modifications to it. "Object code" means any non-source + form of a work. + + A "Standard Interface" means an interface that either is an + official standard defined by a recognized standards body, or, in + the case of interfaces specified for a particular programming + language, one that is widely used among developers working in that + language. + + The "System Libraries" of an executable work include anything, + other than the work as a whole, that (a) is included in the normal + form of packaging a Major Component, but which is not part of that + Major Component, and (b) serves only to enable use of the work with + that Major Component, or to implement a Standard Interface for + which an implementation is available to the public in source code + form. A "Major Component", in this context, means a major + essential component (kernel, window system, and so on) of the + specific operating system (if any) on which the executable work + runs, or a compiler used to produce the work, or an object code + interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts + to control those activities. However, it does not include the + work's System Libraries, or general-purpose tools or generally + available free programs which are used unmodified in performing + those activities but which are not part of the work. For example, + Corresponding Source includes interface definition files associated + with source files for the work, and the source code for shared + libraries and dynamically linked subprograms that the work is + specifically designed to require, such as by intimate data + communication or control flow between those subprograms and other + parts of the work. + + The Corresponding Source need not include anything that users can + regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running + a covered work is covered by this License only if the output, given + its content, constitutes a covered work. This License acknowledges + your rights of fair use or other equivalent, as provided by + copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise + remains in force. You may convey covered works to others for the + sole purpose of having them make modifications exclusively for you, + or provide you with facilities for running those works, provided + that you comply with the terms of this License in conveying all + material for which you do not control copyright. Those thus making + or running the covered works for you must do so exclusively on your + behalf, under your direction and control, on terms that prohibit + them from making any copies of your copyrighted material outside + their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section + 10 makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under + article 11 of the WIPO copyright treaty adopted on 20 December + 1996, or similar laws prohibiting or restricting circumvention of + such measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such + circumvention is effected by exercising rights under this License + with respect to the covered work, and you disclaim any intention to + limit operation or modification of the work as a means of + enforcing, against the work's users, your or third parties' legal + rights to forbid circumvention of technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the + code; keep intact all notices of the absence of any warranty; and + give all recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these + conditions: + + a. The work must carry prominent notices stating that you + modified it, and giving a relevant date. + + b. The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in + section 4 to "keep intact all notices". + + c. You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable + section 7 additional terms, to the whole of the work, and all + its parts, regardless of how they are packaged. This License + gives no permission to license the work in any other way, but + it does not invalidate such permission if you have separately + received it. + + d. If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has + interactive interfaces that do not display Appropriate Legal + Notices, your work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered + work, and which are not combined with it such as to form a larger + program, in or on a volume of a storage or distribution medium, is + called an "aggregate" if the compilation and its resulting + copyright are not used to limit the access or legal rights of the + compilation's users beyond what the individual works permit. + Inclusion of a covered work in an aggregate does not cause this + License to apply to the other parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this + License, in one of these ways: + + a. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that + product model, to give anyone who possesses the object code + either (1) a copy of the Corresponding Source for all the + software in the product that is covered by this License, on a + durable physical medium customarily used for software + interchange, for a price no more than your reasonable cost of + physically performing this conveying of source, or (2) access + to copy the Corresponding Source from a network server at no + charge. + + c. Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, + and only if you received the object code with such an offer, + in accord with subsection 6b. + + d. Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to + the Corresponding Source in the same way through the same + place at no further charge. You need not require recipients + to copy the Corresponding Source along with the object code. + If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by + you or a third party) that supports equivalent copying + facilities, provided you maintain clear directions next to the + object code saying where to find the Corresponding Source. + Regardless of what server hosts the Corresponding Source, you + remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + + e. Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the + general public at no charge under subsection 6d. + + A separable portion of the object code, whose source code is + excluded from the Corresponding Source as a System Library, need + not be included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means + any tangible personal property which is normally used for personal, + family, or household purposes, or (2) anything designed or sold for + incorporation into a dwelling. In determining whether a product is + a consumer product, doubtful cases shall be resolved in favor of + coverage. For a particular product received by a particular user, + "normally used" refers to a typical or common use of that class of + product, regardless of the status of the particular user or of the + way in which the particular user actually uses, or expects or is + expected to use, the product. A product is a consumer product + regardless of whether the product has substantial commercial, + industrial or non-consumer uses, unless such uses represent the + only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, + procedures, authorization keys, or other information required to + install and execute modified versions of a covered work in that + User Product from a modified version of its Corresponding Source. + The information must suffice to ensure that the continued + functioning of the modified object code is in no case prevented or + interfered with solely because modification has been made. + + If you convey an object code work under this section in, or with, + or specifically for use in, a User Product, and the conveying + occurs as part of a transaction in which the right of possession + and use of the User Product is transferred to the recipient in + perpetuity or for a fixed term (regardless of how the transaction + is characterized), the Corresponding Source conveyed under this + section must be accompanied by the Installation Information. But + this requirement does not apply if neither you nor any third party + retains the ability to install modified object code on the User + Product (for example, the work has been installed in ROM). + + The requirement to provide Installation Information does not + include a requirement to continue to provide support service, + warranty, or updates for a work that has been modified or installed + by the recipient, or for the User Product in which it has been + modified or installed. Access to a network may be denied when the + modification itself materially and adversely affects the operation + of the network or violates the rules and protocols for + communication across the network. + + Corresponding Source conveyed, and Installation Information + provided, in accord with this section must be in a format that is + publicly documented (and with an implementation available to the + public in source code form), and must require no special password + or key for unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of + this License by making exceptions from one or more of its + conditions. Additional permissions that are applicable to the + entire Program shall be treated as though they were included in + this License, to the extent that they are valid under applicable + law. If additional permissions apply only to part of the Program, + that part may be used separately under those permissions, but the + entire Program remains governed by this License without regard to + the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part + of it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material + you add to a covered work, you may (if authorized by the copyright + holders of that material) supplement the terms of this License with + terms: + + a. Disclaiming warranty or limiting liability differently from + the terms of sections 15 and 16 of this License; or + + b. Requiring preservation of specified reasonable legal notices + or author attributions in that material or in the Appropriate + Legal Notices displayed by works containing it; or + + c. Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked + in reasonable ways as different from the original version; or + + d. Limiting the use for publicity purposes of names of licensors + or authors of the material; or + + e. Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f. Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified + versions of it) with contractual assumptions of liability to + the recipient, for any liability that these contractual + assumptions directly impose on those licensors and authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as + you received it, or any part of it, contains a notice stating that + it is governed by this License along with a term that is a further + restriction, you may remove that term. If a license document + contains a further restriction but permits relicensing or conveying + under this License, you may add to a covered work material governed + by the terms of that license document, provided that the further + restriction does not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in + the form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights + under this License (including any patent licenses granted under the + third paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, you do not qualify to receive new licenses + for the same material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer + transmission to receive a copy likewise does not require + acceptance. However, nothing other than this License grants you + permission to propagate or modify any covered work. These actions + infringe copyright if you do not accept this License. Therefore, + by modifying or propagating a covered work, you indicate your + acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not + responsible for enforcing compliance by third parties with this + License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a + covered work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party's predecessor in interest had or + could give under the previous paragraph, plus a right to possession + of the Corresponding Source of the work from the predecessor in + interest, if the predecessor has it or can get it with reasonable + efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you + may not impose a license fee, royalty, or other charge for exercise + of rights granted under this License, and you may not initiate + litigation (including a cross-claim or counterclaim in a lawsuit) + alleging that any patent claim is infringed by making, using, + selling, offering for sale, or importing the Program or any portion + of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. + The work thus licensed is called the contributor's "contributor + version". + + A contributor's "essential patent claims" are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, + permitted by this License, of making, using, or selling its + contributor version, but do not include claims that would be + infringed only as a consequence of further modification of the + contributor version. For purposes of this definition, "control" + includes the right to grant patent sublicenses in a manner + consistent with the requirements of this License. + + Each contributor grants you a non-exclusive, worldwide, + royalty-free patent license under the contributor's essential + patent claims, to make, use, sell, offer for sale, import and + otherwise run, modify and propagate the contents of its contributor + version. + + In the following three paragraphs, a "patent license" is any + express agreement or commitment, however denominated, not to + enforce a patent (such as an express permission to practice a + patent or covenant not to sue for patent infringement). To "grant" + such a patent license to a party means to make such an agreement or + commitment not to enforce a patent against the party. + + If you convey a covered work, knowingly relying on a patent + license, and the Corresponding Source of the work is not available + for anyone to copy, free of charge and under the terms of this + License, through a publicly available network server or other + readily accessible means, then you must either (1) cause the + Corresponding Source to be so available, or (2) arrange to deprive + yourself of the benefit of the patent license for this particular + work, or (3) arrange, in a manner consistent with the requirements + of this License, to extend the patent license to downstream + recipients. "Knowingly relying" means you have actual knowledge + that, but for the patent license, your conveying the covered work + in a country, or your recipient's use of the covered work in a + country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, + modify or convey a specific copy of the covered work, then the + patent license you grant is automatically extended to all + recipients of the covered work and works based on it. + + A patent license is "discriminatory" if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that + are specifically granted under this License. You may not convey a + covered work if you are a party to an arrangement with a third + party that is in the business of distributing software, under which + you make payment to the third party based on the extent of your + activity of conveying the work, and under which the third party + grants, to any of the parties who would receive the covered work + from you, a discriminatory patent license (a) in connection with + copies of the covered work conveyed by you (or copies made from + those copies), or (b) primarily for and in connection with specific + products or compilations that contain the covered work, unless you + entered into that arrangement, or that patent license was granted, + prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement + or otherwise) that contradict the conditions of this License, they + do not excuse you from the conditions of this License. If you + cannot convey a covered work so as to satisfy simultaneously your + obligations under this License and any other pertinent obligations, + then as a consequence you may not convey it at all. For example, + if you agree to terms that obligate you to collect a royalty for + further conveying from those to whom you convey the Program, the + only way you could satisfy both those terms and this License would + be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a + single combined work, and to convey the resulting work. The terms + of this License will continue to apply to the part which is the + covered work, but the special requirements of the GNU Affero + General Public License, section 13, concerning interaction through + a network will apply to the combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new + versions of the GNU General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU + General Public License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that numbered version or of any later version published by the Free + Software Foundation. If the Program does not specify a version + number of the GNU General Public License, you may choose any + version ever published by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that + proxy's public statement of acceptance of a version permanently + authorizes you to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE + COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE + RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. + SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES + AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE + THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA + BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely + approximates an absolute waiver of all civil liability in + connection with the Program, unless a warranty or assumption of + liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS +=========================== + +How to Apply These Terms to Your New Programs +============================================= + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) YEAR NAME OF AUTHOR + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper +mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + PROGRAM Copyright (C) YEAR NAME OF AUTHOR + This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type 'show c' for details. + + The hypothetical commands 'show w' and 'show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + + You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + + The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . + + +Tag Table: + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/transient-20220806.2224/transient-autoloads.el b/code/elpa/transient-20220806.2224/transient-autoloads.el new file mode 100644 index 0000000..e578fc3 --- /dev/null +++ b/code/elpa/transient-20220806.2224/transient-autoloads.el @@ -0,0 +1,84 @@ +;;; transient-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "transient" "transient.el" (0 0 0 0)) +;;; Generated autoloads from transient.el + +(autoload 'transient-insert-suffix "transient" "\ +Insert a SUFFIX into PREFIX before LOC. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +Remove a conflicting binding unless optional KEEP-OTHER is + non-nil. +See info node `(transient)Modifying Existing Transients'. + +\(fn PREFIX LOC SUFFIX &optional KEEP-OTHER)" nil nil) + +(function-put 'transient-insert-suffix 'lisp-indent-function 'defun) + +(autoload 'transient-append-suffix "transient" "\ +Insert a SUFFIX into PREFIX after LOC. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +Remove a conflicting binding unless optional KEEP-OTHER is + non-nil. +See info node `(transient)Modifying Existing Transients'. + +\(fn PREFIX LOC SUFFIX &optional KEEP-OTHER)" nil nil) + +(function-put 'transient-append-suffix 'lisp-indent-function 'defun) + +(autoload 'transient-replace-suffix "transient" "\ +Replace the suffix at LOC in PREFIX with SUFFIX. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'. + +\(fn PREFIX LOC SUFFIX)" nil nil) + +(function-put 'transient-replace-suffix 'lisp-indent-function 'defun) + +(autoload 'transient-remove-suffix "transient" "\ +Remove the suffix or group at LOC in PREFIX. +PREFIX is a prefix command, a symbol. +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'. + +\(fn PREFIX LOC)" nil nil) + +(function-put 'transient-remove-suffix 'lisp-indent-function 'defun) + +(register-definition-prefixes "transient" '("transient")) + +;;;*** + +;;;### (autoloads nil nil ("transient-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; transient-autoloads.el ends here diff --git a/code/elpa/transient-20220806.2224/transient-pkg.el b/code/elpa/transient-20220806.2224/transient-pkg.el new file mode 100644 index 0000000..cd9400b --- /dev/null +++ b/code/elpa/transient-20220806.2224/transient-pkg.el @@ -0,0 +1,13 @@ +(define-package "transient" "20220806.2224" "Transient commands" + '((emacs "25.1") + (compat "28.1.1.0")) + :commit "6b9c93af9c1d1646be4445656c46ee9390c9f129" :authors + '(("Jonas Bernoulli" . "jonas@bernoul.li")) + :maintainer + '("Jonas Bernoulli" . "jonas@bernoul.li") + :keywords + '("extensions") + :url "https://github.com/magit/transient") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/transient-20220806.2224/transient.el b/code/elpa/transient-20220806.2224/transient.el new file mode 100644 index 0000000..8dce4d6 --- /dev/null +++ b/code/elpa/transient-20220806.2224/transient.el @@ -0,0 +1,4092 @@ +;;; transient.el --- Transient commands -*- lexical-binding:t -*- + +;; Copyright (C) 2018-2022 Free Software Foundation, Inc. + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/transient +;; Keywords: extensions + +;; Package-Version: 0.3.7-git +;; Package-Requires: ((emacs "25.1") (compat "28.1.1.0")) + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Taking inspiration from prefix keys and prefix arguments, Transient +;; implements a similar abstraction involving a prefix command, infix +;; arguments and suffix commands. We could call this abstraction a +;; "transient command", but because it always involves at least two +;; commands (a prefix and a suffix) we prefer to call it just a +;; "transient". + +;; When the user calls a transient prefix command, then a transient +;; (temporary) keymap is activated, which binds the transient's infix +;; and suffix commands, and functions that control the transient state +;; are added to `pre-command-hook' and `post-command-hook'. The +;; available suffix and infix commands and their state are shown in +;; the echo area until the transient is exited by invoking a suffix +;; command. + +;; Calling an infix command causes its value to be changed, possibly +;; by reading a new value in the minibuffer. + +;; Calling a suffix command usually causes the transient to be exited +;; but suffix commands can also be configured to not exit the +;; transient state. + +;;; Code: + +(require 'cl-lib) +(require 'compat) +(require 'eieio) +(require 'edmacro) +(require 'format-spec) +(require 'seq) + +(eval-when-compile (require 'subr-x)) + +(declare-function info "info" (&optional file-or-node buffer)) +(declare-function Man-find-section "man" (section)) +(declare-function Man-next-section "man" (n)) +(declare-function Man-getpage-in-background "man" (topic)) + +(defvar display-line-numbers) ; since Emacs 26.1 +(defvar Man-notify-method) + +(define-obsolete-function-alias 'define-transient-command + 'transient-define-prefix "Transient 0.3.0") +(define-obsolete-function-alias 'define-suffix-command + 'transient-define-suffix "Transient 0.3.0") +(define-obsolete-function-alias 'define-infix-command + 'transient-define-infix "Transient 0.3.0") +(define-obsolete-function-alias 'define-infix-argument + #'transient-define-argument "Transient 0.3.0") + +(define-obsolete-variable-alias 'transient--source-buffer + 'transient--original-buffer "Transient 0.2.0") +(define-obsolete-variable-alias 'current-transient-prefix + 'transient-current-prefix "Transient 0.3.0") +(define-obsolete-variable-alias 'current-transient-command + 'transient-current-command "Transient 0.3.0") +(define-obsolete-variable-alias 'current-transient-suffixes + 'transient-current-suffixes "Transient 0.3.0") +(define-obsolete-variable-alias 'post-transient-hook + 'transient-exit-hook "Transient 0.3.0") + +(defmacro transient--with-emergency-exit (&rest body) + (declare (indent defun)) + `(condition-case err + (let ((debugger #'transient--exit-and-debug)) + ,(macroexp-progn body)) + ((debug error) + (transient--emergency-exit) + (signal (car err) (cdr err))))) + +(defun transient--exit-and-debug (&rest args) + (transient--emergency-exit) + (apply #'debug args)) + +;;; Options + +(defgroup transient nil + "Transient commands." + :group 'extensions) + +(defcustom transient-show-popup t + "Whether to show the current transient in a popup buffer. +\\ +- If t, then show the popup as soon as a transient prefix command + is invoked. + +- If nil, then do not show the popup unless the user explicitly + requests it, by pressing \\[transient-show] or a prefix key. + +- If a number, then delay displaying the popup and instead show + a brief one-line summary. If zero or negative, then suppress + even showing that summary and display the pressed key only. + + Show the popup when the user explicitly requests it by pressing + \\[transient-show] or a prefix key. Unless zero, then also show the popup + after that many seconds of inactivity (using the absolute value)." + :package-version '(transient . "0.1.0") + :group 'transient + :type '(choice (const :tag "instantly" t) + (const :tag "on demand" nil) + (const :tag "on demand (no summary)" 0) + (number :tag "after delay" 1))) + +(defcustom transient-enable-popup-navigation t + "Whether navigation commands are enabled in the transient popup. + +While a transient is active the transient popup buffer is not the +current buffer, making it necessary to use dedicated commands to +act on that buffer itself. If this is non-nil, then the following +bindings are available: + +\\\ +- \\[transient-backward-button] moves the cursor to the previous suffix. +- \\[transient-forward-button] moves the cursor to the next suffix. +- \\[transient-push-button] invokes the suffix the cursor is on. +\\\ +- \\`' and \\`' invoke the clicked on suffix. +\\\ +- \\[transient-isearch-backward]\ + and \\[transient-isearch-forward] start isearch in the popup buffer. + +\\`' and \\`' are bound in `transient-push-button'. +All other bindings are in `transient-popup-navigation-map'. + +By default \\`M-RET' is bound to `transient-push-button', instead of +\\`RET', because if a transient allows the invocation of non-suffixes +then it is likely that you would want \\`RET' to do what it would do +if no transient were active." + :package-version '(transient . "0.4.0") + :group 'transient + :type 'boolean) + +(defcustom transient-display-buffer-action + '(display-buffer-in-side-window + (side . bottom) + (dedicated . t) + (inhibit-same-window . t) + (window-parameters (no-other-window . t))) + "The action used to display the transient popup buffer. + +The transient popup buffer is displayed in a window using + + (display-buffer BUFFER transient-display-buffer-action) + +The value of this option has the form (FUNCTION . ALIST), +where FUNCTION is a function or a list of functions. Each such +function should accept two arguments: a buffer to display and an +alist of the same form as ALIST. See info node `(elisp)Choosing +Window' for details. + +The default is: + + (display-buffer-in-side-window + (side . bottom) + (dedicated . t) + (inhibit-same-window . t) + (window-parameters (no-other-window . t))) + +This displays the window at the bottom of the selected frame. +Another useful FUNCTION is `display-buffer-below-selected', which +is what `magit-popup' used by default. For more alternatives see +info node `(elisp)Display Action Functions' and info node +`(elisp)Buffer Display Action Alists'. + +Note that the buffer that was current before the transient buffer +is shown should remain the current buffer. Many suffix commands +act on the thing at point, if appropriate, and if the transient +buffer became the current buffer, then that would change what is +at point. To that effect `inhibit-same-window' ensures that the +selected window is not used to show the transient buffer. + +It may be possible to display the window in another frame, but +whether that works in practice depends on the window-manager. +If the window manager selects the new window (Emacs frame), +then that unfortunately changes which buffer is current. + +If you change the value of this option, then you might also +want to change the value of `transient-mode-line-format'." + :package-version '(transient . "0.3.0") + :group 'transient + :type '(cons (choice function (repeat :tag "Functions" function)) + alist)) + +(defcustom transient-mode-line-format 'line + "The mode-line format for the transient popup buffer. + +If nil, then the buffer has no mode-line. If the buffer is not +displayed right above the echo area, then this probably is not +a good value. + +If `line' (the default), then the buffer also has no mode-line, +but a thin line is drawn instead, using the background color of +the face `transient-separator'. Termcap frames cannot display +thin lines and therefore fallback to treating `line' like nil. + +Otherwise this can be any mode-line format. +See `mode-line-format' for details." + :package-version '(transient . "0.2.0") + :group 'transient + :type '(choice (const :tag "hide mode-line" nil) + (const :tag "substitute thin line" line) + (const :tag "name of prefix command" + ("%e" mode-line-front-space + mode-line-buffer-identification)) + (sexp :tag "custom mode-line format"))) + +(defcustom transient-show-common-commands nil + "Whether to show common transient suffixes in the popup buffer. + +These commands are always shown after typing the prefix key +\"C-x\" when a transient command is active. To toggle the value +of this variable use \"C-x t\" when a transient is active." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +(defcustom transient-read-with-initial-input nil + "Whether to use the last history element as initial minibuffer input." + :package-version '(transient . "0.2.0") + :group 'transient + :type 'boolean) + +(defcustom transient-highlight-mismatched-keys nil + "Whether to highlight keys that do not match their argument. + +This only affects infix arguments that represent command-line +arguments. When this option is non-nil, then the key binding +for infix argument are highlighted when only a long argument +\(e.g. \"--verbose\") is specified but no shor-thand (e.g \"-v\"). +In the rare case that a short-hand is specified but does not +match the key binding, then it is highlighted differently. + +The highlighting is done using `transient-mismatched-key' +and `transient-nonstandard-key'." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +(defcustom transient-highlight-higher-levels nil + "Whether to highlight suffixes on higher levels. + +This is primarily intended for package authors. + +When non-nil then highlight the description of suffixes whose +level is above 4, the default of `transient-default-level'. +Assuming you have set that variable to 7, this highlights all +suffixes that won't be available to users without them making +the same customization." + :package-version '(transient . "0.3.6") + :group 'transient + :type 'boolean) + +(defcustom transient-substitute-key-function nil + "Function used to modify key bindings. + +This function is called with one argument, the prefix object, +and must return a key binding description, either the existing +key description it finds in the `key' slot, or a substitution. + +This is intended to let users replace certain prefix keys. It +could also be used to make other substitutions, but that is +discouraged. + +For example, \"=\" is hard to reach using my custom keyboard +layout, so I substitute \"(\" for that, which is easy to reach +using a layout optimized for Lisp. + + (setq transient-substitute-key-function + (lambda (obj) + (let ((key (oref obj key))) + (if (string-match \"\\\\`\\\\(=\\\\)[a-zA-Z]\" key) + (replace-match \"(\" t t key 1) + key)))))" + :package-version '(transient . "0.1.0") + :group 'transient + :type '(choice (const :tag "Transform no keys (nil)" nil) function)) + +(defcustom transient-semantic-coloring nil + "Whether to color prefixes and suffixes in Hydra-like fashion. +This feature is experimental. + +If non-nil, then the key binding of each suffix is colorized to +indicate whether it exits the transient state or not. The color +of the prefix is indicated using the line that is drawn when the +value of `transient-mode-line-format' is `line'. + +For more information about how Hydra uses colors see +https://github.com/abo-abo/hydra#color and +https://oremacs.com/2015/02/19/hydra-colors-reloaded." + :package-version '(transient . "0.3.0") + :group 'transient + :type 'boolean) + +(defcustom transient-detect-key-conflicts nil + "Whether to detect key binding conflicts. + +Conflicts are detected when a transient prefix command is invoked +and results in an error, which prevents the transient from being +used." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +(defcustom transient-align-variable-pitch nil + "Whether to align columns pixel-wise in the popup buffer. + +If this is non-nil, then columns are aligned pixel-wise to +support variable-pitch fonts. Keys are not aligned, so you +should use a fixed-pitch font for the `transient-key' face. +Other key faces inherit from that face unless a theme is +used that breaks that relationship. + +This option is intended for users who use a variable-pitch +font for the `default' face. + +Also see `transient-force-fixed-pitch'." + :package-version '(transient . "0.4.0") + :group 'transient + :type 'boolean) + +(defcustom transient-force-fixed-pitch nil + "Whether to force use of monospaced font in the popup buffer. + +Even if you use a proportional font for the `default' face, +you might still want to use a monospaced font in transient's +popup buffer. Setting this option to t causes `default' to +be remapped to `fixed-pitch' in that buffer. + +Also see `transient-align-variable-pitch'." + :package-version '(transient . "0.2.0") + :group 'transient + :type 'boolean) + +(defcustom transient-force-single-column nil + "Whether to force use of a single column to display suffixes. + +This might be useful for users with low vision who use large +text and might otherwise have to scroll in two dimensions." + :package-version '(transient . "0.3.6") + :group 'transient + :type 'boolean) + +(defcustom transient-hide-during-minibuffer-read nil + "Whether to hide the transient buffer while reading in the minibuffer." + :package-version '(transient . "0.4.0") + :group 'transient + :type 'boolean) + +(defconst transient--default-child-level 1) + +(defconst transient--default-prefix-level 4) + +(defcustom transient-default-level transient--default-prefix-level + "Control what suffix levels are made available by default. + +Each suffix command is placed on a level and each prefix command +has a level, which controls which suffix commands are available. +Integers between 1 and 7 (inclusive) are valid levels. + +The levels of individual transients and/or their individual +suffixes can be changed individually, by invoking the prefix and +then pressing \"C-x l\". + +The default level for both transients and their suffixes is 4. +This option only controls the default for transients. The default +suffix level is always 4. The author of a transient should place +certain suffixes on a higher level if they expect that it won't be +of use to most users, and they should place very important suffixes +on a lower level so that they remain available even if the user +lowers the transient level. + +\(Magit currently places nearly all suffixes on level 4 and lower +levels are not used at all yet. So for the time being you should +not set a lower level here and using a higher level might not +give you as many additional suffixes as you hoped.)" + :package-version '(transient . "0.1.0") + :group 'transient + :type '(choice (const :tag "1 - fewest suffixes" 1) + (const 2) + (const 3) + (const :tag "4 - default" 4) + (const 5) + (const 6) + (const :tag "7 - most suffixes" 7))) + +(defcustom transient-levels-file + (locate-user-emacs-file "transient/levels.el") + "File used to save levels of transients and their suffixes." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'file) + +(defcustom transient-values-file + (locate-user-emacs-file "transient/values.el") + "File used to save values of transients." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'file) + +(defcustom transient-history-file + (locate-user-emacs-file "transient/history.el") + "File used to save history of transients and their infixes." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'file) + +(defcustom transient-history-limit 10 + "Number of history elements to keep when saving to file." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'integer) + +(defcustom transient-save-history t + "Whether to save history of transient commands when exiting Emacs." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +;;; Faces + +(defgroup transient-faces nil + "Faces used by Transient." + :group 'transient) + +(defface transient-heading '((t :inherit font-lock-keyword-face)) + "Face used for headings." + :group 'transient-faces) + +(defface transient-key '((t :inherit font-lock-builtin-face)) + "Face used for keys." + :group 'transient-faces) + +(defface transient-argument '((t :inherit font-lock-warning-face)) + "Face used for enabled arguments." + :group 'transient-faces) + +(defface transient-value '((t :inherit font-lock-string-face)) + "Face used for values." + :group 'transient-faces) + +(defface transient-inactive-argument '((t :inherit shadow)) + "Face used for inactive arguments." + :group 'transient-faces) + +(defface transient-inactive-value '((t :inherit shadow)) + "Face used for inactive values." + :group 'transient-faces) + +(defface transient-unreachable '((t :inherit shadow)) + "Face used for suffixes unreachable from the current prefix sequence." + :group 'transient-faces) + +(defface transient-active-infix '((t :inherit secondary-selection)) + "Face used for the infix for which the value is being read." + :group 'transient-faces) + +(defface transient-unreachable-key '((t :inherit (transient-key shadow))) + "Face used for keys unreachable from the current prefix sequence." + :group 'transient-faces) + +(defface transient-nonstandard-key '((t :underline t)) + "Face optionally used to highlight keys conflicting with short-argument. +Also see option `transient-highlight-mismatched-keys'." + :group 'transient-faces) + +(defface transient-mismatched-key '((t :underline t)) + "Face optionally used to highlight keys without a short-argument. +Also see option `transient-highlight-mismatched-keys'." + :group 'transient-faces) + +(defface transient-inapt-suffix '((t :inherit shadow :italic t)) + "Face used for suffixes that are inapt at this time." + :group 'transient-faces) + +(defface transient-enabled-suffix + '((t :background "green" :foreground "black" :weight bold)) + "Face used for enabled levels while editing suffix levels. +See info node `(transient)Enabling and Disabling Suffixes'." + :group 'transient-faces) + +(defface transient-disabled-suffix + '((t :background "red" :foreground "black" :weight bold)) + "Face used for disabled levels while editing suffix levels. +See info node `(transient)Enabling and Disabling Suffixes'." + :group 'transient-faces) + +(defface transient-higher-level '((t :underline t)) + "Face optionally used to highlight suffixes on higher levels. +Also see option `transient-highlight-higher-levels'." + :group 'transient-faces) + +(defface transient-separator + `((((class color) (background light)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey80") + (((class color) (background dark)) + ,@(and (>= emacs-major-version 27) '(:extend t)) + :background "grey30")) + "Face used to draw line below transient popup window. +This is only used if `transient-mode-line-format' is `line'. +Only the background color is significant." + :group 'transient-faces) + +(defgroup transient-color-faces + '((transient-semantic-coloring custom-variable)) + "Faces used by Transient for Hydra-like command coloring. +These faces are only used if `transient-semantic-coloring' +\(which see) is non-nil." + :group 'transient-faces) + +(defface transient-red + '((t :inherit transient-key :foreground "red")) + "Face used for red prefixes and suffixes." + :group 'transient-color-faces) + +(defface transient-blue + '((t :inherit transient-key :foreground "blue")) + "Face used for blue prefixes and suffixes." + :group 'transient-color-faces) + +(defface transient-amaranth + '((t :inherit transient-key :foreground "#E52B50")) + "Face used for amaranth prefixes." + :group 'transient-color-faces) + +(defface transient-pink + '((t :inherit transient-key :foreground "#FF6EB4")) + "Face used for pink prefixes." + :group 'transient-color-faces) + +(defface transient-teal + '((t :inherit transient-key :foreground "#367588")) + "Face used for teal prefixes." + :group 'transient-color-faces) + +(defface transient-purple + '((t :inherit transient-key :foreground "#a020f0")) + "Face used for purple prefixes. + +This is an addition to the colors supported by Hydra. It is +used by suffixes that quit the current prefix but return to +the previous prefix." + :group 'transient-color-faces) + +;;; Persistence + +(defun transient--read-file-contents (file) + (with-demoted-errors "Transient error: %S" + (and (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (read (current-buffer)))))) + +(defun transient--pp-to-file (list file) + (make-directory (file-name-directory file) t) + (setq list (cl-sort (copy-sequence list) #'string< :key #'car)) + (with-temp-file file + (let ((print-level nil) + (print-length nil)) + (pp list (current-buffer))))) + +(defvar transient-values + (transient--read-file-contents transient-values-file) + "Values of transient commands. +The value of this variable persists between Emacs sessions +and you usually should not change it manually.") + +(defun transient-save-values () + (transient--pp-to-file transient-values transient-values-file)) + +(defvar transient-levels + (transient--read-file-contents transient-levels-file) + "Levels of transient commands. +The value of this variable persists between Emacs sessions +and you usually should not change it manually.") + +(defun transient-save-levels () + (transient--pp-to-file transient-levels transient-levels-file)) + +(defvar transient-history + (transient--read-file-contents transient-history-file) + "History of transient commands and infix arguments. +The value of this variable persists between Emacs sessions +\(unless `transient-save-history' is nil) and you usually +should not change it manually.") + +(defun transient-save-history () + (setq transient-history + (cl-sort (mapcar (pcase-lambda (`(,key . ,val)) + (cons key (seq-take (delete-dups val) + transient-history-limit))) + transient-history) + #'string< :key #'car)) + (transient--pp-to-file transient-history transient-history-file)) + +(defun transient-maybe-save-history () + "Save the value of `transient-history'. +If `transient-save-history' is nil, then do nothing." + (when transient-save-history + (transient-save-history))) + +(unless noninteractive + (add-hook 'kill-emacs-hook #'transient-maybe-save-history)) + +;;; Classes +;;;; Prefix + +(defclass transient-prefix () + ((prototype :initarg :prototype) + (command :initarg :command) + (level :initarg :level) + (variable :initarg :variable :initform nil) + (init-value :initarg :init-value) + (value) (default-value :initarg :value) + (scope :initarg :scope :initform nil) + (history :initarg :history :initform nil) + (history-pos :initarg :history-pos :initform 0) + (history-key :initarg :history-key :initform nil) + (show-help :initarg :show-help :initform nil) + (info-manual :initarg :info-manual :initform nil) + (man-page :initarg :man-page :initform nil) + (transient-suffix :initarg :transient-suffix :initform nil) + (transient-non-suffix :initarg :transient-non-suffix :initform nil) + (incompatible :initarg :incompatible :initform nil) + (suffix-description :initarg :suffix-description) + (variable-pitch :initarg :variable-pitch :initform nil)) + "Transient prefix command. + +Each transient prefix command consists of a command, which is +stored in a symbol's function slot and an object, which is +stored in the `transient--prefix' property of the same symbol. + +When a transient prefix command is invoked, then a clone of that +object is stored in the global variable `transient--prefix' and +the prototype is stored in the clone's `prototype' slot.") + +;;;; Suffix + +(defclass transient-child () + ((level + :initarg :level + :initform (symbol-value 'transient--default-child-level) + :documentation "Enable if level of prefix is equal or greater.") + (if + :initarg :if + :initform nil + :documentation "Enable if predicate returns non-nil.") + (if-not + :initarg :if-not + :initform nil + :documentation "Enable if predicate returns nil.") + (if-non-nil + :initarg :if-non-nil + :initform nil + :documentation "Enable if variable's value is non-nil.") + (if-nil + :initarg :if-nil + :initform nil + :documentation "Enable if variable's value is nil.") + (if-mode + :initarg :if-mode + :initform nil + :documentation "Enable if major-mode matches value.") + (if-not-mode + :initarg :if-not-mode + :initform nil + :documentation "Enable if major-mode does not match value.") + (if-derived + :initarg :if-derived + :initform nil + :documentation "Enable if major-mode derives from value.") + (if-not-derived + :initarg :if-not-derived + :initform nil + :documentation "Enable if major-mode does not derive from value.")) + "Abstract superclass for group and suffix classes. + +It is undefined what happens if more than one `if*' predicate +slot is non-nil." + :abstract t) + +(defclass transient-suffix (transient-child) + ((key :initarg :key) + (command :initarg :command) + (transient :initarg :transient) + (format :initarg :format :initform " %k %d") + (description :initarg :description :initform nil) + (show-help :initarg :show-help :initform nil) + (inapt :initform nil) + (inapt-if + :initarg :inapt-if + :initform nil + :documentation "Inapt if predicate returns non-nil.") + (inapt-if-not + :initarg :inapt-if-not + :initform nil + :documentation "Inapt if predicate returns nil.") + (inapt-if-non-nil + :initarg :inapt-if-non-nil + :initform nil + :documentation "Inapt if variable's value is non-nil.") + (inapt-if-nil + :initarg :inapt-if-nil + :initform nil + :documentation "Inapt if variable's value is nil.") + (inapt-if-mode + :initarg :inapt-if-mode + :initform nil + :documentation "Inapt if major-mode matches value.") + (inapt-if-not-mode + :initarg :inapt-if-not-mode + :initform nil + :documentation "Inapt if major-mode does not match value.") + (inapt-if-derived + :initarg :inapt-if-derived + :initform nil + :documentation "Inapt if major-mode derives from value.") + (inapt-if-not-derived + :initarg :inapt-if-not-derived + :initform nil + :documentation "Inapt if major-mode does not derive from value.")) + "Superclass for suffix command.") + +(defclass transient-infix (transient-suffix) + ((transient :initform t) + (argument :initarg :argument) + (shortarg :initarg :shortarg) + (value :initform nil) + (init-value :initarg :init-value) + (unsavable :initarg :unsavable :initform nil) + (multi-value :initarg :multi-value :initform nil) + (always-read :initarg :always-read :initform nil) + (allow-empty :initarg :allow-empty :initform nil) + (history-key :initarg :history-key :initform nil) + (reader :initarg :reader :initform nil) + (prompt :initarg :prompt :initform nil) + (choices :initarg :choices :initform nil) + (format :initform " %k %d (%v)")) + "Transient infix command." + :abstract t) + +(defclass transient-argument (transient-infix) () + "Abstract superclass for infix arguments." + :abstract t) + +(defclass transient-switch (transient-argument) () + "Class used for command-line argument that can be turned on and off.") + +(defclass transient-option (transient-argument) () + "Class used for command-line argument that can take a value.") + +(defclass transient-variable (transient-infix) + ((variable :initarg :variable) + (format :initform " %k %d %v")) + "Abstract superclass for infix commands that set a variable." + :abstract t) + +(defclass transient-switches (transient-argument) + ((argument-format :initarg :argument-format) + (argument-regexp :initarg :argument-regexp)) + "Class used for sets of mutually exclusive command-line switches.") + +(defclass transient-files (transient-option) () + ((key :initform "--") + (argument :initform "--") + (multi-value :initform rest) + (reader :initform transient-read-files)) + "Class used for the \"--\" argument or similar. +All remaining arguments are treated as files. +They become the value of this argument.") + +;;;; Group + +(defclass transient-group (transient-child) + ((suffixes :initarg :suffixes :initform nil) + (hide :initarg :hide :initform nil) + (description :initarg :description :initform nil) + (setup-children :initarg :setup-children) + (pad-keys :initarg :pad-keys)) + "Abstract superclass of all group classes." + :abstract t) + +(defclass transient-column (transient-group) () + "Group class that displays each element on a separate line.") + +(defclass transient-row (transient-group) () + "Group class that displays all elements on a single line.") + +(defclass transient-columns (transient-group) () + "Group class that displays elements organized in columns. +Direct elements have to be groups whose elements have to be +commands or string. Each subgroup represents a column. This +class takes care of inserting the subgroups' elements.") + +(defclass transient-subgroups (transient-group) () + "Group class that wraps other groups. + +Direct elements have to be groups whose elements have to be +commands or strings. This group inserts an empty line between +subgroups. The subgroups are responsible for displaying their +elements themselves.") + +;;; Define + +(defmacro transient-define-prefix (name arglist &rest args) + "Define NAME as a transient prefix command. + +ARGLIST are the arguments that command takes. +DOCSTRING is the documentation string and is optional. + +These arguments can optionally be followed by key-value pairs. +Each key has to be a keyword symbol, either `:class' or a keyword +argument supported by the constructor of that class. The +`transient-prefix' class is used if the class is not specified +explicitly. + +GROUPs add key bindings for infix and suffix commands and specify +how these bindings are presented in the popup buffer. At least +one GROUP has to be specified. See info node `(transient)Binding +Suffix and Infix Commands'. + +The BODY is optional. If it is omitted, then ARGLIST is also +ignored and the function definition becomes: + + (lambda () + (interactive) + (transient-setup \\='NAME)) + +If BODY is specified, then it must begin with an `interactive' +form that matches ARGLIST, and it must call `transient-setup'. +It may however call that function only when some condition is +satisfied; that is one of the reason why you might want to use +an explicit BODY. + +All transients have a (possibly nil) value, which is exported +when suffix commands are called, so that they can consume that +value. For some transients it might be necessary to have a sort +of secondary value, called a scope. Such a scope would usually +be set in the commands `interactive' form and has to be passed +to the setup function: + + (transient-setup \\='NAME nil nil :scope SCOPE) + +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... GROUP... [BODY...])" + (declare (debug ( &define name lambda-list + [&optional lambda-doc] + [&rest keywordp sexp] + [&rest vectorp] + [&optional ("interactive" interactive) def-body])) + (indent defun) + (doc-string 3)) + (pcase-let ((`(,class ,slots ,suffixes ,docstr ,body) + (transient--expand-define-args args))) + `(progn + (defalias ',name + ,(if body + `(lambda ,arglist ,@body) + `(lambda () + (interactive) + (transient-setup ',name)))) + (put ',name 'interactive-only t) + (put ',name 'function-documentation ,docstr) + (put ',name 'transient--prefix + (,(or class 'transient-prefix) :command ',name ,@slots)) + (put ',name 'transient--layout + ',(cl-mapcan (lambda (s) (transient--parse-child name s)) + suffixes))))) + +(defmacro transient-define-suffix (name arglist &rest args) + "Define NAME as a transient suffix command. + +ARGLIST are the arguments that the command takes. +DOCSTRING is the documentation string and is optional. + +These arguments can optionally be followed by key-value pairs. +Each key has to be a keyword symbol, either `:class' or a +keyword argument supported by the constructor of that class. +The `transient-suffix' class is used if the class is not +specified explicitly. + +The BODY must begin with an `interactive' form that matches +ARGLIST. The infix arguments are usually accessed by using +`transient-args' inside `interactive'. + +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... BODY...)" + (declare (debug ( &define name lambda-list + [&optional lambda-doc] + [&rest keywordp sexp] + ("interactive" interactive) + def-body)) + (indent defun) + (doc-string 3)) + (pcase-let ((`(,class ,slots ,_ ,docstr ,body) + (transient--expand-define-args args))) + `(progn + (defalias ',name (lambda ,arglist ,@body)) + (put ',name 'interactive-only t) + (put ',name 'function-documentation ,docstr) + (put ',name 'transient--suffix + (,(or class 'transient-suffix) :command ',name ,@slots))))) + +(defmacro transient-define-infix (name _arglist &rest args) + "Define NAME as a transient infix command. + +ARGLIST is always ignored and reserved for future use. +DOCSTRING is the documentation string and is optional. + +The key-value pairs are mandatory. All transient infix commands +are equal to each other (but not eq), so it is meaningless to +define an infix command without also setting at least `:class' +and one other keyword (which it is depends on the used class, +usually `:argument' or `:variable'). + +Each key has to be a keyword symbol, either `:class' or a keyword +argument supported by the constructor of that class. The +`transient-switch' class is used if the class is not specified +explicitly. + +The function definitions is always: + + (lambda () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) + +`transient-infix-read' and `transient-infix-set' are generic +functions. Different infix commands behave differently because +the concrete methods are different for different infix command +classes. In rare case the above command function might not be +suitable, even if you define your own infix command class. In +that case you have to use `transient-define-suffix' to define +the infix command and use t as the value of the `:transient' +keyword. + +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)" + (declare (debug ( &define name lambda-list + [&optional lambda-doc] + [&rest keywordp sexp])) + (indent defun) + (doc-string 3)) + (pcase-let ((`(,class ,slots ,_ ,docstr ,_) + (transient--expand-define-args args))) + `(progn + (defalias ',name ,(transient--default-infix-command)) + (put ',name 'interactive-only t) + (put ',name 'command-modes (list 'not-a-mode)) + (put ',name 'function-documentation ,docstr) + (put ',name 'transient--suffix + (,(or class 'transient-switch) :command ',name ,@slots))))) + +(defalias 'transient-define-argument #'transient-define-infix + "Define NAME as a transient infix command. + +Only use this alias to define an infix command that actually +sets an infix argument. To define a infix command that, for +example, sets a variable use `transient-define-infix' instead. + +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)") + +(defun transient--expand-define-args (args) + (let (class keys suffixes docstr) + (when (stringp (car args)) + (setq docstr (pop args))) + (while (keywordp (car args)) + (let ((k (pop args)) + (v (pop args))) + (if (eq k :class) + (setq class v) + (push k keys) + (push v keys)))) + (while (let ((arg (car args))) + (if (vectorp arg) + (setcar args (eval (cdr (backquote-process arg)))) + (and arg (symbolp arg)))) + (push (pop args) suffixes)) + (list (if (eq (car-safe class) 'quote) + (cadr class) + class) + (nreverse keys) + (nreverse suffixes) + docstr + args))) + +(defun transient--parse-child (prefix spec) + (cl-etypecase spec + (symbol (let ((value (symbol-value spec))) + (if (and (listp value) + (or (listp (car value)) + (vectorp (car value)))) + (cl-mapcan (lambda (s) (transient--parse-child prefix s)) value) + (transient--parse-child prefix value)))) + (vector (and-let* ((c (transient--parse-group prefix spec))) (list c))) + (list (and-let* ((c (transient--parse-suffix prefix spec))) (list c))) + (string (list spec)))) + +(defun transient--parse-group (prefix spec) + (setq spec (append spec nil)) + (cl-symbol-macrolet + ((car (car spec)) + (pop (pop spec))) + (let (level class args) + (when (integerp car) + (setq level pop)) + (when (stringp car) + (setq args (plist-put args :description pop))) + (while (keywordp car) + (let ((k pop)) + (if (eq k :class) + (setq class pop) + (setq args (plist-put args k pop))))) + (vector (or level transient--default-child-level) + (or class + (if (vectorp car) + 'transient-columns + 'transient-column)) + args + (cl-mapcan (lambda (s) (transient--parse-child prefix s)) spec))))) + +(defun transient--parse-suffix (prefix spec) + (let (level class args) + (cl-symbol-macrolet + ((car (car spec)) + (pop (pop spec))) + (when (integerp car) + (setq level pop)) + (when (or (stringp car) + (vectorp car)) + (setq args (plist-put args :key pop))) + (when (or (stringp car) + (eq (car-safe car) 'lambda) + (and (symbolp car) + (not (commandp car)) + (commandp (cadr spec)))) + (setq args (plist-put args :description pop))) + (cond + ((keywordp car) + (error "Need command, got %S" car)) + ((symbolp car) + (setq args (plist-put args :command pop))) + ((and (commandp car) + (not (stringp car))) + (let ((cmd pop) + (sym (intern (format "transient:%s:%s" + prefix + (or (plist-get args :description) + (plist-get args :key)))))) + (defalias sym cmd) + (setq args (plist-put args :command sym)))) + ((or (stringp car) + (and car (listp car))) + (let ((arg pop)) + (cl-typecase arg + (list + (setq args (plist-put args :shortarg (car arg))) + (setq args (plist-put args :argument (cadr arg))) + (setq arg (cadr arg))) + (string + (when-let ((shortarg (transient--derive-shortarg arg))) + (setq args (plist-put args :shortarg shortarg))) + (setq args (plist-put args :argument arg)))) + (setq args (plist-put args :command + (intern (format "transient:%s:%s" + prefix arg)))) + (cond ((and car (not (keywordp car))) + (setq class 'transient-option) + (setq args (plist-put args :reader pop))) + ((not (string-suffix-p "=" arg)) + (setq class 'transient-switch)) + (t + (setq class 'transient-option))))) + (t + (error "Needed command or argument, got %S" car))) + (while (keywordp car) + (let ((k pop)) + (cl-case k + (:class (setq class pop)) + (:level (setq level pop)) + (t (setq args (plist-put args k pop))))))) + (unless (plist-get args :key) + (when-let ((shortarg (plist-get args :shortarg))) + (setq args (plist-put args :key shortarg)))) + (list (or level transient--default-child-level) + (or class 'transient-suffix) + args))) + +(defun transient--default-infix-command () + (cons 'lambda + '(() + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)))) + +(defun transient--ensure-infix-command (obj) + (let ((cmd (oref obj command))) + (unless (or (commandp cmd) + (get cmd 'transient--infix-command)) + (if (or (cl-typep obj 'transient-switch) + (cl-typep obj 'transient-option)) + (put cmd 'transient--infix-command + (transient--default-infix-command)) + ;; This is not an anonymous infix argument. + (when (transient--use-suffix-p obj) + (error "Suffix %s is not defined or autoloaded as a command" cmd)))))) + +(defun transient--derive-shortarg (arg) + (save-match-data + (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg) + (match-string 1 arg)))) + +;;; Edit + +(defun transient--insert-suffix (prefix loc suffix action &optional keep-other) + (let* ((suf (cl-etypecase suffix + (vector (transient--parse-group prefix suffix)) + (list (transient--parse-suffix prefix suffix)) + (string suffix))) + (mem (transient--layout-member loc prefix)) + (elt (car mem))) + (cond + ((not mem) + (message "Cannot insert %S into %s; %s not found" + suffix prefix loc)) + ((or (and (vectorp suffix) (not (vectorp elt))) + (and (listp suffix) (vectorp elt)) + (and (stringp suffix) (vectorp elt))) + (message "Cannot place %S into %s at %s; %s" + suffix prefix loc + "suffixes and groups cannot be siblings")) + (t + (when-let* ((bindingp (listp suf)) + (key (transient--spec-key suf)) + (conflict (car (transient--layout-member key prefix))) + (conflictp + (and (not (and (eq action 'replace) + (eq conflict elt))) + (or (not keep-other) + (eq (plist-get (nth 2 suf) :command) + (plist-get (nth 2 conflict) :command))) + (equal (transient--suffix-predicate suf) + (transient--suffix-predicate conflict))))) + (transient-remove-suffix prefix key)) + (cl-ecase action + (insert (setcdr mem (cons elt (cdr mem))) + (setcar mem suf)) + (append (setcdr mem (cons suf (cdr mem)))) + (replace (setcar mem suf))))))) + +;;;###autoload +(defun transient-insert-suffix (prefix loc suffix &optional keep-other) + "Insert a SUFFIX into PREFIX before LOC. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +Remove a conflicting binding unless optional KEEP-OTHER is + non-nil. +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--insert-suffix prefix loc suffix 'insert keep-other)) + +;;;###autoload +(defun transient-append-suffix (prefix loc suffix &optional keep-other) + "Insert a SUFFIX into PREFIX after LOC. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +Remove a conflicting binding unless optional KEEP-OTHER is + non-nil. +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--insert-suffix prefix loc suffix 'append keep-other)) + +;;;###autoload +(defun transient-replace-suffix (prefix loc suffix) + "Replace the suffix at LOC in PREFIX with SUFFIX. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--insert-suffix prefix loc suffix 'replace)) + +;;;###autoload +(defun transient-remove-suffix (prefix loc) + "Remove the suffix or group at LOC in PREFIX. +PREFIX is a prefix command, a symbol. +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--layout-member loc prefix 'remove)) + +(defun transient-get-suffix (prefix loc) + "Return the suffix or group at LOC in PREFIX. +PREFIX is a prefix command, a symbol. +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (if-let ((mem (transient--layout-member loc prefix))) + (car mem) + (error "%s not found in %s" loc prefix))) + +(defun transient-suffix-put (prefix loc prop value) + "Edit the suffix at LOC in PREFIX, setting PROP to VALUE. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (let ((suf (transient-get-suffix prefix loc))) + (setf (elt suf 2) + (plist-put (elt suf 2) prop value)))) + +(defun transient--layout-member (loc prefix &optional remove) + (let ((val (or (get prefix 'transient--layout) + (error "%s is not a transient command" prefix)))) + (when (listp loc) + (while (integerp (car loc)) + (let* ((children (if (vectorp val) (aref val 3) val)) + (mem (transient--nthcdr (pop loc) children))) + (if (and remove (not loc)) + (let ((rest (delq (car mem) children))) + (if (vectorp val) + (aset val 3 rest) + (put prefix 'transient--layout rest)) + (setq val nil)) + (setq val (if loc (car mem) mem))))) + (setq loc (car loc))) + (if loc + (transient--layout-member-1 (transient--kbd loc) val remove) + val))) + +(defun transient--layout-member-1 (loc layout remove) + (cond ((listp layout) + (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) + layout)) + ((vectorp (car (aref layout 3))) + (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) + (aref layout 3))) + (remove + (aset layout 3 + (delq (car (transient--group-member loc layout)) + (aref layout 3))) + nil) + (t (transient--group-member loc layout)))) + +(defun transient--group-member (loc group) + (cl-member-if (lambda (suffix) + (and (listp suffix) + (let* ((def (nth 2 suffix)) + (cmd (plist-get def :command))) + (if (symbolp loc) + (eq cmd loc) + (equal (transient--kbd + (or (plist-get def :key) + (transient--command-key cmd))) + loc))))) + (aref group 3))) + +(defun transient--kbd (keys) + (when (vectorp keys) + (setq keys (key-description keys))) + (when (stringp keys) + (setq keys (kbd keys))) + keys) + +(defun transient--spec-key (spec) + (let ((plist (nth 2 spec))) + (or (plist-get plist :key) + (transient--command-key + (plist-get plist :command))))) + +(defun transient--command-key (cmd) + (and-let* ((obj (get cmd 'transient--suffix))) + (cond ((slot-boundp obj 'key) + (oref obj key)) + ((slot-exists-p obj 'shortarg) + (if (slot-boundp obj 'shortarg) + (oref obj shortarg) + (transient--derive-shortarg (oref obj argument))))))) + +(defun transient--nthcdr (n list) + (nthcdr (if (< n 0) (- (length list) (abs n)) n) list)) + +;;; Variables + +(defvar transient-current-prefix nil + "The transient from which this suffix command was invoked. +This is an object representing that transient, use +`transient-current-command' to get the respective command.") + +(defvar transient-current-command nil + "The transient from which this suffix command was invoked. +This is a symbol representing that transient, use +`transient-current-prefix' to get the respective object.") + +(defvar transient-current-suffixes nil + "The suffixes of the transient from which this suffix command was invoked. +This is a list of objects. Usually it is sufficient to instead +use the function `transient-args', which returns a list of +values. In complex cases it might be necessary to use this +variable instead.") + +(defvar transient-exit-hook nil + "Hook run after exiting a transient.") + +(defvar transient--prefix nil) +(defvar transient--layout nil) +(defvar transient--suffixes nil) + +(defconst transient--stay t "Do not exit the transient.") +(defconst transient--exit nil "Do exit the transient.") + +(defvar transient--exitp nil "Whether to exit the transient.") +(defvar transient--showp nil "Whether the transient is show in a popup buffer.") +(defvar transient--helpp nil "Whether help-mode is active.") +(defvar transient--editp nil "Whether edit-mode is active.") + +(defvar transient--active-infix nil "The active infix awaiting user input.") + +(defvar transient--timer nil) + +(defvar transient--stack nil) + +(defvar transient--minibuffer-depth 0) + +(defvar transient--buffer-name " *transient*" + "Name of the transient buffer.") + +(defvar transient--window nil + "The window used to display the transient popup.") + +(defvar transient--original-window nil + "The window that was selected before the transient was invoked. +Usually it remains selected while the transient is active.") + +(defvar transient--original-buffer nil + "The buffer that was current before the transient was invoked. +Usually it remains current while the transient is active.") + +(defvar transient--debug nil "Whether put debug information into *Messages*.") + +(defvar transient--history nil) + +(defvar transient--abort-commands + '(abort-minibuffers ; (minibuffer-quit-recursive-edit) + abort-recursive-edit ; (throw 'exit t) + exit-recursive-edit ; (throw 'exit nil) + keyboard-escape-quit ; dwim + keyboard-quit ; (signal 'quit nil) + minibuffer-keyboard-quit ; (abort-minibuffers) + minibuffer-quit-recursive-edit ; (throw 'exit (lambda () + ; (signal 'minibuffer-quit nil))) + top-level)) ; (throw 'top-level nil) + +(defvar transient--scroll-commands + '(transient-scroll-up + transient-scroll-down + mwheel-scroll + scroll-bar-toolkit-scroll)) + +;;; Identities + +(defun transient-suffix-object (&optional command) + "Return the object associated with the current suffix command. + +Each suffix commands is associated with an object, which holds +additional information about the suffix, such as its value (in +the case of an infix command, which is a kind of suffix command). + +This function is intended to be called by infix commands, whose +command definition usually (at least when defined using +`transient-define-infix') is this: + + (lambda () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) + +\(User input is read outside of `interactive' to prevent the +command from being added to `command-history'. See #23.) + +Such commands need to be able to access their associated object +to guide how `transient-infix-read' reads the new value and to +store the read value. Other suffix commands (including non-infix +commands) may also need the object to guide their behavior. + +This function attempts to return the object associated with the +current suffix command even if the suffix command was not invoked +from a transient. (For some suffix command that is a valid thing +to do, for others it is not.) In that case nil may be returned +if the command was not defined using one of the macros intended +to define such commands. + +The optional argument COMMAND is intended for internal use. If +you are contemplating using it in your own code, then you should +probably use this instead: + + (get COMMAND \\='transient--suffix)" + (when command + (cl-check-type command command)) + (if (or transient--prefix + transient-current-prefix) + (cl-find-if (lambda (obj) + (eq (transient--suffix-command obj) + (or command this-command))) + (or transient--suffixes + transient-current-suffixes)) + (when-let* ((obj (get (or command this-command) 'transient--suffix)) + (obj (clone obj))) + ;; Cannot use and-let* because of debbugs#31840. + (transient-init-scope obj) + (transient-init-value obj) + obj))) + +(defun transient--suffix-command (object) + "Return the command represented by OBJECT. + +If the value of OBJECT's `command' slot is a command, then return +that. Otherwise it is a symbol whose `transient--infix-command' +property holds an anonymous command, which is returned instead." + (cl-check-type object transient-suffix) + (let ((sym (oref object command))) + (if (commandp sym) + sym + (get sym 'transient--infix-command)))) + +(defun transient--suffix-symbol (arg) + "Return a symbol representing ARG. + +ARG must be a command and/or a symbol. If it is a symbol, +then just return it. Otherwise return the symbol whose +`transient--infix-command' property's value is ARG." + (or (cl-typep arg 'command) + (cl-typep arg 'symbol) + (signal 'wrong-type-argument `((command symbol) ,arg))) + (if (symbolp arg) + arg + (let* ((obj (transient-suffix-object)) + (sym (oref obj command))) + (if (eq (get sym 'transient--infix-command) arg) + sym + (catch 'found + (mapatoms (lambda (sym) + (when (eq (get sym 'transient--infix-command) arg) + (throw 'found sym))))))))) + +;;; Keymaps + +(defvar transient-base-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "ESC ESC ESC") #'transient-quit-all) + (define-key map (kbd "C-g") #'transient-quit-one) + (define-key map (kbd "C-q") #'transient-quit-all) + (define-key map (kbd "C-z") #'transient-suspend) + (define-key map (kbd "C-v") #'transient-scroll-up) + (define-key map (kbd "C-M-v") #'transient-scroll-down) + (define-key map [next] #'transient-scroll-up) + (define-key map [prior] #'transient-scroll-down) + map) + "Parent of other keymaps used by Transient. + +This is the parent keymap of all the keymaps that are used in +all transients: `transient-map' (which in turn is the parent +of the transient-specific keymaps), `transient-edit-map' and +`transient-sticky-map'. + +If you change a binding here, then you might also have to edit +`transient-sticky-map' and `transient-common-commands'. While +the latter isn't a proper transient prefix command, it can be +edited using the same functions as used for transients. + +If you add a new command here, then you must also add a binding +to `transient-predicate-map'.") + +(defvar transient-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map transient-base-map) + (define-key map (kbd "C-u") #'universal-argument) + (define-key map (kbd "C--") #'negative-argument) + (define-key map (kbd "C-t") #'transient-show) + (define-key map (kbd "?") #'transient-help) + (define-key map (kbd "C-h") #'transient-help) + ;; Also bound to "C-x p" and "C-x n" in transient-common-commands. + (define-key map (kbd "C-M-p") #'transient-history-prev) + (define-key map (kbd "C-M-n") #'transient-history-next) + map) + "Top-level keymap used by all transients. + +If you add a new command here, then you must also add a binding +to `transient-predicate-map'. Also see `transient-base-map'.") + +(defvar transient-edit-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map transient-base-map) + (define-key map (kbd "?") #'transient-help) + (define-key map (kbd "C-h") #'transient-help) + (define-key map (kbd "C-x l") #'transient-set-level) + map) + "Keymap that is active while a transient in is in \"edit mode\".") + +(defvar transient-sticky-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map transient-base-map) + (define-key map (kbd "C-g") #'transient-quit-seq) + map) + "Keymap that is active while an incomplete key sequence is active.") + +(defvar transient--common-command-prefixes '(?\C-x)) + +(put 'transient-common-commands + 'transient--layout + (cl-mapcan + (lambda (s) (transient--parse-child 'transient-common-commands s)) + `([:hide ,(lambda () + (and (not (memq (car (bound-and-true-p + transient--redisplay-key)) + transient--common-command-prefixes)) + (not transient-show-common-commands))) + ["Value commands" + ("C-x s " "Set" transient-set) + ("C-x C-s" "Save" transient-save) + ("C-x C-k" "Reset" transient-reset) + ("C-x p " "Previous value" transient-history-prev) + ("C-x n " "Next value" transient-history-next)] + ["Sticky commands" + ;; Like `transient-sticky-map' except that + ;; "C-g" has to be bound to a different command. + ("C-g" "Quit prefix or transient" transient-quit-one) + ("C-q" "Quit transient stack" transient-quit-all) + ("C-z" "Suspend transient stack" transient-suspend)] + ["Customize" + ("C-x t" transient-toggle-common + :description ,(lambda () + (if transient-show-common-commands + "Hide common commands" + "Show common permanently"))) + ("C-x l" "Show/hide suffixes" transient-set-level)]]))) + +(defvar transient-popup-navigation-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "") #'transient-noop) + (define-key map (kbd "") #'transient-backward-button) + (define-key map (kbd "") #'transient-forward-button) + (define-key map (kbd "C-r") #'transient-isearch-backward) + (define-key map (kbd "C-s") #'transient-isearch-forward) + (define-key map (kbd "M-RET") #'transient-push-button) + map) + "One of the keymaps used when popup navigation is enabled. +See `transient-enable-popup-navigation'.") + +(defvar transient-button-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "") #'transient-push-button) + (define-key map (kbd "") #'transient-push-button) + map) + "One of the keymaps used when popup navigation is enabled. +See `transient-enable-popup-navigation'.") + +(defvar transient-predicate-map + (let ((map (make-sparse-keymap))) + (define-key map [transient-suspend] #'transient--do-suspend) + (define-key map [transient-help] #'transient--do-stay) + (define-key map [transient-set-level] #'transient--do-stay) + (define-key map [transient-history-prev] #'transient--do-stay) + (define-key map [transient-history-next] #'transient--do-stay) + (define-key map [universal-argument] #'transient--do-stay) + (define-key map [negative-argument] #'transient--do-minus) + (define-key map [digit-argument] #'transient--do-stay) + (define-key map [transient-quit-all] #'transient--do-quit-all) + (define-key map [transient-quit-one] #'transient--do-quit-one) + (define-key map [transient-quit-seq] #'transient--do-stay) + (define-key map [transient-show] #'transient--do-stay) + (define-key map [transient-update] #'transient--do-stay) + (define-key map [transient-toggle-common] #'transient--do-stay) + (define-key map [transient-set] #'transient--do-call) + (define-key map [transient-save] #'transient--do-call) + (define-key map [transient-reset] #'transient--do-call) + (define-key map [describe-key-briefly] #'transient--do-stay) + (define-key map [describe-key] #'transient--do-stay) + (define-key map [transient-scroll-up] #'transient--do-stay) + (define-key map [transient-scroll-down] #'transient--do-stay) + (define-key map [mwheel-scroll] #'transient--do-stay) + (define-key map [scroll-bar-toolkit-scroll] #'transient--do-stay) + (define-key map [transient-noop] #'transient--do-noop) + (define-key map [transient-mouse-push-button] #'transient--do-move) + (define-key map [transient-push-button] #'transient--do-push-button) + (define-key map [transient-backward-button] #'transient--do-move) + (define-key map [transient-forward-button] #'transient--do-move) + (define-key map [transient-isearch-backward] #'transient--do-move) + (define-key map [transient-isearch-forward] #'transient--do-move) + ;; If a valid but incomplete prefix sequence is followed by + ;; an unbound key, then Emacs calls the `undefined' command + ;; but does not set `this-command', `this-original-command' + ;; or `real-this-command' accordingly. Instead they are nil. + (define-key map [nil] #'transient--do-warn) + map) + "Base keymap used to map common commands to their transient behavior. + +The \"transient behavior\" of a command controls, among other +things, whether invoking the command causes the transient to be +exited or not and whether infix arguments are exported before +doing so. + +Each \"key\" is a command that is common to all transients and +that is bound in `transient-map', `transient-edit-map', +`transient-sticky-map' and/or `transient-common-command'. + +Each binding is a \"pre-command\", a function that controls the +transient behavior of the respective command. + +For transient commands that are bound in individual transients, +the transient behavior is specified using the `:transient' slot +of the corresponding object.") + +(defvar transient--transient-map nil) +(defvar transient--predicate-map nil) +(defvar transient--redisplay-map nil) +(defvar transient--redisplay-key nil) + +(defun transient--push-keymap (var) + (let ((map (symbol-value var))) + (transient--debug " push %s%s" var (if map "" " VOID")) + (when map + (with-demoted-errors "transient--push-keymap: %S" + (internal-push-keymap map 'overriding-terminal-local-map))))) + +(defun transient--pop-keymap (var) + (let ((map (symbol-value var))) + (transient--debug " pop %s%s" var (if map "" " VOID")) + (when map + (with-demoted-errors "transient--pop-keymap: %S" + (internal-pop-keymap map 'overriding-terminal-local-map))))) + +(defun transient--make-transient-map () + (let ((map (make-sparse-keymap))) + (set-keymap-parent map (if transient--editp + transient-edit-map + transient-map)) + (dolist (obj transient--suffixes) + (let ((key (oref obj key))) + (when (vectorp key) + (setq key (key-description key)) + (oset obj key key)) + (when transient-substitute-key-function + (setq key (save-match-data + (funcall transient-substitute-key-function obj))) + (oset obj key key)) + (let ((kbd (kbd key)) + (cmd (transient--suffix-command obj))) + (when-let ((conflict (and transient-detect-key-conflicts + (transient--lookup-key map kbd)))) + (unless (eq cmd conflict) + (error "Cannot bind %S to %s and also %s" + (string-trim key) + cmd conflict))) + (define-key map kbd cmd)))) + (when-let ((b (lookup-key map "-"))) (define-key map [kp-subtract] b)) + (when-let ((b (lookup-key map "="))) (define-key map [kp-equal] b)) + (when-let ((b (lookup-key map "+"))) (define-key map [kp-add] b)) + (when transient-enable-popup-navigation + ;; `transient--make-redisplay-map' maps only over bindings that are + ;; directly in the base keymap, so that cannot be a composed keymap. + (set-keymap-parent + map (make-composed-keymap + (keymap-parent map) + transient-popup-navigation-map))) + map)) + +(defun transient--make-predicate-map () + (let ((map (make-sparse-keymap))) + (set-keymap-parent map transient-predicate-map) + (when (memq (oref transient--prefix transient-non-suffix) + '(nil transient--do-warn transient--do-noop)) + (define-key map [handle-switch-frame] #'transient--do-suspend)) + (dolist (obj transient--suffixes) + (let* ((cmd (oref obj command)) + (sub-prefix (and (symbolp cmd) (get cmd 'transient--prefix) t)) + (sym (transient--suffix-symbol cmd))) + (cond + ((oref obj inapt) + (define-key map (vector sym) #'transient--do-warn-inapt)) + ((slot-boundp obj 'transient) + (define-key map (vector sym) + (let ((do (oref obj transient))) + (pcase (list do sub-prefix) + ('(t t) #'transient--do-recurse) + ('(t nil) (if (cl-typep obj 'transient-infix) + #'transient--do-stay + #'transient--do-call)) + ('(nil t) #'transient--do-replace) + ('(nil nil) #'transient--do-exit) + (_ do))))) + ((not (lookup-key transient-predicate-map (vector sym))) + (define-key map (vector sym) + (if sub-prefix + #'transient--do-replace + (or (oref transient--prefix transient-suffix) + #'transient--do-exit))))))) + map)) + +(defun transient--make-redisplay-map () + (setq transient--redisplay-key + (cl-case this-command + (transient-update + (setq transient--showp t) + (setq unread-command-events + (listify-key-sequence (this-single-command-raw-keys)))) + (transient-quit-seq + (setq unread-command-events + (butlast (listify-key-sequence + (this-single-command-raw-keys)) + 2)) + (butlast transient--redisplay-key)) + (t nil))) + (let ((topmap (make-sparse-keymap)) + (submap (make-sparse-keymap))) + (when transient--redisplay-key + (define-key topmap (vconcat transient--redisplay-key) submap) + (set-keymap-parent submap transient-sticky-map)) + (map-keymap-internal + (lambda (key def) + (when (and (not (eq key ?\e)) + (listp def) + (keymapp def)) + (define-key topmap (vconcat transient--redisplay-key (list key)) + #'transient-update))) + (if transient--redisplay-key + (let ((key (vconcat transient--redisplay-key))) + (or (lookup-key transient--transient-map key) + (and-let* ((regular (lookup-key local-function-key-map key))) + (lookup-key transient--transient-map (vconcat regular))))) + transient--transient-map)) + topmap)) + +;;; Setup + +(defun transient-setup (&optional name layout edit &rest params) + "Setup the transient specified by NAME. + +This function is called by transient prefix commands to setup the +transient. In that case NAME is mandatory, LAYOUT and EDIT must +be nil and PARAMS may be (but usually is not) used to set e.g. the +\"scope\" of the transient (see `transient-define-prefix'). + +This function is also called internally in which case LAYOUT and +EDIT may be non-nil." + (transient--debug 'setup) + (transient--with-emergency-exit + (cond + ((not name) + ;; Switching between regular and edit mode. + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (setq name (oref transient--prefix command)) + (setq params (list :scope (oref transient--prefix scope)))) + (transient--prefix + ;; Invoked as a ":transient-non-suffix 'transient--do-{stay,call}" + ;; of an outer prefix. Unlike the usual `transient--do-replace', + ;; these predicates fail to clean up after the outer prefix. + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map)) + ((not (or layout ; resuming parent/suspended prefix + transient-current-command)) ; entering child prefix + (transient--stack-zap)) ; replace suspended prefix, if any + (edit + ;; Returning from help to edit. + (setq transient--editp t))) + (transient--init-objects name layout params) + (transient--history-init transient--prefix) + (setq transient--predicate-map (transient--make-predicate-map)) + (setq transient--transient-map (transient--make-transient-map)) + (setq transient--redisplay-map (transient--make-redisplay-map)) + (setq transient--original-window (selected-window)) + (setq transient--original-buffer (current-buffer)) + (setq transient--minibuffer-depth (minibuffer-depth)) + (transient--redisplay) + (transient--init-transient) + (transient--suspend-which-key-mode))) + +(cl-defgeneric transient-setup-children (group children) + "Setup the CHILDREN of GROUP. +If the value of the `setup-children' slot is non-nil, then call +that function with CHILDREN as the only argument and return the +value. Otherwise return CHILDREN as is." + (if (slot-boundp group 'setup-children) + (funcall (oref group setup-children) children) + children)) + +(defun transient--init-objects (name layout params) + (setq transient--prefix (transient--init-prefix name params)) + (setq transient--layout (or layout (transient--init-suffixes name))) + (setq transient--suffixes (transient--flatten-suffixes transient--layout))) + +(defun transient--init-prefix (name &optional params) + (let ((obj (let ((proto (get name 'transient--prefix))) + (apply #'clone proto + :prototype proto + :level (or (alist-get t (alist-get name transient-levels)) + transient-default-level) + params)))) + (transient--setup-recursion obj) + (transient-init-value obj) + obj)) + +(defun transient--init-suffixes (name) + (let ((levels (alist-get name transient-levels))) + (cl-mapcan (lambda (c) (transient--init-child levels c)) + (append (get name 'transient--layout) + (and (not transient--editp) + (get 'transient-common-commands + 'transient--layout)))))) + +(defun transient--flatten-suffixes (layout) + (cl-labels ((s (def) + (cond + ((stringp def) nil) + ((listp def) (cl-mapcan #'s def)) + ((transient-group--eieio-childp def) + (cl-mapcan #'s (oref def suffixes))) + ((transient-suffix--eieio-childp def) + (list def))))) + (cl-mapcan #'s layout))) + +(defun transient--init-child (levels spec) + (cl-etypecase spec + (vector (transient--init-group levels spec)) + (list (transient--init-suffix levels spec)) + (string (list spec)))) + +(defun transient--init-group (levels spec) + (pcase-let ((`(,level ,class ,args ,children) (append spec nil))) + (when-let* ((- (transient--use-level-p level)) + (obj (apply class :level level args)) + (- (transient--use-suffix-p obj)) + (suffixes (cl-mapcan (lambda (c) (transient--init-child levels c)) + (transient-setup-children obj children)))) + ;; Cannot use and-let* because of debbugs#31840. + (oset obj suffixes suffixes) + (list obj)))) + +(defun transient--init-suffix (levels spec) + (pcase-let* ((`(,level ,class ,args) spec) + (cmd (plist-get args :command)) + (level (or (alist-get (transient--suffix-symbol cmd) levels) + level))) + (let ((fn (and (symbolp cmd) + (symbol-function cmd)))) + (when (autoloadp fn) + (transient--debug " autoload %s" cmd) + (autoload-do-load fn))) + (when (transient--use-level-p level) + (let ((obj (if-let ((proto (and cmd + (symbolp cmd) + (get cmd 'transient--suffix)))) + (apply #'clone proto :level level args) + (apply class :level level args)))) + (transient--init-suffix-key obj) + (transient--ensure-infix-command obj) + (when (transient--use-suffix-p obj) + (if (transient--inapt-suffix-p obj) + (oset obj inapt t) + (transient-init-scope obj) + (transient-init-value obj)) + (list obj)))))) + +(cl-defmethod transient--init-suffix-key ((obj transient-suffix)) + (unless (slot-boundp obj 'key) + (error "No key for %s" (oref obj command)))) + +(cl-defmethod transient--init-suffix-key ((obj transient-argument)) + (if (transient-switches--eieio-childp obj) + (cl-call-next-method obj) + (unless (slot-boundp obj 'shortarg) + (when-let ((shortarg (transient--derive-shortarg (oref obj argument)))) + (oset obj shortarg shortarg))) + (unless (slot-boundp obj 'key) + (if (slot-boundp obj 'shortarg) + (oset obj key (oref obj shortarg)) + (error "No key for %s" (oref obj command)))))) + +(defun transient--use-level-p (level &optional edit) + (or (and transient--editp (not edit)) + (and (>= level 1) + (<= level (oref transient--prefix level))))) + +(defun transient--use-suffix-p (obj) + (transient--do-suffix-p + (oref obj if) + (oref obj if-not) + (oref obj if-nil) + (oref obj if-non-nil) + (oref obj if-mode) + (oref obj if-not-mode) + (oref obj if-derived) + (oref obj if-not-derived) + t)) + +(defun transient--inapt-suffix-p (obj) + (transient--do-suffix-p + (oref obj inapt-if) + (oref obj inapt-if-not) + (oref obj inapt-if-nil) + (oref obj inapt-if-non-nil) + (oref obj inapt-if-mode) + (oref obj inapt-if-not-mode) + (oref obj inapt-if-derived) + (oref obj inapt-if-not-derived) + nil)) + +(defun transient--do-suffix-p + (if if-not if-nil if-non-nil if-mode if-not-mode if-derived if-not-derived + default) + (cond + (if (funcall if)) + (if-not (not (funcall if-not))) + (if-non-nil (symbol-value if-non-nil)) + (if-nil (not (symbol-value if-nil))) + (if-mode (if (atom if-mode) + (eq major-mode if-mode) + (memq major-mode if-mode))) + (if-not-mode (not (if (atom if-not-mode) + (eq major-mode if-not-mode) + (memq major-mode if-not-mode)))) + (if-derived (if (atom if-derived) + (derived-mode-p if-derived) + (apply #'derived-mode-p if-derived))) + (if-not-derived (not (if (atom if-not-derived) + (derived-mode-p if-not-derived) + (apply #'derived-mode-p if-not-derived)))) + (t default))) + +(defun transient--suffix-predicate (spec) + (let ((plist (nth 2 spec))) + (seq-some (lambda (prop) + (and-let* ((pred (plist-get plist prop))) + (list prop pred))) + '( :if :if-not + :if-nil :if-non-nil + :if-mode :if-not-mode + :if-derived :if-not-derived + :inapt-if :inapt-if-not + :inapt-if-nil :inapt-if-non-nil + :inapt-if-mode :inapt-if-not-mode + :inapt-if-derived :inapt-if-not-derived)))) + +;;; Flow-Control + +(defun transient--init-transient () + (transient--debug 'init-transient) + (transient--push-keymap 'transient--transient-map) + (transient--push-keymap 'transient--redisplay-map) + (add-hook 'pre-command-hook #'transient--pre-command) + (add-hook 'post-command-hook #'transient--post-command) + (when transient--exitp + ;; This prefix command was invoked as the suffix of another. + ;; Prevent `transient--post-command' from removing the hooks + ;; that we just added. + (setq transient--exitp 'replace))) + +(defun transient--pre-command () + (transient--debug 'pre-command) + (transient--with-emergency-exit + ;; The use of `overriding-terminal-local-map' does not prevent the + ;; lookup of command remappings in the overridden maps, which can + ;; lead to a suffix being remapped to a non-suffix. We have to undo + ;; the remapping in that case. However, remapping a non-suffix to + ;; another should remain possible. + (when (and (transient--get-predicate-for this-original-command 'suffix) + (not (transient--get-predicate-for this-command 'suffix))) + (setq this-command this-original-command)) + (cond + ((memq this-command '(transient-update transient-quit-seq)) + (transient--pop-keymap 'transient--redisplay-map)) + ((and transient--helpp + (not (memq this-command '(transient-quit-one + transient-quit-all)))) + (cond + ((transient-help) + (transient--do-suspend) + (setq this-command 'transient-suspend) + (transient--pre-exit)) + ((not (transient--edebug-command-p)) + (setq this-command 'transient-undefined)))) + ((and transient--editp + (transient-suffix-object) + (not (memq this-command '(transient-quit-one + transient-quit-all + transient-help)))) + (setq this-command 'transient-set-level)) + (t + (setq transient--exitp nil) + (when (eq (transient--do-pre-command) transient--exit) + (transient--pre-exit)))))) + +(defun transient--do-pre-command () + (if-let ((fn (transient--get-predicate-for this-command))) + (let ((action (funcall fn))) + (when (eq action transient--exit) + (setq transient--exitp (or transient--exitp t))) + action) + (if (let ((keys (this-command-keys-vector))) + (eq (aref keys (1- (length keys))) ?\C-g)) + (setq this-command 'transient-noop) + (unless (transient--edebug-command-p) + (setq this-command 'transient-undefined))) + transient--stay)) + +(defun transient--get-predicate-for (cmd &optional suffix-only) + (or (ignore-errors + (lookup-key transient--predicate-map + (vector (transient--suffix-symbol cmd)))) + (and (not suffix-only) + (let ((pred (oref transient--prefix transient-non-suffix))) + (pcase pred + ('t #'transient--do-stay) + ('nil #'transient--do-warn) + (_ pred)))))) + +(defun transient--pre-exit () + (transient--debug 'pre-exit) + (transient--delete-window) + (transient--timer-cancel) + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (unless transient--showp + (let ((message-log-max nil)) + (message ""))) + (setq transient--transient-map nil) + (setq transient--predicate-map nil) + (setq transient--redisplay-map nil) + (setq transient--redisplay-key nil) + (setq transient--helpp nil) + (setq transient--editp nil) + (setq transient--prefix nil) + (setq transient--layout nil) + (setq transient--suffixes nil) + (setq transient--original-window nil) + (setq transient--original-buffer nil) + (setq transient--window nil)) + +(defun transient--delete-window () + (when (window-live-p transient--window) + (let ((remain-in-minibuffer-window + (and (minibuffer-selected-window) + (selected-window))) + (buf (window-buffer transient--window))) + ;; Only delete the window if it never showed another buffer. + (unless (eq (car (window-parameter transient--window 'quit-restore)) 'other) + (with-demoted-errors "Error while exiting transient: %S" + (delete-window transient--window))) + (kill-buffer buf) + (when remain-in-minibuffer-window + (select-window remain-in-minibuffer-window))))) + +(defun transient--export () + (setq transient-current-prefix transient--prefix) + (setq transient-current-command (oref transient--prefix command)) + (setq transient-current-suffixes transient--suffixes) + (transient--history-push transient--prefix)) + +(defun transient--suspend-override (&optional nohide) + (transient--debug 'suspend-override) + (transient--timer-cancel) + (cond ((and (not nohide) transient-hide-during-minibuffer-read) + (transient--delete-window)) + ((and transient--prefix transient--redisplay-key) + (setq transient--redisplay-key nil) + (when transient--showp + (transient--show)))) + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (remove-hook 'pre-command-hook #'transient--pre-command) + (remove-hook 'post-command-hook #'transient--post-command)) + +(defun transient--resume-override () + (transient--debug 'resume-override) + (when (and transient--showp transient-hide-during-minibuffer-read) + (transient--show)) + (transient--push-keymap 'transient--transient-map) + (transient--push-keymap 'transient--redisplay-map) + (add-hook 'pre-command-hook #'transient--pre-command) + (add-hook 'post-command-hook #'transient--post-command)) + +(defmacro transient--with-suspended-override (&rest body) + (let ((depth (make-symbol "depth")) + (setup (make-symbol "setup")) + (exit (make-symbol "exit"))) + `(if (and transient--transient-map + (memq transient--transient-map + overriding-terminal-local-map)) + (let ((,depth (1+ (minibuffer-depth))) ,setup ,exit) + (setq ,setup + (lambda () "@transient--with-suspended-override" + (transient--debug 'minibuffer-setup) + (remove-hook 'minibuffer-setup-hook ,setup) + (transient--suspend-override))) + (setq ,exit + (lambda () "@transient--with-suspended-override" + (transient--debug 'minibuffer-exit) + (when (= (minibuffer-depth) ,depth) + (transient--resume-override)))) + (unwind-protect + (progn + (add-hook 'minibuffer-setup-hook ,setup) + (add-hook 'minibuffer-exit-hook ,exit) + ,@body) + (remove-hook 'minibuffer-setup-hook ,setup) + (remove-hook 'minibuffer-exit-hook ,exit))) + ,@body))) + +(defun transient--post-command-hook () + (run-hooks 'transient--post-command-hook)) + +(add-hook 'post-command-hook #'transient--post-command-hook) + +(defun transient--delay-post-command (&optional abort-only) + (transient--debug 'delay-post-command) + (let ((depth (minibuffer-depth)) + (command this-command) + (delayed (if transient--exitp + (apply-partially #'transient--post-exit this-command) + #'transient--resume-override)) + post-command abort-minibuffer) + (unless abort-only + (setq post-command + (lambda () "@transient--delay-post-command" + (let ((act (and (not (eq (this-command-keys-vector) [])) + (or (eq this-command command) + ;; `execute-extended-command' was + ;; used to call another command + ;; that also uses the minibuffer. + (equal + (string-to-multibyte (this-command-keys)) + (format "\M-x%s\r" this-command)))))) + (transient--debug 'post-command-hook "act: %s" act) + (when act + (remove-hook 'transient--post-command-hook post-command) + (remove-hook 'minibuffer-exit-hook abort-minibuffer) + (funcall delayed))))) + (add-hook 'transient--post-command-hook post-command)) + (setq abort-minibuffer + (lambda () "@transient--delay-post-command" + (let ((act (and (or (memq this-command transient--abort-commands) + (equal (this-command-keys) "")) + (= (minibuffer-depth) depth)))) + (transient--debug + 'abort-minibuffer + "mini: %s|%s, act %s" (minibuffer-depth) depth act) + (when act + (remove-hook 'transient--post-command-hook post-command) + (remove-hook 'minibuffer-exit-hook abort-minibuffer) + (funcall delayed))))) + (add-hook 'minibuffer-exit-hook abort-minibuffer))) + +(defun transient--post-command () + (transient--debug 'post-command) + (transient--with-emergency-exit + (cond + ((and (eq (this-command-keys-vector) []) + (= (minibuffer-depth) + (1+ transient--minibuffer-depth))) + (transient--suspend-override) + (transient--delay-post-command (eq transient--exitp 'replace))) + (transient--exitp + (transient--post-exit)) + ((eq this-command (oref transient--prefix command))) + (t + (let ((old transient--redisplay-map) + (new (transient--make-redisplay-map))) + (unless (equal old new) + (transient--pop-keymap 'transient--redisplay-map) + (setq transient--redisplay-map new) + (transient--push-keymap 'transient--redisplay-map))) + (transient--redisplay))))) + +(defun transient--post-exit (&optional command) + (transient--debug 'post-exit) + (unless (and (eq transient--exitp 'replace) + (or transient--prefix + ;; The current command could act as a prefix, + ;; but decided not to call `transient-setup', + ;; or it is prevented from doing so because it + ;; uses the minibuffer and the user aborted + ;; that. + (prog1 nil + (if (let ((obj (transient-suffix-object command))) + (and (slot-boundp obj 'transient) + (oref obj transient))) + ;; This sub-prefix is a transient suffix; + ;; go back to outer prefix, by calling + ;; `transient--stack-pop' further down. + (setq transient--exitp nil) + (transient--stack-zap))))) + (remove-hook 'pre-command-hook #'transient--pre-command) + (remove-hook 'post-command-hook #'transient--post-command)) + (setq transient-current-prefix nil) + (setq transient-current-command nil) + (setq transient-current-suffixes nil) + (let ((resume (and transient--stack + (not (memq transient--exitp '(replace suspend)))))) + (unless (or resume (eq transient--exitp 'replace)) + (setq transient--showp nil)) + (setq transient--exitp nil) + (setq transient--helpp nil) + (setq transient--editp nil) + (setq transient--minibuffer-depth 0) + (run-hooks 'transient-exit-hook) + (when resume + (transient--stack-pop)))) + +(defun transient--stack-push () + (transient--debug 'stack-push) + (push (list (oref transient--prefix command) + transient--layout + transient--editp + :scope (oref transient--prefix scope)) + transient--stack)) + +(defun transient--stack-pop () + (transient--debug 'stack-pop) + (and transient--stack + (prog1 t (apply #'transient-setup (pop transient--stack))))) + +(defun transient--stack-zap () + (transient--debug 'stack-zap) + (setq transient--stack nil)) + +(defun transient--redisplay () + (if (or (eq transient-show-popup t) + transient--showp) + (unless + (or (memq this-command transient--scroll-commands) + (and (or (memq this-command '(mouse-drag-region + mouse-set-region)) + (equal (key-description (this-command-keys-vector)) + "")) + (and (eq (current-buffer) + (get-buffer transient--buffer-name))))) + (transient--show)) + (when (and (numberp transient-show-popup) + (not (zerop transient-show-popup)) + (not transient--timer)) + (transient--timer-start)) + (transient--show-brief))) + +(defun transient--timer-start () + (setq transient--timer + (run-at-time (abs transient-show-popup) nil + (lambda () + (transient--timer-cancel) + (transient--show) + (let ((message-log-max nil)) + (message "")))))) + +(defun transient--timer-cancel () + (when transient--timer + (cancel-timer transient--timer) + (setq transient--timer nil))) + +(defun transient--debug (arg &rest args) + (when transient--debug + (let ((inhibit-message (not (eq transient--debug 'message)))) + (if (symbolp arg) + (message "-- %-18s (cmd: %s, event: %S, exit: %s%s)" + arg + (or (ignore-errors (transient--suffix-symbol this-command)) + (if (byte-code-function-p this-command) + "#[...]" + this-command)) + (key-description (this-command-keys-vector)) + transient--exitp + (cond ((stringp (car args)) + (concat ", " (apply #'format args))) + (args + (concat ", " (apply (car args) (cdr args)))) + (t ""))) + (apply #'message arg args))))) + +(defun transient--emergency-exit () + "Exit the current transient command after an error occurred. +When no transient is active (i.e. when `transient--prefix' is +nil) then do nothing." + (transient--debug 'emergency-exit) + (when transient--prefix + (setq transient--stack nil) + (setq transient--exitp t) + (transient--pre-exit) + (transient--post-exit))) + +;;; Pre-Commands + +(defun transient--do-stay () + "Call the command without exporting variables and stay transient." + transient--stay) + +(defun transient--do-noop () + "Call `transient-noop' and stay transient." + (setq this-command 'transient-noop) + transient--stay) + +(defun transient--do-warn () + "Call `transient-undefined' and stay transient." + (setq this-command 'transient-undefined) + transient--stay) + +(defun transient--do-warn-inapt () + "Call `transient-inapt' and stay transient." + (setq this-command 'transient-inapt) + transient--stay) + +(defun transient--do-call () + "Call the command after exporting variables and stay transient." + (transient--export) + transient--stay) + +(defun transient--do-return () + "Call the command after exporting variables and return to parent prefix. +If there is no parent prefix, then behave like `transient--do-exit'." + (if (not transient--stack) + (transient--do-exit) + (transient--export) + transient--exit)) + +(defun transient--do-exit () + "Call the command after exporting variables and exit the transient." + (transient--export) + (transient--stack-zap) + transient--exit) + +(defun transient--do-push-button () + "Call the command represented by the activated button. +Use that command's pre-command to determine transient behavior." + (if (and (mouse-event-p last-command-event) + (not (eq (posn-window (event-start last-command-event)) + transient--window))) + transient--stay + (setq this-command + (with-selected-window transient--window + (get-text-property (if (mouse-event-p last-command-event) + (posn-point (event-start last-command-event)) + (point)) + 'command))) + (transient--do-pre-command))) + +(defun transient--do-recurse () + "Call the transient prefix command, preparing for return to active transient. +If there is no parent prefix, then just call the command." + (transient--do-replace)) + +(defun transient--setup-recursion (prefix-obj) + (when transient--stack + (let ((command (oref prefix-obj command))) + (when-let ((suffix-obj (transient-suffix-object command))) + (when (and (slot-boundp suffix-obj 'transient) + (memq (oref suffix-obj transient) + (list t #'transient--do-recurse))) + (oset prefix-obj transient-suffix 'transient--do-return)))))) + +(defun transient--do-replace () + "Call the transient prefix command, replacing the active transient." + (transient--export) + (transient--stack-push) + (setq transient--exitp 'replace) + transient--exit) + +(defun transient--do-suspend () + "Suspend the active transient, saving the transient stack." + (transient--stack-push) + (setq transient--exitp 'suspend) + transient--exit) + +(defun transient--do-quit-one () + "If active, quit help or edit mode, else exit the active transient." + (cond (transient--helpp + (setq transient--helpp nil) + transient--stay) + (transient--editp + (setq transient--editp nil) + (transient-setup) + transient--stay) + (t transient--exit))) + +(defun transient--do-quit-all () + "Exit all transients without saving the transient stack." + (transient--stack-zap) + transient--exit) + +(defun transient--do-move () + "Call the command if `transient-enable-popup-navigation' is non-nil. +In that case behave like `transient--do-stay', otherwise similar +to `transient--do-warn'." + (unless transient-enable-popup-navigation + (setq this-command 'transient-popup-navigation-help)) + transient--stay) + +(defun transient--do-minus () + "Call `negative-argument' or pivot to `transient-update'. +If `negative-argument' is invoked using \"-\" then preserve the +prefix argument and pivot to `transient-update'." + (when (equal (this-command-keys) "-") + (setq this-command 'transient-update)) + transient--stay) + +(put 'transient--do-stay 'transient-color 'transient-red) +(put 'transient--do-noop 'transient-color 'transient-red) +(put 'transient--do-warn 'transient-color 'transient-red) +(put 'transient--do-warn-inapt 'transient-color 'transient-red) +(put 'transient--do-call 'transient-color 'transient-red) +(put 'transient--do-return 'transient-color 'transient-purple) +(put 'transient--do-exit 'transient-color 'transient-blue) +(put 'transient--do-recurse 'transient-color 'transient-red) +(put 'transient--do-replace 'transient-color 'transient-blue) +(put 'transient--do-suspend 'transient-color 'transient-blue) +(put 'transient--do-quit-one 'transient-color 'transient-blue) +(put 'transient--do-quit-all 'transient-color 'transient-blue) +(put 'transient--do-move 'transient-color 'transient-red) +(put 'transient--do-minus 'transient-color 'transient-red) + +;;; Commands + +(defun transient-noop () + "Do nothing at all." + (interactive)) + +(defun transient-undefined () + "Warn the user that the pressed key is not bound to any suffix." + (interactive) + (transient--invalid "Unbound suffix")) + +(defun transient-inapt () + "Warn the user that the invoked command is inapt." + (interactive) + (transient--invalid "Inapt command")) + +(defun transient--invalid (msg) + (ding) + (message "%s: `%s' (Use `%s' to abort, `%s' for help) [%s]" + msg + (propertize (key-description (this-single-command-keys)) + 'face 'font-lock-warning-face) + (propertize "C-g" 'face 'transient-key) + (propertize "?" 'face 'transient-key) + ;; `this-command' is `transient--undefined' or similar at this + ;; point. Show the command the user actually tried to invoke. + (propertize (symbol-name (transient--suffix-symbol + this-original-command)) + 'face 'font-lock-warning-face)) + (unless (and transient--transient-map + (memq transient--transient-map overriding-terminal-local-map)) + (let ((transient--prefix (or transient--prefix 'sic))) + (transient--emergency-exit)) + (view-lossage) + (other-window 1) + (display-warning 'transient "Inconsistent transient state detected. +This should never happen. +Please open an issue and post the shown command log. +This is a heisenbug, so any additional details might help. +Thanks!" :error))) + +(defun transient-toggle-common () + "Toggle whether common commands are always shown." + (interactive) + (setq transient-show-common-commands (not transient-show-common-commands))) + +(defun transient-suspend () + "Suspend the current transient. +It can later be resumed using `transient-resume' while no other +transient is active." + (interactive)) + +(defun transient-quit-all () + "Exit all transients without saving the transient stack." + (interactive)) + +(defun transient-quit-one () + "Exit the current transients, possibly returning to the previous." + (interactive)) + +(defun transient-quit-seq () + "Abort the current incomplete key sequence." + (interactive)) + +(defun transient-update () + "Redraw the transient's state in the popup buffer." + (interactive) + (when (equal this-original-command 'negative-argument) + (setq prefix-arg current-prefix-arg))) + +(defun transient-show () + "Show the transient's state in the popup buffer." + (interactive) + (setq transient--showp t)) + +(defvar-local transient--restore-winconf nil) + +(defvar transient-resume-mode) + +(defun transient-help () + "Show help for the active transient or one of its suffixes." + (interactive) + (if (called-interactively-p 'any) + (setq transient--helpp t) + (with-demoted-errors "transient-help: %S" + (when (lookup-key transient--transient-map + (this-single-command-raw-keys)) + (setq transient--helpp nil) + (let ((winconf (current-window-configuration))) + (transient-show-help + (if (eq this-original-command 'transient-help) + transient--prefix + (or (transient-suffix-object) + this-original-command))) + (setq transient--restore-winconf winconf)) + (fit-window-to-buffer nil (frame-height) (window-height)) + (transient-resume-mode) + (message "Type \"q\" to resume transient command.") + t)))) + +(defun transient-set-level (&optional command level) + "Set the level of the transient or one of its suffix commands." + (interactive + (let ((command this-original-command) + (prefix (oref transient--prefix command))) + (and (or (not (eq command 'transient-set-level)) + (and transient--editp + (setq command prefix))) + (list command + (let ((keys (this-single-command-raw-keys))) + (and (lookup-key transient--transient-map keys) + (string-to-number + (let ((transient--active-infix + (transient-suffix-object command))) + (transient--show) + (transient--read-number-N + (format "Set level for `%s': " + (transient--suffix-symbol command)) + nil nil (not (eq command prefix))))))))))) + (cond + ((not command) + (setq transient--editp t) + (transient-setup)) + (level + (let* ((prefix (oref transient--prefix command)) + (alist (alist-get prefix transient-levels)) + (sym (transient--suffix-symbol command))) + (if (eq command prefix) + (progn (oset transient--prefix level level) + (setq sym t)) + (oset (transient-suffix-object command) level level)) + (setf (alist-get sym alist) level) + (setf (alist-get prefix transient-levels) alist)) + (transient-save-levels) + (transient--show)) + (t + (transient-undefined)))) + +(defun transient-set () + "Save the value of the active transient for this Emacs session." + (interactive) + (transient-set-value (or transient--prefix transient-current-prefix))) + +(defun transient-save () + "Save the value of the active transient persistenly across Emacs sessions." + (interactive) + (transient-save-value (or transient--prefix transient-current-prefix))) + +(defun transient-reset () + "Clear the set and saved values of the active transient." + (interactive) + (transient-reset-value (or transient--prefix transient-current-prefix))) + +(defun transient-history-next () + "Switch to the next value used for the active transient." + (interactive) + (let* ((obj transient--prefix) + (pos (1- (oref obj history-pos))) + (hst (oref obj history))) + (if (< pos 0) + (user-error "End of history") + (oset obj history-pos pos) + (oset obj value (nth pos hst)) + (mapc #'transient-init-value transient--suffixes)))) + +(defun transient-history-prev () + "Switch to the previous value used for the active transient." + (interactive) + (let* ((obj transient--prefix) + (pos (1+ (oref obj history-pos))) + (hst (oref obj history)) + (len (length hst))) + (if (> pos (1- len)) + (user-error "End of history") + (oset obj history-pos pos) + (oset obj value (nth pos hst)) + (mapc #'transient-init-value transient--suffixes)))) + +(defun transient-scroll-up (&optional arg) + "Scroll text of transient popup window upward ARG lines. +If ARG is nil scroll near full screen. This is a wrapper +around `scroll-up-command' (which see)." + (interactive "^P") + (with-selected-window transient--window + (scroll-up-command arg))) + +(defun transient-scroll-down (&optional arg) + "Scroll text of transient popup window down ARG lines. +If ARG is nil scroll near full screen. This is a wrapper +around `scroll-down-command' (which see)." + (interactive "^P") + (with-selected-window transient--window + (scroll-down-command arg))) + +(defun transient-push-button () + "Invoke the suffix command represented by this button." + (interactive)) + +(defun transient-resume () + "Resume a previously suspended stack of transients." + (interactive) + (cond (transient--stack + (let ((winconf transient--restore-winconf)) + (kill-local-variable 'transient--restore-winconf) + (when transient-resume-mode + (transient-resume-mode -1) + (quit-window)) + (when winconf + (set-window-configuration winconf))) + (transient--stack-pop)) + (transient-resume-mode + (kill-local-variable 'transient--restore-winconf) + (transient-resume-mode -1) + (quit-window)) + (t + (message "No suspended transient command")))) + +;;; Value +;;;; Init + +(cl-defgeneric transient-init-scope (obj) + "Set the scope of the suffix object OBJ. + +The scope is actually a property of the transient prefix, not of +individual suffixes. However it is possible to invoke a suffix +command directly instead of from a transient. In that case, if +the suffix expects a scope, then it has to determine that itself +and store it in its `scope' slot. + +This function is called for all suffix commands, but unless a +concrete method is implemented this falls through to the default +implementation, which is a noop.") + +(cl-defmethod transient-init-scope ((_ transient-suffix)) + "Noop." nil) + +(cl-defgeneric transient-init-value (_) + "Set the initial value of the object OBJ. + +This function is called for all prefix and suffix commands. + +For suffix commands (including infix argument commands) the +default implementation is a noop. Classes derived from the +abstract `transient-infix' class must implement this function. +Non-infix suffix commands usually don't have a value." + nil) + +(cl-defmethod transient-init-value :around ((obj transient-prefix)) + "If bound, then call OBJ's `init-value' function. +Otherwise call the primary method according to object's class." + (if (slot-boundp obj 'init-value) + (funcall (oref obj init-value) obj) + (cl-call-next-method obj))) + +(cl-defmethod transient-init-value :around ((obj transient-infix)) + "If bound, then call OBJ's `init-value' function. +Otherwise call the primary method according to object's class." + (if (slot-boundp obj 'init-value) + (funcall (oref obj init-value) obj) + (cl-call-next-method obj))) + +(cl-defmethod transient-init-value ((obj transient-prefix)) + (if (slot-boundp obj 'value) + (oref obj value) + (oset obj value + (if-let ((saved (assq (oref obj command) transient-values))) + (cdr saved) + (transient-default-value obj))))) + +(cl-defmethod transient-init-value ((obj transient-argument)) + (oset obj value + (let ((value (oref transient--prefix value)) + (argument (and (slot-boundp obj 'argument) + (oref obj argument))) + (multi-value (oref obj multi-value)) + (case-fold-search nil) + (regexp (if (slot-exists-p obj 'argument-regexp) + (oref obj argument-regexp) + (format "\\`%s\\(.*\\)" (oref obj argument))))) + (if (memq multi-value '(t rest)) + (cdr (assoc argument value)) + (let ((match (lambda (v) + (and (stringp v) + (string-match regexp v) + (match-string 1 v))))) + (if multi-value + (delq nil (mapcar match value)) + (cl-some match value))))))) + +(cl-defmethod transient-init-value ((obj transient-switch)) + (oset obj value + (car (member (oref obj argument) + (oref transient--prefix value))))) + +;;;; Default + +(cl-defgeneric transient-default-value (_) + "Return the default value." + nil) + +(cl-defmethod transient-default-value ((obj transient-prefix)) + (if-let ((default (and (slot-boundp obj 'default-value) + (oref obj default-value)))) + (if (functionp default) + (funcall default) + default) + nil)) + +;;;; Read + +(cl-defgeneric transient-infix-read (obj) + "Determine the new value of the infix object OBJ. + +This function merely determines the value; `transient-infix-set' +is used to actually store the new value in the object. + +For most infix classes this is done by reading a value from the +user using the reader specified by the `reader' slot (using the +`transient-infix' method described below). + +For some infix classes the value is changed without reading +anything in the minibuffer, i.e. the mere act of invoking the +infix command determines what the new value should be, based +on the previous value.") + +(cl-defmethod transient-infix-read :around ((obj transient-infix)) + "Highlight the infix in the popup buffer. + +This also wraps the call to `cl-call-next-method' with two +macros. + +`transient--with-suspended-override' is necessary to allow +reading user input using the minibuffer. + +`transient--with-emergency-exit' arranges for the transient to +be exited in case of an error because otherwise Emacs would get +stuck in an inconsistent state, which might make it necessary to +kill it from the outside. + +If you replace this method, then you must make sure to always use +the latter macro and most likely also the former." + (let ((transient--active-infix obj)) + (transient--show)) + (transient--with-emergency-exit + (transient--with-suspended-override + (cl-call-next-method obj)))) + +(cl-defmethod transient-infix-read ((obj transient-infix)) + "Read a value while taking care of history. + +This method is suitable for a wide variety of infix commands, +including but not limited to inline arguments and variables. + +If you do not use this method for your own infix class, then +you should likely replicate a lot of the behavior of this +method. If you fail to do so, then users might not appreciate +the lack of history, for example. + +Only for very simple classes that toggle or cycle through a very +limited number of possible values should you replace this with a +simple method that does not handle history. (E.g. for a command +line switch the only possible values are \"use it\" and \"don't use +it\", in which case it is pointless to preserve history.)" + (with-slots (value multi-value always-read allow-empty choices) obj + (if (and value + (not multi-value) + (not always-read) + transient--prefix) + (oset obj value nil) + (let* ((enable-recursive-minibuffers t) + (reader (oref obj reader)) + (prompt (transient-prompt obj)) + (value (if multi-value (mapconcat #'identity value ",") value)) + (history-key (or (oref obj history-key) + (oref obj command))) + (transient--history (alist-get history-key transient-history)) + (transient--history (if (or (null value) + (eq value (car transient--history))) + transient--history + (cons value transient--history))) + (initial-input (and transient-read-with-initial-input + (car transient--history))) + (history (if initial-input + (cons 'transient--history 1) + 'transient--history)) + (value + (cond + (reader (funcall reader prompt initial-input history)) + (multi-value + (completing-read-multiple prompt choices nil nil + initial-input history)) + (choices + (completing-read prompt choices nil t initial-input history)) + (t (read-string prompt initial-input history))))) + (cond ((and (equal value "") (not allow-empty)) + (setq value nil)) + ((and (equal value "\"\"") allow-empty) + (setq value ""))) + (when value + (when (and (bound-and-true-p ivy-mode) + (stringp (car transient--history))) + (set-text-properties 0 (length (car transient--history)) nil + (car transient--history))) + (setf (alist-get history-key transient-history) + (delete-dups transient--history))) + value)))) + +(cl-defmethod transient-infix-read ((obj transient-switch)) + "Toggle the switch on or off." + (if (oref obj value) nil (oref obj argument))) + +(cl-defmethod transient-infix-read ((obj transient-switches)) + "Cycle through the mutually exclusive switches. +The last value is \"don't use any of these switches\"." + (let ((choices (mapcar (apply-partially #'format (oref obj argument-format)) + (oref obj choices)))) + (if-let ((value (oref obj value))) + (cadr (member value choices)) + (car choices)))) + +(cl-defmethod transient-infix-read ((command symbol)) + "Elsewhere use the reader of the infix command COMMAND. +Use this if you want to share an infix's history with a regular +stand-alone command." + (cl-letf (((symbol-function #'transient--show) #'ignore)) + (transient-infix-read (get command 'transient--suffix)))) + +;;;; Readers + +(defun transient-read-file (prompt _initial-input _history) + "Read a file." + (file-local-name (expand-file-name (read-file-name prompt)))) + +(defun transient-read-existing-file (prompt _initial-input _history) + "Read an existing file." + (file-local-name (expand-file-name (read-file-name prompt nil nil t)))) + +(defun transient-read-directory (prompt _initial-input _history) + "Read a directory." + (file-local-name (expand-file-name (read-directory-name prompt)))) + +(defun transient-read-existing-directory (prompt _initial-input _history) + "Read an existing directory." + (file-local-name (expand-file-name (read-directory-name prompt nil nil t)))) + +(defun transient-read-number-N0 (prompt initial-input history) + "Read a natural number (including zero) and return it as a string." + (transient--read-number-N prompt initial-input history t)) + +(defun transient-read-number-N+ (prompt initial-input history) + "Read a natural number (excluding zero) and return it as a string." + (transient--read-number-N prompt initial-input history nil)) + +(defun transient--read-number-N (prompt initial-input history include-zero) + (save-match-data + (cl-block nil + (while t + (let ((str (read-from-minibuffer prompt initial-input nil nil history))) + (when (or (string-equal str "") + (string-match-p (if include-zero + "\\`\\(0\\|[1-9][0-9]*\\)\\'" + "\\`[1-9][0-9]*\\'") + str)) + (cl-return str))) + (message "Please enter a natural number (%s zero)." + (if include-zero "including" "excluding")) + (sit-for 1))))) + +(defun transient-read-date (prompt default-time _history) + "Read a date using `org-read-date' (which see)." + (require 'org) + (when (fboundp 'org-read-date) + (org-read-date 'with-time nil nil prompt default-time))) + +;;;; Prompt + +(cl-defgeneric transient-prompt (obj) + "Return the prompt to be used to read infix object OBJ's value.") + +(cl-defmethod transient-prompt ((obj transient-infix)) + "Return the prompt to be used to read infix object OBJ's value. + +This implementation should be suitable for almost all infix +commands. + +If the value of OBJ's `prompt' slot is non-nil, then it must be +a string or a function. If it is a string, then use that. If +it is a function, then call that with OBJ as the only argument. +That function must return a string, which is then used as the +prompt. + +Otherwise, if the value of either the `argument' or `variable' +slot of OBJ is a string, then base the prompt on that (preferring +the former), appending either \"=\" (if it appears to be a +command-line option) or \": \". + +Finally fall through to using \"(BUG: no prompt): \" as the +prompt." + (if-let ((prompt (oref obj prompt))) + (let ((prompt (if (functionp prompt) + (funcall prompt obj) + prompt))) + (if (stringp prompt) + prompt + "(BUG: no prompt): ")) + (or (and-let* ((arg (and (slot-boundp obj 'argument) (oref obj argument)))) + (if (and (stringp arg) (string-suffix-p "=" arg)) + arg + (concat arg ": "))) + (and-let* ((var (and (slot-boundp obj 'variable) (oref obj variable)))) + (and (stringp var) + (concat var ": "))) + "(BUG: no prompt): "))) + +;;;; Set + +(defvar transient--unset-incompatible t) + +(cl-defgeneric transient-infix-set (obj value) + "Set the value of infix object OBJ to value.") + +(cl-defmethod transient-infix-set ((obj transient-infix) value) + "Set the value of infix object OBJ to value." + (oset obj value value)) + +(cl-defmethod transient-infix-set :around ((obj transient-argument) value) + "Unset incompatible infix arguments." + (let ((arg (if (slot-boundp obj 'argument) + (oref obj argument) + (oref obj argument-regexp)))) + (if-let ((sic (and value arg transient--unset-incompatible)) + (spec (oref transient--prefix incompatible)) + (incomp (cl-mapcan (lambda (rule) + (and (member arg rule) + (remove arg rule))) + spec))) + (progn + (cl-call-next-method obj value) + (dolist (arg incomp) + (when-let ((obj (cl-find-if (lambda (obj) + (and (slot-boundp obj 'argument) + (equal (oref obj argument) arg))) + transient--suffixes))) + (let ((transient--unset-incompatible nil)) + (transient-infix-set obj nil))))) + (cl-call-next-method obj value)))) + +(cl-defgeneric transient-set-value (obj) + "Set the value of the transient prefix OBJ.") + +(cl-defmethod transient-set-value ((obj transient-prefix)) + (oset (oref obj prototype) value (transient-get-value)) + (transient--history-push obj)) + +;;;; Save + +(cl-defgeneric transient-save-value (obj) + "Save the value of the transient prefix OBJ.") + +(cl-defmethod transient-save-value ((obj transient-prefix)) + (let ((value (transient-get-value))) + (oset (oref obj prototype) value value) + (setf (alist-get (oref obj command) transient-values) value) + (transient-save-values)) + (transient--history-push obj)) + +;;;; Reset + +(cl-defgeneric transient-reset-value (obj) + "Clear the set and saved values of the transient prefix OBJ.") + +(cl-defmethod transient-reset-value ((obj transient-prefix)) + (let ((value (transient-default-value obj))) + (oset obj value value) + (oset (oref obj prototype) value value) + (setf (alist-get (oref obj command) transient-values nil 'remove) nil) + (transient-save-values)) + (transient--history-push obj) + (mapc #'transient-init-value transient--suffixes)) + +;;;; Get + +(defun transient-args (prefix) + "Return the value of the transient prefix command PREFIX. +If the current command was invoked from the transient prefix +command PREFIX, then return the active infix arguments. If +the current command was not invoked from PREFIX, then return +the set, saved or default value for PREFIX." + (cl-mapcan #'transient--get-wrapped-value (transient-suffixes prefix))) + +(defun transient-suffixes (prefix) + "Return the suffix objects of the transient prefix command PREFIX." + (if (eq transient-current-command prefix) + transient-current-suffixes + (let ((transient--prefix (transient--init-prefix prefix))) + (transient--flatten-suffixes + (transient--init-suffixes prefix))))) + +(defun transient-get-value () + (transient--with-emergency-exit + (cl-mapcan (lambda (obj) + (and (or (not (slot-exists-p obj 'unsavable)) + (not (oref obj unsavable))) + (transient--get-wrapped-value obj))) + transient-current-suffixes))) + +(defun transient--get-wrapped-value (obj) + (and-let* ((value (transient-infix-value obj))) + (cl-ecase (and (slot-exists-p obj 'multi-value) + (oref obj multi-value)) + ((nil) (list value)) + ((t rest) (list value)) + (repeat value)))) + +(cl-defgeneric transient-infix-value (obj) + "Return the value of the suffix object OBJ. + +This function is called by `transient-args' (which see), meaning +this function is how the value of a transient is determined so +that the invoked suffix command can use it. + +Currently most values are strings, but that is not set in stone. +Nil is not a value, it means \"no value\". + +Usually only infixes have a value, but see the method for +`transient-suffix'.") + +(cl-defmethod transient-infix-value ((_ transient-suffix)) + "Return nil, which means \"no value\". + +Infix arguments contribute the transient's value while suffix +commands consume it. This function is called for suffixes anyway +because a command that both contributes to the transient's value +and also consumes it is not completely unconceivable. + +If you define such a command, then you must define a derived +class and implement this function because this default method +does nothing." nil) + +(cl-defmethod transient-infix-value ((obj transient-infix)) + "Return the value of OBJ's `value' slot." + (oref obj value)) + +(cl-defmethod transient-infix-value ((obj transient-option)) + "Return ARGUMENT and VALUE as a unit or nil if the latter is nil." + (and-let* ((value (oref obj value))) + (let ((arg (oref obj argument))) + (cl-ecase (oref obj multi-value) + ((nil) (concat arg value)) + ((t rest) (cons arg value)) + (repeat (mapcar (lambda (v) (concat arg v)) value)))))) + +(cl-defmethod transient-infix-value ((_ transient-variable)) + "Return nil, which means \"no value\". + +Setting the value of a variable is done by, well, setting the +value of the variable. I.e. this is a side-effect and does not +contribute to the value of the transient." + nil) + +;;;; Utilities + +(defun transient-arg-value (arg args) + "Return the value of ARG as it appears in ARGS. + +For a switch return a boolean. For an option return the value as +a string, using the empty string for the empty value, or nil if +the option does not appear in ARGS." + (if (string-suffix-p "=" arg) + (save-match-data + (and-let* ((match (let ((case-fold-search nil) + (re (format "\\`%s\\(?:=\\(.+\\)\\)?\\'" + (substring arg 0 -1)))) + (cl-find-if (lambda (a) + (and (stringp a) + (string-match re a))) + args)))) + (or (match-string 1 match) ""))) + (and (member arg args) t))) + +;;; History + +(cl-defgeneric transient--history-key (obj) + "Return OBJ's history key. +If the value of the `history-key' slot is non-nil, then return +that. Otherwise return the value of the `command' slot." + (or (oref obj history-key) + (oref obj command))) + +(cl-defgeneric transient--history-push (obj) + "Push the current value of OBJ to its entry in `transient-history'." + (let ((key (transient--history-key obj))) + (setf (alist-get key transient-history) + (let ((args (transient-get-value))) + (cons args (delete args (alist-get key transient-history))))))) + +(cl-defgeneric transient--history-init (obj) + "Initialize OBJ's `history' slot. +This is the transient-wide history; many individual infixes also +have a history of their own.") + +(cl-defmethod transient--history-init ((obj transient-prefix)) + "Initialize OBJ's `history' slot from the variable `transient-history'." + (let ((val (oref obj value))) + (oset obj history + (cons val (delete val (alist-get (transient--history-key obj) + transient-history)))))) + +;;; Draw + +(defun transient--show-brief () + (let ((message-log-max nil)) + (if (and transient-show-popup (<= transient-show-popup 0)) + (message "%s-" (key-description (this-command-keys))) + (message + "%s- [%s] %s" + (key-description (this-command-keys)) + (oref transient--prefix command) + (mapconcat + #'identity + (sort + (cl-mapcan + (lambda (suffix) + (let ((key (kbd (oref suffix key)))) + ;; Don't list any common commands. + (and (not (memq (oref suffix command) + `(,(lookup-key transient-map key) + ,(lookup-key transient-sticky-map key) + ;; From transient-common-commands: + transient-set + transient-save + transient-history-prev + transient-history-next + transient-quit-one + transient-toggle-common + transient-set-level))) + (list (propertize (oref suffix key) 'face 'transient-key))))) + transient--suffixes) + #'string<) + (propertize "|" 'face 'transient-unreachable-key)))))) + +(defun transient--show () + (transient--timer-cancel) + (setq transient--showp t) + (let ((buf (get-buffer-create transient--buffer-name)) + (focus nil)) + (with-current-buffer buf + (when transient-enable-popup-navigation + (setq focus (or (button-get (point) 'command) + (transient--heading-at-point)))) + (erase-buffer) + (setq window-size-fixed t) + (when (bound-and-true-p tab-line-format) + (setq tab-line-format nil)) + (setq header-line-format nil) + (setq mode-line-format (if (eq transient-mode-line-format 'line) + nil + transient-mode-line-format)) + (setq mode-line-buffer-identification + (symbol-name (oref transient--prefix command))) + (if transient-enable-popup-navigation + (setq-local cursor-in-non-selected-windows 'box) + (setq cursor-type nil)) + (setq display-line-numbers nil) + (setq show-trailing-whitespace nil) + (transient--insert-groups) + (when (or transient--helpp transient--editp) + (transient--insert-help)) + (when (and (eq transient-mode-line-format 'line) + window-system) + (let ((face + (if-let ((f (and (transient--semantic-coloring-p) + (transient--prefix-color transient--prefix)))) + `(,@(and (>= emacs-major-version 27) '(:extend t)) + :background ,(face-foreground f)) + 'transient-separator))) + (insert (propertize "__" 'face face 'display '(space :height (1)))) + (insert (propertize "\n" 'face face 'line-height t)))) + (when transient-force-fixed-pitch + (transient--force-fixed-pitch))) + (unless (window-live-p transient--window) + (setq transient--window + (display-buffer buf transient-display-buffer-action))) + (when (window-live-p transient--window) + (with-selected-window transient--window + (goto-char (point-min)) + (when transient-enable-popup-navigation + (transient--goto-button focus)) + (transient--fit-window-to-buffer transient--window))))) + +(defun transient--fit-window-to-buffer (window) + (let ((window-resize-pixelwise t) + (window-size-fixed nil)) + (if (eq (car (window-parameter window 'quit-restore)) 'other) + ;; Grow but never shrink window that previously displayed + ;; another buffer and is going to display that again. + (fit-window-to-buffer window nil (window-height window)) + (fit-window-to-buffer window nil 1)))) + +(defun transient--insert-groups () + (let ((groups (cl-mapcan (lambda (group) + (let ((hide (oref group hide))) + (and (not (and (functionp hide) + (funcall hide))) + (list group)))) + transient--layout)) + group) + (while (setq group (pop groups)) + (transient--insert-group group) + (when groups + (insert ?\n))))) + +(defvar transient--max-group-level 1) + +(cl-defgeneric transient--insert-group (group) + "Format GROUP and its elements and insert the result.") + +(cl-defmethod transient--insert-group :around ((group transient-group)) + "Insert GROUP's description, if any." + (when-let ((desc (transient-format-description group))) + (insert desc ?\n)) + (let ((transient--max-group-level + (max (oref group level) transient--max-group-level))) + (cl-call-next-method group))) + +(cl-defmethod transient--insert-group ((group transient-row)) + (transient--maybe-pad-keys group) + (dolist (suffix (oref group suffixes)) + (insert (transient-format suffix)) + (insert " ")) + (insert ?\n)) + +(cl-defmethod transient--insert-group ((group transient-column)) + (transient--maybe-pad-keys group) + (dolist (suffix (oref group suffixes)) + (let ((str (transient-format suffix))) + (insert str) + (unless (string-match-p ".\n\\'" str) + (insert ?\n))))) + +(cl-defmethod transient--insert-group ((group transient-columns)) + (let* ((columns + (mapcar + (lambda (column) + (transient--maybe-pad-keys column group) + (let ((rows (mapcar #'transient-format (oref column suffixes)))) + (when-let ((desc (transient-format-description column))) + (push desc rows)) + (flatten-tree rows))) + (oref group suffixes))) + (vp (or (oref transient--prefix variable-pitch) + transient-align-variable-pitch)) + (rs (apply #'max (mapcar #'length columns))) + (cs (length columns)) + (cw (mapcar (lambda (col) + (apply #'max + (mapcar (if vp #'transient--pixel-width #'length) + col))) + columns)) + (cc (transient--seq-reductions-from + (apply-partially #'+ (* 3 (if vp (transient--pixel-width " ") 1))) + cw 0))) + (if transient-force-single-column + (dotimes (c cs) + (dotimes (r rs) + (when-let ((cell (nth r (nth c columns)))) + (unless (equal cell "") + (insert cell ?\n)))) + (unless (= c (1- cs)) + (insert ?\n))) + (dotimes (r rs) + (dotimes (c cs) + (if vp + (progn + (when-let ((cell (nth r (nth c columns)))) + (insert cell)) + (if (= c (1- cs)) + (insert ?\n) + (insert (propertize " " 'display + `(space :align-to (,(nth (1+ c) cc))))))) + (insert (make-string (- (nth c cc) (current-column)) ?\s)) + (when-let ((cell (nth r (nth c columns)))) + (insert cell)) + (when (= c (1- cs)) + (insert ?\n)))))))) + +(defun transient--pixel-width (string) + (save-window-excursion + (with-temp-buffer + (insert string) + (set-window-dedicated-p nil nil) + (set-window-buffer nil (current-buffer)) + (car (window-text-pixel-size + nil (line-beginning-position) (point)))))) + +(cl-defmethod transient--insert-group ((group transient-subgroups)) + (let* ((subgroups (oref group suffixes)) + (n (length subgroups))) + (dotimes (s n) + (let ((subgroup (nth s subgroups))) + (transient--maybe-pad-keys subgroup group) + (transient--insert-group subgroup) + (when (< s (1- n)) + (insert ?\n)))))) + +(cl-defgeneric transient-format (obj) + "Format and return OBJ for display. + +When this function is called, then the current buffer is some +temporary buffer. If you need the buffer from which the prefix +command was invoked to be current, then do so by temporarily +making `transient--original-buffer' current.") + +(cl-defmethod transient-format ((arg string)) + "Return the string ARG after applying the `transient-heading' face." + (propertize arg 'face 'transient-heading)) + +(cl-defmethod transient-format ((_ null)) + "Return a string containing just the newline character." + "\n") + +(cl-defmethod transient-format ((arg integer)) + "Return a string containing just the ARG character." + (char-to-string arg)) + +(cl-defmethod transient-format :around ((obj transient-infix)) + "When reading user input for this infix, then highlight it." + (let ((str (cl-call-next-method obj))) + (when (eq obj transient--active-infix) + (setq str (concat str "\n")) + (add-face-text-property + (if (eq this-command 'transient-set-level) 3 0) + (length str) + 'transient-active-infix nil str)) + str)) + +(cl-defmethod transient-format :around ((obj transient-suffix)) + "When edit-mode is enabled, then prepend the level information. +Optional support for popup buttons is also implemented here." + (let ((str (concat + (and transient--editp + (let ((level (oref obj level))) + (propertize (format " %s " level) + 'face (if (transient--use-level-p level t) + 'transient-enabled-suffix + 'transient-disabled-suffix)))) + (cl-call-next-method obj)))) + (when (oref obj inapt) + (add-face-text-property 0 (length str) 'transient-inapt-suffix nil str)) + (if transient-enable-popup-navigation + (make-text-button str nil + 'type 'transient + 'command (transient--suffix-command obj)) + str))) + +(cl-defmethod transient-format ((obj transient-infix)) + "Return a string generated using OBJ's `format'. +%k is formatted using `transient-format-key'. +%d is formatted using `transient-format-description'. +%v is formatted using `transient-format-value'." + (format-spec (oref obj format) + `((?k . ,(transient-format-key obj)) + (?d . ,(transient-format-description obj)) + (?v . ,(transient-format-value obj))))) + +(cl-defmethod transient-format ((obj transient-suffix)) + "Return a string generated using OBJ's `format'. +%k is formatted using `transient-format-key'. +%d is formatted using `transient-format-description'." + (format-spec (oref obj format) + `((?k . ,(transient-format-key obj)) + (?d . ,(transient-format-description obj))))) + +(cl-defgeneric transient-format-key (obj) + "Format OBJ's `key' for display and return the result.") + +(cl-defmethod transient-format-key ((obj transient-suffix)) + "Format OBJ's `key' for display and return the result." + (let ((key (oref obj key)) + (cmd (oref obj command))) + (if transient--redisplay-key + (let ((len (length transient--redisplay-key)) + (seq (cl-coerce (edmacro-parse-keys key t) 'list))) + (cond + ((member (seq-take seq len) + (list transient--redisplay-key + (thread-last transient--redisplay-key + (cl-substitute ?- 'kp-subtract) + (cl-substitute ?= 'kp-equal) + (cl-substitute ?+ 'kp-add)))) + (let ((pre (key-description (vconcat (seq-take seq len)))) + (suf (key-description (vconcat (seq-drop seq len))))) + (setq pre (string-replace "RET" "C-m" pre)) + (setq pre (string-replace "TAB" "C-i" pre)) + (setq suf (string-replace "RET" "C-m" suf)) + (setq suf (string-replace "TAB" "C-i" suf)) + ;; We use e.g. "-k" instead of the more correct "- k", + ;; because the former is prettier. If we did that in + ;; the definition, then we want to drop the space that + ;; is reinserted above. False-positives are possible + ;; for silly bindings like "-C-c C-c". + (unless (string-search " " key) + (setq pre (string-replace " " "" pre)) + (setq suf (string-replace " " "" suf))) + (concat (propertize pre 'face 'transient-unreachable-key) + (and (string-prefix-p (concat pre " ") key) " ") + (transient--colorize-key suf cmd) + (save-excursion + (and (string-match " +\\'" key) + (propertize (match-string 0 key) + 'face 'fixed-pitch)))))) + ((transient--lookup-key transient-sticky-map (kbd key)) + (transient--colorize-key key cmd)) + (t + (propertize key 'face 'transient-unreachable-key)))) + (transient--colorize-key key cmd)))) + +(defun transient--colorize-key (key command) + (propertize key 'face + (or (and (transient--semantic-coloring-p) + (transient--suffix-color command)) + 'transient-key))) + +(cl-defmethod transient-format-key :around ((obj transient-argument)) + (let ((key (cl-call-next-method obj))) + (cond ((not transient-highlight-mismatched-keys)) + ((not (slot-boundp obj 'shortarg)) + (add-face-text-property + 0 (length key) 'transient-nonstandard-key nil key)) + ((not (string-equal key (oref obj shortarg))) + (add-face-text-property + 0 (length key) 'transient-mismatched-key nil key))) + key)) + +(cl-defgeneric transient-format-description (obj) + "Format OBJ's `description' for display and return the result.") + +(cl-defmethod transient-format-description ((obj transient-child)) + "The `description' slot may be a function, in which case that is +called inside the correct buffer (see `transient-insert-group') +and its value is returned to the caller." + (and-let* ((desc (oref obj description))) + (if (functionp desc) + (with-current-buffer transient--original-buffer + (funcall desc)) + desc))) + +(cl-defmethod transient-format-description ((obj transient-group)) + "Format the description by calling the next method. If the result +doesn't use the `face' property at all, then apply the face +`transient-heading' to the complete string." + (and-let* ((desc (cl-call-next-method obj))) + (if (text-property-not-all 0 (length desc) 'face nil desc) + desc + (propertize desc 'face 'transient-heading)))) + +(cl-defmethod transient-format-description :around ((obj transient-suffix)) + "Format the description by calling the next method. If the result +is nil, then use \"(BUG: no description)\" as the description. +If the OBJ's `key' is currently unreachable, then apply the face +`transient-unreachable' to the complete string." + (let ((desc (or (cl-call-next-method obj) + (and (slot-boundp transient--prefix 'suffix-description) + (funcall (oref transient--prefix suffix-description) + obj)) + (propertize "(BUG: no description)" 'face 'error)))) + (cond ((transient--key-unreachable-p obj) + (propertize desc 'face 'transient-unreachable)) + ((and transient-highlight-higher-levels + (> (max (oref obj level) transient--max-group-level) + transient--default-prefix-level)) + (add-face-text-property + 0 (length desc) 'transient-higher-level nil desc) + desc) + (t + desc)))) + +(cl-defgeneric transient-format-value (obj) + "Format OBJ's value for display and return the result.") + +(cl-defmethod transient-format-value ((obj transient-suffix)) + (propertize (oref obj argument) + 'face (if (oref obj value) + 'transient-argument + 'transient-inactive-argument))) + +(cl-defmethod transient-format-value ((obj transient-option)) + (let ((argument (oref obj argument))) + (if-let ((value (oref obj value))) + (propertize + (cl-ecase (oref obj multi-value) + ((nil) (concat argument value)) + ((t rest) (concat argument + (and (not (string-suffix-p " " argument)) " ") + (mapconcat #'prin1-to-string value " "))) + (repeat (mapconcat (lambda (v) (concat argument v)) value " "))) + 'face 'transient-value) + (propertize argument 'face 'transient-inactive-value)))) + +(cl-defmethod transient-format-value ((obj transient-switches)) + (with-slots (value argument-format choices) obj + (format (propertize argument-format + 'face (if value + 'transient-value + 'transient-inactive-value)) + (concat + (propertize "[" 'face 'transient-inactive-value) + (mapconcat + (lambda (choice) + (propertize choice 'face + (if (equal (format argument-format choice) value) + 'transient-value + 'transient-inactive-value))) + choices + (propertize "|" 'face 'transient-inactive-value)) + (propertize "]" 'face 'transient-inactive-value))))) + +(defun transient--key-unreachable-p (obj) + (and transient--redisplay-key + (let ((key (oref obj key))) + (not (or (equal (seq-take (cl-coerce (edmacro-parse-keys key t) 'list) + (length transient--redisplay-key)) + transient--redisplay-key) + (transient--lookup-key transient-sticky-map (kbd key))))))) + +(defun transient--lookup-key (keymap key) + (let ((val (lookup-key keymap key))) + (and val (not (integerp val)) val))) + +(defun transient--maybe-pad-keys (group &optional parent) + (when-let ((pad (if (slot-boundp group 'pad-keys) + (oref group pad-keys) + (and parent + (slot-boundp parent 'pad-keys) + (oref parent pad-keys))))) + (let ((width (apply #'max + (cons (if (integerp pad) pad 0) + (mapcar (lambda (suffix) + (length (oref suffix key))) + (oref group suffixes)))))) + (dolist (suffix (oref group suffixes)) + (oset suffix key + (truncate-string-to-width (oref suffix key) width nil ?\s)))))) + +(defun transient-command-summary-or-name (obj) + "Return the summary or name of the command represented by OBJ. + +If the command has a doc-string, then return the first line of +that, else its name. + +Intended to be temporarily used as the `:suffix-description' of +a prefix command, while porting a regular keymap to a transient." + (let ((command (transient--suffix-symbol (oref obj command)))) + (if-let ((doc (documentation command))) + (propertize (car (split-string doc "\n")) 'face 'font-lock-doc-face) + (propertize (symbol-name command) 'face 'font-lock-function-name-face)))) + +;;; Help + +(cl-defgeneric transient-show-help (obj) + "Show documentation for the command represented by OBJ.") + +(cl-defmethod transient-show-help ((obj transient-prefix)) + "Call `show-help' if non-nil, else show `info-manual', +if non-nil, else show the `man-page' if non-nil, else use +`describe-function'." + (with-slots (show-help info-manual man-page command) obj + (cond (show-help (funcall show-help obj)) + (info-manual (transient--show-manual info-manual)) + (man-page (transient--show-manpage man-page)) + (t (transient--describe-function command))))) + +(cl-defmethod transient-show-help ((obj transient-suffix)) + "Call `show-help' if non-nil, else use `describe-function'. +Also used to dispatch showing documentation for the current +prefix. If the suffix is a sub-prefix, then also call the +prefix method." + (cond + ((eq this-command 'transient-help) + (transient-show-help transient--prefix)) + ((let ((prefix (get (transient--suffix-command obj) + 'transient--prefix))) + (and prefix (not (eq (oref transient--prefix command) this-command)) + (prog1 t (transient-show-help prefix))))) + (t (if-let ((show-help (oref obj show-help))) + (funcall show-help obj) + (transient--describe-function this-command))))) + +(cl-defmethod transient-show-help ((obj transient-infix)) + "Call `show-help' if non-nil, else show the `man-page' +if non-nil, else use `describe-function'. When showing the +manpage, then try to jump to the correct location." + (if-let ((show-help (oref obj show-help))) + (funcall show-help obj) + (if-let ((man-page (oref transient--prefix man-page)) + (argument (and (slot-boundp obj 'argument) + (oref obj argument)))) + (transient--show-manpage man-page argument) + (transient--describe-function this-command)))) + +;; `cl-generic-generalizers' doesn't support `command' et al. +(cl-defmethod transient-show-help (cmd) + "Show the command doc-string." + (transient--describe-function cmd)) + +(defun transient--describe-function (fn) + (describe-function (if (symbolp fn) fn 'transient--anonymous-infix-argument)) + (select-window (get-buffer-window (help-buffer)))) + +(defun transient--anonymous-infix-argument () + "Cannot show any documentation for this anonymous infix command. + +The infix command in question was defined anonymously, i.e., +it was define when the prefix command that it belongs to was +defined, which means that it gets no docstring and also that +no symbol is bound to it. + +When you request help for an infix command, then we usually +show the respective man-page and jump to the location where +the respective argument is being described. + +Because the containing prefix command does not specify any +man-page, we cannot do that in this case. Sorry about that.") + +(defun transient--show-manual (manual) + (info manual)) + +(defun transient--show-manpage (manpage &optional argument) + (require 'man) + (let* ((Man-notify-method 'meek) + (buf (Man-getpage-in-background manpage)) + (proc (get-buffer-process buf))) + (while (and proc (eq (process-status proc) 'run)) + (accept-process-output proc)) + (switch-to-buffer buf) + (when argument + (transient--goto-argument-description argument)))) + +(defun transient--goto-argument-description (arg) + (goto-char (point-min)) + (let ((case-fold-search nil) + ;; This matches preceding/proceeding options. Options + ;; such as "-a", "-S[]", and "--grep=" + ;; are matched by this regex without the shy group. + ;; The ". " in the shy group is for options such as + ;; "-m parent-number", and the "-[^[:space:]]+ " is + ;; for options such as "--mainline parent-number" + (others "-\\(?:. \\|-[^[:space:]]+ \\)?[^[:space:]]+")) + (when (re-search-forward + (if (equal arg "--") + ;; Special case. + "^[\t\s]+\\(--\\(?: \\|$\\)\\|\\[--\\]\\)" + ;; Should start with whitespace and may have + ;; any number of options before and/or after. + (format + "^[\t\s]+\\(?:%s, \\)*?\\(?1:%s\\)%s\\(?:, %s\\)*$" + others + ;; Options don't necessarily end in an "=" + ;; (e.g., "--gpg-sign[=]") + (string-remove-suffix "=" arg) + ;; Simple options don't end in an "=". Splitting this + ;; into 2 cases should make getting false positives + ;; less likely. + (if (string-suffix-p "=" arg) + ;; "[^[:space:]]*[^.[:space:]]" matches the option + ;; value, which is usually after the option name + ;; and either '=' or '[='. The value can't end in + ;; a period, as that means it's being used at the + ;; end of a sentence. The space is for options + ;; such as '--mainline parent-number'. + "\\(?: \\|\\[?=\\)[^[:space:]]*[^.[:space:]]" + ;; Either this doesn't match anything (e.g., "-a"), + ;; or the option is followed by a value delimited + ;; by a "[", "<", or ":". A space might appear + ;; before this value, as in "-f ". The + ;; space alternative is for options such as + ;; "-m parent-number". + "\\(?:\\(?: \\| ?[\\[<:]\\)[^[:space:]]*[^.[:space:]]\\)?") + others)) + nil t) + (goto-char (match-beginning 1))))) + +(defun transient--insert-help () + (unless (looking-back "\n\n" 2) + (insert "\n")) + (when transient--helpp + (insert + (format (propertize "\ +Type a %s to show help for that suffix command, or %s to show manual. +Type %s to exit help.\n" + 'face 'transient-heading) + (propertize "" 'face 'transient-key) + (propertize "?" 'face 'transient-key) + (propertize "C-g" 'face 'transient-key)))) + (when transient--editp + (unless transient--helpp + (insert + (format (propertize "\ +Type a %s to set level for that suffix command. +Type %s to set what levels are available for this prefix command.\n" + 'face 'transient-heading) + (propertize "" 'face 'transient-key) + (propertize "C-x l" 'face 'transient-key)))) + (with-slots (level) transient--prefix + (insert + (format (propertize " +Suffixes on levels %s are available. +Suffixes on levels %s and %s are unavailable.\n" + 'face 'transient-heading) + (propertize (format "1-%s" level) + 'face 'transient-enabled-suffix) + (propertize " 0 " + 'face 'transient-disabled-suffix) + (propertize (format ">=%s" (1+ level)) + 'face 'transient-disabled-suffix)))))) + +(defvar transient-resume-mode-map + (let ((map (make-sparse-keymap))) + (define-key map [remap Man-quit] #'transient-resume) + (define-key map [remap Info-exit] #'transient-resume) + (define-key map [remap quit-window] #'transient-resume) + map) + "Keymap for `transient-resume-mode'. + +This keymap remaps every command that would usually just quit the +documentation buffer to `transient-resume', which additionally +resumes the suspended transient.") + +(define-minor-mode transient-resume-mode + "Auxiliary minor-mode used to resume a transient after viewing help.") + +(defun transient-toggle-debug () + "Toggle debugging statements for transient commands." + (interactive) + (setq transient--debug (not transient--debug)) + (message "Debugging transient %s" + (if transient--debug "enabled" "disabled"))) + +;;; Popup Navigation + +(defun transient-popup-navigation-help () + "Inform the user how to enable popup navigation commands." + (interactive) + (message "This command is only available if `%s' is non-nil" + 'transient-enable-popup-navigation)) + +(define-button-type 'transient + 'face nil + 'keymap transient-button-map) + +(defun transient-backward-button (n) + "Move to the previous button in the transient popup buffer. +See `backward-button' for information about N." + (interactive "p") + (with-selected-window transient--window + (backward-button n t))) + +(defun transient-forward-button (n) + "Move to the next button in the transient popup buffer. +See `forward-button' for information about N." + (interactive "p") + (with-selected-window transient--window + (forward-button n t))) + +(defun transient--goto-button (command) + (cond + ((stringp command) + (when (re-search-forward (concat "^" (regexp-quote command)) nil t) + (goto-char (match-beginning 0)))) + (command + (while (and (ignore-errors (forward-button 1)) + (not (eq (button-get (button-at (point)) 'command) command)))) + (unless (eq (button-get (button-at (point)) 'command) command) + (goto-char (point-min)) + (forward-button 1))))) + +(defun transient--heading-at-point () + (and (eq (get-text-property (point) 'face) 'transient-heading) + (let ((beg (line-beginning-position))) + (buffer-substring-no-properties + beg (next-single-property-change + beg 'face nil (line-end-position)))))) + +;;; Compatibility +;;;; Popup Isearch + +(defvar transient--isearch-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map isearch-mode-map) + (define-key map [remap isearch-exit] #'transient-isearch-exit) + (define-key map [remap isearch-cancel] #'transient-isearch-cancel) + (define-key map [remap isearch-abort] #'transient-isearch-abort) + map)) + +(defun transient-isearch-backward (&optional regexp-p) + "Do incremental search backward. +With a prefix argument, do an incremental regular expression +search instead." + (interactive "P") + (transient--isearch-setup) + (let ((isearch-mode-map transient--isearch-mode-map)) + (isearch-mode nil regexp-p))) + +(defun transient-isearch-forward (&optional regexp-p) + "Do incremental search forward. +With a prefix argument, do an incremental regular expression +search instead." + (interactive "P") + (transient--isearch-setup) + (let ((isearch-mode-map transient--isearch-mode-map)) + (isearch-mode t regexp-p))) + +(defun transient-isearch-exit () + "Like `isearch-exit' but adapted for `transient'." + (interactive) + (isearch-exit) + (transient--isearch-exit)) + +(defun transient-isearch-cancel () + "Like `isearch-cancel' but adapted for `transient'." + (interactive) + (condition-case nil (isearch-cancel) (quit)) + (transient--isearch-exit)) + +(defun transient-isearch-abort () + "Like `isearch-abort' but adapted for `transient'." + (interactive) + (condition-case nil (isearch-abort) (quit)) + (transient--isearch-exit)) + +(defun transient--isearch-setup () + (select-window transient--window) + (transient--suspend-override t)) + +(defun transient--isearch-exit () + (select-window transient--original-window) + (transient--resume-override)) + +;;;; Hydra Color Emulation + +(defun transient--semantic-coloring-p () + (and transient-semantic-coloring + (not transient--helpp) + (not transient--editp))) + +(defun transient--suffix-color (command) + (or (get command 'transient-color) + (get (transient--get-predicate-for command) 'transient-color))) + +(defun transient--prefix-color (command) + (let* ((nonsuf (or (oref command transient-non-suffix) + 'transient--do-warn)) + (nonsuf (if (memq nonsuf '(transient--do-noop transient--do-warn)) + 'disallow + (get nonsuf 'transient-color))) + (suffix (if-let ((pred (oref command transient-suffix))) + (get pred 'transient-color) + (if (eq nonsuf 'transient-red) + 'transient-red + 'transient-blue)))) + (pcase (list suffix nonsuf) + (`(transient-purple ,_) 'transient-purple) + ('(transient-red disallow) 'transient-amaranth) + ('(transient-blue disallow) 'transient-teal) + ('(transient-red transient-red) 'transient-pink) + ('(transient-red transient-blue) 'transient-red) + ('(transient-blue transient-blue) 'transient-blue)))) + +;;;; Edebug + +(defun transient--edebug--recursive-edit (fn arg-mode) + (transient--debug 'edebug--recursive-edit) + (if (not transient--prefix) + (funcall fn arg-mode) + (transient--suspend-override t) + (funcall fn arg-mode) + (transient--resume-override))) + +(advice-add 'edebug--recursive-edit :around #'transient--edebug--recursive-edit) + +(defun transient--abort-edebug () + (when (bound-and-true-p edebug-active) + (transient--emergency-exit))) + +(advice-add 'abort-recursive-edit :before #'transient--abort-edebug) +(advice-add 'top-level :before #'transient--abort-edebug) + +(defun transient--edebug-command-p () + (and (bound-and-true-p edebug-active) + (or (memq this-command '(top-level abort-recursive-edit)) + (string-prefix-p "edebug" (symbol-name this-command))))) + +;;;; Miscellaneous + +(cl-pushnew (list nil (concat "^\\s-*(" + (eval-when-compile + (regexp-opt + '("transient-define-prefix" + "transient-define-suffix" + "transient-define-infix" + "transient-define-argument") + t)) + "\\s-+\\(" lisp-mode-symbol-regexp "\\)") + 2) + lisp-imenu-generic-expression :test #'equal) + +(declare-function which-key-mode "which-key" (&optional arg)) + +(defun transient--suspend-which-key-mode () + (when (bound-and-true-p which-key-mode) + (which-key-mode -1) + (add-hook 'transient-exit-hook #'transient--resume-which-key-mode))) + +(defun transient--resume-which-key-mode () + (unless transient--prefix + (which-key-mode 1) + (remove-hook 'transient-exit-hook #'transient--resume-which-key-mode))) + +(defun transient-bind-q-to-quit () + "Modify some keymaps to bind \"q\" to the appropriate quit command. + +\"C-g\" is the default binding for such commands now, but Transient's +predecessor Magit-Popup used \"q\" instead. If you would like to get +that binding back, then call this function in your init file like so: + + (with-eval-after-load \\='transient + (transient-bind-q-to-quit)) + +Individual transients may already bind \"q\" to something else +and such a binding would shadow the quit binding. If that is the +case then \"Q\" is bound to whatever \"q\" would have been bound +to by setting `transient-substitute-key-function' to a function +that does that. Of course \"Q\" may already be bound to something +else, so that function binds \"M-q\" to that command instead. +Of course \"M-q\" may already be bound to something else, but +we stop there." + (define-key transient-base-map "q" #'transient-quit-one) + (define-key transient-sticky-map "q" #'transient-quit-seq) + (setq transient-substitute-key-function + #'transient-rebind-quit-commands)) + +(defun transient-rebind-quit-commands (obj) + "See `transient-bind-q-to-quit'." + (let ((key (oref obj key))) + (cond ((string-equal key "q") "Q") + ((string-equal key "Q") "M-q") + (t key)))) + +(defun transient--force-fixed-pitch () + (require 'face-remap) + (face-remap-reset-base 'default) + (face-remap-add-relative 'default 'fixed-pitch)) + +;;;; Missing from Emacs + +(defun transient--seq-reductions-from (function sequence initial-value) + (let ((acc (list initial-value))) + (seq-doseq (elt sequence) + (push (funcall function (car acc) elt) acc)) + (nreverse acc))) + +(defun transient-plist-to-alist (plist) + (let (alist) + (while plist + (push (cons (let* ((symbol (pop plist)) + (name (symbol-name symbol))) + (if (eq (aref name 0) ?:) + (intern (substring name 1)) + symbol)) + (pop plist)) + alist)) + (nreverse alist))) + +;;; Font-Lock + +(defconst transient-font-lock-keywords + (eval-when-compile + `((,(concat "(" + (regexp-opt (list "transient-define-prefix" + "transient-define-infix" + "transient-define-argument" + "transient-define-suffix") + t) + "\\_>[ \t'(]*" + "\\(\\(?:\\sw\\|\\s_\\)+\\)?") + (1 'font-lock-keyword-face) + (2 'font-lock-function-name-face nil t))))) + +(font-lock-add-keywords 'emacs-lisp-mode transient-font-lock-keywords) + +;;; Auxiliary Classes +;;;; `transient-lisp-variable' + +(defclass transient-lisp-variable (transient-variable) + ((reader :initform #'transient-lisp-variable--reader) + (always-read :initform t) + (set-value :initarg :set-value :initform #'set)) + "[Experimental] Class used for Lisp variables.") + +(cl-defmethod transient-init-value ((obj transient-lisp-variable)) + (oset obj value (symbol-value (oref obj variable)))) + +(cl-defmethod transient-infix-set ((obj transient-lisp-variable) value) + (funcall (oref obj set-value) + (oref obj variable) + (oset obj value value))) + +(cl-defmethod transient-format-description ((obj transient-lisp-variable)) + (or (cl-call-next-method obj) + (symbol-name (oref obj variable)))) + +(cl-defmethod transient-format-value ((obj transient-lisp-variable)) + (propertize (prin1-to-string (oref obj value)) + 'face 'transient-value)) + +(cl-defmethod transient-prompt ((obj transient-lisp-variable)) + (format "Set %s: " (oref obj variable))) + +(defun transient-lisp-variable--reader (prompt initial-input _history) + (read--expression prompt initial-input)) + +;;; _ +(provide 'transient) +;; Local Variables: +;; indent-tabs-mode: nil +;; checkdoc-symbol-words: ("command-line" "edit-mode" "help-mode") +;; End: +;;; transient.el ends here diff --git a/code/elpa/transient-20220806.2224/transient.info b/code/elpa/transient-20220806.2224/transient.info new file mode 100644 index 0000000..ad014b5 --- /dev/null +++ b/code/elpa/transient-20220806.2224/transient.info @@ -0,0 +1,3377 @@ +This is transient.info, produced by makeinfo version 6.7 from +transient.texi. + + Copyright (C) 2018-2022 Free Software Foundation, Inc. + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +INFO-DIR-SECTION Emacs misc features +START-INFO-DIR-ENTRY +* Transient: (transient). Transient Commands. +END-INFO-DIR-ENTRY + + +File: transient.info, Node: Top, Next: Introduction, Up: (dir) + +Transient User and Developer Manual +*********************************** + +Taking inspiration from prefix keys and prefix arguments, Transient +implements a similar abstraction involving a prefix command, infix +arguments and suffix commands. We could call this abstraction a +“transient command”, but because it always involves at least two +commands (a prefix and a suffix) we prefer to call it just a +“transient”. + + When the user calls a transient prefix command, a transient +(temporary) keymap is activated, which binds the transient’s infix and +suffix commands, and functions that control the transient state are +added to ‘pre-command-hook’ and ‘post-command-hook’. The available +suffix and infix commands and their state are shown in a popup buffer +until the transient is exited by invoking a suffix command. + + Calling an infix command causes its value to be changed, possibly by +reading a new value in the minibuffer. + + Calling a suffix command usually causes the transient to be exited +but suffix commands can also be configured to not exit the transient. + +This manual is for Transient version 0.3.7-git. + + Copyright (C) 2018-2022 Free Software Foundation, Inc. + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +* Menu: + +* Introduction:: +* Usage:: +* Modifying Existing Transients:: +* Defining New Commands:: +* Classes and Methods:: +* Related Abstractions and Packages:: +* FAQ:: +* Keystroke Index:: +* Command and Function Index:: +* Variable Index:: +* Concept Index:: +* GNU General Public License:: + +— The Detailed Node Listing — + +Usage + +* Invoking Transients:: +* Aborting and Resuming Transients:: +* Common Suffix Commands:: +* Saving Values:: +* Using History:: +* Getting Help for Suffix Commands:: +* Enabling and Disabling Suffixes:: +* Other Commands:: +* Configuration:: + +Defining New Commands + +* Defining Transients:: +* Binding Suffix and Infix Commands:: +* Defining Suffix and Infix Commands:: +* Using Infix Arguments:: +* Transient State:: + +Binding Suffix and Infix Commands + +* Group Specifications:: +* Suffix Specifications:: + + +Classes and Methods + +* Group Classes:: +* Group Methods:: +* Prefix Classes:: +* Suffix Classes:: +* Suffix Methods:: +* Prefix Slots:: +* Suffix Slots:: +* Predicate Slots:: + +Suffix Methods + +* Suffix Value Methods:: +* Suffix Format Methods:: + + +Related Abstractions and Packages + +* Comparison With Prefix Keys and Prefix Arguments:: +* Comparison With Other Packages:: + + + +File: transient.info, Node: Introduction, Next: Usage, Prev: Top, Up: Top + +1 Introduction +************** + +Taking inspiration from prefix keys and prefix arguments, Transient +implements a similar abstraction involving a prefix command, infix +arguments and suffix commands. We could call this abstraction a +“transient command”, but because it always involves at least two +commands (a prefix and a suffix) we prefer to call it just a +“transient”. + + Transient keymaps are a feature provided by Emacs. Transients as + implemented by this package involve the use of transient keymaps. + + Emacs provides a feature that it calls “prefix commands”. When we + talk about “prefix commands” in this manual, then we mean our own + kind of “prefix commands”, unless specified otherwise. To avoid + ambiguity we sometimes use the terms “transient prefix command” for + our kind and “regular prefix command” for Emacs’ kind. + + When the user calls a transient prefix command, a transient +(temporary) keymap is activated, which binds the transient’s infix and +suffix commands, and functions that control the transient state are +added to ‘pre-command-hook’ and ‘post-command-hook’. The available +suffix and infix commands and their state are shown in a popup buffer +until the transient state is exited by invoking a suffix command. + + Calling an infix command causes its value to be changed. How that is +done depends on the type of the infix command. The simplest case is an +infix command that represents a command-line argument that does not take +a value. Invoking such an infix command causes the switch to be toggled +on or off. More complex infix commands may read a value from the user, +using the minibuffer. + + Calling a suffix command usually causes the transient to be exited; +the transient keymaps and hook functions are removed, the popup buffer +no longer shows information about the (no longer bound) suffix commands, +the values of some public global variables are set, while some internal +global variables are unset, and finally the command is actually called. +Suffix commands can also be configured to not exit the transient. + + A suffix command can, but does not have to, use the infix arguments +in much the same way any command can choose to use or ignore the prefix +arguments. For a suffix command that was invoked from a transient, the +variable ‘transient-current-suffixes’ and the function ‘transient-args’ +serve about the same purpose as the variables ‘prefix-arg’ and +‘current-prefix-arg’ do for any command that was called after the prefix +arguments have been set using a command such as ‘universal-argument’. + + The information shown in the popup buffer while a transient is active +looks a bit like this: + + ,----------------------------------------- + |Arguments + | -f Force (--force) + | -a Annotate (--annotate) + | + |Create + | t tag + | r release + `----------------------------------------- + + This is a simplified version of ‘magit-tag’. Info manuals do not + support images or colored text, so the above “screenshot” lacks + some information; in practice you would be able to tell whether the + arguments ‘--force’ and ‘--annotate’ are enabled or not based on + their color. + + Transient can be used to implement simple “command dispatchers”. The +main benefit then is that the user can see all the available commands in +a popup buffer. That is useful by itself because it frees the user from +having to remember all the keys that are valid after a certain prefix +key or command. Magit’s ‘magit-dispatch’ (on ‘C-x M-g’) command is an +example of using Transient to merely implement a command dispatcher. + + In addition to that, Transient also allows users to interactively +pass arguments to commands. These arguments can be much more complex +than what is reasonable when using prefix arguments. There is a limit +to how many aspects of a command can be controlled using prefix +arguments. Furthermore, what a certain prefix argument means for +different commands can be completely different, and users have to read +documentation to learn and then commit to memory what a certain prefix +argument means to a certain command. + + Transient suffix commands, on the other hand, can accept dozens of +different arguments without the user having to remember anything. When +using Transient, one can call a command with arguments that are just as +complex as when calling the same function non-interactively from Lisp. + + Invoking a transient command with arguments is similar to invoking a +command in a shell with command-line completion and history enabled. +One benefit of the Transient interface is that it remembers history not +only on a global level (“this command was invoked using these arguments, +and previously it was invoked using those other arguments”), but also +remembers the values of individual arguments independently. See *note +Using History::. + + After a transient prefix command is invoked, ‘C-h KEY’ can be used to +show the documentation for the infix or suffix command that KEY is bound +to (see *note Getting Help for Suffix Commands::) and infixes and +suffixes can be removed from the transient using ‘C-x l KEY’. Infixes +and suffixes that are disabled by default can be enabled the same way. +See *note Enabling and Disabling Suffixes::. + + Transient ships with support for a few different types of specialized +infix commands. A command that sets a command line option, for example, +has different needs than a command that merely toggles a boolean flag. +Additionally, Transient provides abstractions for defining new types, +which the author of Transient did not anticipate (or didn’t get around +to implementing yet). + + +File: transient.info, Node: Usage, Next: Modifying Existing Transients, Prev: Introduction, Up: Top + +2 Usage +******* + +* Menu: + +* Invoking Transients:: +* Aborting and Resuming Transients:: +* Common Suffix Commands:: +* Saving Values:: +* Using History:: +* Getting Help for Suffix Commands:: +* Enabling and Disabling Suffixes:: +* Other Commands:: +* Configuration:: + + +File: transient.info, Node: Invoking Transients, Next: Aborting and Resuming Transients, Up: Usage + +2.1 Invoking Transients +======================= + +A transient prefix command is invoked like any other command by pressing +the key that is bound to that command. The main difference to other +commands is that a transient prefix command activates a transient +keymap, which temporarily binds the transient’s infix and suffix +commands. Bindings from other keymaps may, or may not, be disabled +while the transient state is in effect. + + There are two kinds of commands that are available after invoking a +transient prefix command; infix and suffix commands. Infix commands set +some value (which is then shown in a popup buffer), without leaving the +transient. Suffix commands, on the other hand, usually quit the +transient and they may use the values set by the infix commands, i.e., +the infix *arguments*. + + Instead of setting arguments to be used by a suffix command, infix +commands may also set some value by side-effect, e.g., by setting the +value of some variable. + + +File: transient.info, Node: Aborting and Resuming Transients, Next: Common Suffix Commands, Prev: Invoking Transients, Up: Usage + +2.2 Aborting and Resuming Transients +==================================== + +To quit the transient without invoking a suffix command press ‘C-g’. + + Key bindings in transient keymaps may be longer than a single event. +After pressing a valid prefix key, all commands whose bindings do not +begin with that prefix key are temporarily unavailable and grayed out. +To abort the prefix key press ‘C-g’ (which in this case only quits the +prefix key, but not the complete transient). + + A transient prefix command can be bound as a suffix of another +transient. Invoking such a suffix replaces the current transient state +with a new transient state, i.e., the available bindings change and the +information displayed in the popup buffer is updated accordingly. +Pressing ‘C-g’ while a nested transient is active only quits the +innermost transient, causing a return to the previous transient. + + ‘C-q’ or ‘C-z’ on the other hand always exits all transients. If you +use the latter, then you can later resume the stack of transients using +‘M-x transient-resume’. + +‘C-g’ (‘transient-quit-seq’) +‘C-g’ (‘transient-quit-one’) + This key quits the currently active incomplete key sequence, if + any, or else the current transient. When quitting the current + transient, it returns to the previous transient, if any. + + Transient’s predecessor bound ‘q’ instead of ‘C-g’ to the quit +command. To learn how to get that binding back see +‘transient-bind-q-to-quit’’s doc string. + +‘C-q’ (‘transient-quit-all’) + This command quits the currently active incomplete key sequence, if + any, and all transients, including the active transient and all + suspended transients, if any. + +‘C-z’ (‘transient-suspend’) + Like ‘transient-quit-all’, this command quits an incomplete key + sequence, if any, and all transients. Additionally, it saves the + stack of transients so that it can easily be resumed (which is + particularly useful if you quickly need to do “something else” and + the stack is deeper than a single transient, and/or you have + already changed the values of some infix arguments). + + Note that only a single stack of transients can be saved at a time. + If another stack is already saved, then saving a new stack discards + the previous stack. + +‘M-x transient-resume’ + This command resumes the previously suspended stack of transients, + if any. + + +File: transient.info, Node: Common Suffix Commands, Next: Saving Values, Prev: Aborting and Resuming Transients, Up: Usage + +2.3 Common Suffix Commands +========================== + +A few shared suffix commands are available in all transients. These +suffix commands are not shown in the popup buffer by default. + + This includes the aborting commands mentioned in the previous +section, as well as some other commands that are all bound to ‘C-x KEY’. +After ‘C-x’ is pressed, a section featuring all these common commands is +temporarily shown in the popup buffer. After invoking one of them, the +section disappears again. Note, however, that one of these commands is +described as “Show common permanently”; invoke that if you want the +common commands to always be shown for all transients. + +‘C-x t’ (‘transient-toggle-common’) + This command toggles whether the generic commands that are common + to all transients are always displayed or only after typing the + incomplete prefix key sequence ‘C-x’. This only affects the + current Emacs session. + + -- User Option: transient-show-common-commands + This option controls whether shared suffix commands are shown + alongside the transient-specific infix and suffix commands. By + default, the shared commands are not shown to avoid overwhelming + the user with too many options. + + While a transient is active, pressing ‘C-x’ always shows the common + commands. The value of this option can be changed for the current + Emacs session by typing ‘C-x t’ while a transient is active. + + The other common commands are described in either the previous or in +one of the following sections. + + Some of Transient’s key bindings differ from the respective bindings +of Magit-Popup; see *note FAQ:: for more information. + + +File: transient.info, Node: Saving Values, Next: Using History, Prev: Common Suffix Commands, Up: Usage + +2.4 Saving Values +================= + +After setting the infix arguments in a transient, the user can save +those arguments for future invocations. + + Most transients will start out with the saved arguments when they are +invoked. There are a few exceptions, though. Some transients are +designed so that the value that they use is stored externally as the +buffer-local value of some variable. Invoking such a transient again +uses the buffer-local value. (1) + + If the user does not save the value and just exits using a regular +suffix command, then the value is merely saved to the transient’s +history. That value won’t be used when the transient is next invoked, +but it is easily accessible (see *note Using History::). + +‘C-x s’ (‘transient-set’) + This command saves the value of the active transient for this Emacs + session. + +‘C-x C-s’ (‘transient-save’) + Save the value of the active transient persistently across Emacs + sessions. + +‘C-x C-k’ (‘transient-save’) + Clear the set and saved value of the active transient. + + -- User Option: transient-values-file + This option names the file that is used to persist the values of + transients between Emacs sessions. + + ---------- Footnotes ---------- + + (1) ‘magit-diff’ and ‘magit-log’ are two prominent examples, and +their handling of buffer-local values is actually a bit more complicated +than outlined above and even customizable. + + +File: transient.info, Node: Using History, Next: Getting Help for Suffix Commands, Prev: Saving Values, Up: Usage + +2.5 Using History +================= + +Every time the user invokes a suffix command the transient’s current +value is saved to its history. These values can be cycled through the +same way one can cycle through the history of commands that read +user-input in the minibuffer. + +‘C-M-p’ (‘transient-history-prev’) +‘C-x p’ + This command switches to the previous value used for the active + transient. + +‘C-M-n’ (‘transient-history-next’) +‘C-x n’ + This command switches to the next value used for the active + transient. + + In addition to the transient-wide history, Transient of course +supports per-infix history. When an infix reads user-input using the +minibuffer, the user can use the regular minibuffer history commands to +cycle through previously used values. Usually the same keys as those +mentioned above are bound to those commands. + + Authors of transients should arrange for different infix commands +that read the same kind of value to also use the same history key (see +*note Suffix Slots::). + + Both kinds of history are saved to a file when Emacs is exited. + + -- User Option: transient-history-file + This option names the file that is used to persist the history of + transients and their infixes between Emacs sessions. + + -- User Option: transient-history-limit + This option controls how many history elements are kept at the time + the history is saved in ‘transient-history-file’. + + +File: transient.info, Node: Getting Help for Suffix Commands, Next: Enabling and Disabling Suffixes, Prev: Using History, Up: Usage + +2.6 Getting Help for Suffix Commands +==================================== + +Transients can have many suffixes and infixes that the user might not be +familiar with. To make it trivial to get help for these, Transient +provides access to the documentation directly from the active transient. + +‘C-h’ (‘transient-help’) + This command enters help mode. When help mode is active, typing a + key shows information about the suffix command that the key + normally is bound to (instead of invoking it). Pressing ‘C-h’ a + second time shows information about the _prefix_ command. + + After typing a key, the stack of transient states is suspended and + information about the suffix command is shown instead. Typing ‘q’ + in the help buffer buries that buffer and resumes the transient + state. + + What sort of documentation is shown depends on how the transient was +defined. For infix commands that represent command-line arguments this +ideally shows the appropriate manpage. ‘transient-help’ then tries to +jump to the correct location within that. Info manuals are also +supported. The fallback is to show the command’s doc string, for +non-infix suffixes this is usually appropriate. + + +File: transient.info, Node: Enabling and Disabling Suffixes, Next: Other Commands, Prev: Getting Help for Suffix Commands, Up: Usage + +2.7 Enabling and Disabling Suffixes +=================================== + +The user base of a package that uses transients can be very diverse. +This is certainly the case for Magit; some users have been using it and +Git for a decade, while others are just getting started now. + + For that reason a mechanism is needed that authors can use to +classify a transient’s infixes and suffixes along the +essentials...everything spectrum. We use the term “levels” to describe +that mechanism. + + Each suffix command is placed on a level and each transient has a +level (called “transient-level”), which controls which suffix commands +are available. Integers between 1 and 7 (inclusive) are valid levels. +For suffixes, 0 is also valid; it means that the suffix is not displayed +at any level. + + The levels of individual transients and/or their individual suffixes +can be changed interactively, by invoking the transient and then +pressing ‘C-x l’ to enter the “edit” mode, see below. + + The default level for both transients and their suffixes is 4. The +‘transient-default-level’ option only controls the default for +transients. The default suffix level is always 4. The authors of +transients should place certain suffixes on a higher level, if they +expect that it won’t be of use to most users, and they should place very +important suffixes on a lower level, so that they remain available even +if the user lowers the transient level. + + -- User Option: transient-default-level + This option controls which suffix levels are made available by + default. It sets the transient-level for transients for which the + user has not set that individually. + + -- User Option: transient-levels-file + This option names the file that is used to persist the levels of + transients and their suffixes between Emacs sessions. + +‘C-x l’ (‘transient-set-level’) + This command enters edit mode. When edit mode is active, then all + infixes and suffixes that are currently usable are displayed along + with their levels. The colors of the levels indicate whether they + are enabled or not. The level of the transient is also displayed + along with some usage information. + + In edit mode, pressing the key that would usually invoke a certain + suffix instead prompts the user for the level that suffix should be + placed on. + + Help mode is available in edit mode. + + To change the transient level press ‘C-x l’ again. + + To exit edit mode press ‘C-g’. + + Note that edit mode does not display any suffixes that are not + currently usable. ‘magit-rebase’, for example, shows different + suffixes depending on whether a rebase is already in progress or + not. The predicates also apply in edit mode. + + Therefore, to control which suffixes are available given a certain + state, you have to make sure that that state is currently active. + + +File: transient.info, Node: Other Commands, Next: Configuration, Prev: Enabling and Disabling Suffixes, Up: Usage + +2.8 Other Commands +================== + +When invoking a transient in a small frame, the transient window may not +show the complete buffer, making it necessary to scroll, using the +following commands. These commands are never shown in the transient +window, and the key bindings are the same as for ‘scroll-up-command’ and +‘scroll-down-command’ in other buffers. + + -- Command: transient-scroll-up arg + This command scrolls text of transient popup window upward ARG + lines. If ARG is ‘nil’, then it scrolls near full screen. This is + a wrapper around ‘scroll-up-command’ (which see). + + -- Command: transient-scroll-down arg + This command scrolls text of transient popup window down ARG lines. + If ARG is ‘nil’, then it scrolls near full screen. This is a + wrapper around ‘scroll-down-command’ (which see). + + +File: transient.info, Node: Configuration, Prev: Other Commands, Up: Usage + +2.9 Configuration +================= + +More options are described in *note Common Suffix Commands::, in *note +Saving Values::, in *note Using History:: and in *note Enabling and +Disabling Suffixes::. + +Essential Options +----------------- + +Also see *note Common Suffix Commands::. + + -- User Option: transient-show-popup + This option controls whether the current transient’s infix and + suffix commands are shown in the popup buffer. + + • If ‘t’ (the default) then the popup buffer is shown as soon as + a transient prefix command is invoked. + + • If ‘nil’, then the popup buffer is not shown unless the user + explicitly requests it, by pressing an incomplete prefix key + sequence. + + • If a number, then the a brief one-line summary is shown + instead of the popup buffer. If zero or negative, then not + even that summary is shown; only the pressed key itself is + shown. + + The popup is shown when the user explicitly requests it by + pressing an incomplete prefix key sequence. Unless this is + zero, the popup is shown after that many seconds of inactivity + (using the absolute value). + + -- User Option: transient-enable-popup-navigation + This option controls whether navigation commands are enabled in the + transient popup buffer. + + While a transient is active the transient popup buffer is not the + current buffer, making it necessary to use dedicated commands to + act on that buffer itself. This is disabled by default. If this + option is non-‘nil’, then the following features are available: + + • ‘’ moves the cursor to the previous suffix. + • ‘’ moves the cursor to the next suffix. + • ‘’ invokes the suffix the cursor is on. + • ‘mouse-1’ invokes the clicked on suffix. + • ‘C-s’ and ‘C-r’ start isearch in the popup buffer. + + -- User Option: transient-display-buffer-action + This option specifies the action used to display the transient + popup buffer. The transient popup buffer is displayed in a window + using ‘(display-buffer BUFFER transient-display-buffer-action)’. + + The value of this option has the form ‘(FUNCTION . ALIST)’, where + FUNCTION is a function or a list of functions. Each such function + should accept two arguments: a buffer to display and an alist of + the same form as ALIST. See *note (elisp)Choosing Window::, for + details. + + The default is: + + (display-buffer-in-side-window + (side . bottom) + (inhibit-same-window . t) + (window-parameters (no-other-window . t))) + + This displays the window at the bottom of the selected frame. + Another useful FUNCTION is ‘display-buffer-below-selected’, which + is what ‘magit-popup’ used by default. For more alternatives see + *note (elisp)Display Action Functions:: and *note (elisp)Buffer + Display Action Alists::. + + Note that the buffer that was current before the transient buffer + is shown should remain the current buffer. Many suffix commands + act on the thing at point, if appropriate, and if the transient + buffer became the current buffer, then that would change what is at + point. To that effect ‘inhibit-same-window’ ensures that the + selected window is not used to show the transient buffer. + + It may be possible to display the window in another frame, but + whether that works in practice depends on the window-manager. If + the window manager selects the new window (Emacs frame), then that + unfortunately changes which buffer is current. + + If you change the value of this option, then you might also want to + change the value of ‘transient-mode-line-format’. + +Accessibility Options +--------------------- + + -- User Option: transient-force-single-column + This option controls whether the use of a single column to display + suffixes is enforced. This might be useful for users with low + vision who use large text and might otherwise have to scroll in two + dimensions. + +Auxiliary Options +----------------- + + -- User Option: transient-mode-line-format + This option controls whether the transient popup buffer has a + mode-line, separator line, or neither. + + If ‘nil’, then the buffer has no mode-line. If the buffer is not + displayed right above the echo area, then this probably is not a + good value. + + If ‘line’ (the default), then the buffer also has no mode-line, but + a thin line is drawn instead, using the background color of the + face ‘transient-separator’. Text-mode frames cannot display thin + lines, and therefore fall back to treating ‘line’ like ‘nil’. + + Otherwise this can be any mode-line format. See *note (elisp)Mode + Line Format::, for details. + + -- User Option: transient-semantic-coloring + This option controls whether prefixes and suffixes are colored in a + Hydra-like fashion. + + If non-‘nil’, then the key binding of each suffix is colorized to + indicate whether it exits the transient state or not. The color of + the prefix is indicated using the line that is drawn when the value + of ‘transient-mode-line-format’ is ‘line’. + + For more information about how Hydra uses colors see + and + . + + -- User Option: transient-highlight-mismatched-keys + This option controls whether key bindings of infix commands that do + not match the respective command-line argument should be + highlighted. For other infix commands this option has no effect. + + When this option is non-‘nil’, the key binding for an infix + argument is highlighted when only a long argument (e.g., + ‘--verbose’) is specified but no shorthand (e.g., ‘-v’). In the + rare case that a shorthand is specified but the key binding does + not match, then it is highlighted differently. + + Highlighting mismatched key bindings is useful when learning the + arguments of the underlying command-line tool; you wouldn’t want to + learn any short-hands that do not actually exist. + + The highlighting is done using one of the faces + ‘transient-mismatched-key’ and ‘transient-nonstandard-key’. + + -- User Option: transient-substitute-key-function + This function is used to modify key bindings. If the value of this + option is ‘nil’ (the default), then no substitution is performed. + + This function is called with one argument, the prefix object, and + must return a key binding description, either the existing key + description it finds in the ‘key’ slot, or the key description that + replaces the prefix key. It could be used to make other + substitutions, but that is discouraged. + + For example, ‘=’ is hard to reach using my custom keyboard layout, + so I substitute ‘(’ for that, which is easy to reach using a layout + optimized for lisp. + + (setq transient-substitute-key-function + (lambda (obj) + (let ((key (oref obj key))) + (if (string-match "\\`\\(=\\)[a-zA-Z]" key) + (replace-match "(" t t key 1) + key)))) + + -- User Option: transient-read-with-initial-input + This option controls whether the last history element is used as + the initial minibuffer input when reading the value of an infix + argument from the user. If ‘nil’, there is no initial input and + the first element has to be accessed the same way as the older + elements. + + -- User Option: transient-hide-during-minibuffer-read + This option controls whether the transient buffer is hidden while + user input is being read in the minibuffer. + + -- User Option: transient-align-variable-pitch + This option controls whether columns are aligned pixel-wise in the + popup buffer. + + If this is non-‘nil’, then columns are aligned pixel-wise to + support variable-pitch fonts. Keys are not aligned, so you should + use a fixed-pitch font for the ‘transient-key’ face. Other key + faces inherit from that face unless a theme is used that breaks + that relationship. + + This option is intended for users who use a variable-pitch font for + the ‘default’ face. + + -- User Option: transient-force-fixed-pitch + This option controls whether to force the use of a monospaced font + in popup buffer. Even if you use a proportional font for the + ‘default’ face, you might still want to use a monospaced font in + transient’s popup buffer. Setting this option to ‘t’ causes + ‘default’ to be remapped to ‘fixed-pitch’ in that buffer. + +Developer Options +----------------- + +These options are mainly intended for developers. + + -- User Option: transient-detect-key-conflicts + This option controls whether key binding conflicts should be + detected at the time the transient is invoked. If so, this results + in an error, which prevents the transient from being used. Because + of that, conflicts are ignored by default. + + Conflicts cannot be determined earlier, i.e., when the transient is + being defined and when new suffixes are being added, because at + that time there can be false-positives. It is actually valid for + multiple suffixes to share a common key binding, provided the + predicates of those suffixes prevent that more than one of them is + enabled at a time. + + -- User Option: transient-highlight-higher-levels + This option controls whether suffixes that would not be available + by default are highlighted. + + When non-‘nil’ then the descriptions of suffixes are highlighted if + their level is above 4, the default of ‘transient-default-level’. + Assuming you have set that variable to 7, this highlights all + suffixes that won’t be available to users without them making the + same customization. + + +File: transient.info, Node: Modifying Existing Transients, Next: Defining New Commands, Prev: Usage, Up: Top + +3 Modifying Existing Transients +******************************* + +To an extent, transients can be customized interactively, see *note +Enabling and Disabling Suffixes::. This section explains how existing +transients can be further modified non-interactively. + + The following functions share a few arguments: + + • PREFIX is a transient prefix command, a symbol. + + • SUFFIX is a transient infix or suffix specification in the same + form as expected by ‘transient-define-prefix’. Note that an infix + is a special kind of suffix. Depending on context “suffixes” means + “suffixes (including infixes)” or “non-infix suffixes”. Here it + means the former. See *note Suffix Specifications::. + + SUFFIX may also be a group in the same form as expected by + ‘transient-define-prefix’. See *note Group Specifications::. + + • LOC is a command, a key vector, a key description (a string as + returned by ‘key-description’), or a list specifying coordinates + (the last element may also be a command or key). For example ‘(1 0 + -1)’ identifies the last suffix (‘-1’) of the first subgroup (‘0’) + of the second group (‘1’). + + If LOC is a list of coordinates, then it can be used to identify a + group, not just an individual suffix command. + + The function ‘transient-get-suffix’ can be useful to determine + whether a certain coordination list identifies the suffix or group + that you expect it to identify. In hairy cases it may be necessary + to look at the definition of the transient prefix command. + + These functions operate on the information stored in the +‘transient--layout’ property of the PREFIX symbol. Suffix entries in +that tree are not objects but have the form ‘(LEVEL CLASS PLIST)’, where +PLIST should set at least ‘:key’, ‘:description’ and ‘:command’. + + -- Function: transient-insert-suffix prefix loc suffix &optional + keep-other + -- Function: transient-append-suffix prefix loc suffix &optional + keep-other + These functions insert the suffix or group SUFFIX into PREFIX + before or after LOC. + + Conceptually adding a binding to a transient prefix is similar to + adding a binding to a keymap, but this is complicated by the fact + that multiple suffix commands can be bound to the same key, + provided they are never active at the same time, see *note + Predicate Slots::. + + Unfortunately both false-positives and false-negatives are + possible. To deal with the former use non-nil KEEP-OTHER. To deal + with the latter remove the conflicting binding explicitly. + + -- Function: transient-replace-suffix prefix loc suffix + This function replaces the suffix or group at LOC in PREFIX with + suffix or group SUFFIX. + + -- Function: transient-remove-suffix prefix loc + This function removes the suffix or group at LOC in PREFIX. + + -- Function: transient-get-suffix prefix loc + This function returns the suffix or group at LOC in PREFIX. The + returned value has the form mentioned above. + + -- Function: transient-suffix-put prefix loc prop value + This function edits the suffix or group at LOC in PREFIX, by + setting the PROP of its plist to VALUE. + + Most of these functions do not signal an error if they cannot perform +the requested modification. The functions that insert new suffixes show +a warning if LOC cannot be found in PREFIX without signaling an error. +The reason for doing it like this is that establishing a key binding +(and that is what we essentially are trying to do here) should not +prevent the rest of the configuration from loading. Among these +functions only ‘transient-get-suffix’ and ‘transient-suffix-put’ may +signal an error. + + +File: transient.info, Node: Defining New Commands, Next: Classes and Methods, Prev: Modifying Existing Transients, Up: Top + +4 Defining New Commands +*********************** + +* Menu: + +* Defining Transients:: +* Binding Suffix and Infix Commands:: +* Defining Suffix and Infix Commands:: +* Using Infix Arguments:: +* Transient State:: + + +File: transient.info, Node: Defining Transients, Next: Binding Suffix and Infix Commands, Up: Defining New Commands + +4.1 Defining Transients +======================= + +A transient consists of a prefix command and at least one suffix +command, though usually a transient has several infix and suffix +commands. The below macro defines the transient prefix command *and* +binds the transient’s infix and suffix commands. In other words, it +defines the complete transient, not just the transient prefix command +that is used to invoke that transient. + + -- Macro: transient-define-prefix name arglist [docstring] [keyword + value]... group... [body...] + This macro defines NAME as a transient prefix command and binds the + transient’s infix and suffix commands. + + ARGLIST are the arguments that the prefix command takes. DOCSTRING + is the documentation string and is optional. + + These arguments can optionally be followed by keyword-value pairs. + Each key has to be a keyword symbol, either ‘:class’ or a keyword + argument supported by the constructor of that class. The + ‘transient-prefix’ class is used if the class is not specified + explicitly. + + GROUPs add key bindings for infix and suffix commands and specify + how these bindings are presented in the popup buffer. At least one + GROUP has to be specified. See *note Binding Suffix and Infix + Commands::. + + The BODY is optional. If it is omitted, then ARGLIST is ignored + and the function definition becomes: + + (lambda () + (interactive) + (transient-setup 'NAME)) + + If BODY is specified, then it must begin with an ‘interactive’ form + that matches ARGLIST, and it must call ‘transient-setup’. It may, + however, call that function only when some condition is satisfied. + + All transients have a (possibly ‘nil’) value, which is exported + when suffix commands are called, so that they can consume that + value. For some transients it might be necessary to have a sort of + secondary value, called a “scope”. Such a scope would usually be + set in the command’s ‘interactive’ form and has to be passed to the + setup function: + + (transient-setup 'NAME nil nil :scope SCOPE) + + For example, the scope of the ‘magit-branch-configure’ transient is + the branch whose variables are being configured. + + +File: transient.info, Node: Binding Suffix and Infix Commands, Next: Defining Suffix and Infix Commands, Prev: Defining Transients, Up: Defining New Commands + +4.2 Binding Suffix and Infix Commands +===================================== + +The macro ‘transient-define-prefix’ is used to define a transient. This +defines the actual transient prefix command (see *note Defining +Transients::) and adds the transient’s infix and suffix bindings, as +described below. + + Users and third-party packages can add additional bindings using +functions such as ‘transient-insert-suffix’ (See *note Modifying +Existing Transients::). These functions take a “suffix specification” +as one of their arguments, which has the same form as the specifications +used in ‘transient-define-prefix’. + +* Menu: + +* Group Specifications:: +* Suffix Specifications:: + + +File: transient.info, Node: Group Specifications, Next: Suffix Specifications, Up: Binding Suffix and Infix Commands + +4.2.1 Group Specifications +-------------------------- + +The suffix and infix commands of a transient are organized in groups. +The grouping controls how the descriptions of the suffixes are outlined +visually but also makes it possible to set certain properties for a set +of suffixes. + + Several group classes exist, some of which organize suffixes in +subgroups. In most cases the class does not have to be specified +explicitly, but see *note Group Classes::. + + Groups are specified in the call to ‘transient-define-prefix’, using +vectors. Because groups are represented using vectors, we cannot use +square brackets to indicate an optional element and instead use curly +brackets to do the latter. + + Group specifications then have this form: + + [{LEVEL} {DESCRIPTION} {KEYWORD VALUE}... ELEMENT...] + + The LEVEL is optional and defaults to 4. See *note Enabling and +Disabling Suffixes::. + + The DESCRIPTION is optional. If present, it is used as the heading +of the group. + + The KEYWORD-VALUE pairs are optional. Each keyword has to be a +keyword symbol, either ‘:class’ or a keyword argument supported by the +constructor of that class. + + • One of these keywords, ‘:description’, is equivalent to specifying + DESCRIPTION at the very beginning of the vector. The + recommendation is to use ‘:description’ if some other keyword is + also used, for consistency, or DESCRIPTION otherwise, because it + looks better. + + • Likewise ‘:level’ is equivalent to LEVEL. + + • Other important keywords include the ‘:if...’ keywords. These + keywords control whether the group is available in a certain + situation. + + For example, one group of the ‘magit-rebase’ transient uses ‘:if + magit-rebase-in-progress-p’, which contains the suffixes that are + useful while rebase is already in progress; and another that uses + ‘:if-not magit-rebase-in-progress-p’, which contains the suffixes + that initiate a rebase. + + These predicates can also be used on individual suffixes and are + only documented once, see *note Predicate Slots::. + + • The value of ‘:hide’, if non-‘nil’, is a predicate that controls + whether the group is hidden by default. The key bindings for + suffixes of a hidden group should all use the same prefix key. + Pressing that prefix key should temporarily show the group and its + suffixes, which assumes that a predicate like this is used: + + (lambda () + (eq (car transient--redisplay-key) + ?\C-c)) ; the prefix key shared by all bindings + + • The value of ‘:setup-children’, if non-‘nil’, is a function that + takes two arguments the group object itself and a list of children. + The children are given as a (potentially empty) list consisting of + either group or suffix specifications. It can make arbitrary + changes to the children including constructing new children from + scratch. Also see ‘transient-setup-children’. + + • The boolean ‘:pad-keys’ argument controls whether keys of all + suffixes contained in a group are right padded, effectively + aligning the descriptions. + + The ELEMENTs are either all subgroups (vectors), or all suffixes +(lists) and strings. (At least currently no group type exists that +would allow mixing subgroups with commands at the same level, though in +principle there is nothing that prevents that.) + + If the ELEMENTs are not subgroups, then they can be a mixture of +lists that specify commands and strings. Strings are inserted verbatim. +The empty string can be used to insert gaps between suffixes, which is +particularly useful if the suffixes are outlined as a table. + + Variables are supported inside group specifications. For example in +place of a direct subgroup specification, a variable can be used whose +value is a vector that qualifies as a group specification. Likewise, a +variable can be used where a suffix specification is expected. Lists of +group or suffix specifications are also supported. Indirect +specifications are resolved when the transient prefix is being defined. + + The form of suffix specifications is documented in the next node. + + +File: transient.info, Node: Suffix Specifications, Prev: Group Specifications, Up: Binding Suffix and Infix Commands + +4.2.2 Suffix Specifications +--------------------------- + +A transient’s suffix and infix commands are bound when the transient +prefix command is defined using ‘transient-define-prefix’, see *note +Defining Transients::. The commands are organized into groups, see +*note Group Specifications::. Here we describe the form used to bind an +individual suffix command. + + The same form is also used when later binding additional commands +using functions such as ‘transient-insert-suffix’, see *note Modifying +Existing Transients::. + + Note that an infix is a special kind of suffix. Depending on context +“suffixes” means “suffixes (including infixes)” or “non-infix suffixes”. +Here it means the former. + + Suffix specifications have this form: + + ([LEVEL] [KEY] [DESCRIPTION] COMMAND|ARGUMENT [KEYWORD VALUE]...) + + LEVEL, KEY and DESCRIPTION can also be specified using the KEYWORDs +‘:level’, ‘:key’ and ‘:description’. If the object that is associated +with COMMAND sets these properties, then they do not have to be +specified here. You can however specify them here anyway, possibly +overriding the object’s values just for the binding inside this +transient. + + • LEVEL is the suffix level, an integer between 1 and 7. See *note + Enabling and Disabling Suffixes::. + + • KEY is the key binding, either a vector or key description string. + + • DESCRIPTION is the description, either a string or a function that + returns a string. The function should be a lambda expression to + avoid ambiguity. In some cases a symbol that is bound as a + function would also work but to be safe you should use + ‘:description’ in that case. + + The next element is either a command or an argument. This is the +only argument that is mandatory in all cases. + + • COMMAND should be a symbol that is bound as a function, which has + to be defined or at least autoloaded as a command by the time the + containing prefix command is invoked. + + Any command will do; it does not need to have an object associated + with it (as would be the case if ‘transient-define-suffix’ or + ‘transient-define-infix’ were used to define it). + + Anonymous, dynamically defined suffix commands are also support. + See information about the ‘:setup-children’ function in *note Group + Specifications::. + + As mentioned above, the object that is associated with a command + can be used to set the default for certain values that otherwise + have to be set in the suffix specification. Therefore if there is + no object, then you have to make sure to specify the KEY and the + DESCRIPTION. + + As a special case, if you want to add a command that might be + neither defined nor autoloaded, you can use a workaround like: + + (transient-insert-suffix 'some-prefix "k" + '("!" "Ceci n'est pas une commande" no-command + :if (lambda () (featurep 'no-library)))) + + Instead of ‘featurep’ you could also use ‘require’ with a non-‘nil’ + value for NOERROR. + + • The mandatory argument can also be a command-line argument, a + string. In that case an anonymous command is defined and bound. + + Instead of a string, this can also be a list of two strings, in + which case the first string is used as the short argument (which + can also be specified using ‘:shortarg’) and the second as the long + argument (which can also be specified using ‘:argument’). + + Only the long argument is displayed in the popup buffer. See + ‘transient-detect-key-conflicts’ for how the short argument may be + used. + + Unless the class is specified explicitly, the appropriate class is + guessed based on the long argument. If the argument ends with ‘=’ + (e.g., ‘--format=’) then ‘transient-option’ is used, otherwise + ‘transient-switch’. + + Finally, details can be specified using optional KEYWORD-VALUE pairs. +Each keyword has to be a keyword symbol, either ‘:class’ or a keyword +argument supported by the constructor of that class. See *note Suffix +Slots::. + + +File: transient.info, Node: Defining Suffix and Infix Commands, Next: Using Infix Arguments, Prev: Binding Suffix and Infix Commands, Up: Defining New Commands + +4.3 Defining Suffix and Infix Commands +====================================== + +Note that an infix is a special kind of suffix. Depending on context +“suffixes” means “suffixes (including infixes)” or “non-infix suffixes”. + + -- Macro: transient-define-suffix name arglist [docstring] [keyword + value]... body... + This macro defines NAME as a transient suffix command. + + ARGLIST are the arguments that the command takes. DOCSTRING is the + documentation string and is optional. + + These arguments can optionally be followed by keyword-value pairs. + Each keyword has to be a keyword symbol, either ‘:class’ or a + keyword argument supported by the constructor of that class. The + ‘transient-suffix’ class is used if the class is not specified + explicitly. + + The BODY must begin with an ‘interactive’ form that matches + ARGLIST. The infix arguments are usually accessed by using + ‘transient-args’ inside ‘interactive’. + + -- Macro: transient-define-infix name arglist [docstring] [keyword + value]... + This macro defines NAME as a transient infix command. + + ARGLIST is always ignored (but mandatory never-the-less) and + reserved for future use. DOCSTRING is the documentation string and + is optional. + + The keyword-value pairs are mandatory. All transient infix + commands are ‘equal’ to each other (but not ‘eq’), so it is + meaningless to define an infix command without also setting at + least ‘:class’ and one other keyword (which it is depends on the + used class, usually ‘:argument’ or ‘:variable’). + + Each keyword has to be a keyword symbol, either ‘:class’ or a + keyword argument supported by the constructor of that class. The + ‘transient-switch’ class is used if the class is not specified + explicitly. + + The function definition is always: + + (lambda () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) + + ‘transient-infix-read’ and ‘transient-infix-set’ are generic + functions. Different infix commands behave differently because the + concrete methods are different for different infix command classes. + In rare cases the above command function might not be suitable, + even if you define your own infix command class. In that case you + have to use ‘transient-define-suffix’ to define the infix command + and use ‘t’ as the value of the ‘:transient’ keyword. + + -- Macro: transient-define-argument name arglist [docstring] [keyword + value]... + This macro defines NAME as a transient infix command. + + This is an alias for ‘transient-define-infix’. Only use this alias + to define an infix command that actually sets an infix argument. + To define an infix command that, for example, sets a variable, use + ‘transient-define-infix’ instead. + + +File: transient.info, Node: Using Infix Arguments, Next: Transient State, Prev: Defining Suffix and Infix Commands, Up: Defining New Commands + +4.4 Using Infix Arguments +========================= + +The functions and the variables described below allow suffix commands to +access the value of the transient from which they were invoked; which is +the value of its infix arguments. These variables are set when the user +invokes a suffix command that exits the transient, but before actually +calling the command. + + When returning to the command-loop after calling the suffix command, +the arguments are reset to ‘nil’ (which causes the function to return +‘nil’ too). + + Like for Emacs’ prefix arguments, it is advisable, but not mandatory, +to access the infix arguments inside the command’s ‘interactive’ form. +The preferred way of doing that is to call the ‘transient-args’ +function, which for infix arguments serves about the same purpose as +‘prefix-arg’ serves for prefix arguments. + + -- Function: transient-args prefix + This function returns the value of the transient prefix command + PREFIX. + + If the current command was invoked from the transient prefix + command PREFIX, then it returns the active infix arguments. If the + current command was not invoked from PREFIX, then it returns the + set, saved or default value for PREFIX. + + -- Function: transient-arg-value arg args + This function return the value of ARG as it appears in ARGS. + + For a switch a boolean is returned. For an option the value is + returned as a string, using the empty string for the empty value, + or ‘nil’ if the option does not appear in ARGS. + + -- Function: transient-suffixes prefix + This function returns the suffixes of the transient prefix command + PREFIX. This is a list of objects. This function should only be + used if you need the objects (as opposed to just their values) and + if the current command is not being invoked from PREFIX. + + -- Variable: transient-current-suffixes + The suffixes of the transient from which this suffix command was + invoked. This is a list of objects. Usually it is sufficient to + instead use the function ‘transient-args’, which returns a list of + values. In complex cases it might be necessary to use this + variable instead, i.e., if you need access to information beside + the value. + + -- Variable: transient-current-prefix + The transient from which this suffix command was invoked. The + returned value is a ‘transient-prefix’ object, which holds + information associated with the transient prefix command. + + -- Variable: transient-current-command + The transient from which this suffix command was invoked. The + returned value is a symbol, the transient prefix command. + + +File: transient.info, Node: Transient State, Prev: Using Infix Arguments, Up: Defining New Commands + +4.5 Transient State +=================== + +Invoking a transient prefix command “activates” the respective +transient, i.e., it puts a transient keymap into effect, which binds the +transient’s infix and suffix commands. + + The default behavior while a transient is active is as follows: + + • Invoking an infix command does not affect the transient state; the + transient remains active. + + • Invoking a (non-infix) suffix command “deactivates” the transient + state by removing the transient keymap and performing some + additional cleanup. + + • Invoking a command that is bound in a keymap other than the + transient keymap is disallowed and trying to do so results in a + warning. This does not “deactivate” the transient. + + But these are just the defaults. Whether a certain command +deactivates or “exits” the transient is configurable. There is more +than one way in which a command can be “transient” or “non-transient”; +the exact behavior is implemented by calling a so-called “pre-command” +function. Whether non-suffix commands are allowed to be called is +configurable per transient. + + • The transient-ness of suffix commands (including infix commands) is + controlled by the value of their ‘transient’ slot, which can be set + either when defining the command or when adding a binding to a + transient while defining the respective transient prefix command. + + Valid values are booleans and the pre-commands described below. + + • ‘t’ is equivalent to ‘transient--do-stay’. + • ‘nil’ is equivalent to ‘transient--do-exit’. + • If ‘transient’ is unbound (and that is actually the default + for non-infix suffixes) then the value of the prefix’s + ‘transient-suffix’ slot is used instead. The default value of + that slot is ‘nil’, so the suffix’s ‘transient’ slot being + unbound is essentially equivalent to it being ‘nil’. + + • A suffix command can be a prefix command itself, i.e., a + “sub-prefix”. While a sub-prefix is active we nearly always want + ‘C-g’ to take the user back to the “super-prefix”. However in rare + cases this may not be desirable, and that makes the following + complication necessary: + + For ‘transient-suffix’ objects the ‘transient’ slot is unbound. We + can ignore that for the most part because, as stated above, ‘nil’ + and the slot being unbound are equivalent, and mean “do exit”. + That isn’t actually true for suffixes that are sub-prefixes though. + For such suffixes unbound means “do exit but allow going back”, + which is the default, while ‘nil’ means “do exit permanently”, + which requires that slot to be explicitly set to that value. + + • The transient-ness of certain built-in suffix commands is specified + using ‘transient-predicate-map’. This is a special keymap, which + binds commands to pre-commands (as opposed to keys to commands) and + takes precedence over the ‘transient’ slot. + + The available pre-command functions are documented below. They are +called by ‘transient--pre-command’, a function on ‘pre-command-hook’ and +the value that they return determines whether the transient is exited. +To do so the value of one of the constants ‘transient--exit’ or +‘transient--stay’ is used (that way we don’t have to remember if ‘t’ +means “exit” or “stay”). + + Additionally, these functions may change the value of ‘this-command’ +(which explains why they have to be called using ‘pre-command-hook’), +call ‘transient-export’, ‘transient--stack-zap’ or +‘transient--stack-push’; and set the values of ‘transient--exitp’, +‘transient--helpp’ or ‘transient--editp’. + +Pre-commands for Infixes +------------------------ + +The default for infixes is ‘transient--do-stay’. This is also the only +function that makes sense for infixes. + + -- Function: transient--do-stay + Call the command without exporting variables and stay transient. + +Pre-commands for Suffixes +------------------------- + +The default for suffixes is ‘transient--do-exit’. + + -- Function: transient--do-exit + Call the command after exporting variables and exit the transient. + + -- Function: transient--do-return + Call the command after exporting variables and return to parent + prefix. If there is no parent prefix, then call + ‘transient--do-exit’. + + -- Function: transient--do-call + Call the command after exporting variables and stay transient. + + The following pre-commands are suitable for sub-prefixes. Only the +first should ever explicitly be set as the value of the ‘transient’ +slot. + + -- Function: transient--do-recurse + Call the transient prefix command, preparing for return to active + transient. + + Whether we actually return to the parent transient is ultimately + under the control of each invoked suffix. The difference between + this pre-command and ‘transient--do-replace’ is that it changes the + value of the ‘transient-suffix’ slot to ‘transient--do-return’. + + If there is no parent transient, then only call this command and + skip the second step. + + -- Function: transient--do-replace + Call the transient prefix command, replacing the active transient. + + Unless ‘transient--do-recurse’ is explicitly used, this pre-command + is automatically used for suffixes that are prefixes themselves, + i.e., for sub-prefixes. + + -- Function: transient--do-suspend + Suspend the active transient, saving the transient stack. + + This is used by the command ‘transient-suspend’ and optionally also + by “external events” such as ‘handle-switch-frame’. Such bindings + should be added to ‘transient-predicate-map’. + +Pre-commands for Non-Suffixes +----------------------------- + +The default for non-suffixes, i.e., commands that are bound in other +keymaps beside the transient keymap, is ‘transient--do-warn’. Silently +ignoring the user-error is also an option, though probably not a good +one. + + If you want to let the user invoke non-suffix commands, then use +‘transient--do-stay’ as the value of the prefix’s ‘transient-non-suffix’ +slot. + + -- Function: transient--do-warn + Call ‘transient-undefined’ and stay transient. + + -- Function: transient--do-noop + Call ‘transient-noop’ and stay transient. + +Special Pre-Commands +-------------------- + + -- Function: transient--do-quit-one + If active, quit help or edit mode, else exit the active transient. + + This is used when the user pressed ‘C-g’. + + -- Function: transient--do-quit-all + Exit all transients without saving the transient stack. + + This is used when the user pressed ‘C-q’. + + -- Function: transient--do-suspend + Suspend the active transient, saving the transient stack. + + This is used when the user pressed ‘C-z’. + + +File: transient.info, Node: Classes and Methods, Next: Related Abstractions and Packages, Prev: Defining New Commands, Up: Top + +5 Classes and Methods +********************* + +Transient uses classes and generic functions to make it possible to +define new types of suffix commands that are similar to existing types, +but behave differently in some aspects. It does the same for groups and +prefix commands, though at least for prefix commands that *currently* +appears to be less important. + + Every prefix, infix and suffix command is associated with an object, +which holds information that controls certain aspects of its behavior. +This happens in two ways. + + • Associating a command with a certain class gives the command a + type. This makes it possible to use generic functions to do + certain things that have to be done differently depending on what + type of command it acts on. + + That in turn makes it possible for third-parties to add new types + without having to convince the maintainer of Transient that that + new type is important enough to justify adding a special case to a + dozen or so functions. + + • Associating a command with an object makes it possible to easily + store information that is specific to that particular command. + + Two commands may have the same type, but obviously their key + bindings and descriptions still have to be different, for example. + + The values of some slots are functions. The ‘reader’ slot for + example holds a function that is used to read a new value for an + infix command. The values of such slots are regular functions. + + Generic functions are used when a function should do something + different based on the type of the command, i.e., when all commands + of a certain type should behave the same way but different from the + behavior for other types. Object slots that hold a regular + function as value are used when the task that they perform is + likely to differ even between different commands of the same type. + +* Menu: + +* Group Classes:: +* Group Methods:: +* Prefix Classes:: +* Suffix Classes:: +* Suffix Methods:: +* Prefix Slots:: +* Suffix Slots:: +* Predicate Slots:: + + +File: transient.info, Node: Group Classes, Next: Group Methods, Up: Classes and Methods + +5.1 Group Classes +================= + +The type of a group can be specified using the ‘:class’ property at the +beginning of the class specification, e.g., ‘[:class transient-columns +...]’ in a call to ‘transient-define-prefix’. + + • The abstract ‘transient-child’ class is the base class of both + ‘transient-group’ (and therefore all groups) as well as of + ‘transient-suffix’ (and therefore all suffix and infix commands). + + This class exists because the elements (or “children”) of certain + groups can be other groups instead of suffix and infix commands. + + • The abstract ‘transient-group’ class is the superclass of all other + group classes. + + • The ‘transient-column’ class is the simplest group. + + This is the default “flat” group. If the class is not specified + explicitly and the first element is not a vector (i.e., not a + group), then this class is used. + + This class displays each element on a separate line. + + • The ‘transient-row’ class displays all elements on a single line. + + • The ‘transient-columns’ class displays commands organized in + columns. + + Direct elements have to be groups whose elements have to be + commands or strings. Each subgroup represents a column. This + class takes care of inserting the subgroups’ elements. + + This is the default “nested” group. If the class is not specified + explicitly and the first element is a vector (i.e., a group), then + this class is used. + + • The ‘transient-subgroups’ class wraps other groups. + + Direct elements have to be groups whose elements have to be + commands or strings. This group inserts an empty line between + subgroups. The subgroups themselves are responsible for displaying + their elements. + + +File: transient.info, Node: Group Methods, Next: Prefix Classes, Prev: Group Classes, Up: Classes and Methods + +5.2 Group Methods +================= + + -- Function: transient-setup-children group children + This generic function can be used to setup the children or a group. + + The default implementation usually just returns the children + unchanged, but if the ‘setup-children’ slot of GROUP is non-‘nil’, + then it calls that function with CHILDREN as the only argument and + returns the value. + + The children are given as a (potentially empty) list consisting of + either group or suffix specifications. These functions can make + arbitrary changes to the children including constructing new + children from scratch. + + -- Function: transient--insert-group group + This generic function formats the group and its elements and + inserts the result into the current buffer, which is a temporary + buffer. The contents of that buffer are later inserted into the + popup buffer. + + Functions that are called by this function may need to operate in + the buffer from which the transient was called. To do so they can + temporarily make the ‘transient--source-buffer’ the current buffer. + + +File: transient.info, Node: Prefix Classes, Next: Suffix Classes, Prev: Group Methods, Up: Classes and Methods + +5.3 Prefix Classes +================== + +Currently the ‘transient-prefix’ class is being used for all prefix +commands and there is only a single generic function that can be +specialized based on the class of a prefix command. + + -- Function: transient--history-init obj + This generic function is called while setting up the transient and + is responsible for initializing the ‘history’ slot. This is the + transient-wide history; many individual infixes also have a history + of their own. + + The default (and currently only) method extracts the value from the + global variable ‘transient-history’. + + A transient prefix command’s object is stored in the +‘transient--prefix’ property of the command symbol. While a transient +is active, a clone of that object is stored in the variable +‘transient--prefix’. A clone is used because some changes that are made +to the active transient’s object should not affect later invocations. + + +File: transient.info, Node: Suffix Classes, Next: Suffix Methods, Prev: Prefix Classes, Up: Classes and Methods + +5.4 Suffix Classes +================== + + • All suffix and infix classes derive from ‘transient-suffix’, which + in turn derives from ‘transient-child’, from which + ‘transient-group’ also derives (see *note Group Classes::). + + • All infix classes derive from the abstract ‘transient-infix’ class, + which in turn derives from the ‘transient-suffix’ class. + + Infixes are a special type of suffixes. The primary difference is + that infixes always use the ‘transient--do-stay’ pre-command, while + non-infix suffixes use a variety of pre-commands (see *note + Transient State::). Doing that is most easily achieved by using + this class, though theoretically it would be possible to define an + infix class that does not do so. If you do that then you get to + implement many methods. + + Also, infixes and non-infix suffixes are usually defined using + different macros (see *note Defining Suffix and Infix Commands::). + + • Classes used for infix commands that represent arguments should be + derived from the abstract ‘transient-argument’ class. + + • The ‘transient-switch’ class (or a derived class) is used for infix + arguments that represent command-line switches (arguments that do + not take a value). + + • The ‘transient-option’ class (or a derived class) is used for infix + arguments that represent command-line options (arguments that do + take a value). + + • The ‘transient-switches’ class can be used for a set of mutually + exclusive command-line switches. + + • The ‘transient-files’ class can be used for a ‘--’ argument that + indicates that all remaining arguments are files. + + • Classes used for infix commands that represent variables should + derived from the abstract ‘transient-variables’ class. + + Magit defines additional classes, which can serve as examples for the +fancy things you can do without modifying Transient. Some of these +classes will likely get generalized and added to Transient. For now +they are very much subject to change and not documented. + + +File: transient.info, Node: Suffix Methods, Next: Prefix Slots, Prev: Suffix Classes, Up: Classes and Methods + +5.5 Suffix Methods +================== + +To get information about the methods implementing these generic +functions use ‘describe-function’. + +* Menu: + +* Suffix Value Methods:: +* Suffix Format Methods:: + + +File: transient.info, Node: Suffix Value Methods, Next: Suffix Format Methods, Up: Suffix Methods + +5.5.1 Suffix Value Methods +-------------------------- + + -- Function: transient-init-value obj + This generic function sets the initial value of the object OBJ. + + This function is called for all suffix commands, but unless a + concrete method is implemented this falls through to the default + implementation, which is a noop. In other words this usually only + does something for infix commands, but note that this is not + implemented for the abstract class ‘transient-infix’, so if your + class derives from that directly, then you must implement a method. + + -- Function: transient-infix-read obj + This generic function determines the new value of the infix object + OBJ. + + This function merely determines the value; ‘transient-infix-set’ is + used to actually store the new value in the object. + + For most infix classes this is done by reading a value from the + user using the reader specified by the ‘reader’ slot (using the + ‘transient-infix-value’ method described below). + + For some infix classes the value is changed without reading + anything in the minibuffer, i.e., the mere act of invoking the + infix command determines what the new value should be, based on the + previous value. + + -- Function: transient-prompt obj + This generic function returns the prompt to be used to read infix + object OBJ’s value. + + -- Function: transient-infix-set obj value + This generic function sets the value of infix object OBJ to VALUE. + + -- Function: transient-infix-value obj + This generic function returns the value of the suffix object OBJ. + + This function is called by ‘transient-args’ (which see), meaning + this function is how the value of a transient is determined so that + the invoked suffix command can use it. + + Currently most values are strings, but that is not set in stone. + ‘nil’ is not a value, it means “no value”. + + Usually only infixes have a value, but see the method for + ‘transient-suffix’. + + -- Function: transient-init-scope obj + This generic function sets the scope of the suffix object OBJ. + + The scope is actually a property of the transient prefix, not of + individual suffixes. However it is possible to invoke a suffix + command directly instead of from a transient. In that case, if the + suffix expects a scope, then it has to determine that itself and + store it in its ‘scope’ slot. + + This function is called for all suffix commands, but unless a + concrete method is implemented this falls through to the default + implementation, which is a noop. + + +File: transient.info, Node: Suffix Format Methods, Prev: Suffix Value Methods, Up: Suffix Methods + +5.5.2 Suffix Format Methods +--------------------------- + + -- Function: transient-format obj + This generic function formats and returns OBJ for display. + + When this function is called, then the current buffer is some + temporary buffer. If you need the buffer from which the prefix + command was invoked to be current, then do so by temporarily making + ‘transient--source-buffer’ current. + + -- Function: transient-format-key obj + This generic function formats OBJ’s ‘key’ for display and returns + the result. + + -- Function: transient-format-description obj + This generic function formats OBJ’s ‘description’ for display and + returns the result. + + -- Function: transient-format-value obj + This generic function formats OBJ’s value for display and returns + the result. + + -- Function: transient-show-help obj + Show help for the prefix, infix or suffix command represented by + OBJ. + + For prefixes, show the info manual, if that is specified using the + ‘info-manual’ slot. Otherwise, show the manpage if that is + specified using the ‘man-page’ slot. Otherwise, show the command’s + doc string. + + For suffixes, show the command’s doc string. + + For infixes, show the manpage if that is specified. Otherwise show + the command’s doc string. + + +File: transient.info, Node: Prefix Slots, Next: Suffix Slots, Prev: Suffix Methods, Up: Classes and Methods + +5.6 Prefix Slots +================ + + • ‘show-help’, ‘man-page’ or ‘info-manual’ can be used to specify the + documentation for the prefix and its suffixes. The command + ‘transient-help’ uses the method ‘transient-show-help’ (which see) + to lookup and use these values. + + • ‘history-key’ If multiple prefix commands should share a single + value, then this slot has to be set to the same value for all of + them. You probably don’t want that. + + • ‘transient-suffix’ and ‘transient-non-suffix’ play a part when + determining whether the currently active transient prefix command + remains active/transient when a suffix or abitrary non-suffix + command is invoked. See *note Transient State::. + + • ‘incompatible’ A list of lists. Each sub-list specifies a set of + mutually exclusive arguments. Enabling one of these arguments + causes the others to be disabled. An argument may appear in + multiple sub-lists. + + • ‘scope’ For some transients it might be necessary to have a sort of + secondary value, called a “scope”. See ‘transient-define-prefix’. + +Internal Prefix Slots +--------------------- + +These slots are mostly intended for internal use. They should not be +set in calls to ‘transient-define-prefix’. + + • ‘prototype’ When a transient prefix command is invoked, then a + clone of that object is stored in the global variable + ‘transient--prefix’ and the prototype is stored in the clone’s + ‘prototype’ slot. + + • ‘command’ The command, a symbol. Each transient prefix command + consists of a command, which is stored in a symbol’s function slot + and an object, which is stored in the ‘transient--prefix’ property + of the same symbol. + + • ‘level’ The level of the prefix commands. The suffix commands + whose layer is equal or lower are displayed. See *note Enabling + and Disabling Suffixes::. + + • ‘value’ The likely outdated value of the prefix. Instead of + accessing this slot directly you should use the function + ‘transient-get-value’, which is guaranteed to return the up-to-date + value. + + • ‘history’ and ‘history-pos’ are used to keep track of historic + values. Unless you implement your own ‘transient-infix-read’ + method you should not have to deal with these slots. + + +File: transient.info, Node: Suffix Slots, Next: Predicate Slots, Prev: Prefix Slots, Up: Classes and Methods + +5.7 Suffix Slots +================ + +Here we document most of the slots that are only available for suffix +objects. Some slots are shared by suffix and group objects, they are +documented in *note Predicate Slots::. + + Also see *note Suffix Classes::. + +Slots of ‘transient-suffix’ +--------------------------- + + • ‘key’ The key, a key vector or a key description string. + + • ‘command’ The command, a symbol. + + • ‘transient’ Whether to stay transient. See *note Transient + State::. + + • ‘format’ The format used to display the suffix in the popup buffer. + It must contain the following %-placeholders: + + • ‘%k’ For the key. + • ‘%d’ For the description. + • ‘%v’ For the infix value. Non-infix suffixes don’t have a + value. + + • ‘description’ The description, either a string or a function that + is called with no argument and returns a string. + + • ‘show-help’ A function used to display help for the suffix. If + unspecified, the prefix controls how hlep is displayed for its + suffixes. + +Slots of ‘transient-infix’ +-------------------------- + +Some of these slots are only meaningful for some of the subclasses. +They are defined here anyway to allow sharing certain methods. + + • ‘argument’ The long argument, e.g., ‘--verbose’. + + • ‘shortarg’ The short argument, e.g., ‘-v’. + + • ‘value’ The value. Should not be accessed directly. + + • ‘init-value’ Function that is responsible for setting the object’s + value. If bound, then this is called with the object as the only + argument. Usually this is not bound, in which case the object’s + primary ‘transient-init-value’ method is called instead. + + • ‘unsavable’ Whether the value of the suffix is not saved as part of + the prefixes. + + • ‘multi-value’ For options, whether the option can have multiple + values. If this is non-‘nil’, then the values are read using + ‘completing-read-multiple’ by default and if you specify your own + reader, then it should read the values using that function or + similar. + + Supported non-‘nil’ values are: + + • Use ‘rest’ for an option that can have multiple values. This + is useful e.g., for an ‘--’ argument that indicates that all + remaining arguments are files (such as ‘git log -- file1 + file2’). + + In the list returned by ‘transient-args’ such an option and + its values are represented by a single list of the form + ‘(ARGUMENT . VALUES)’. + + • Use ‘repeat’ for an option that can be specified multiple + times. + + In the list returned by ‘transient-args’ each instance of the + option and its value appears separately in the usual from, for + example: ‘("--another-argument" "--option=first" + "--option=second")’. + + In both cases the option’s values have to be specified in the + default value of a prefix using the same format as returned by + ‘transient-args’, e.g., ‘("--other" "--o=1" "--o=2" ("--" "f1" + "f2"))’. + + • ‘always-read’ For options, whether to read a value on every + invocation. If this is nil, then options that have a value are + simply unset and have to be invoked a second time to set a new + value. + + • ‘allow-empty’ For options, whether the empty string is a valid + value. + + • ‘history-key’ The key used to store the history. This defaults to + the command name. This is useful when multiple infixes should + share the same history because their values are of the same kind. + + • ‘reader’ The function used to read the value of an infix. Not used + for switches. The function takes three arguments, PROMPT, + INITIAL-INPUT and HISTORY, and must return a string. + + • ‘prompt’ The prompt used when reading the value, either a string or + a function that takes the object as the only argument and which + returns a prompt string. + + • ‘choices’ A list of valid values. How exactly that is used depends + on the class of the object. + +Slots of ‘transient-variable’ +----------------------------- + + • ‘variable’ The variable. + +Slots of ‘transient-switches’ +----------------------------- + + • ‘argument-format’ The display format. Must contain ‘%s’, one of + the ‘choices’ is substituted for that. E.g., ‘--%s-order’. + + • ‘argument-regexp’ The regexp used to match any one of the switches. + E.g., ‘\\(--\\(topo\\|author-date\\|date\\)-order\\)’. + + +File: transient.info, Node: Predicate Slots, Prev: Suffix Slots, Up: Classes and Methods + +5.8 Predicate Slots +=================== + +Suffix and group objects share some predicate slots that control whether +a group or suffix should be available depending on some state. Only one +of these slots can be used at the same time. It is undefined what +happens if you use more than one. + + • ‘if’ Enable if predicate returns non-‘nil’. + • ‘if-not’ Enable if predicate returns ‘nil’. + • ‘if-non-nil’ Enable if variable’s value is non-‘nil’. + • ‘if-nil’ Enable if variable’s value is ‘nil’. + • ‘if-mode’ Enable if major-mode matches value. + • ‘if-not-mode’ Enable if major-mode does not match value. + • ‘if-derived’ Enable if major-mode derives from value. + • ‘if-not-derived’ Enable if major-mode does not derive from value. + + One more slot is shared between group and suffix classes, ‘level’. +Like the slots documented above, it is a predicate, but it is used for a +different purpose. The value has to be an integer between 1 and 7. +‘level’ controls whether a suffix or a group should be available +depending on user preference. See *note Enabling and Disabling +Suffixes::. + + +File: transient.info, Node: Related Abstractions and Packages, Next: FAQ, Prev: Classes and Methods, Up: Top + +6 Related Abstractions and Packages +*********************************** + +* Menu: + +* Comparison With Prefix Keys and Prefix Arguments:: +* Comparison With Other Packages:: + + +File: transient.info, Node: Comparison With Prefix Keys and Prefix Arguments, Next: Comparison With Other Packages, Up: Related Abstractions and Packages + +6.1 Comparison With Prefix Keys and Prefix Arguments +==================================================== + +While transient commands were inspired by regular prefix keys and prefix +arguments, they are also quite different and much more complex. + + The following diagrams illustrate some of the differences. + + • ‘(c)’ represents a return to the command loop. + • ‘(+)’ represents the user’s choice to press one key or another. + • ‘{WORD}’ are possible behaviors. + • ‘{NUMBER}’ is a footnote. + +Regular Prefix Commands +----------------------- + +See *note (elisp)Prefix Keys::. + + ,--> command1 --> (c) + | + (c)-(+)-> prefix command or key --+--> command2 --> (c) + | + `--> command3 --> (c) + +Regular Prefix Arguments +------------------------ + +See *note (elisp)Prefix Command Arguments::. + + ,----------------------------------, + | | + v | + (c)-(+)---> prefix argument command --(c)-(+)-> any command --> (c) + | ^ | + | | | + `-- sets or changes --, ,-- maybe used --' | + | | | + v | | + prefix argument state | + ^ | + | | + `-------- discards --------' + +Transients +---------- + +(∩`-´)⊃━☆゚.*・。゚ + + This diagram ignores the infix value and external state: + + (c) + | ,- {stay} ------<-,-<------------<-,-<---, + (+) | | | | + | | | | | + | | ,--> infix1 --| | | + | | | | | | + | | |--> infix2 --| | | + v v | | | | + prefix -(c)-(+)-> infix3 --' ^ | + | | | + |---------------> suffix1 -->--| | + | | | + |---------------> suffix2 ----{1}------> {exit} --> (c) + | | + |---------------> suffix3 -------------> {exit} --> (c) + | | + `--> any command --{2}-> {warn} -->--| + | | + |--> {noop} -->--| + | | + |--> {call} -->--' + | + `------------------> {exit} --> (c) + + This diagram takes the infix value into account to an extend, while +still ignoring external state: + + (c) + | ,- {stay} ------<-,-<------------<-,-<---, + (+) | | | | + | | | | | + | | ,--> infix1 --| | | + | | | | | | | + | | ,--> infix2 --| | | + v v | | | | | + prefix -(c)-(+)-> infix3 --' | | + | | ^ | + | | | | + |---------------> suffix1 -->--| | + | | ^ | | + | | | | | + |---------------> suffix2 ----{1}------> {exit} --> (c) + | | ^ | | + | | | | v + | | | | | + |---------------> suffix3 -------------> {exit} --> (c) + | | ^ | | + | sets | | v + | | maybe | | + | | used | | + | | | | | + | | infix --' | | + | `---> value | | + | ^ | | + | | | | + | hides | | + | | | | + | `--------------------------<---| + | | | + `--> any command --{2}-> {warn} -->--| | + | | | + |--> {noop} -->--| | + | | | + |--> {call} -->--' ^ + | | + `------------------> {exit} --> (c) + + This diagram provides more information about the infix value and also +takes external state into account. + + ,----sets--- "anything" + | + v + ,---------> external + | state + | | | + | initialized | ☉‿⚆ + sets from | + | | maybe + | ,----------' used + | | | + (c) | | v + | ,- {stay} --|---<-,-<------|-----<-,-<---, + (+) | | | | | | | + | | | v | | | | + | | ,--> infix1 --| | | | + | | | | | | | | | + | | | | v | | | | + | | ,--> infix2 --| | | | + | | | | ^ | | | | + v v | | | | | | | + prefix -(c)-(+)-> infix3 --' | | | + | | ^ | ^ | + | | | v | | + |---------------> suffix1 -->--| | + | | | ^ | | | + | | | | v | | + |---------------> suffix2 ----{1}------> {exit} --> (c) + | | | ^ | | | + | | | | | | v + | | | | v | | + |---------------> suffix3 -------------> {exit} --> (c) + | | | ^ | | + | sets | | | v + | | initialized maybe | | + | | from used | | + | | | | | | + | | `-- infix ---' | | + | `---> value -----------------------------> persistent + | ^ ^ | | across + | | | | | invocations -, + | hides | | | | + | | `----------------------------------------------' + | | | | + | `--------------------------<---| + | | | + `--> any command --{2}-> {warn} -->--| | + | | | + |--> {noop} -->--| | + | | | + |--> {call} -->--' ^ + | | + `------------------> {exit} --> (c) + + • ‘{1}’ Transients can be configured to be exited when a suffix + command is invoked. The default is to do so for all suffixes + except for those that are common to all transients and which are + used to perform tasks such as providing help and saving the value + of the infix arguments for future invocations. The behavior can + also be specified for individual suffix commands and may even + depend on state. + + • ‘{2}’ Transients can be configured to allow the user to invoke + non-suffix commands. The default is to not allow that and instead + warn the user. + + Despite already being rather complex, even the last diagram leaves +out many details. Most importantly it implies that the decision whether +to remain transient is made later than it actually is made (for the most +part a function on ‘pre-command-hook’ is responsible). But such +implementation details are of little relevance to users and are covered +elsewhere. + + +File: transient.info, Node: Comparison With Other Packages, Prev: Comparison With Prefix Keys and Prefix Arguments, Up: Related Abstractions and Packages + +6.2 Comparison With Other Packages +================================== + +Magit-Popup +----------- + +Transient is the successor to Magit-Popup (see *note +(magit-popup)Top::). + + One major difference between these two implementations of the same +ideas is that while Transient uses transient keymaps and embraces the +command-loop, Magit-Popup implemented an inferior mechanism that does +not use transient keymaps and that instead of using the command-loop +implements a naive alternative based on ‘read-char’. + + Magit-Popup does not use classes and generic functions and defining a +new command type is near impossible as it involves adding hard-coded +special-cases to many functions. Because of that only a single new type +was added, which was not already part of Magit-Popup’s initial release. + + A lot of things are hard-coded in Magit-Popup. One random example is +that the key bindings for switches must begin with ‘-’ and those for +options must begin with ‘=’. + +Hydra +----- + +Hydra (see ) is another package that +provides features similar to those of Transient. + + Both packages use transient keymaps to make a set of commands +temporarily available and show the available commands in a popup buffer. + + A Hydra “body” is equivalent to a Transient “prefix” and a Hydra +“head” is equivalent to a Transient “suffix”. Hydra has no equivalent +of a Transient “infix”. + + Both hydras and transients can be used as simple command dispatchers. +Used like this they are similar to regular prefix commands and prefix +keys, except that the available commands are shown in the popup buffer. + + (Another package that does this is ‘which-key’. It does so +automatically for any incomplete key sequence. The advantage of that +approach is that no additional work is necessary; the disadvantage is +that the available commands are not organized semantically.) + + Both Hydra and Transient provide features that go beyond simple +command dispatchers: + + • Invoking a command from a hydra does not necessarily exit the + hydra. That makes it possible to invoke the same command again, + but using a shorter key sequence (i.e., the key that was used to + enter the hydra does not have to be pressed again). + + Transient supports that too, but for now this feature is not a + focus and the interface is a bit more complicated. A very basic + example using the current interface: + + (transient-define-prefix outline-navigate () + :transient-suffix 'transient--do-stay + :transient-non-suffix 'transient--do-warn + [("p" "previous visible heading" outline-previous-visible-heading) + ("n" "next visible heading" outline-next-visible-heading)]) + + • Transient supports infix arguments; values that are set by infix + commands and then consumed by the invoked suffix command(s). + + To my knowledge, Hydra does not support that. + + Both packages make it possible to specify how exactly the available +commands are outlined: + + • With Hydra this is often done using an explicit format string, + which gives authors a lot of flexibility and makes it possible to + do fancy things. + + The downside of this is that it becomes harder for a user to add + additional commands to an existing hydra and to change key + bindings. + + • Transient allows the author of a transient to organize the commands + into groups and the use of generic functions allows authors of + transients to control exactly how a certain command type is + displayed. + + However while Transient supports giving sections a heading it does + not currently support giving the displayed information more + structure by, for example, using box-drawing characters. + + That could be implemented by defining a new group class, which lets + the author specify a format string. It should be possible to + implement that without modifying any existing code, but it does not + currently exist. + + +File: transient.info, Node: FAQ, Next: Keystroke Index, Prev: Related Abstractions and Packages, Up: Top + +Appendix A FAQ +************** + +A.1 Can I control how the popup buffer is displayed? +==================================================== + +Yes, see ‘transient-display-buffer-action’ in *note Configuration::. + +A.2 Why did some of the key bindings change? +============================================ + +You may have noticed that the bindings for some of the common commands +do *not* have the prefix ‘C-x’ and that furthermore some of these +commands are grayed out while others are not. That unfortunately is a +bit confusing if the section of common commands is not shown +permanently, making the following explanation necessary. + + The purpose of usually hiding that section but showing it after the +user pressed the respective prefix key is to conserve space and not +overwhelm users with too much noise, while allowing the user to quickly +list common bindings on demand. + + That however should not keep us from using the best possible key +bindings. The bindings that do use a prefix do so to avoid wasting too +many non-prefix bindings, keeping them available for use in individual +transients. The bindings that do not use a prefix and that are *not* +grayed out are very important bindings that are *always* available, even +when invoking the “common command key prefix” or *any other* +transient-specific prefix. The non-prefix keys that *are* grayed out +however, are not available when any incomplete prefix key sequence is +active. They do not use the “common command key prefix” because it is +likely that users want to invoke them several times in a row and e.g., +‘M-p M-p M-p’ is much more convenient than ‘C-x M-p C-x M-p C-x M-p’. + + You may also have noticed that the “Set” command is bound to ‘C-x s’, +while Magit-Popup used to bind ‘C-c C-c’ instead. I have seen several +users praise the latter binding (sic), so I did not change it +willy-nilly. The reason that I changed it is that using different +prefix keys for different common commands, would have made the temporary +display of the common commands even more confusing, i.e., after pressing +‘C-c’ all the bindings that begin with the ‘C-x’ prefix would be grayed +out. + + Using a single prefix for common commands key means that all other +potential prefix keys can be used for transient-specific commands +*without* the section of common commands also popping up. ‘C-c’ in +particular is a prefix that I want to (and already do) use for Magit, +and also using that for a common command would prevent me from doing so. + + (Also see the next question.) + +A.3 Why does ‘q’ not quit popups anymore? +========================================= + +I agree that ‘q’ is a good binding for commands that quit something. +This includes quitting whatever transient is currently active, but it +also includes quitting whatever it is that some specific transient is +controlling. The transient ‘magit-blame’ for example binds ‘q’ to the +command that turns ‘magit-blame-mode’ off. + + So I had to decide if ‘q’ should quit the active transient (like +Magit-Popup used to) or whether ‘C-g’ should do that instead, so that +‘q’ could be bound in individual transient to whatever commands make +sense for them. Because all other letters are already reserved for use +by individual transients, I have decided to no longer make an exception +for ‘q’. + + If you want to get ‘q’’s old binding back then you can do so. Doing +that is a bit more complicated than changing a single key binding, so I +have implemented a function, ‘transient-bind-q-to-quit’ that makes the +necessary changes. See its doc string for more information. + + +File: transient.info, Node: Keystroke Index, Next: Command and Function Index, Prev: FAQ, Up: Top + +Appendix B Keystroke Index +************************** + +[index] +* Menu: + +* C-g: Aborting and Resuming Transients. + (line 27) +* C-g <1>: Aborting and Resuming Transients. + (line 27) +* C-h: Getting Help for Suffix Commands. + (line 11) +* C-M-n: Using History. (line 18) +* C-M-p: Using History. (line 13) +* C-q: Aborting and Resuming Transients. + (line 36) +* C-x C-k: Saving Values. (line 29) +* C-x C-s: Saving Values. (line 25) +* C-x l: Enabling and Disabling Suffixes. + (line 43) +* C-x n: Using History. (line 18) +* C-x p: Using History. (line 13) +* C-x s: Saving Values. (line 21) +* C-x t: Common Suffix Commands. + (line 18) +* C-z: Aborting and Resuming Transients. + (line 41) + + +File: transient.info, Node: Command and Function Index, Next: Variable Index, Prev: Keystroke Index, Up: Top + +Appendix C Command and Function Index +************************************* + +[index] +* Menu: + +* transient--do-call: Transient State. (line 99) +* transient--do-exit: Transient State. (line 91) +* transient--do-noop: Transient State. (line 147) +* transient--do-quit-all: Transient State. (line 158) +* transient--do-quit-one: Transient State. (line 153) +* transient--do-recurse: Transient State. (line 106) +* transient--do-replace: Transient State. (line 118) +* transient--do-return: Transient State. (line 94) +* transient--do-stay: Transient State. (line 83) +* transient--do-suspend: Transient State. (line 125) +* transient--do-suspend <1>: Transient State. (line 163) +* transient--do-warn: Transient State. (line 144) +* transient--history-init: Prefix Classes. (line 10) +* transient--insert-group: Group Methods. (line 19) +* transient-append-suffix: Modifying Existing Transients. + (line 44) +* transient-arg-value: Using Infix Arguments. + (line 31) +* transient-args: Using Infix Arguments. + (line 22) +* transient-define-argument: Defining Suffix and Infix Commands. + (line 61) +* transient-define-infix: Defining Suffix and Infix Commands. + (line 26) +* transient-define-prefix: Defining Transients. (line 13) +* transient-define-suffix: Defining Suffix and Infix Commands. + (line 9) +* transient-format: Suffix Format Methods. + (line 6) +* transient-format-description: Suffix Format Methods. + (line 18) +* transient-format-key: Suffix Format Methods. + (line 14) +* transient-format-value: Suffix Format Methods. + (line 22) +* transient-get-suffix: Modifying Existing Transients. + (line 66) +* transient-help: Getting Help for Suffix Commands. + (line 11) +* transient-history-next: Using History. (line 18) +* transient-history-prev: Using History. (line 13) +* transient-infix-read: Suffix Value Methods. + (line 16) +* transient-infix-set: Suffix Value Methods. + (line 36) +* transient-infix-value: Suffix Value Methods. + (line 39) +* transient-init-scope: Suffix Value Methods. + (line 52) +* transient-init-value: Suffix Value Methods. + (line 6) +* transient-insert-suffix: Modifying Existing Transients. + (line 42) +* transient-prompt: Suffix Value Methods. + (line 32) +* transient-quit-all: Aborting and Resuming Transients. + (line 36) +* transient-quit-one: Aborting and Resuming Transients. + (line 27) +* transient-quit-seq: Aborting and Resuming Transients. + (line 27) +* transient-remove-suffix: Modifying Existing Transients. + (line 63) +* transient-replace-suffix: Modifying Existing Transients. + (line 59) +* transient-resume: Aborting and Resuming Transients. + (line 53) +* transient-save: Saving Values. (line 25) +* transient-save <1>: Saving Values. (line 29) +* transient-scroll-down: Other Commands. (line 17) +* transient-scroll-up: Other Commands. (line 12) +* transient-set: Saving Values. (line 21) +* transient-set-level: Enabling and Disabling Suffixes. + (line 43) +* transient-setup-children: Group Methods. (line 6) +* transient-show-help: Suffix Format Methods. + (line 26) +* transient-suffix-put: Modifying Existing Transients. + (line 70) +* transient-suffixes: Using Infix Arguments. + (line 38) +* transient-suspend: Aborting and Resuming Transients. + (line 41) +* transient-toggle-common: Common Suffix Commands. + (line 18) + + +File: transient.info, Node: Variable Index, Next: Concept Index, Prev: Command and Function Index, Up: Top + +Appendix D Variable Index +************************* + +[index] +* Menu: + +* transient-align-variable-pitch: Configuration. (line 181) +* transient-current-command: Using Infix Arguments. + (line 57) +* transient-current-prefix: Using Infix Arguments. + (line 52) +* transient-current-suffixes: Using Infix Arguments. + (line 44) +* transient-default-level: Enabling and Disabling Suffixes. + (line 33) +* transient-detect-key-conflicts: Configuration. (line 206) +* transient-display-buffer-action: Configuration. (line 51) +* transient-enable-popup-navigation: Configuration. (line 36) +* transient-force-fixed-pitch: Configuration. (line 194) +* transient-force-single-column: Configuration. (line 93) +* transient-hide-during-minibuffer-read: Configuration. (line 177) +* transient-highlight-higher-levels: Configuration. (line 219) +* transient-highlight-mismatched-keys: Configuration. (line 131) +* transient-history-file: Using History. (line 33) +* transient-history-limit: Using History. (line 37) +* transient-levels-file: Enabling and Disabling Suffixes. + (line 38) +* transient-mode-line-format: Configuration. (line 102) +* transient-read-with-initial-input: Configuration. (line 170) +* transient-semantic-coloring: Configuration. (line 118) +* transient-show-common-commands: Common Suffix Commands. + (line 23) +* transient-show-popup: Configuration. (line 15) +* transient-substitute-key-function: Configuration. (line 149) +* transient-values-file: Saving Values. (line 31) + + +File: transient.info, Node: Concept Index, Next: GNU General Public License, Prev: Variable Index, Up: Top + +Appendix E Concept Index +************************ + +[index] +* Menu: + +* aborting transients: Aborting and Resuming Transients. + (line 6) +* classes and methods: Classes and Methods. (line 6) +* command dispatchers: Introduction. (line 70) +* common suffix commands: Common Suffix Commands. + (line 6) +* defining infix commands: Defining Suffix and Infix Commands. + (line 6) +* defining suffix commands: Defining Suffix and Infix Commands. + (line 6) +* disabling suffixes: Enabling and Disabling Suffixes. + (line 6) +* enabling suffixes: Enabling and Disabling Suffixes. + (line 6) +* getting help: Getting Help for Suffix Commands. + (line 6) +* group specifications: Group Specifications. (line 6) +* invoking transients: Invoking Transients. (line 6) +* levels: Enabling and Disabling Suffixes. + (line 10) +* modifying existing transients: Modifying Existing Transients. + (line 6) +* quit transient: Aborting and Resuming Transients. + (line 6) +* resuming transients: Aborting and Resuming Transients. + (line 6) +* saving values of arguments: Saving Values. (line 6) +* scope of a transient: Defining Transients. (line 43) +* suffix specifications: Suffix Specifications. + (line 6) +* transient prefix command: Introduction. (line 13) +* transient state: Transient State. (line 6) +* transient-level: Enabling and Disabling Suffixes. + (line 15) +* value history: Using History. (line 6) + + +File: transient.info, Node: GNU General Public License, Prev: Concept Index, Up: Top + +Appendix F GNU General Public License +************************************* + + Version 3, 29 June 2007 + + Copyright © 2007 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies of this + license document, but changing it is not allowed. + +Preamble +======== + +The GNU General Public License is a free, copyleft license for software +and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program—to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers’ and authors’ protection, the GPL clearly explains +that there is no warranty for this free software. For both users’ and +authors’ sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users’ freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS +==================== + + 0. Definitions. + + “This License” refers to version 3 of the GNU General Public + License. + + “Copyright” also means copyright-like laws that apply to other + kinds of works, such as semiconductor masks. + + “The Program” refers to any copyrightable work licensed under this + License. Each licensee is addressed as “you”. “Licensees” and + “recipients” may be individuals or organizations. + + To “modify” a work means to copy from or adapt all or part of the + work in a fashion requiring copyright permission, other than the + making of an exact copy. The resulting work is called a “modified + version” of the earlier work or a work “based on” the earlier work. + + A “covered work” means either the unmodified Program or a work + based on the Program. + + To “propagate” a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on + a computer or modifying a private copy. Propagation includes + copying, distribution (with or without modification), making + available to the public, and in some countries other activities as + well. + + To “convey” a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user + through a computer network, with no transfer of a copy, is not + conveying. + + An interactive user interface displays “Appropriate Legal Notices” + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to + the extent that warranties are provided), that licensees may convey + the work under this License, and how to view a copy of this + License. If the interface presents a list of user commands or + options, such as a menu, a prominent item in the list meets this + criterion. + + 1. Source Code. + + The “source code” for a work means the preferred form of the work + for making modifications to it. “Object code” means any non-source + form of a work. + + A “Standard Interface” means an interface that either is an + official standard defined by a recognized standards body, or, in + the case of interfaces specified for a particular programming + language, one that is widely used among developers working in that + language. + + The “System Libraries” of an executable work include anything, + other than the work as a whole, that (a) is included in the normal + form of packaging a Major Component, but which is not part of that + Major Component, and (b) serves only to enable use of the work with + that Major Component, or to implement a Standard Interface for + which an implementation is available to the public in source code + form. A “Major Component”, in this context, means a major + essential component (kernel, window system, and so on) of the + specific operating system (if any) on which the executable work + runs, or a compiler used to produce the work, or an object code + interpreter used to run it. + + The “Corresponding Source” for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts + to control those activities. However, it does not include the + work’s System Libraries, or general-purpose tools or generally + available free programs which are used unmodified in performing + those activities but which are not part of the work. For example, + Corresponding Source includes interface definition files associated + with source files for the work, and the source code for shared + libraries and dynamically linked subprograms that the work is + specifically designed to require, such as by intimate data + communication or control flow between those subprograms and other + parts of the work. + + The Corresponding Source need not include anything that users can + regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running + a covered work is covered by this License only if the output, given + its content, constitutes a covered work. This License acknowledges + your rights of fair use or other equivalent, as provided by + copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise + remains in force. You may convey covered works to others for the + sole purpose of having them make modifications exclusively for you, + or provide you with facilities for running those works, provided + that you comply with the terms of this License in conveying all + material for which you do not control copyright. Those thus making + or running the covered works for you must do so exclusively on your + behalf, under your direction and control, on terms that prohibit + them from making any copies of your copyrighted material outside + their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section + 10 makes it unnecessary. + + 3. Protecting Users’ Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under + article 11 of the WIPO copyright treaty adopted on 20 December + 1996, or similar laws prohibiting or restricting circumvention of + such measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such + circumvention is effected by exercising rights under this License + with respect to the covered work, and you disclaim any intention to + limit operation or modification of the work as a means of + enforcing, against the work’s users, your or third parties’ legal + rights to forbid circumvention of technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program’s source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the + code; keep intact all notices of the absence of any warranty; and + give all recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these + conditions: + + a. The work must carry prominent notices stating that you + modified it, and giving a relevant date. + + b. The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in + section 4 to “keep intact all notices”. + + c. You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable + section 7 additional terms, to the whole of the work, and all + its parts, regardless of how they are packaged. This License + gives no permission to license the work in any other way, but + it does not invalidate such permission if you have separately + received it. + + d. If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has + interactive interfaces that do not display Appropriate Legal + Notices, your work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered + work, and which are not combined with it such as to form a larger + program, in or on a volume of a storage or distribution medium, is + called an “aggregate” if the compilation and its resulting + copyright are not used to limit the access or legal rights of the + compilation’s users beyond what the individual works permit. + Inclusion of a covered work in an aggregate does not cause this + License to apply to the other parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this + License, in one of these ways: + + a. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that + product model, to give anyone who possesses the object code + either (1) a copy of the Corresponding Source for all the + software in the product that is covered by this License, on a + durable physical medium customarily used for software + interchange, for a price no more than your reasonable cost of + physically performing this conveying of source, or (2) access + to copy the Corresponding Source from a network server at no + charge. + + c. Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, + and only if you received the object code with such an offer, + in accord with subsection 6b. + + d. Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to + the Corresponding Source in the same way through the same + place at no further charge. You need not require recipients + to copy the Corresponding Source along with the object code. + If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by + you or a third party) that supports equivalent copying + facilities, provided you maintain clear directions next to the + object code saying where to find the Corresponding Source. + Regardless of what server hosts the Corresponding Source, you + remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + + e. Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the + general public at no charge under subsection 6d. + + A separable portion of the object code, whose source code is + excluded from the Corresponding Source as a System Library, need + not be included in conveying the object code work. + + A “User Product” is either (1) a “consumer product”, which means + any tangible personal property which is normally used for personal, + family, or household purposes, or (2) anything designed or sold for + incorporation into a dwelling. In determining whether a product is + a consumer product, doubtful cases shall be resolved in favor of + coverage. For a particular product received by a particular user, + “normally used” refers to a typical or common use of that class of + product, regardless of the status of the particular user or of the + way in which the particular user actually uses, or expects or is + expected to use, the product. A product is a consumer product + regardless of whether the product has substantial commercial, + industrial or non-consumer uses, unless such uses represent the + only significant mode of use of the product. + + “Installation Information” for a User Product means any methods, + procedures, authorization keys, or other information required to + install and execute modified versions of a covered work in that + User Product from a modified version of its Corresponding Source. + The information must suffice to ensure that the continued + functioning of the modified object code is in no case prevented or + interfered with solely because modification has been made. + + If you convey an object code work under this section in, or with, + or specifically for use in, a User Product, and the conveying + occurs as part of a transaction in which the right of possession + and use of the User Product is transferred to the recipient in + perpetuity or for a fixed term (regardless of how the transaction + is characterized), the Corresponding Source conveyed under this + section must be accompanied by the Installation Information. But + this requirement does not apply if neither you nor any third party + retains the ability to install modified object code on the User + Product (for example, the work has been installed in ROM). + + The requirement to provide Installation Information does not + include a requirement to continue to provide support service, + warranty, or updates for a work that has been modified or installed + by the recipient, or for the User Product in which it has been + modified or installed. Access to a network may be denied when the + modification itself materially and adversely affects the operation + of the network or violates the rules and protocols for + communication across the network. + + Corresponding Source conveyed, and Installation Information + provided, in accord with this section must be in a format that is + publicly documented (and with an implementation available to the + public in source code form), and must require no special password + or key for unpacking, reading or copying. + + 7. Additional Terms. + + “Additional permissions” are terms that supplement the terms of + this License by making exceptions from one or more of its + conditions. Additional permissions that are applicable to the + entire Program shall be treated as though they were included in + this License, to the extent that they are valid under applicable + law. If additional permissions apply only to part of the Program, + that part may be used separately under those permissions, but the + entire Program remains governed by this License without regard to + the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part + of it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material + you add to a covered work, you may (if authorized by the copyright + holders of that material) supplement the terms of this License with + terms: + + a. Disclaiming warranty or limiting liability differently from + the terms of sections 15 and 16 of this License; or + + b. Requiring preservation of specified reasonable legal notices + or author attributions in that material or in the Appropriate + Legal Notices displayed by works containing it; or + + c. Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked + in reasonable ways as different from the original version; or + + d. Limiting the use for publicity purposes of names of licensors + or authors of the material; or + + e. Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f. Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified + versions of it) with contractual assumptions of liability to + the recipient, for any liability that these contractual + assumptions directly impose on those licensors and authors. + + All other non-permissive additional terms are considered “further + restrictions” within the meaning of section 10. If the Program as + you received it, or any part of it, contains a notice stating that + it is governed by this License along with a term that is a further + restriction, you may remove that term. If a license document + contains a further restriction but permits relicensing or conveying + under this License, you may add to a covered work material governed + by the terms of that license document, provided that the further + restriction does not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in + the form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights + under this License (including any patent licenses granted under the + third paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, you do not qualify to receive new licenses + for the same material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer + transmission to receive a copy likewise does not require + acceptance. However, nothing other than this License grants you + permission to propagate or modify any covered work. These actions + infringe copyright if you do not accept this License. Therefore, + by modifying or propagating a covered work, you indicate your + acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not + responsible for enforcing compliance by third parties with this + License. + + An “entity transaction” is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a + covered work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party’s predecessor in interest had or + could give under the previous paragraph, plus a right to possession + of the Corresponding Source of the work from the predecessor in + interest, if the predecessor has it or can get it with reasonable + efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you + may not impose a license fee, royalty, or other charge for exercise + of rights granted under this License, and you may not initiate + litigation (including a cross-claim or counterclaim in a lawsuit) + alleging that any patent claim is infringed by making, using, + selling, offering for sale, or importing the Program or any portion + of it. + + 11. Patents. + + A “contributor” is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. + The work thus licensed is called the contributor’s “contributor + version”. + + A contributor’s “essential patent claims” are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, + permitted by this License, of making, using, or selling its + contributor version, but do not include claims that would be + infringed only as a consequence of further modification of the + contributor version. For purposes of this definition, “control” + includes the right to grant patent sublicenses in a manner + consistent with the requirements of this License. + + Each contributor grants you a non-exclusive, worldwide, + royalty-free patent license under the contributor’s essential + patent claims, to make, use, sell, offer for sale, import and + otherwise run, modify and propagate the contents of its contributor + version. + + In the following three paragraphs, a “patent license” is any + express agreement or commitment, however denominated, not to + enforce a patent (such as an express permission to practice a + patent or covenant not to sue for patent infringement). To “grant” + such a patent license to a party means to make such an agreement or + commitment not to enforce a patent against the party. + + If you convey a covered work, knowingly relying on a patent + license, and the Corresponding Source of the work is not available + for anyone to copy, free of charge and under the terms of this + License, through a publicly available network server or other + readily accessible means, then you must either (1) cause the + Corresponding Source to be so available, or (2) arrange to deprive + yourself of the benefit of the patent license for this particular + work, or (3) arrange, in a manner consistent with the requirements + of this License, to extend the patent license to downstream + recipients. “Knowingly relying” means you have actual knowledge + that, but for the patent license, your conveying the covered work + in a country, or your recipient’s use of the covered work in a + country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, + modify or convey a specific copy of the covered work, then the + patent license you grant is automatically extended to all + recipients of the covered work and works based on it. + + A patent license is “discriminatory” if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that + are specifically granted under this License. You may not convey a + covered work if you are a party to an arrangement with a third + party that is in the business of distributing software, under which + you make payment to the third party based on the extent of your + activity of conveying the work, and under which the third party + grants, to any of the parties who would receive the covered work + from you, a discriminatory patent license (a) in connection with + copies of the covered work conveyed by you (or copies made from + those copies), or (b) primarily for and in connection with specific + products or compilations that contain the covered work, unless you + entered into that arrangement, or that patent license was granted, + prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others’ Freedom. + + If conditions are imposed on you (whether by court order, agreement + or otherwise) that contradict the conditions of this License, they + do not excuse you from the conditions of this License. If you + cannot convey a covered work so as to satisfy simultaneously your + obligations under this License and any other pertinent obligations, + then as a consequence you may not convey it at all. For example, + if you agree to terms that obligate you to collect a royalty for + further conveying from those to whom you convey the Program, the + only way you could satisfy both those terms and this License would + be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a + single combined work, and to convey the resulting work. The terms + of this License will continue to apply to the part which is the + covered work, but the special requirements of the GNU Affero + General Public License, section 13, concerning interaction through + a network will apply to the combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new + versions of the GNU General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU + General Public License “or any later version” applies to it, you + have the option of following the terms and conditions either of + that numbered version or of any later version published by the Free + Software Foundation. If the Program does not specify a version + number of the GNU General Public License, you may choose any + version ever published by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that + proxy’s public statement of acceptance of a version permanently + authorizes you to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE + COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE + RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. + SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES + AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE + THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA + BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely + approximates an absolute waiver of all civil liability in + connection with the Program, unless a warranty or assumption of + liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS +=========================== + +How to Apply These Terms to Your New Programs +============================================= + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least the +“copyright” line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) YEAR NAME OF AUTHOR + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper +mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + PROGRAM Copyright (C) YEAR NAME OF AUTHOR + This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’. + This is free software, and you are welcome to redistribute it + under certain conditions; type ‘show c’ for details. + + The hypothetical commands ‘show w’ and ‘show c’ should show the +appropriate parts of the General Public License. Of course, your +program’s commands might be different; for a GUI interface, you would +use an “about box”. + + You should also get your employer (if you work as a programmer) or +school, if any, to sign a “copyright disclaimer” for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + + The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . + + + +Tag Table: +Node: Top761 +Node: Introduction3707 +Node: Usage9548 +Node: Invoking Transients9916 +Node: Aborting and Resuming Transients10995 +Node: Common Suffix Commands13606 +Node: Saving Values15442 +Ref: Saving Values-Footnote-116811 +Node: Using History17004 +Node: Getting Help for Suffix Commands18578 +Node: Enabling and Disabling Suffixes19946 +Node: Other Commands23014 +Node: Configuration23990 +Ref: Essential Options24270 +Ref: Accessibility Options27923 +Ref: Auxiliary Options28246 +Ref: Developer Options32973 +Node: Modifying Existing Transients34221 +Node: Defining New Commands38121 +Node: Defining Transients38457 +Node: Binding Suffix and Infix Commands40893 +Node: Group Specifications41751 +Node: Suffix Specifications46094 +Node: Defining Suffix and Infix Commands50368 +Node: Using Infix Arguments53574 +Node: Transient State56408 +Ref: Pre-commands for Infixes60371 +Ref: Pre-commands for Suffixes60642 +Ref: Pre-commands for Non-Suffixes62415 +Ref: Special Pre-Commands63030 +Node: Classes and Methods63538 +Node: Group Classes65752 +Node: Group Methods67679 +Node: Prefix Classes68932 +Node: Suffix Classes70023 +Node: Suffix Methods72270 +Node: Suffix Value Methods72591 +Node: Suffix Format Methods75349 +Node: Prefix Slots76798 +Ref: Internal Prefix Slots78077 +Node: Suffix Slots79334 +Ref: Slots of transient-suffix79702 +Ref: Slots of transient-infix80556 +Ref: Slots of transient-variable83676 +Ref: Slots of transient-switches83778 +Node: Predicate Slots84141 +Node: Related Abstractions and Packages85406 +Node: Comparison With Prefix Keys and Prefix Arguments85693 +Ref: Regular Prefix Commands86378 +Ref: Regular Prefix Arguments86726 +Ref: Transients87695 +Node: Comparison With Other Packages95966 +Ref: Magit-Popup96197 +Ref: Hydra97104 +Node: FAQ100160 +Ref: Can I control how the popup buffer is displayed?100303 +Ref: Why did some of the key bindings change?100484 +Ref: Why does q not quit popups anymore?102837 +Node: Keystroke Index103930 +Node: Command and Function Index105648 +Node: Variable Index111947 +Node: Concept Index114220 +Node: GNU General Public License116964 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/use-package-20220819.553/dir b/code/elpa/use-package-20220819.553/dir new file mode 100644 index 0000000..651b05d --- /dev/null +++ b/code/elpa/use-package-20220819.553/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* use-package: (use-package). Declarative package configuration for Emacs. diff --git a/code/elpa/use-package-20220819.553/use-package-autoloads.el b/code/elpa/use-package-20220819.553/use-package-autoloads.el new file mode 100644 index 0000000..fcb6095 --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-autoloads.el @@ -0,0 +1,230 @@ +;;; use-package-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "use-package-bind-key" "use-package-bind-key.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from use-package-bind-key.el + +(autoload 'use-package-autoload-keymap "use-package-bind-key" "\ +Loads PACKAGE and then binds the key sequence used to invoke +this function to KEYMAP-SYMBOL. It then simulates pressing the +same key sequence a again, so that the next key pressed is routed +to the newly loaded keymap. + +This function supports use-package's :bind-keymap keyword. It +works by binding the given key sequence to an invocation of this +function for a particular keymap. The keymap is expected to be +defined by the package. In this way, loading the package is +deferred until the prefix key sequence is pressed. + +\(fn KEYMAP-SYMBOL PACKAGE OVERRIDE)" nil nil) + +(autoload 'use-package-normalize-binder "use-package-bind-key" "\ + + +\(fn NAME KEYWORD ARGS)" nil nil) + +(defalias 'use-package-normalize/:bind 'use-package-normalize-binder) + +(defalias 'use-package-normalize/:bind* 'use-package-normalize-binder) + +(defalias 'use-package-autoloads/:bind 'use-package-autoloads-mode) + +(defalias 'use-package-autoloads/:bind* 'use-package-autoloads-mode) + +(autoload 'use-package-handler/:bind "use-package-bind-key" "\ + + +\(fn NAME KEYWORD ARGS REST STATE &optional BIND-MACRO)" nil nil) + +(defalias 'use-package-normalize/:bind-keymap 'use-package-normalize-binder) + +(defalias 'use-package-normalize/:bind-keymap* 'use-package-normalize-binder) + +(autoload 'use-package-handler/:bind-keymap "use-package-bind-key" "\ + + +\(fn NAME KEYWORD ARGS REST STATE &optional OVERRIDE)" nil nil) + +(autoload 'use-package-handler/:bind-keymap* "use-package-bind-key" "\ + + +\(fn NAME KEYWORD ARG REST STATE)" nil nil) + +(register-definition-prefixes "use-package-bind-key" '("use-package-handler/:bind*")) + +;;;*** + +;;;### (autoloads nil "use-package-core" "use-package-core.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from use-package-core.el + +(autoload 'use-package "use-package-core" "\ +Declare an Emacs package by specifying a group of configuration options. + +For full documentation, please see the README file that came with +this file. Usage: + + (use-package package-name + [:keyword [option]]...) + +:init Code to run before PACKAGE-NAME has been loaded. +:config Code to run after PACKAGE-NAME has been loaded. Note that + if loading is deferred for any reason, this code does not + execute until the lazy load has occurred. +:preface Code to be run before everything except `:disabled'; this + can be used to define functions for use in `:if', or that + should be seen by the byte-compiler. + +:mode Form to be added to `auto-mode-alist'. +:magic Form to be added to `magic-mode-alist'. +:magic-fallback Form to be added to `magic-fallback-mode-alist'. +:interpreter Form to be added to `interpreter-mode-alist'. + +:commands Define autoloads for commands that will be defined by the + package. This is useful if the package is being lazily + loaded, and you wish to conditionally call functions in your + `:init' block that are defined in the package. +:hook Specify hook(s) to attach this package to. + +:bind Bind keys, and define autoloads for the bound commands. +:bind* Bind keys, and define autoloads for the bound commands, + *overriding all minor mode bindings*. +:bind-keymap Bind a key prefix to an auto-loaded keymap defined in the + package. This is like `:bind', but for keymaps. +:bind-keymap* Like `:bind-keymap', but overrides all minor mode bindings + +:defer Defer loading of a package -- this is implied when using + `:commands', `:bind', `:bind*', `:mode', `:magic', `:hook', + `:magic-fallback', or `:interpreter'. This can be an integer, + to force loading after N seconds of idle time, if the package + has not already been loaded. +:after Delay the use-package declaration until after the named modules + have loaded. Once load, it will be as though the use-package + declaration (without `:after') had been seen at that moment. +:demand Prevent the automatic deferred loading introduced by constructs + such as `:bind' (see `:defer' for the complete list). + +:if EXPR Initialize and load only if EXPR evaluates to a non-nil value. +:disabled The package is ignored completely if this keyword is present. +:defines Declare certain variables to silence the byte-compiler. +:functions Declare certain functions to silence the byte-compiler. +:load-path Add to the `load-path' before attempting to load the package. +:diminish Support for diminish.el (if installed). +:delight Support for delight.el (if installed). +:custom Call `Custom-set' or `set-default' with each variable + definition without modifying the Emacs `custom-file'. + (compare with `custom-set-variables'). +:custom-face Call `custom-set-faces' with each face definition. +:ensure Loads the package using package.el if necessary. +:pin Pin the package to an archive. + +\(fn NAME &rest ARGS)" nil t) + +(function-put 'use-package 'lisp-indent-function 'defun) + +(register-definition-prefixes "use-package-core" '("use-package-")) + +;;;*** + +;;;### (autoloads nil "use-package-delight" "use-package-delight.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from use-package-delight.el + +(autoload 'use-package-normalize/:delight "use-package-delight" "\ +Normalize arguments to delight. + +\(fn NAME KEYWORD ARGS)" nil nil) + +(autoload 'use-package-handler/:delight "use-package-delight" "\ + + +\(fn NAME KEYWORD ARGS REST STATE)" nil nil) + +(register-definition-prefixes "use-package-delight" '("use-package-normalize-delight")) + +;;;*** + +;;;### (autoloads nil "use-package-diminish" "use-package-diminish.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from use-package-diminish.el + +(autoload 'use-package-normalize/:diminish "use-package-diminish" "\ + + +\(fn NAME KEYWORD ARGS)" nil nil) + +(autoload 'use-package-handler/:diminish "use-package-diminish" "\ + + +\(fn NAME KEYWORD ARG REST STATE)" nil nil) + +(register-definition-prefixes "use-package-diminish" '("use-package-normalize-diminish")) + +;;;*** + +;;;### (autoloads nil "use-package-ensure" "use-package-ensure.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from use-package-ensure.el + +(autoload 'use-package-normalize/:ensure "use-package-ensure" "\ + + +\(fn NAME KEYWORD ARGS)" nil nil) + +(autoload 'use-package-handler/:ensure "use-package-ensure" "\ + + +\(fn NAME KEYWORD ENSURE REST STATE)" nil nil) + +(register-definition-prefixes "use-package-ensure" '("use-package-")) + +;;;*** + +;;;### (autoloads nil "use-package-jump" "use-package-jump.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from use-package-jump.el + +(autoload 'use-package-jump-to-package-form "use-package-jump" "\ +Attempt to find and jump to the `use-package' form that loaded +PACKAGE. This will only find the form if that form actually +required PACKAGE. If PACKAGE was previously required then this +function will jump to the file that originally required PACKAGE +instead. + +\(fn PACKAGE)" t nil) + +(register-definition-prefixes "use-package-jump" '("use-package-find-require")) + +;;;*** + +;;;### (autoloads nil "use-package-lint" "use-package-lint.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from use-package-lint.el + +(autoload 'use-package-lint "use-package-lint" "\ +Check for errors in use-package declarations. +For example, if the module's `:if' condition is met, but even +with the specified `:load-path' the module cannot be found." t nil) + +(register-definition-prefixes "use-package-lint" '("use-package-lint-declaration")) + +;;;*** + +;;;### (autoloads nil nil ("use-package-pkg.el" "use-package.el") +;;;;;; (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; use-package-autoloads.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-bind-key.el b/code/elpa/use-package-20220819.553/use-package-bind-key.el new file mode 100644 index 0000000..9642f31 --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-bind-key.el @@ -0,0 +1,179 @@ +;;; use-package-bind-key.el --- Support for the :bind/:bind-keymap keywords -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 4 Dec 2017 +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (use-package "2.4") (bind-key "2.4")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Provides support for the :bind, :bind*, :bind-keymap and :bind-keymap* +;; keywords. Note that these are currently still baked into +;; `use-package-keywords' and `use-package-deferring-keywords', although this +;; is harmless if they are never used. + +;;; Code: + +(require 'use-package-core) +(require 'bind-key) + +;;;###autoload +(defun use-package-autoload-keymap (keymap-symbol package override) + "Loads PACKAGE and then binds the key sequence used to invoke +this function to KEYMAP-SYMBOL. It then simulates pressing the +same key sequence a again, so that the next key pressed is routed +to the newly loaded keymap. + +This function supports use-package's :bind-keymap keyword. It +works by binding the given key sequence to an invocation of this +function for a particular keymap. The keymap is expected to be +defined by the package. In this way, loading the package is +deferred until the prefix key sequence is pressed." + (if (not (require package nil t)) + (use-package-error (format "Cannot load package.el: %s" package)) + (if (and (boundp keymap-symbol) + (keymapp (symbol-value keymap-symbol))) + (let* ((kv (this-command-keys-vector)) + (key (key-description kv)) + (keymap (symbol-value keymap-symbol))) + (if override + (bind-key* key keymap) + (bind-key key keymap)) + (setq unread-command-events + (mapcar (lambda (ev) (cons t ev)) + (listify-key-sequence kv)))) + (use-package-error + (format "package.el %s failed to define keymap %s" + package keymap-symbol))))) + +;;;###autoload +(defun use-package-normalize-binder (name keyword args) + (let ((arg args) + args*) + (while arg + (let ((x (car arg))) + (cond + ;; (KEY . COMMAND) + ((and (consp x) + (or (stringp (car x)) + (vectorp (car x))) + (or (use-package-recognize-function (cdr x) t #'stringp))) + (setq args* (nconc args* (list x))) + (setq arg (cdr arg))) + ;; KEYWORD + ;; :map KEYMAP + ;; :prefix-docstring STRING + ;; :prefix-map SYMBOL + ;; :prefix STRING + ;; :repeat-docstring STRING + ;; :repeat-map SYMBOL + ;; :filter SEXP + ;; :menu-name STRING + ;; :package SYMBOL + ;; :continue and :exit are used within :repeat-map + ((or (and (eq x :map) (symbolp (cadr arg))) + (and (eq x :prefix) (stringp (cadr arg))) + (and (eq x :prefix-map) (symbolp (cadr arg))) + (and (eq x :prefix-docstring) (stringp (cadr arg))) + (and (eq x :repeat-map) (symbolp (cadr arg))) + (eq x :continue) + (eq x :exit) + (and (eq x :repeat-docstring) (stringp (cadr arg))) + (eq x :filter) + (and (eq x :menu-name) (stringp (cadr arg))) + (and (eq x :package) (symbolp (cadr arg)))) + (setq args* (nconc args* (list x (cadr arg)))) + (setq arg (cddr arg))) + ((listp x) + (setq args* + (nconc args* (use-package-normalize-binder name keyword x))) + (setq arg (cdr arg))) + (t + ;; Error! + (use-package-error + (concat (symbol-name name) + " wants arguments acceptable to the `bind-keys' macro," + " or a list of such values")))))) + args*)) + +;;;; :bind, :bind* + +;;;###autoload +(defalias 'use-package-normalize/:bind 'use-package-normalize-binder) +;;;###autoload +(defalias 'use-package-normalize/:bind* 'use-package-normalize-binder) + +;; jww (2017-12-07): This is too simplistic. It will fail to determine +;; autoloads in this situation: +;; (use-package foo +;; :bind (:map foo-map (("C-a" . func)))) +;;;###autoload +(defalias 'use-package-autoloads/:bind 'use-package-autoloads-mode) +;;;###autoload +(defalias 'use-package-autoloads/:bind* 'use-package-autoloads-mode) + +;;;###autoload +(defun use-package-handler/:bind + (name _keyword args rest state &optional bind-macro) + (use-package-concat + (use-package-process-keywords name rest state) + `(,@(mapcar + #'(lambda (xs) + `(,(if bind-macro bind-macro 'bind-keys) + :package ,name ,@(use-package-normalize-commands xs))) + (use-package-split-list-at-keys :break args))))) + +(defun use-package-handler/:bind* (name keyword arg rest state) + (use-package-handler/:bind name keyword arg rest state 'bind-keys*)) + +;;;; :bind-keymap, :bind-keymap* + +;;;###autoload +(defalias 'use-package-normalize/:bind-keymap 'use-package-normalize-binder) +;;;###autoload +(defalias 'use-package-normalize/:bind-keymap* 'use-package-normalize-binder) + +;;;###autoload +(defun use-package-handler/:bind-keymap + (name _keyword args rest state &optional override) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (binding) + `(,(if override 'bind-key* 'bind-key) + ,(car binding) + #'(lambda () + (interactive) + (use-package-autoload-keymap + ',(cdr binding) ',(use-package-as-symbol name) + ,override)))) + args))) + +;;;###autoload +(defun use-package-handler/:bind-keymap* (name keyword arg rest state) + (use-package-handler/:bind-keymap name keyword arg rest state t)) + +(provide 'use-package-bind-key) + +;;; use-package-bind-key.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-core.el b/code/elpa/use-package-20220819.553/use-package-core.el new file mode 100644 index 0000000..ab35131 --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-core.el @@ -0,0 +1,1691 @@ +;;; use-package-core.el --- A configuration macro for simplifying your .emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 29 Nov 2017 +;; Version: 2.4.1 +;; Package-Requires: ((emacs "24.3")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; The `use-package' declaration macro allows you to isolate package +;; configuration in your ".emacs" in a way that is performance-oriented and, +;; well, just tidy. I created it because I have over 80 packages that I use +;; in Emacs, and things were getting difficult to manage. Yet with this +;; utility my total load time is just under 1 second, with no loss of +;; functionality! +;; +;; Please see README.md from the same repository for documentation. + +;;; Code: + +(require 'bytecomp) +(require 'cl-lib) +(require 'tabulated-list) + +(eval-and-compile + ;; Declare a synthetic theme for :custom variables. + ;; Necessary in order to avoid having those variables saved by custom.el. + (deftheme use-package)) + +(enable-theme 'use-package) +;; Remove the synthetic use-package theme from the enabled themes, so +;; iterating over them to "disable all themes" won't disable it. +(setq custom-enabled-themes (remq 'use-package custom-enabled-themes)) + +(if (and (eq emacs-major-version 24) (eq emacs-minor-version 3)) + (defsubst hash-table-keys (hash-table) + "Return a list of keys in HASH-TABLE." + (cl-loop for k being the hash-keys of hash-table collect k)) + (eval-when-compile (require 'subr-x))) + +(eval-when-compile + (require 'regexp-opt)) + +(defgroup use-package nil + "A use-package declaration for simplifying your `.emacs'." + :group 'startup) + +(defconst use-package-version "2.4.1" + "This version of use-package.") + +(defcustom use-package-keywords + '(:disabled + :load-path + :requires + :defines + :functions + :preface + :if :when :unless + :no-require + :catch + :after + :custom + :custom-face + :bind + :bind* + :bind-keymap + :bind-keymap* + :interpreter + :mode + :magic + :magic-fallback + :hook + ;; Any other keyword that also declares commands to be autoloaded (such as + ;; :bind) must appear before this keyword. + :commands + :init + :defer + :demand + :load + ;; This must occur almost last; the only forms which should appear after + ;; are those that must happen directly after the config forms. + :config + :local) + "The set of valid keywords, in the order they are processed in. +The order of this list is *very important*, so it is only +advisable to insert new keywords, never to delete or reorder +them. Further, attention should be paid to the NEWS.md if the +default order ever changes, as they may have subtle effects on +the semantics of use-package declarations and may necessitate +changing where you had inserted a new keyword earlier. + +Note that `:disabled' is special in this list, as it causes +nothing at all to happen, even if the rest of the use-package +declaration is incorrect." + :type '(repeat symbol) + :group 'use-package) + +(defcustom use-package-deferring-keywords + '(:bind-keymap + :bind-keymap* + :commands) + "Unless `:demand' is used, keywords in this list imply deferred loading. +The reason keywords like `:hook' are not in this list is that +they only imply deferred loading if they reference actual +function symbols that can be autoloaded from the module; whereas +the default keywords provided here always defer loading unless +otherwise requested." + :type '(repeat symbol) + :group 'use-package) + +(defcustom use-package-ignore-unknown-keywords nil + "If non-nil, issue warning instead of error when unknown +keyword is encountered. The unknown keyword and its associated +arguments will be ignored in the `use-package' expansion." + :type 'boolean + :group 'use-package) + +(defcustom use-package-use-theme t + "If non-nil, use a custom theme to avoid saving :custom +variables twice (once in the Custom file, once in the use-package +call)." + :type 'boolean + :group 'use-package) + +(defcustom use-package-verbose nil + "Whether to report about loading and configuration details. +If you customize this, then you should require the `use-package' +feature in files that use `use-package', even if these files only +contain compiled expansions of the macros. If you don't do so, +then the expanded macros do their job silently." + :type '(choice (const :tag "Quiet, without catching errors" errors) + (const :tag "Quiet" nil) + (const :tag "Verbose" t) + (const :tag "Debug" debug)) + :group 'use-package) + +(defcustom use-package-check-before-init nil + "If non-nil, check that package exists before executing its `:init' block. +This check is performed by calling `locate-library'." + :type 'boolean + :group 'use-package) + +(defcustom use-package-always-defer nil + "If non-nil, assume `:defer t' unless `:demand' is used. +See also `use-package-defaults', which uses this value." + :type 'boolean + :group 'use-package) + +(defcustom use-package-always-demand nil + "If non-nil, assume `:demand t' unless `:defer' is used. +See also `use-package-defaults', which uses this value." + :type 'boolean + :group 'use-package) + +(defcustom use-package-defaults + '(;; this '(t) has special meaning; see `use-package-handler/:config' + (:config '(t) t) + (:init nil t) + (:catch t (lambda (name args) + (not use-package-expand-minimally))) + (:defer use-package-always-defer + (lambda (name args) + (and use-package-always-defer + (not (plist-member args :defer)) + (not (plist-member args :demand))))) + (:demand use-package-always-demand + (lambda (name args) + (and use-package-always-demand + (not (plist-member args :defer)) + (not (plist-member args :demand)))))) + "Default values for specified `use-package' keywords. +Each entry in the alist is a list of three elements: +The first element is the `use-package' keyword. + +The second is a form that can be evaluated to get the default +value. It can also be a function that will receive the name of +the use-package declaration and the keyword plist given to +`use-package', in normalized form. The value it returns should +also be in normalized form (which is sometimes *not* what one +would normally write in a `use-package' declaration, so use +caution). + +The third element is a form that can be evaluated to determine +whether or not to assign a default value; if it evaluates to nil, +then the default value is not assigned even if the keyword is not +present in the `use-package' form. This third element may also be +a function, in which case it receives the name of the package (as +a symbol) and a list of keywords (in normalized form). It should +return nil or non-nil depending on whether defaulting should be +attempted." + :type `(repeat + (list (choice :tag "Keyword" + ,@(mapcar #'(lambda (k) (list 'const k)) + use-package-keywords)) + (choice :tag "Default value" sexp function) + (choice :tag "Enable if non-nil" sexp function))) + :group 'use-package) + +(defcustom use-package-merge-key-alist + '((:if . (lambda (new old) `(and ,new ,old))) + (:after . (lambda (new old) `(:all ,new ,old))) + (:defer . (lambda (new old) old)) + (:bind . (lambda (new old) (append new (list :break) old)))) + "Alist of keys and the functions used to merge multiple values. +For example, if the following form is provided: + + (use-package foo :if pred1 :if pred2) + +Then based on the above defaults, the merged result will be: + + (use-package foo :if (and pred1 pred2)) + +This is done so that, at the stage of invoking handlers, each +handler is called only once." + :type `(repeat + (cons (choice :tag "Keyword" + ,@(mapcar #'(lambda (k) (list 'const k)) + use-package-keywords) + (const :tag "Any" t)) + function)) + :group 'use-package) + +(defcustom use-package-hook-name-suffix "-hook" + "Text append to the name of hooks mentioned by :hook. +Set to nil if you don't want this to happen; it's only a +convenience." + :type '(choice string (const :tag "No suffix" nil)) + :group 'use-package) + +(defcustom use-package-minimum-reported-time 0.1 + "Minimal load time that will be reported. +Note that `use-package-verbose' has to be set to a non-nil value +for anything to be reported at all." + :type 'number + :group 'use-package) + +(defcustom use-package-inject-hooks nil + "If non-nil, add hooks to the `:init' and `:config' sections. +In particular, for a given package `foo', the following hooks +become available: + + `use-package--foo--pre-init-hook' + `use-package--foo--post-init-hook' + `use-package--foo--pre-config-hook' + `use-package--foo--post-config-hook' + +This way, you can add to these hooks before evaluation of a +`use-package` declaration, and exercise some control over what +happens. + +NOTE: These hooks are run even if the user does not specify an +`:init' or `:config' block, and they will happen at the regular +time when initialization and configuration would have been +performed. + +NOTE: If the `pre-init' hook return a nil value, that block's +user-supplied configuration is not evaluated, so be certain to +return t if you only wish to add behavior to what the user had +specified." + :type 'boolean + :group 'use-package) + +(defcustom use-package-expand-minimally nil + "If non-nil, make the expanded code as minimal as possible. +This disables: + + - Printing to the *Messages* buffer of slowly-evaluating forms + - Capturing of load errors (normally redisplayed as warnings) + - Conditional loading of packages (load failures become errors) + +The main advantage to this variable is that, if you know your +configuration works, it will make the byte-compiled file as +minimal as possible. It can also help with reading macro-expanded +definitions, to understand the main intent of what's happening." + :type 'boolean + :group 'use-package) + +(defcustom use-package-form-regexp-eval + `(concat ,(eval-when-compile + (concat "^\\s-*(" + (regexp-opt '("use-package" "require") t) + "\\s-+\\(")) + (or (bound-and-true-p lisp-mode-symbol-regexp) + "\\(?:\\sw\\|\\s_\\|\\\\.\\)+") "\\)") + "Sexp providing regexp for finding use-package forms in user files. +This is used by `use-package-jump-to-package-form' and +`use-package-enable-imenu-support'." + :type 'sexp + :group 'use-package) + +(defcustom use-package-enable-imenu-support nil + "If non-nil, cause imenu to see `use-package' declarations. +This is done by adjusting `lisp-imenu-generic-expression' to +include support for finding `use-package' and `require' forms. + +Must be set before loading use-package." + :type 'boolean + :set + #'(lambda (sym value) + (eval-after-load 'lisp-mode + (if value + `(add-to-list 'lisp-imenu-generic-expression + (list "Packages" ,use-package-form-regexp-eval 2)) + `(setq lisp-imenu-generic-expression + (remove (list "Packages" ,use-package-form-regexp-eval 2) + lisp-imenu-generic-expression)))) + (set-default sym value)) + :group 'use-package) + +(defconst use-package-font-lock-keywords + '(("(\\(use-package\\)\\_>[ \t']*\\(\\(?:\\sw\\|\\s_\\)+\\)?" + (1 font-lock-keyword-face) + (2 font-lock-constant-face nil t)))) + +(font-lock-add-keywords 'emacs-lisp-mode use-package-font-lock-keywords) + +(defcustom use-package-compute-statistics nil + "If non-nil, compute statistics concerned use-package declarations. +View the statistical report using `use-package-report'. Note that +if this option is enabled, you must require `use-package' in your +user init file at loadup time, or you will see errors concerning +undefined variables." + :type 'boolean + :group 'use-package) + +(defvar use-package-statistics (make-hash-table)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Utility functions +;; + +(defsubst use-package-error (msg) + "Report MSG as an error, so the user knows it came from this package." + (error "use-package: %s" msg)) + +(defsubst use-package-concat (&rest elems) + "Delete all empty lists from ELEMS (nil or (list nil)), and append them." + (apply #'append (delete nil (delete (list nil) elems)))) + +(defsubst use-package-non-nil-symbolp (sym) + (and sym (symbolp sym))) + +(defsubst use-package-as-symbol (string-or-symbol) + "If STRING-OR-SYMBOL is already a symbol, return it. Otherwise +convert it to a symbol and return that." + (if (symbolp string-or-symbol) string-or-symbol + (intern string-or-symbol))) + +(defsubst use-package-as-string (string-or-symbol) + "If STRING-OR-SYMBOL is already a string, return it. Otherwise +convert it to a string and return that." + (if (stringp string-or-symbol) string-or-symbol + (symbol-name string-or-symbol))) + +(defsubst use-package-regex-p (re) + "Return t if RE is some regexp-like thing." + (or (and (listp re) (eq (car re) 'rx)) + (stringp re))) + +(defun use-package-normalize-regex (re) + "Given some regexp-like thing in RE, resolve to a regular expression." + (cond + ((and (listp re) (eq (car re) 'rx)) (eval re)) + ((stringp re) re) + (t (error "Not recognized as regular expression: %s" re)))) + +(defsubst use-package-is-pair (x car-pred cdr-pred) + "Return non-nil if X is a cons satisfying the given predicates. +CAR-PRED and CDR-PRED are applied to X's `car' and `cdr', +respectively." + (and (consp x) + (funcall car-pred (car x)) + (funcall cdr-pred (cdr x)))) + +(defun use-package-as-mode (string-or-symbol) + "If STRING-OR-SYMBOL ends in `-mode' (or its name does), return +it as a symbol. Otherwise, return it as a symbol with `-mode' +appended." + (let ((string (use-package-as-string string-or-symbol))) + (intern (if (string-match "-mode\\'" string) + string + (concat string "-mode"))))) + +(defsubst use-package-load-name (name &optional noerror) + "Return a form which will load or require NAME. +It does the right thing no matter if NAME is a string or symbol. +Argument NOERROR means to indicate load failures as a warning." + (if (stringp name) + `(load ,name ,noerror) + `(require ',name nil ,noerror))) + +(defun use-package-hook-injector (name-string keyword body) + "Wrap pre/post hook injections around the given BODY for KEYWORD. +The BODY is a list of forms, so `((foo))' if only `foo' is being called." + (if (not use-package-inject-hooks) + body + (let ((keyword-name (substring (format "%s" keyword) 1))) + `((when (run-hook-with-args-until-failure + ',(intern (concat "use-package--" name-string + "--pre-" keyword-name "-hook"))) + ,@body + (run-hooks + ',(intern (concat "use-package--" name-string + "--post-" keyword-name "-hook")))))))) + +(defun use-package-with-elapsed-timer (text body) + "BODY is a list of forms, so `((foo))' if only `foo' is being called." + (declare (indent 1)) + (if use-package-expand-minimally + body + (let ((nowvar (make-symbol "now"))) + (if (bound-and-true-p use-package-verbose) + `((let ((,nowvar (current-time))) + (message "%s..." ,text) + (prog1 + ,(macroexp-progn body) + (let ((elapsed + (float-time (time-subtract (current-time) ,nowvar)))) + (if (> elapsed ,use-package-minimum-reported-time) + (message "%s...done (%.3fs)" ,text elapsed) + (message "%s...done" ,text)))))) + body)))) + +(put 'use-package-with-elapsed-timer 'lisp-indent-function 1) + +(defun use-package-require (name &optional no-require body) + (if use-package-expand-minimally + (use-package-concat + (unless no-require + (list (use-package-load-name name))) + body) + (if no-require + body + (use-package-with-elapsed-timer + (format "Loading package %s" name) + `((if (not ,(use-package-load-name name t)) + (display-warning 'use-package + (format "Cannot load %s" ',name) + :error) + ,@body)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Property lists +;; + +(defun use-package-plist-delete (plist property) + "Delete PROPERTY from PLIST. +This is in contrast to merely setting it to 0." + (let (p) + (while plist + (if (not (eq property (car plist))) + (setq p (plist-put p (car plist) (nth 1 plist)))) + (setq plist (cddr plist))) + p)) + +(defun use-package-plist-delete-first (plist property) + "Delete PROPERTY from PLIST. +This is in contrast to merely setting it to 0." + (let (p) + (while plist + (if (eq property (car plist)) + (setq p (nconc p (cddr plist)) + plist nil) + (setq p (nconc p (list (car plist) (cadr plist))) + plist (cddr plist)))) + p)) + +(defsubst use-package-plist-maybe-put (plist property value) + "Add a VALUE for PROPERTY to PLIST, if it does not already exist." + (if (plist-member plist property) + plist + (plist-put plist property value))) + +(defsubst use-package-plist-cons (plist property value) + "Cons VALUE onto the head of the list at PROPERTY in PLIST." + (plist-put plist property (cons value (plist-get plist property)))) + +(defsubst use-package-plist-append (plist property value) + "Append VALUE onto the front of the list at PROPERTY in PLIST." + (plist-put plist property (append value (plist-get plist property)))) + +(defun use-package-split-list (pred xs) + (let ((ys (list nil)) (zs (list nil)) flip) + (cl-dolist (x xs) + (if flip + (nconc zs (list x)) + (if (funcall pred x) + (progn + (setq flip t) + (nconc zs (list x))) + (nconc ys (list x))))) + (cons (cdr ys) (cdr zs)))) + +(defun use-package-split-list-at-keys (key lst) + (and lst + (let ((xs (use-package-split-list (apply-partially #'eq key) lst))) + (cons (car xs) (use-package-split-list-at-keys key (cddr xs)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Keywords +;; + +(defun use-package-keyword-index (keyword) + (cl-loop named outer + with index = 0 + for k in use-package-keywords do + (if (eq k keyword) + (cl-return-from outer index)) + (cl-incf index))) + +(defun use-package-normalize-plist (name input &optional plist merge-function) + "Given a pseudo-plist, normalize it to a regular plist. +The normalized key/value pairs from input are added to PLIST, +extending any keys already present." + (if (null input) + plist + (let* ((keyword (car input)) + (xs (use-package-split-list #'keywordp (cdr input))) + (args (car xs)) + (tail (cdr xs)) + (normalizer + (intern-soft (concat "use-package-normalize/" + (symbol-name keyword)))) + (arg (and (functionp normalizer) + (funcall normalizer name keyword args))) + (error-string (format "Unrecognized keyword: %s" keyword))) + (if (memq keyword use-package-keywords) + (progn + (setq plist (use-package-normalize-plist + name tail plist merge-function)) + (plist-put plist keyword + (if (plist-member plist keyword) + (funcall merge-function keyword arg + (plist-get plist keyword)) + arg))) + (if use-package-ignore-unknown-keywords + (progn + (display-warning 'use-package error-string) + (use-package-normalize-plist + name tail plist merge-function)) + (use-package-error error-string)))))) + +(defun use-package-unalias-keywords (_name args) + (setq args (cl-nsubstitute :if :when args)) + (let (temp) + (while (setq temp (plist-get args :unless)) + (setq args (use-package-plist-delete-first args :unless) + args (append args `(:if (not ,temp)))))) + args) + +(defun use-package-merge-keys (key new old) + (let ((merger (assq key use-package-merge-key-alist))) + (if merger + (funcall (cdr merger) new old) + (append new old)))) + +(defun use-package-sort-keywords (plist) + (let (plist-grouped) + (while plist + (push (cons (car plist) (cadr plist)) + plist-grouped) + (setq plist (cddr plist))) + (let (result) + (cl-dolist + (x + (nreverse + (sort plist-grouped + #'(lambda (l r) (< (use-package-keyword-index (car l)) + (use-package-keyword-index (car r))))))) + (setq result (cons (car x) (cons (cdr x) result)))) + result))) + +(defun use-package-normalize-keywords (name args) + (let* ((name-symbol (if (stringp name) (intern name) name)) + (name-string (symbol-name name-symbol))) + + ;; The function `elisp--local-variables' inserts this unbound variable into + ;; macro forms to determine the locally bound variables for + ;; `elisp-completion-at-point'. It ends up throwing a lot of errors since it + ;; can occupy the position of a keyword (or look like a second argument to a + ;; keyword that takes one). Deleting it when it's at the top level should be + ;; harmless since there should be no locally bound variables to discover + ;; here anyway. + (setq args (delq 'elisp--witness--lisp args)) + + ;; Reduce the set of keywords down to its most fundamental expression. + (setq args (use-package-unalias-keywords name-symbol args)) + + ;; Normalize keyword values, coalescing multiple occurrences. + (setq args (use-package-normalize-plist name-symbol args nil + #'use-package-merge-keys)) + + ;; Add default values for keywords not specified, when applicable. + (cl-dolist (spec use-package-defaults) + (when (let ((func (nth 2 spec))) + (if (and func (functionp func)) + (funcall func name args) + (eval func))) + (setq args (use-package-plist-maybe-put + args (nth 0 spec) + (let ((func (nth 1 spec))) + (if (and func (functionp func)) + (funcall func name args) + (eval func))))))) + + ;; Determine any autoloads implied by the keywords used. + (let ((iargs args) + commands) + (while iargs + (when (keywordp (car iargs)) + (let ((autoloads + (intern-soft (concat "use-package-autoloads/" + (symbol-name (car iargs)))))) + (when (functionp autoloads) + (setq commands + ;; jww (2017-12-07): Right now we just ignored the type of + ;; the autoload being requested, and assume they are all + ;; `command'. + (append (mapcar + #'car + (funcall autoloads name-symbol (car iargs) + (cadr iargs))) + commands))))) + (setq iargs (cddr iargs))) + (when commands + (setq args + ;; Like `use-package-plist-append', but removing duplicates. + (plist-put args :commands + (delete-dups + (append commands (plist-get args :commands))))))) + + ;; If byte-compiling, pre-load the package so all its symbols are in + ;; scope. This is done by prepending statements to the :preface. + (when (bound-and-true-p byte-compile-current-file) + (setq args + (use-package-plist-append + args :preface + (use-package-concat + (mapcar #'(lambda (var) `(defvar ,var)) + (plist-get args :defines)) + (mapcar #'(lambda (fn) `(declare-function ,fn ,name-string)) + (plist-get args :functions)) + `((eval-when-compile + (with-demoted-errors + ,(format "Cannot load %s: %%S" name-string) + ,(when (eq use-package-verbose 'debug) + `(message ,(format "Compiling package %s" name-string))) + ,(unless (plist-get args :no-require) + `(unless (featurep ',name-symbol) + (load ,name-string nil t)))))))))) + + ;; Certain keywords imply :defer, if :demand was not specified. + (when (and (not (plist-member args :demand)) + (not (plist-member args :defer)) + (not (or (equal '(t) (plist-get args :load)) + (equal (list (use-package-as-string name)) + (mapcar #'use-package-as-string + (plist-get args :load))))) + (cl-some #'identity + (mapcar (apply-partially #'plist-member args) + use-package-deferring-keywords))) + (setq args (append args '(:defer t)))) + + ;; The :load keyword overrides :no-require + (when (and (plist-member args :load) + (plist-member args :no-require)) + (setq args (use-package-plist-delete args :no-require))) + + ;; If at this point no :load, :defer or :no-require has been seen, then + ;; :load the package itself. + (when (and (not (plist-member args :load)) + (not (plist-member args :defer)) + (not (plist-member args :no-require))) + (setq args (append args `(:load (,name))))) + + ;; Sort the list of keywords based on the order of `use-package-keywords'. + (use-package-sort-keywords args))) + +(defun use-package-process-keywords (name plist &optional state) + "Process the next keyword in the free-form property list PLIST. +The values in the PLIST have each been normalized by the function +use-package-normalize/KEYWORD (minus the colon). + +STATE is a property list that the function may modify and/or +query. This is useful if a package defines multiple keywords and +wishes them to have some kind of stateful interaction. + +Unless the KEYWORD being processed intends to ignore remaining +keywords, it must call this function recursively, passing in the +plist with its keyword and argument removed, and passing in the +next value for the STATE." + (declare (indent 1)) + (unless (null plist) + (let* ((keyword (car plist)) + (arg (cadr plist)) + (rest (cddr plist))) + (unless (keywordp keyword) + (use-package-error (format "%s is not a keyword" keyword))) + (let* ((handler (concat "use-package-handler/" (symbol-name keyword))) + (handler-sym (intern handler))) + (if (functionp handler-sym) + (funcall handler-sym name keyword arg rest state) + (use-package-error + (format "Keyword handler not defined: %s" handler))))))) + +(put 'use-package-process-keywords 'lisp-indent-function 'defun) + +(defun use-package-list-insert (elem xs &optional anchor after test) + "Insert ELEM into the list XS. +If ANCHOR is also a keyword, place the new KEYWORD before that +one. +If AFTER is non-nil, insert KEYWORD either at the end of the +keywords list, or after the ANCHOR if one has been provided. +If TEST is non-nil, it is the test used to compare ELEM to list +elements. The default is `eq'. +The modified list is returned. The original list is not modified." + (let (result) + (dolist (k xs) + (if (funcall (or test #'eq) k anchor) + (if after + (setq result (cons k result) + result (cons elem result)) + (setq result (cons elem result) + result (cons k result))) + (setq result (cons k result)))) + (if anchor + (nreverse result) + (if after + (nreverse (cons elem result)) + (cons elem (nreverse result)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Argument Processing +;; + +(defun use-package-only-one (label args f) + "Call F on the first member of ARGS if it has exactly one element." + (declare (indent 1)) + (cond + ((and (listp args) (listp (cdr args)) + (= (length args) 1)) + (funcall f label (car args))) + (t + (use-package-error + (concat label " wants exactly one argument"))))) + +(put 'use-package-only-one 'lisp-indent-function 'defun) + +(defun use-package-as-one (label args f &optional allow-empty) + "Call F on the first element of ARGS if it has one element, or all of ARGS. +If ALLOW-EMPTY is non-nil, it's OK for ARGS to be an empty list." + (declare (indent 1)) + (if (if args + (and (listp args) (listp (cdr args))) + allow-empty) + (if (= (length args) 1) + (funcall f label (car args)) + (funcall f label args)) + (use-package-error + (concat label " wants a non-empty list")))) + +(put 'use-package-as-one 'lisp-indent-function 'defun) + +(defun use-package-memoize (f arg) + "Ensure the macro-expansion of F applied to ARG evaluates ARG +no more than once." + (let ((loaded (cl-gentemp "use-package--loaded")) + (result (cl-gentemp "use-package--result")) + (next (cl-gentemp "use-package--next"))) + `((defvar ,loaded nil) + (defvar ,result nil) + (defvar ,next #'(lambda () (if ,loaded ,result + (setq ,loaded t ,result ,arg)))) + ,@(funcall f `((funcall ,next)))))) + +(defsubst use-package-normalize-value (_label arg) + "Normalize the Lisp value given by ARG. +The argument LABEL is ignored." + (cond ((null arg) nil) + ((eq t arg) t) + ((use-package-non-nil-symbolp arg) + `(symbol-value ',arg)) + ((functionp arg) + `(funcall #',arg)) + (t arg))) + +(defun use-package-normalize-symbols (label arg &optional recursed) + "Normalize a list of symbols." + (cond + ((use-package-non-nil-symbolp arg) + (list arg)) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) (car (use-package-normalize-symbols label x t))) arg)) + (t + (use-package-error + (concat label " wants a symbol, or list of symbols"))))) + +(defun use-package-normalize-symlist (_name keyword args) + (use-package-as-one (symbol-name keyword) args + #'use-package-normalize-symbols)) + +(defun use-package-normalize-recursive-symbols (label arg) + "Normalize a list of symbols." + (cond + ((use-package-non-nil-symbolp arg) + arg) + ((and (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) (use-package-normalize-recursive-symbols label x)) + arg)) + (t + (use-package-error + (concat label " wants a symbol, or nested list of symbols"))))) + +(defun use-package-normalize-recursive-symlist (_name keyword args) + (use-package-as-one (symbol-name keyword) args + #'use-package-normalize-recursive-symbols)) + +(defun use-package-normalize-paths (label arg &optional recursed) + "Normalize a list of filesystem paths." + (cond + ((and arg (or (use-package-non-nil-symbolp arg) (functionp arg))) + (let ((value (use-package-normalize-value label arg))) + (use-package-normalize-paths label (eval value)))) + ((stringp arg) + (let ((path (if (file-name-absolute-p arg) + arg + (expand-file-name arg user-emacs-directory)))) + (list path))) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) + (car (use-package-normalize-paths label x t))) arg)) + (t + (use-package-error + (concat label " wants a directory path, or list of paths"))))) + +(defun use-package-normalize-predicate (_name keyword args) + (if (null args) + t + (use-package-only-one (symbol-name keyword) args + #'use-package-normalize-value))) + +(defun use-package-normalize-form (label args) + "Given a list of forms, return it wrapped in `progn'." + (unless (listp (car args)) + (use-package-error (concat label " wants a sexp or list of sexps"))) + (mapcar #'(lambda (form) + (if (and (consp form) + (memq (car form) + '(use-package bind-key bind-key* + unbind-key bind-keys bind-keys*))) + (macroexpand form) + form)) args)) + +(defun use-package-normalize-forms (_name keyword args) + (use-package-normalize-form (symbol-name keyword) args)) + +(defun use-package-normalize-pairs + (key-pred val-pred name label arg &optional recursed) + "Normalize a list of pairs. +KEY-PRED and VAL-PRED are predicates recognizing valid keys and +values, respectively. +If RECURSED is non-nil, recurse into sublists." + (cond + ((funcall key-pred arg) + (list (cons arg (use-package-as-symbol name)))) + ((use-package-is-pair arg key-pred val-pred) + (list arg)) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (let (last-item) + (mapcar + #'(lambda (x) + (prog1 + (let ((ret (use-package-normalize-pairs + key-pred val-pred name label x t))) + (if (and (listp ret) + (not (keywordp last-item))) + (car ret) + ret)) + (setq last-item x))) arg))) + (t arg))) + +(defun use-package-recognize-function (v &optional binding additional-pred) + "A predicate that recognizes functional constructions: + nil + sym + \\='sym + (quote sym) + #'sym + (function sym) + (lambda () ...) + \\='(lambda () ...) + (quote (lambda () ...)) + #'(lambda () ...) + (function (lambda () ...))" + (or (if binding + (symbolp v) + (use-package-non-nil-symbolp v)) + (and (listp v) + (memq (car v) '(quote function)) + (use-package-non-nil-symbolp (cadr v))) + (if binding (commandp v) (functionp v)) + (and additional-pred + (funcall additional-pred v)))) + +(defun use-package-normalize-function (v) + "Reduce functional constructions to one of two normal forms: + sym + #'(lambda () ...)" + (cond ((symbolp v) v) + ((and (listp v) + (memq (car v) '(quote function)) + (use-package-non-nil-symbolp (cadr v))) + (cadr v)) + ((and (consp v) + (eq 'lambda (car v))) + v) + ((and (listp v) + (memq (car v) '(quote function)) + (eq 'lambda (car (cadr v)))) + (cadr v)) + (t v))) + +(defun use-package-normalize-commands (args) + "Map over ARGS of the form ((_ . F) ...), normalizing functional F's." + (mapcar #'(lambda (x) + (if (consp x) + (cons (car x) (use-package-normalize-function (cdr x))) + x)) + args)) + +(defun use-package-normalize-mode (name keyword args) + "Normalize arguments for keywords which add regexp/mode pairs to an alist." + (use-package-as-one (symbol-name keyword) args + (apply-partially #'use-package-normalize-pairs + #'use-package-regex-p + #'use-package-recognize-function + name))) + +(defun use-package-autoloads-mode (_name _keyword args) + (mapcar + #'(lambda (x) (cons (cdr x) 'command)) + (cl-remove-if-not #'(lambda (x) + (and (consp x) + (use-package-non-nil-symbolp (cdr x)))) + args))) + +(defun use-package-handle-mode (name alist args rest state) + "Handle keywords which add regexp/mode pairs to an alist." + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (thing) + `(add-to-list + ',alist + ',(cons (use-package-normalize-regex (car thing)) + (cdr thing)))) + (use-package-normalize-commands args)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Statistics +;; + +(defun use-package-reset-statistics () + (interactive) + (setq use-package-statistics (make-hash-table))) + +(defun use-package-statistics-status (package) + "Return loading configuration status of PACKAGE statistics." + (cond ((gethash :config package) "Configured") + ((gethash :init package) "Initialized") + ((gethash :preface package) "Prefaced") + ((gethash :use-package package) "Declared"))) + +(defun use-package-statistics-last-event (package) + "Return the date when PACKAGE's status last changed. +The date is returned as a string." + (or (gethash :config package) + (gethash :init package) + (gethash :preface package) + (gethash :use-package package))) + +(defun use-package-statistics-time (package) + "Return the time is took for PACKAGE to load." + (+ (float-time (gethash :config-secs package '(0 0 0 0))) + (float-time (gethash :init-secs package '(0 0 0 0))) + (float-time (gethash :preface-secs package '(0 0 0 0))) + (float-time (gethash :use-package-secs package '(0 0 0 0))))) + +(defun use-package-statistics-convert (package) + "Return information about PACKAGE. + +The information is formatted in a way suitable for +`use-package-statistics-mode'." + (let ((statistics (gethash package use-package-statistics))) + (list + package + (vector + (symbol-name package) + (use-package-statistics-status statistics) + (format-time-string + "%H:%M:%S.%6N" + (use-package-statistics-last-event statistics)) + (format "%.2f" (use-package-statistics-time statistics)))))) + +(defun use-package-report () + "Show current statistics gathered about use-package declarations. +In the table that's generated, the status field has the following +meaning: + Configured :config has been processed (the package is loaded!) + Initialized :init has been processed (load status unknown) + Prefaced :preface has been processed + Declared the use-package declaration was seen" + (interactive) + (with-current-buffer (get-buffer-create "*use-package statistics*") + (setq tabulated-list-entries + (mapcar #'use-package-statistics-convert + (hash-table-keys use-package-statistics))) + (use-package-statistics-mode) + (tabulated-list-print) + (display-buffer (current-buffer)))) + +(defvar use-package-statistics-status-order + '(("Declared" . 0) + ("Prefaced" . 1) + ("Initialized" . 2) + ("Configured" . 3))) + +(define-derived-mode use-package-statistics-mode tabulated-list-mode + "use-package statistics" + "Show current statistics gathered about use-package declarations." + (setq tabulated-list-format + ;; The sum of column width is 80 characters: + [("Package" 25 t) + ("Status" 13 + (lambda (a b) + (< (assoc-default + (use-package-statistics-status + (gethash (car a) use-package-statistics)) + use-package-statistics-status-order) + (assoc-default + (use-package-statistics-status + (gethash (car b) use-package-statistics)) + use-package-statistics-status-order)))) + ("Last Event" 23 + (lambda (a b) + (< (float-time + (use-package-statistics-last-event + (gethash (car a) use-package-statistics))) + (float-time + (use-package-statistics-last-event + (gethash (car b) use-package-statistics)))))) + ("Time" 10 + (lambda (a b) + (< (use-package-statistics-time + (gethash (car a) use-package-statistics)) + (use-package-statistics-time + (gethash (car b) use-package-statistics)))))]) + (setq tabulated-list-sort-key '("Time" . t)) + (tabulated-list-init-header)) + +(defun use-package-statistics-gather (keyword name after) + (let* ((hash (gethash name use-package-statistics + (make-hash-table))) + (before (and after (gethash keyword hash (current-time))))) + (puthash keyword (current-time) hash) + (when after + (puthash (intern (concat (symbol-name keyword) "-secs")) + (time-subtract (current-time) before) hash)) + (puthash name hash use-package-statistics))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Handlers +;; + +;;;; :disabled + +;; Don't alias this to `ignore', as that will cause the resulting +;; function to be interactive. +(defun use-package-normalize/:disabled (_name _keyword _arg) + "Do nothing, return nil.") + +(defun use-package-handler/:disabled (name _keyword _arg rest state) + (use-package-process-keywords name rest state)) + +;;;; :if, :when and :unless + +(defun use-package-normalize-test (_name keyword args) + (use-package-only-one (symbol-name keyword) args + #'use-package-normalize-value)) + +(defalias 'use-package-normalize/:if 'use-package-normalize-test) + +(defun use-package-handler/:if (name _keyword pred rest state) + (let ((body (use-package-process-keywords name rest state))) + `((when ,pred ,@body)))) + +(defalias 'use-package-normalize/:when 'use-package-normalize-test) + +(defalias 'use-package-handler/:when 'use-package-handler/:if) + +(defalias 'use-package-normalize/:unless 'use-package-normalize-test) + +(defun use-package-handler/:unless (name _keyword pred rest state) + (let ((body (use-package-process-keywords name rest state))) + `((unless ,pred ,@body)))) + +;;;; :requires + +(defalias 'use-package-normalize/:requires 'use-package-normalize-symlist) + +(defun use-package-handler/:requires (name _keyword requires rest state) + (let ((body (use-package-process-keywords name rest state))) + (if (null requires) + body + `((when ,(if (> (length requires) 1) + `(not (member nil (mapcar #'featurep ',requires))) + `(featurep ',(car requires))) + ,@body))))) + +;;;; :load-path + +(defun use-package-normalize/:load-path (_name keyword args) + (use-package-as-one (symbol-name keyword) args + #'use-package-normalize-paths)) + +(defun use-package-handler/:load-path (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + (mapcar #'(lambda (path) + `(eval-and-compile (add-to-list 'load-path ,path))) + arg) + body))) + +;;;; :no-require + +(defalias 'use-package-normalize/:no-require 'use-package-normalize-predicate) + +(defun use-package-handler/:no-require (name _keyword _arg rest state) + (use-package-process-keywords name rest state)) + +;;;; :defines + +(defalias 'use-package-normalize/:defines 'use-package-normalize-symlist) + +(defun use-package-handler/:defines (name _keyword _arg rest state) + (use-package-process-keywords name rest state)) + +;;;; :functions + +(defalias 'use-package-normalize/:functions 'use-package-normalize-symlist) + +(defun use-package-handler/:functions (name _keyword _arg rest state) + (use-package-process-keywords name rest state)) + +;;;; :preface + +(defalias 'use-package-normalize/:preface 'use-package-normalize-forms) + +(defun use-package-handler/:preface (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :preface ',name nil))) + (when arg + `((eval-and-compile ,@arg))) + body + (when use-package-compute-statistics + `((use-package-statistics-gather :preface ',name t)))))) + +;;;; :catch + +(defvar use-package--form) +(defvar use-package--hush-function #'(lambda (_keyword body) body)) + +(defsubst use-package-hush (context keyword body) + `((condition-case-unless-debug err + ,(macroexp-progn body) + (error (funcall ,context ,keyword err))))) + +(defun use-package-normalize/:catch (_name keyword args) + (if (null args) + t + (use-package-only-one (symbol-name keyword) args + use-package--hush-function))) + +(defun use-package-handler/:catch (name keyword arg rest state) + (let* ((context (cl-gentemp "use-package--warning"))) + (cond + ((not arg) + (use-package-process-keywords name rest state)) + ((eq arg t) + `((defvar ,context + #'(lambda (keyword err) + (let ((msg (format "%s/%s: %s" ',name keyword + (error-message-string err)))) + ,@(when (eq use-package-verbose 'debug) + `((with-current-buffer + (get-buffer-create "*use-package*") + (goto-char (point-max)) + (insert "-----\n" msg ,use-package--form) + (emacs-lisp-mode)) + (setq msg + (concat msg + " (see the *use-package* buffer)")))) + (display-warning 'use-package msg :error)))) + ,@(let ((use-package--hush-function + (apply-partially #'use-package-hush context))) + (funcall use-package--hush-function keyword + (use-package-process-keywords name rest state))))) + ((functionp arg) + `((defvar ,context ,arg) + ,@(let ((use-package--hush-function + (apply-partially #'use-package-hush context))) + (funcall use-package--hush-function keyword + (use-package-process-keywords name rest state))))) + (t + (use-package-error "The :catch keyword expects 't' or a function"))))) + +;;;; :interpreter + +(defalias 'use-package-normalize/:interpreter 'use-package-normalize-mode) +(defalias 'use-package-autoloads/:interpreter 'use-package-autoloads-mode) + +(defun use-package-handler/:interpreter (name _keyword arg rest state) + (use-package-handle-mode name 'interpreter-mode-alist arg rest state)) + +;;;; :mode + +(defalias 'use-package-normalize/:mode 'use-package-normalize-mode) +(defalias 'use-package-autoloads/:mode 'use-package-autoloads-mode) + +(defun use-package-handler/:mode (name _keyword arg rest state) + (use-package-handle-mode name 'auto-mode-alist arg rest state)) + +;;;; :magic + +(defalias 'use-package-normalize/:magic 'use-package-normalize-mode) +(defalias 'use-package-autoloads/:magic 'use-package-autoloads-mode) + +(defun use-package-handler/:magic (name _keyword arg rest state) + (use-package-handle-mode name 'magic-mode-alist arg rest state)) + +;;;; :magic-fallback + +(defalias 'use-package-normalize/:magic-fallback 'use-package-normalize-mode) +(defalias 'use-package-autoloads/:magic-fallback 'use-package-autoloads-mode) + +(defun use-package-handler/:magic-fallback (name _keyword arg rest state) + (use-package-handle-mode name 'magic-fallback-mode-alist arg rest state)) + +;;;; :hook + +(defun use-package-normalize/:hook (name keyword args) + (use-package-as-one (symbol-name keyword) args + #'(lambda (label arg) + (unless (or (use-package-non-nil-symbolp arg) (consp arg)) + (use-package-error + (concat label " a or ( . )" + " or list of these"))) + (use-package-normalize-pairs + #'(lambda (k) + (or (use-package-non-nil-symbolp k) + (and k (let ((every t)) + (while (and every k) + (if (and (consp k) + (use-package-non-nil-symbolp (car k))) + (setq k (cdr k)) + (setq every nil))) + every)))) + #'use-package-recognize-function + name label arg)))) + +(defalias 'use-package-autoloads/:hook 'use-package-autoloads-mode) + +(defun use-package-handler/:hook (name _keyword args rest state) + "Generate use-package custom keyword code." + (use-package-concat + (use-package-process-keywords name rest state) + (cl-mapcan + #'(lambda (def) + (let ((syms (car def)) + (fun (cdr def))) + (when fun + (mapcar + #'(lambda (sym) + `(add-hook + (quote ,(intern + (concat (symbol-name sym) + use-package-hook-name-suffix))) + (function ,fun))) + (use-package-hook-handler-normalize-mode-symbols syms))))) + (use-package-normalize-commands args)))) + +(defun use-package-hook-handler-normalize-mode-symbols (syms) + "Ensure that `SYMS' turns into a list of modes." + (if (use-package-non-nil-symbolp syms) (list syms) syms)) + +;;;; :commands + +(defalias 'use-package-normalize/:commands 'use-package-normalize-symlist) + +(defun use-package-handler/:commands (name _keyword arg rest state) + (use-package-concat + ;; Since we deferring load, establish any necessary autoloads, and also + ;; keep the byte-compiler happy. + (let ((name-string (use-package-as-string name))) + (cl-mapcan + #'(lambda (command) + (when (symbolp command) + (append + (unless (plist-get state :demand) + `((unless (fboundp ',command) + (autoload #',command ,name-string nil t)))) + (when (bound-and-true-p byte-compile-current-file) + `((eval-when-compile + (declare-function ,command ,name-string))))))) + (delete-dups arg))) + (use-package-process-keywords name rest state))) + +;;;; :defer + +(defalias 'use-package-normalize/:defer 'use-package-normalize-predicate) + +(defun use-package-handler/:defer (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + ;; Load the package after a set amount of idle time, if the argument to + ;; `:defer' was a number. + (when (numberp arg) + `((run-with-idle-timer ,arg nil #'require + ',(use-package-as-symbol name) nil t))) + (if (or (not arg) (null body)) + body + `((eval-after-load ',name ',(macroexp-progn body))))))) + +;;;; :after + +(defun use-package-normalize/:after (name keyword args) + (setq args (use-package-normalize-recursive-symlist name keyword args)) + (if (consp args) + args + (list args))) + +(defun use-package-after-count-uses (features*) + "Count the number of time the body would appear in the result." + (cond ((use-package-non-nil-symbolp features*) + 1) + ((and (consp features*) + (memq (car features*) '(:or :any))) + (let ((num 0)) + (cl-dolist (next (cdr features*)) + (setq num (+ num (use-package-after-count-uses next)))) + num)) + ((and (consp features*) + (memq (car features*) '(:and :all))) + (apply #'max (mapcar #'use-package-after-count-uses + (cdr features*)))) + ((listp features*) + (use-package-after-count-uses (cons :all features*))))) + +(defun use-package-require-after-load (features* body) + "Generate `eval-after-load' statements to represents FEATURES*. +FEATURES* is a list containing keywords `:and' and `:all', where +no keyword implies `:all'." + (cond + ((use-package-non-nil-symbolp features*) + `((eval-after-load ',features* ',(macroexp-progn body)))) + ((and (consp features*) + (memq (car features*) '(:or :any))) + (cl-mapcan #'(lambda (x) (use-package-require-after-load x body)) + (cdr features*))) + ((and (consp features*) + (memq (car features*) '(:and :all))) + (cl-dolist (next (cdr features*)) + (setq body (use-package-require-after-load next body))) + body) + ((listp features*) + (use-package-require-after-load (cons :all features*) body)))) + +(defun use-package-handler/:after (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state)) + (uses (use-package-after-count-uses arg))) + (if (or (null uses) (null body)) + body + (if (<= uses 1) + (use-package-require-after-load arg body) + (use-package-memoize + (apply-partially #'use-package-require-after-load arg) + (macroexp-progn body)))))) + +;;;; :demand + +(defalias 'use-package-normalize/:demand 'use-package-normalize-predicate) + +(defun use-package-handler/:demand (name _keyword _arg rest state) + (use-package-process-keywords name rest state)) + +;;;; :custom + +(defun use-package-normalize/:custom (_name keyword args) + "Normalize use-package custom keyword." + (use-package-as-one (symbol-name keyword) args + #'(lambda (label arg) + (unless (listp arg) + (use-package-error + (concat label " a ( [comment])" + " or list of these"))) + (if (use-package-non-nil-symbolp (car arg)) + (list arg) + arg)))) + +(defun use-package-handler/:custom (name _keyword args rest state) + "Generate use-package custom keyword code." + (use-package-concat + (if (bound-and-true-p use-package-use-theme) + `((let ((custom--inhibit-theme-enable nil)) + ;; Declare the theme here so use-package can be required inside + ;; eval-and-compile without warnings about unknown theme. + (unless (memq 'use-package custom-known-themes) + (deftheme use-package) + (enable-theme 'use-package) + (setq custom-enabled-themes (remq 'use-package custom-enabled-themes))) + (custom-theme-set-variables + 'use-package + ,@(mapcar + #'(lambda (def) + (let ((variable (nth 0 def)) + (value (nth 1 def)) + (comment (nth 2 def))) + (unless (and comment (stringp comment)) + (setq comment (format "Customized with use-package %s" name))) + `'(,variable ,value nil () ,comment))) + args)))) + (mapcar + #'(lambda (def) + (let ((variable (nth 0 def)) + (value (nth 1 def)) + (comment (nth 2 def))) + (unless (and comment (stringp comment)) + (setq comment (format "Customized with use-package %s" name))) + `(customize-set-variable (quote ,variable) ,value ,comment))) + args)) + (use-package-process-keywords name rest state))) + +;;;; :custom-face + +(defun use-package-normalize/:custom-face (name-symbol _keyword arg) + "Normalize use-package custom-face keyword." + (let ((error-msg + (format "%s wants a ( ) or list of these" + name-symbol))) + (unless (listp arg) + (use-package-error error-msg)) + (cl-dolist (def arg arg) + (unless (listp def) + (use-package-error error-msg)) + (let ((face (nth 0 def)) + (spec (nth 1 def))) + (when (or (not face) + (not spec) + (> (length def) 2)) + (use-package-error error-msg)))))) + +(defun use-package-handler/:custom-face (name _keyword args rest state) + "Generate use-package custom-face keyword code." + (use-package-concat + (mapcar #'(lambda (def) `(custom-set-faces (backquote ,def))) args) + (use-package-process-keywords name rest state))) + +;;;; :init + +(defalias 'use-package-normalize/:init 'use-package-normalize-forms) + +(defun use-package-handler/:init (name _keyword arg rest state) + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :init ',name nil))) + (let ((init-body + (use-package-hook-injector (use-package-as-string name) + :init arg))) + (when init-body + (funcall use-package--hush-function :init + (if use-package-check-before-init + `((when (locate-library ,(use-package-as-string name)) + ,@init-body)) + init-body)))) + (use-package-process-keywords name rest state) + (when use-package-compute-statistics + `((use-package-statistics-gather :init ',name t))))) + +;;;; :load + +(defun use-package-normalize/:load (name keyword args) + (setq args (use-package-normalize-recursive-symlist name keyword args)) + (if (consp args) + args + (list args))) + +(defun use-package-handler/:load (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (cl-dolist (pkg arg) + (setq body (use-package-require (if (eq t pkg) name pkg) nil body))) + body)) + +;;;; :config + +(defalias 'use-package-normalize/:config 'use-package-normalize-forms) + +(defun use-package-handler/:config (name _keyword arg rest state) + (let* ((body (use-package-process-keywords name rest state)) + (name-symbol (use-package-as-symbol name))) + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :config ',name nil))) + (if (and (or (null arg) (equal arg '(t))) (not use-package-inject-hooks)) + body + (use-package-with-elapsed-timer + (format "Configuring package %s" name-symbol) + (funcall use-package--hush-function :config + (use-package-concat + (use-package-hook-injector + (symbol-name name-symbol) :config arg) + body + (list t))))) + (when use-package-compute-statistics + `((use-package-statistics-gather :config ',name t)))))) + +;;;; :local + +(defun use-package-normalize/:local (name keyword args) + (let ((first-arg-name (symbol-name (caar args)))) + (if (not (string-suffix-p "-hook" first-arg-name)) + (let* ((sym-name (symbol-name name)) + (addition (if (string-suffix-p "-mode" sym-name) + "-hook" + "-mode-hook")) + (hook (intern (concat sym-name addition)))) + `((,hook . ,(use-package-normalize-forms name keyword args)))) + (cl-loop for (hook . code) in args + collect `(,hook . ,(use-package-normalize-forms name keyword code)))))) + +(defun use-package-handler/:local (name _keyword arg rest state) + (let* ((body (use-package-process-keywords name rest state))) + (use-package-concat + body + (cl-loop for (hook . code) in arg + for func-name = (intern (concat "use-package-func/" (symbol-name hook))) + collect (progn + (push 'progn code) + `(defun ,func-name () ,code)) + collect `(add-hook ',hook ',func-name))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; The main macro +;; + +(defmacro use-package-core (name args) + `(let* ((args* (use-package-normalize-keywords ,name ,args)) + (use-package--form + (if (eq use-package-verbose 'debug) + (concat "\n\n" + (pp-to-string `(use-package ,name ,@,args)) + "\n -->\n\n" + (pp-to-string `(use-package ,name ,@args*)) + "\n ==>\n\n" + (pp-to-string + (macroexp-progn + (let ((use-package-verbose 'errors) + (use-package-expand-minimally t)) + (use-package-process-keywords name args* + (and (plist-get args* :demand) + (list :demand t))))))) + ""))) + (use-package-process-keywords name args* + (and (plist-get args* :demand) + (list :demand t))))) + +;;;###autoload +(defmacro use-package (name &rest args) + "Declare an Emacs package by specifying a group of configuration options. + +For full documentation, please see the README file that came with +this file. Usage: + + (use-package package-name + [:keyword [option]]...) + +:init Code to run before PACKAGE-NAME has been loaded. +:config Code to run after PACKAGE-NAME has been loaded. Note that + if loading is deferred for any reason, this code does not + execute until the lazy load has occurred. +:preface Code to be run before everything except `:disabled'; this + can be used to define functions for use in `:if', or that + should be seen by the byte-compiler. + +:mode Form to be added to `auto-mode-alist'. +:magic Form to be added to `magic-mode-alist'. +:magic-fallback Form to be added to `magic-fallback-mode-alist'. +:interpreter Form to be added to `interpreter-mode-alist'. + +:commands Define autoloads for commands that will be defined by the + package. This is useful if the package is being lazily + loaded, and you wish to conditionally call functions in your + `:init' block that are defined in the package. +:hook Specify hook(s) to attach this package to. + +:bind Bind keys, and define autoloads for the bound commands. +:bind* Bind keys, and define autoloads for the bound commands, + *overriding all minor mode bindings*. +:bind-keymap Bind a key prefix to an auto-loaded keymap defined in the + package. This is like `:bind', but for keymaps. +:bind-keymap* Like `:bind-keymap', but overrides all minor mode bindings + +:defer Defer loading of a package -- this is implied when using + `:commands', `:bind', `:bind*', `:mode', `:magic', `:hook', + `:magic-fallback', or `:interpreter'. This can be an integer, + to force loading after N seconds of idle time, if the package + has not already been loaded. +:after Delay the use-package declaration until after the named modules + have loaded. Once load, it will be as though the use-package + declaration (without `:after') had been seen at that moment. +:demand Prevent the automatic deferred loading introduced by constructs + such as `:bind' (see `:defer' for the complete list). + +:if EXPR Initialize and load only if EXPR evaluates to a non-nil value. +:disabled The package is ignored completely if this keyword is present. +:defines Declare certain variables to silence the byte-compiler. +:functions Declare certain functions to silence the byte-compiler. +:load-path Add to the `load-path' before attempting to load the package. +:diminish Support for diminish.el (if installed). +:delight Support for delight.el (if installed). +:custom Call `Custom-set' or `set-default' with each variable + definition without modifying the Emacs `custom-file'. + (compare with `custom-set-variables'). +:custom-face Call `custom-set-faces' with each face definition. +:ensure Loads the package using package.el if necessary. +:pin Pin the package to an archive." + (declare (indent defun)) + (unless (memq :disabled args) + (macroexp-progn + (use-package-concat + (when use-package-compute-statistics + `((use-package-statistics-gather :use-package ',name nil))) + (if (eq use-package-verbose 'errors) + (use-package-core name args) + (condition-case-unless-debug err + (use-package-core name args) + (error + (ignore + (display-warning + 'use-package + (format "Failed to parse package %s: %s" + name (error-message-string err)) :error))))) + (when use-package-compute-statistics + `((use-package-statistics-gather :use-package ',name t))))))) + +(provide 'use-package-core) + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: + +;;; use-package-core.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-delight.el b/code/elpa/use-package-20220819.553/use-package-delight.el new file mode 100644 index 0000000..85d5c7c --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-delight.el @@ -0,0 +1,91 @@ +;;; use-package-delight.el --- Support for the :delight keyword -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 3 Dec 2017 +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (use-package "2.4")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Provides support for the :delight keyword, which is made available by +;; default by requiring `use-package'. + +;;; Code: + +(require 'use-package-core) + +(defun use-package-normalize-delight (name args) + "Normalize ARGS for a single call to `delight'." + (when (eq :eval (car args)) + ;; Handle likely common mistake. + (use-package-error ":delight mode line constructs must be quoted")) + (cond ((and (= (length args) 1) + (use-package-non-nil-symbolp (car args))) + `(,(nth 0 args) nil ,name)) + ((= (length args) 2) + `(,(nth 0 args) ,(nth 1 args) ,name)) + ((= (length args) 3) + args) + (t + (use-package-error + ":delight expects `delight' arguments or a list of them")))) + +;;;###autoload +(defun use-package-normalize/:delight (name _keyword args) + "Normalize arguments to delight." + (cond ((null args) + `((,(use-package-as-mode name) nil ,name))) + ((and (= (length args) 1) + (use-package-non-nil-symbolp (car args))) + `((,(car args) nil ,name))) + ((and (= (length args) 1) + (stringp (car args))) + `((,(use-package-as-mode name) ,(car args) ,name))) + ((and (= (length args) 1) + (listp (car args)) + (eq 'quote (caar args))) + `((,(use-package-as-mode name) ,@(cdar args) ,name))) + ((and (= (length args) 2) + (listp (nth 1 args)) + (eq 'quote (car (nth 1 args)))) + `((,(car args) ,@(cdr (nth 1 args)) ,name))) + (t (mapcar + (apply-partially #'use-package-normalize-delight name) + (if (use-package-non-nil-symbolp (car args)) + (list args) + args))))) + +;;;###autoload +(defun use-package-handler/:delight (name _keyword args rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + body + `((if (fboundp 'delight) + (delight '(,@args))))))) + +(add-to-list 'use-package-keywords :delight t) + +(provide 'use-package-delight) + +;;; use-package-delight.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-diminish.el b/code/elpa/use-package-20220819.553/use-package-diminish.el new file mode 100644 index 0000000..1f3895f --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-diminish.el @@ -0,0 +1,80 @@ +;;; use-package-diminish.el --- Support for the :diminish keyword -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 3 Dec 2017 +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (use-package "2.4")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Provides support for the :diminish keyword, which is made available by +;; default by requiring `use-package'. + +;;; Code: + +(require 'use-package-core) + +(defun use-package-normalize-diminish (name label arg &optional recursed) + "Normalize the arguments to diminish down to a list of one of two forms: + SYMBOL + (SYMBOL . STRING)" + (cond + ((not arg) + (list (use-package-as-mode name))) + ((use-package-non-nil-symbolp arg) + (list arg)) + ((stringp arg) + (list (cons (use-package-as-mode name) arg))) + ((and (consp arg) (stringp (cdr arg))) + (list arg)) + ((and (not recursed) (listp arg) (listp (cdr arg))) + (mapcar #'(lambda (x) (car (use-package-normalize-diminish + name label x t))) arg)) + (t + (use-package-error + (concat label " wants a string, symbol, " + "(symbol . string) or list of these"))))) + +;;;###autoload +(defun use-package-normalize/:diminish (name keyword args) + (use-package-as-one (symbol-name keyword) args + (apply-partially #'use-package-normalize-diminish name) t)) + +;;;###autoload +(defun use-package-handler/:diminish (name _keyword arg rest state) + (let ((body (use-package-process-keywords name rest state))) + (use-package-concat + (mapcar #'(lambda (var) + `(if (fboundp 'diminish) + ,(if (consp var) + `(diminish ',(car var) ,(cdr var)) + `(diminish ',var)))) + arg) + body))) + +(add-to-list 'use-package-keywords :diminish t) + +(provide 'use-package-diminish) + +;;; use-package-diminish.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-ensure.el b/code/elpa/use-package-20220819.553/use-package-ensure.el new file mode 100644 index 0000000..cb31b4b --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-ensure.el @@ -0,0 +1,214 @@ +;;; use-package-ensure.el --- Support for the :ensure and :pin keywords -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 3 Dec 2017 +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (use-package "2.4")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Provides support for the :ensure and :pin keywords, which is made available +;; by default by requiring `use-package'. + +;;; Code: + +(require 'cl-lib) +(require 'use-package-core) + +(defgroup use-package-ensure nil + "Support for :ensure and :pin keywords in use-package declarations." + :group 'use-package) + +(eval-when-compile + (declare-function package-installed-p "package") + (declare-function package-read-all-archive-contents "package" ())) + +(defcustom use-package-always-ensure nil + "Treat every package as though it had specified using `:ensure SEXP'. +See also `use-package-defaults', which uses this value." + :type 'sexp + :group 'use-package-ensure) + +(defcustom use-package-always-pin nil + "Treat every package as though it had specified using `:pin SYM'. +See also `use-package-defaults', which uses this value." + :type 'symbol + :group 'use-package-ensure) + +(defcustom use-package-ensure-function 'use-package-ensure-elpa + "Function that ensures a package is installed. +This function is called with three arguments: the name of the +package declared in the `use-package' form; the arguments passed +to all `:ensure' keywords (always a list, even if only one); and +the current `state' plist created by previous handlers. + +Note that this function is called whenever `:ensure' is provided, +even if it is nil. It is up to the function to decide on the +semantics of the various values for `:ensure'. + +This function should return non-nil if the package is installed. + +The default value uses package.el to install the package." + :type '(choice (const :tag "package.el" use-package-ensure-elpa) + (function :tag "Custom")) + :group 'use-package-ensure) + +;;;; :pin + +(defun use-package-normalize/:pin (_name keyword args) + (use-package-only-one (symbol-name keyword) args + #'(lambda (_label arg) + (cond + ((stringp arg) arg) + ((use-package-non-nil-symbolp arg) (symbol-name arg)) + (t + (use-package-error + ":pin wants an archive name (a string)")))))) + +(eval-when-compile + (defvar package-pinned-packages) + (defvar package-archives)) + +(defun use-package-archive-exists-p (archive) + "Check if a given ARCHIVE is enabled. + +ARCHIVE can be a string or a symbol or `manual' to indicate a +manually updated package." + (if (member archive '(manual "manual")) + 't + (let ((valid nil)) + (dolist (pa package-archives) + (when (member archive (list (car pa) (intern (car pa)))) + (setq valid 't))) + valid))) + +(defun use-package-pin-package (package archive) + "Pin PACKAGE to ARCHIVE." + (unless (boundp 'package-pinned-packages) + (setq package-pinned-packages ())) + (let ((archive-symbol (if (symbolp archive) archive (intern archive))) + (archive-name (if (stringp archive) archive (symbol-name archive)))) + (if (use-package-archive-exists-p archive-symbol) + (add-to-list 'package-pinned-packages (cons package archive-name)) + (error "Archive '%s' requested for package '%s' is not available." + archive-name package)) + (unless (bound-and-true-p package--initialized) + (package-initialize t)))) + +(defun use-package-handler/:pin (name _keyword archive-name rest state) + (let ((body (use-package-process-keywords name rest state)) + (pin-form (if archive-name + `(use-package-pin-package ',(use-package-as-symbol name) + ,archive-name)))) + ;; Pinning should occur just before ensuring + ;; See `use-package-handler/:ensure'. + (if (bound-and-true-p byte-compile-current-file) + (eval pin-form) ; Eval when byte-compiling, + (push pin-form body)) ; or else wait until runtime. + body)) + +;;;; :ensure + +(defvar package-archive-contents) + +;;;###autoload +(defun use-package-normalize/:ensure (_name keyword args) + (if (null args) + (list t) + (use-package-only-one (symbol-name keyword) args + #'(lambda (_label arg) + (cond + ((symbolp arg) + (list arg)) + ((and (listp arg) (= 3 (length arg)) + (symbolp (nth 0 arg)) + (eq :pin (nth 1 arg)) + (or (stringp (nth 2 arg)) + (symbolp (nth 2 arg)))) + (list (cons (nth 0 arg) (nth 2 arg)))) + (t + (use-package-error + (concat ":ensure wants an optional package name " + "(an unquoted symbol name), or ( :pin )")))))))) + +(defun use-package-ensure-elpa (name args _state &optional _no-refresh) + (dolist (ensure args) + (let ((package + (or (and (eq ensure t) (use-package-as-symbol name)) + ensure))) + (when package + (require 'package) + (when (consp package) + (use-package-pin-package (car package) (cdr package)) + (setq package (car package))) + (unless (package-installed-p package) + (condition-case-unless-debug err + (progn + (when (assoc package (bound-and-true-p + package-pinned-packages)) + (package-read-all-archive-contents)) + (if (assoc package package-archive-contents) + (package-install package) + (package-refresh-contents) + (when (assoc package (bound-and-true-p + package-pinned-packages)) + (package-read-all-archive-contents)) + (package-install package)) + t) + (error + (display-warning 'use-package + (format "Failed to install %s: %s" + name (error-message-string err)) + :error)))))))) + +;;;###autoload +(defun use-package-handler/:ensure (name _keyword ensure rest state) + (let* ((body (use-package-process-keywords name rest state))) + ;; We want to avoid installing packages when the `use-package' macro is + ;; being macro-expanded by elisp completion (see `lisp--local-variables'), + ;; but still install packages when byte-compiling, to avoid requiring + ;; `package' at runtime. + (if (bound-and-true-p byte-compile-current-file) + ;; Eval when byte-compiling, + (funcall use-package-ensure-function name ensure state) + ;; or else wait until runtime. + (push `(,use-package-ensure-function ',name ',ensure ',state) + body)) + body)) + +(add-to-list 'use-package-defaults + '(:ensure (list use-package-always-ensure) + (lambda (name args) + (and use-package-always-ensure + (not (plist-member args :load-path))))) t) + +(add-to-list 'use-package-defaults + '(:pin use-package-always-pin use-package-always-pin) t) + +(add-to-list 'use-package-keywords :ensure) +(add-to-list 'use-package-keywords :pin) + +(provide 'use-package-ensure) + +;;; use-package-ensure.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-jump.el b/code/elpa/use-package-20220819.553/use-package-jump.el new file mode 100644 index 0000000..4044ad1 --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-jump.el @@ -0,0 +1,79 @@ +;;; use-package-jump.el --- Attempt to jump to a use-package declaration -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 3 Dec 2017 +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (use-package "2.4")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Provides the command `M-x use-package-jump-to-package-form', however it +;; only works if the package being jumped to was required during +;; initialization. If it was delay-loaded, it will not work. Improvements are +;; needed. + +;;; Code: + +(require 'use-package-core) + +(defun use-package-find-require (package) + "Find file that required PACKAGE by searching `load-history'. +Returns an absolute file path or nil if none is found." + (catch 'suspect + (dolist (filespec load-history) + (dolist (entry (cdr filespec)) + (when (equal entry (cons 'require package)) + (throw 'suspect (car filespec))))))) + +;;;###autoload +(defun use-package-jump-to-package-form (package) + "Attempt to find and jump to the `use-package' form that loaded +PACKAGE. This will only find the form if that form actually +required PACKAGE. If PACKAGE was previously required then this +function will jump to the file that originally required PACKAGE +instead." + (interactive (list (completing-read "Package: " features))) + (let* ((package (if (stringp package) (intern package) package)) + (requiring-file (use-package-find-require package)) + file location) + (if (null requiring-file) + (user-error "Can't find file requiring file; may have been autoloaded") + (setq file (if (string= (file-name-extension requiring-file) "elc") + (concat (file-name-sans-extension requiring-file) ".el") + requiring-file)) + (when (file-exists-p file) + (find-file-other-window file) + (save-excursion + (goto-char (point-min)) + (setq location + (re-search-forward + (format (eval use-package-form-regexp-eval) package) nil t))) + (if (null location) + (message "No use-package form found.") + (goto-char location) + (beginning-of-line)))))) + +(provide 'use-package-jump) + +;;; use-package-jump.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-lint.el b/code/elpa/use-package-20220819.553/use-package-lint.el new file mode 100644 index 0000000..c6e7c3c --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-lint.el @@ -0,0 +1,84 @@ +;;; use-package-lint.el --- Attempt to find errors in use-package declarations -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 3 Dec 2017 +;; Version: 1.0 +;; Package-Requires: ((emacs "24.3") (use-package "2.4")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; Provides the command `M-x use-package-lint'. + +;;; Code: + +(require 'cl-lib) +(require 'use-package-core) + +(defun use-package-lint-declaration (name plist) + (dolist (path (plist-get plist :load-path)) + (unless (file-exists-p path) + (display-warning + 'use-package + (format "%s :load-path does not exist: %s" + name path) :error))) + + (unless (or (plist-member plist :disabled) + (plist-get plist :no-require) + (locate-library (use-package-as-string name) nil + (plist-get plist :load-path))) + (display-warning + 'use-package + (format "%s module cannot be located" name) :error)) + + ;; (dolist (command (plist-get plist :commands)) + ;; (unless (string= (find-lisp-object-file-name command nil) + ;; (locate-library (use-package-as-string name) nil + ;; (plist-get plist :load-path))) + ;; (display-warning + ;; 'use-package + ;; (format "%s :command is from different path: %s" + ;; name (symbol-name command)) :error))) + ) + +;;;###autoload +(defun use-package-lint () + "Check for errors in use-package declarations. +For example, if the module's `:if' condition is met, but even +with the specified `:load-path' the module cannot be found." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((re (eval use-package-form-regexp-eval))) + (while (re-search-forward re nil t) + (goto-char (match-beginning 0)) + (let ((decl (read (current-buffer)))) + (when (eq (car decl) 'use-package) + (use-package-lint-declaration + (use-package-as-string (cadr decl)) + (use-package-normalize-keywords + (cadr decl) (cddr decl))))))))) + +(provide 'use-package-lint) + +;;; use-package-lint.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package-pkg.el b/code/elpa/use-package-20220819.553/use-package-pkg.el new file mode 100644 index 0000000..31e2d6f --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package-pkg.el @@ -0,0 +1,13 @@ +(define-package "use-package" "20220819.553" "A configuration macro for simplifying your .emacs" + '((emacs "24.3") + (bind-key "2.4")) + :commit "e2d173b1200865a9efd5c2066831a230497582c0" :authors + '(("John Wiegley" . "johnw@newartisans.com")) + :maintainer + '("John Wiegley" . "johnw@newartisans.com") + :keywords + '("dotemacs" "startup" "speed" "config" "package") + :url "https://github.com/jwiegley/use-package") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/use-package-20220819.553/use-package.el b/code/elpa/use-package-20220819.553/use-package.el new file mode 100644 index 0000000..0e194d5 --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package.el @@ -0,0 +1,54 @@ +;;; use-package.el --- A configuration macro for simplifying your .emacs -*- lexical-binding: t; -*- + +;; Copyright (C) 2012-2017 John Wiegley + +;; Author: John Wiegley +;; Maintainer: John Wiegley +;; Created: 17 Jun 2012 +;; Modified: 29 Nov 2017 +;; Version: 2.4.1 +;; Package-Requires: ((emacs "24.3") (bind-key "2.4")) +;; Keywords: dotemacs startup speed config package +;; URL: https://github.com/jwiegley/use-package + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; The `use-package' declaration macro allows you to isolate package +;; configuration in your ".emacs" in a way that is performance-oriented and, +;; well, just tidy. I created it because I have over 80 packages that I use +;; in Emacs, and things were getting difficult to manage. Yet with this +;; utility my total load time is just under 1 second, with no loss of +;; functionality! +;; +;; Please see README.md from the same repository for documentation. + +;;; Code: + +(require 'use-package-core) + +(require 'use-package-bind-key) +(require 'use-package-diminish) +(require 'use-package-delight) +(require 'use-package-ensure) + +(declare-function use-package-jump-to-package-form "use-package-jump") +(autoload #'use-package-jump-to-package-form "use-package-jump" nil t) + +(provide 'use-package) + +;;; use-package.el ends here diff --git a/code/elpa/use-package-20220819.553/use-package.info b/code/elpa/use-package-20220819.553/use-package.info new file mode 100644 index 0000000..c585f73 --- /dev/null +++ b/code/elpa/use-package-20220819.553/use-package.info @@ -0,0 +1,1003 @@ +This is use-package.info, produced by makeinfo version 6.7 from +use-package.texi. + + Copyright (C) 2012-2022 John Wiegley + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* use-package: (use-package). Declarative package configuration for Emacs. +END-INFO-DIR-ENTRY + + +File: use-package.info, Node: Top, Next: Introduction, Up: (dir) + +use-package User Manual +*********************** + +The ‘use-package’ macro allows you to isolate package configuration in +your ‘.emacs’ file in a way that is both performance-oriented and, well, +tidy. I created it because I have over 80 packages that I use in Emacs, +and things were getting difficult to manage. Yet with this utility my +total load time is around 2 seconds, with no loss of functionality! + + Copyright (C) 2012-2022 John Wiegley + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +* Menu: + +* Introduction:: +* Installation:: +* Getting Started:: +* Basic Concepts:: +* Issues/Requests:: +* Keywords:: +* Debugging Tools:: + +— The Detailed Node Listing — + +Installation + +* Installing from an Elpa Archive:: +* Installing from the Git Repository:: +* Post-Installation Tasks:: + +Keywords + +* after:: +* ‘bind-keymap’, ‘bind-keymap*’: bind-keymap bind-keymap*. +* ‘bind’, ‘bind*’: bind bind*. +* commands:: +* ‘preface’, ‘init’, ‘config’: preface init config. +* custom:: +* custom-face:: +* ‘defer’, ‘demand’: defer demand. +* ‘defines’, ‘functions’: defines functions. +* ‘diminish’, ‘delight’: diminish delight. +* disabled:: +* ‘ensure’, ‘pin’: ensure pin. +* hook:: +* ‘if’, ‘when’, ‘unless’: if when unless. +* load-path:: +* ‘mode’, ‘interpreter’: mode interpreter. +* ‘magic’, ‘magic-fallback’: magic magic-fallback. +* no-require:: +* requires:: + +‘:bind’, ‘:bind*’ + +* Binding to local keymaps:: + + + + +File: use-package.info, Node: Introduction, Next: Installation, Prev: Top, Up: Top + +1 Introduction +************** + +The ‘use-package’ macro allows you to isolate package configuration in +your ‘.emacs’ file in a way that is both performance-oriented and, well, +tidy. I created it because I have over 80 packages that I use in Emacs, +and things were getting difficult to manage. Yet with this utility my +total load time is around 2 seconds, with no loss of functionality! + + More text to come... + + +File: use-package.info, Node: Installation, Next: Getting Started, Prev: Introduction, Up: Top + +2 Installation +************** + +use-package can be installed using Emacs’ package manager or manually +from its development repository. + +* Menu: + +* Installing from an Elpa Archive:: +* Installing from the Git Repository:: +* Post-Installation Tasks:: + + +File: use-package.info, Node: Installing from an Elpa Archive, Next: Installing from the Git Repository, Up: Installation + +2.1 Installing from an Elpa Archive +=================================== + +use-package is available from Melpa and Melpa-Stable. If you haven’t +used Emacs’ package manager before, then it is high time you familiarize +yourself with it by reading the documentation in the Emacs manual, see +*note (emacs)Packages::. Then add one of the archives to +‘package-archives’: + + • To use Melpa: + + (require 'package) + (add-to-list 'package-archives + '("melpa" . "https://melpa.org/packages/") t) + + • To use Melpa-Stable: + + (require 'package) + (add-to-list 'package-archives + '("melpa-stable" . "https://stable.melpa.org/packages/") t) + + Once you have added your preferred archive, you need to update the +local package list using: + + M-x package-refresh-contents RET + + Once you have done that, you can install use-package and its +dependencies using: + + M-x package-install RET use-package RET + + Now see *note Post-Installation Tasks::. + + +File: use-package.info, Node: Installing from the Git Repository, Next: Post-Installation Tasks, Prev: Installing from an Elpa Archive, Up: Installation + +2.2 Installing from the Git Repository +====================================== + +First, use Git to clone the use-package repository: + + $ git clone https://github.com/jwiegley/use-package.git ~/.emacs.d/site-lisp/use-package + $ cd ~/.emacs.d/site-lisp/use-package + + Then compile the libraries and generate the info manuals: + + $ make + + You may need to create ‘/path/to/use-package/config.mk’ with the +following content before running ‘make’: + + LOAD_PATH = -L /path/to/use-package + + Finally add this to your init file: + + (add-to-list 'load-path "~/.emacs.d/site-lisp/use-package") + (require 'use-package) + + (with-eval-after-load 'info + (info-initialize) + (add-to-list 'Info-directory-list + "~/.emacs.d/site-lisp/use-package/")) + + Note that elements of ‘load-path’ should not end with a slash, while +those of ‘Info-directory-list’ should. + + Instead of running use-package directly from the repository by adding +it to the ‘load-path’, you might want to instead install it in some +other directory using ‘sudo make install’ and setting ‘load-path’ +accordingly. + + To update use-package use: + + $ git pull + $ make + + At times it might be necessary to run ‘make clean all’ instead. + + To view all available targets use ‘make help’. + + Now see *note Post-Installation Tasks::. + + +File: use-package.info, Node: Post-Installation Tasks, Prev: Installing from the Git Repository, Up: Installation + +2.3 Post-Installation Tasks +=========================== + +After installing use-package you should verify that you are indeed using +the use-package release you think you are using. It’s best to restart +Emacs before doing so, to make sure you are not using an outdated value +for ‘load-path’. + + C-h v use-package-version RET + + should display something like + + use-package-version’s value is "2.4.1" + + If you are completely new to use-package then see *note Getting +Started::. + + If you run into problems, then please see the *note Debugging +Tools::. + + +File: use-package.info, Node: Getting Started, Next: Basic Concepts, Prev: Installation, Up: Top + +3 Getting Started +***************** + +TODO. For now, see ‘README.md’. + + +File: use-package.info, Node: Basic Concepts, Next: Issues/Requests, Prev: Getting Started, Up: Top + +4 Basic Concepts +**************** + +‘use-package’ was created for few basic reasons, each of which drove the +design in various ways. Understanding these reasons may help make some +of those decisions clearer: + + • To gather all configuration details of a package into one place, + making it easier to copy, disable, or move it elsewhere in the init + file. + + • To reduce duplication and boilerplate, capturing several common + practices as mere keywords both easy and intuitive to use. + + • To make startup time of Emacs as quick as possible, without + sacrificing the quantity of add-on packages used. + + • To make it so errors encountered during startup disable only the + package raising the error, and as little else as possible, leaving + a close to a functional Emacs as possible. + + • To allow byte-compilation of one’s init file so that any warnings + or errors seen are meaningful. In this way, even if + byte-compilation is not used for speed (reason 3), it can still be + used as a sanity check. + + +File: use-package.info, Node: Issues/Requests, Next: Keywords, Prev: Basic Concepts, Up: Top + +5 Issues/Requests +***************** + + +File: use-package.info, Node: Keywords, Next: Debugging Tools, Prev: Issues/Requests, Up: Top + +6 Keywords +********** + +* Menu: + +* after:: +* ‘bind-keymap’, ‘bind-keymap*’: bind-keymap bind-keymap*. +* ‘bind’, ‘bind*’: bind bind*. +* commands:: +* ‘preface’, ‘init’, ‘config’: preface init config. +* custom:: +* custom-face:: +* ‘defer’, ‘demand’: defer demand. +* ‘defines’, ‘functions’: defines functions. +* ‘diminish’, ‘delight’: diminish delight. +* disabled:: +* ‘ensure’, ‘pin’: ensure pin. +* hook:: +* ‘if’, ‘when’, ‘unless’: if when unless. +* load-path:: +* ‘mode’, ‘interpreter’: mode interpreter. +* ‘magic’, ‘magic-fallback’: magic magic-fallback. +* no-require:: +* requires:: + + +File: use-package.info, Node: after, Next: bind-keymap bind-keymap*, Up: Keywords + +6.1 ‘:after’ +============ + +Sometimes it only makes sense to configure a package after another has +been loaded, because certain variables or functions are not in scope +until that time. This can achieved using an ‘:after’ keyword that +allows a fairly rich description of the exact conditions when loading +should occur. Here is an example: + + (use-package hydra + :load-path "site-lisp/hydra") + + (use-package ivy + :load-path "site-lisp/swiper") + + (use-package ivy-hydra + :after (ivy hydra)) + + In this case, because all of these packages are demand-loaded in the +order they occur, the use of ‘:after’ is not strictly necessary. By +using it, however, the above code becomes order-independent, without an +implicit depedence on the nature of your init file. + + By default, ‘:after (foo bar)’ is the same as ‘:after (:all foo +bar)’, meaning that loading of the given package will not happen until +both ‘foo’ and ‘bar’ have been loaded. Here are some of the other +possibilities: + + :after (foo bar) + :after (:all foo bar) + :after (:any foo bar) + :after (:all (:any foo bar) (:any baz quux)) + :after (:any (:all foo bar) (:all baz quux)) + + When you nest selectors, such as ‘(:any (:all foo bar) (:all baz +quux))’, it means that the package will be loaded when either both ‘foo’ +and ‘bar’ have been loaded, or both ‘baz’ and ‘quux’ have been loaded. + + *NOTE*: Pay attention if you set ‘use-package-always-defer’ to t, and +also use the ‘:after’ keyword, as you will need to specify how the +declared package is to be loaded: e.g., by some ‘:bind’. If you’re not +using one of the mechanisms that registers autoloads, such as ‘:bind’ or +‘:hook’, and your package manager does not provide autoloads, it’s +possible that without adding ‘:demand t’ to those declarations, your +package will never be loaded. + + +File: use-package.info, Node: bind-keymap bind-keymap*, Next: bind bind*, Prev: after, Up: Keywords + +6.2 ‘:bind-keymap’, ‘:bind-keymap*’ +=================================== + +Normally ‘:bind’ expects that commands are functions that will be +autoloaded from the given package. However, this does not work if one +of those commands is actually a keymap, since keymaps are not functions, +and cannot be autoloaded using Emacs’ ‘autoload’ mechanism. + + To handle this case, ‘use-package’ offers a special, limited variant +of ‘:bind’ called ‘:bind-keymap’. The only difference is that the +"commands" bound to by ‘:bind-keymap’ must be keymaps defined in the +package, rather than command functions. This is handled behind the +scenes by generating custom code that loads the package containing the +keymap, and then re-executes your keypress after the first load, to +reinterpret that keypress as a prefix key. + + For example: + + (use-package projectile + :bind-keymap + ("C-c p" . projectile-command-map) + + +File: use-package.info, Node: bind bind*, Next: commands, Prev: bind-keymap bind-keymap*, Up: Keywords + +6.3 ‘:bind’, ‘:bind*’ +===================== + +Another common thing to do when loading a module is to bind a key to +primary commands within that module: + + (use-package ace-jump-mode + :bind ("C-." . ace-jump-mode)) + + This does two things: first, it creates an autoload for the +‘ace-jump-mode’ command and defers loading of ‘ace-jump-mode’ until you +actually use it. Second, it binds the key ‘C-.’ to that command. After +loading, you can use ‘M-x describe-personal-keybindings’ to see all such +keybindings you’ve set throughout your ‘.emacs’ file. + + A more literal way to do the exact same thing is: + + (use-package ace-jump-mode + :commands ace-jump-mode + :init + (bind-key "C-." 'ace-jump-mode)) + + When you use the ‘:commands’ keyword, it creates autoloads for those +commands and defers loading of the module until they are used. Since +the ‘:init’ form is always run—even if ‘ace-jump-mode’ might not be on +your system—remember to restrict ‘:init’ code to only what would succeed +either way. + + The ‘:bind’ keyword takes either a cons or a list of conses: + + (use-package hi-lock + :bind (("M-o l" . highlight-lines-matching-regexp) + ("M-o r" . highlight-regexp) + ("M-o w" . highlight-phrase))) + + The ‘:commands’ keyword likewise takes either a symbol or a list of +symbols. + + NOTE: Special keys like ‘tab’ or ‘F1’-‘Fn’ can be written in square +brackets, i.e. ‘[tab]’ instead of ‘"tab"’. The syntax for the +keybindings is similar to the "kbd" syntax: see the Emacs Manual +(https://www.gnu.org/software/emacs/manual/html_node/emacs/Init-Rebinding.html) +for more information. + + Examples: + + (use-package helm + :bind (("M-x" . helm-M-x) + ("M-" . helm-find-files) + ([f10] . helm-buffers-list) + ([S-f10] . helm-recentf))) + +* Menu: + +* Binding to local keymaps:: + + +File: use-package.info, Node: Binding to local keymaps, Up: bind bind* + +6.3.1 Binding to local keymaps +------------------------------ + +Slightly different from binding a key to a keymap, is binding a key +*within* a local keymap that only exists after the package is loaded. +‘use-package’ supports this with a ‘:map’ modifier, taking the local +keymap to bind to: + + (use-package helm + :bind (:map helm-command-map + ("C-c h" . helm-execute-persistent-action))) + + The effect of this statement is to wait until ‘helm’ has loaded, and +then to bind the key ‘C-c h’ to ‘helm-execute-persistent-action’ within +Helm’s local keymap, ‘helm-mode-map’. + + Multiple uses of ‘:map’ may be specified. Any binding occurring +before the first use of ‘:map’ are applied to the global keymap: + + (use-package term + :bind (("C-c t" . term) + :map term-mode-map + ("M-p" . term-send-up) + ("M-n" . term-send-down) + :map term-raw-map + ("M-o" . other-window) + ("M-p" . term-send-up) + ("M-n" . term-send-down))) + + +File: use-package.info, Node: commands, Next: preface init config, Prev: bind bind*, Up: Keywords + +6.4 ‘:commands’ +=============== + + +File: use-package.info, Node: preface init config, Next: custom, Prev: commands, Up: Keywords + +6.5 ‘:preface’, ‘:init’, ‘:config’ +================================== + +Here is the simplest ‘use-package’ declaration: + + ;; This is only needed once, near the top of the file + (eval-when-compile + ;; Following line is not needed if use-package.el is in ~/.emacs.d + (add-to-list 'load-path "") + (require 'use-package)) + + (use-package foo) + + This loads in the package ‘foo’, but only if ‘foo’ is available on +your system. If not, a warning is logged to the ‘*Messages*’ buffer. +If it succeeds, a message about ‘"Loading foo"’ is logged, along with +the time it took to load, if it took over 0.1 seconds. + + Use the ‘:init’ keyword to execute code before a package is loaded. +It accepts one or more forms, up until the next keyword: + + (use-package foo + :init + (setq foo-variable t)) + + Similarly, ‘:config’ can be used to execute code after a package is +loaded. In cases where loading is done lazily (see more about +autoloading below), this execution is deferred until after the autoload +occurs: + + (use-package foo + :init + (setq foo-variable t) + :config + (foo-mode 1)) + + As you might expect, you can use ‘:init’ and ‘:config’ together: + + (use-package color-moccur + :commands (isearch-moccur isearch-all) + :bind (("M-s O" . moccur) + :map isearch-mode-map + ("M-o" . isearch-moccur) + ("M-O" . isearch-moccur-all)) + :init + (setq isearch-lazy-highlight t) + :config + (use-package moccur-edit)) + + In this case, I want to autoload the commands ‘isearch-moccur’ and +‘isearch-all’ from ‘color-moccur.el’, and bind keys both at the global +level and within the ‘isearch-mode-map’ (see next section). When the +package is actually loaded (by using one of these commands), +‘moccur-edit’ is also loaded, to allow editing of the ‘moccur’ buffer. + + +File: use-package.info, Node: custom, Next: custom-face, Prev: preface init config, Up: Keywords + +6.6 ‘:custom’ +============= + +The ‘:custom’ keyword allows customization of package custom variables. + + (use-package comint + :custom + (comint-buffer-maximum-size 20000 "Increase comint buffer size.") + (comint-prompt-read-only t "Make the prompt read only.")) + + The documentation string is not mandatory. + + +File: use-package.info, Node: custom-face, Next: defer demand, Prev: custom, Up: Keywords + +6.7 ‘:custom-face’ +================== + +The ‘:custom-face’ keyword allows customization of package custom faces. + + (use-package eruby-mode + :custom-face + (eruby-standard-face ((t (:slant italic))))) + + +File: use-package.info, Node: defer demand, Next: defines functions, Prev: custom-face, Up: Keywords + +6.8 ‘:defer’, ‘:demand’ +======================= + +In almost all cases you don’t need to manually specify ‘:defer t’. This +is implied whenever ‘:bind’ or ‘:mode’ or ‘:interpreter’ is used. +Typically, you only need to specify ‘:defer’ if you know for a fact that +some other package will do something to cause your package to load at +the appropriate time, and thus you would like to defer loading even +though use-package isn’t creating any autoloads for you. + + You can override package deferral with the ‘:demand’ keyword. Thus, +even if you use ‘:bind’, using ‘:demand’ will force loading to occur +immediately and not establish an autoload for the bound key. + + +File: use-package.info, Node: defines functions, Next: diminish delight, Prev: defer demand, Up: Keywords + +6.9 ‘:defines’, ‘:functions’ +============================ + +Another feature of ‘use-package’ is that it always loads every file that +it can when ‘.emacs’ is being byte-compiled. This helps to silence +spurious warnings about unknown variables and functions. + + However, there are times when this is just not enough. For those +times, use the ‘:defines’ and ‘:functions’ keywords to introduce dummy +variable and function declarations solely for the sake of the +byte-compiler: + + (use-package texinfo + :defines texinfo-section-list + :commands texinfo-mode + :init + (add-to-list 'auto-mode-alist '("\\.texi$" . texinfo-mode))) + + If you need to silence a missing function warning, you can use +‘:functions’: + + (use-package ruby-mode + :mode "\\.rb\\'" + :interpreter "ruby" + :functions inf-ruby-keys + :config + (defun my-ruby-mode-hook () + (require 'inf-ruby) + (inf-ruby-keys)) + + (add-hook 'ruby-mode-hook 'my-ruby-mode-hook)) + + +File: use-package.info, Node: diminish delight, Next: disabled, Prev: defines functions, Up: Keywords + +6.10 ‘:diminish’, ‘:delight’ +============================ + +‘use-package’ also provides built-in support for the diminish and +delight utilities—if you have them installed. Their purpose is to +remove or change minor mode strings in your mode-line. + + diminish (https://github.com/myrjola/diminish.el) is invoked with the +‘:diminish’ keyword, which is passed either a minor mode symbol, a cons +of the symbol and its replacement string, or just a replacement string, +in which case the minor mode symbol is guessed to be the package name +with "-mode" appended at the end: + + (use-package abbrev + :diminish abbrev-mode + :config + (if (file-exists-p abbrev-file-name) + (quietly-read-abbrev-file))) + + delight (https://elpa.gnu.org/packages/delight.html) is invoked with +the ‘:delight’ keyword, which is passed a minor mode symbol, a +replacement string or quoted mode-line data +(https://www.gnu.org/software/emacs/manual/html_node/elisp/Mode-Line-Data.html) +(in which case the minor mode symbol is guessed to be the package name +with "-mode" appended at the end), both of these, or several lists of +both. If no arguments are provided, the default mode name is hidden +completely. + + ;; Don't show anything for rainbow-mode. + (use-package rainbow-mode + :delight) + + ;; Don't show anything for auto-revert-mode, which doesn't match + ;; its package name. + (use-package autorevert + :delight auto-revert-mode) + + ;; Remove the mode name for projectile-mode, but show the project name. + (use-package projectile + :delight '(:eval (concat " " (projectile-project-name)))) + + ;; Completely hide visual-line-mode and change auto-fill-mode to " AF". + (use-package emacs + :delight + (auto-fill-function " AF") + (visual-line-mode)) + + +File: use-package.info, Node: disabled, Next: ensure pin, Prev: diminish delight, Up: Keywords + +6.11 ‘:disabled’ +================ + +The ‘:disabled’ keyword can turn off a module you’re having difficulties +with, or stop loading something you’re not using at the present time: + + (use-package ess-site + :disabled + :commands R) + + When byte-compiling your ‘.emacs’ file, disabled declarations are +omitted from the output entirely, to accelerate startup times. + + +File: use-package.info, Node: ensure pin, Next: hook, Prev: disabled, Up: Keywords + +6.12 ‘:ensure’, ‘:pin’ +====================== + +You can use ‘use-package’ to load packages from ELPA with ‘package.el’. +This is particularly useful if you share your ‘.emacs’ among several +machines; the relevant packages are downloaded automatically once +declared in your ‘.emacs’. The ‘:ensure’ keyword causes the package(s) +to be installed automatically if not already present on your system (set +‘(setq use-package-always-ensure t)’ if you wish this behavior to be +global for all packages): + + (use-package magit + :ensure t) + + If you need to install a different package from the one named by +‘use-package’, you can specify it like this: + + (use-package tex + :ensure auctex) + + Lastly, when running on Emacs 24.4 or later, use-package can pin a +package to a specific archive, allowing you to mix and match packages +from different archives. The primary use-case for this is preferring +packages from the ‘melpa-stable’ and ‘gnu’ archives, but using specific +packages from ‘melpa’ when you need to track newer versions than what is +available in the ‘stable’ archives is also a valid use-case. + + By default ‘package.el’ prefers ‘melpa’ over ‘melpa-stable’ due to +the versioning ‘(> evil-20141208.623 evil-1.0.9)’, so even if you are +tracking only a single package from ‘melpa’, you will need to tag all +the non-‘melpa’ packages with the appropriate archive. If this really +annoys you, then you can set ‘use-package-always-pin’ to set a default. + + If you want to manually keep a package updated and ignore upstream +updates, you can pin it to ‘manual’, which as long as there is no +repository by that name, will Just Work(tm). + + ‘use-package’ throws an error if you try to pin a package to an +archive that has not been configured using ‘package-archives’ (apart +from the magic ‘manual’ archive mentioned above): + + Archive 'foo' requested for package 'bar' is not available. + + Example: + + (use-package company + :ensure t + :pin melpa-stable) + + (use-package evil + :ensure t) + ;; no :pin needed, as package.el will choose the version in melpa + + (use-package adaptive-wrap + :ensure t + ;; as this package is available only in the gnu archive, this is + ;; technically not needed, but it helps to highlight where it + ;; comes from + :pin gnu) + + (use-package org + :ensure t + ;; ignore org-mode from upstream and use a manually installed version + :pin manual) + + *NOTE*: the ‘:pin’ argument has no effect on emacs versions < 24.4. + + +File: use-package.info, Node: hook, Next: if when unless, Prev: ensure pin, Up: Keywords + +6.13 ‘:hook’ +============ + +The ‘:hook’ keyword allows adding functions onto hooks, here only the +basename of the hook is required. Thus, all of the following are +equivalent: + + (use-package ace-jump-mode + :hook prog-mode) + + (use-package ace-jump-mode + :hook (prog-mode . ace-jump-mode)) + + (use-package ace-jump-mode + :commands ace-jump-mode + :init + (add-hook 'prog-mode-hook #'ace-jump-mode)) + + And likewise, when multiple hooks should be applied, the following +are also equivalent: + + (use-package ace-jump-mode + :hook (prog-mode text-mode)) + + (use-package ace-jump-mode + :hook ((prog-mode text-mode) . ace-jump-mode)) + + (use-package ace-jump-mode + :hook ((prog-mode . ace-jump-mode) + (text-mode . ace-jump-mode))) + + (use-package ace-jump-mode + :commands ace-jump-mode + :init + (add-hook 'prog-mode-hook #'ace-jump-mode) + (add-hook 'text-mode-hook #'ace-jump-mode)) + + The use of ‘:hook’, as with ‘:bind’, ‘:mode’, ‘:interpreter’, etc., +causes the functions being hooked to implicitly be read as ‘:commands’ +(meaning they will establish interactive ‘autoload’ definitions for that +module, if not already defined as functions), and so ‘:defer t’ is also +implied by ‘:hook’. + + +File: use-package.info, Node: if when unless, Next: load-path, Prev: hook, Up: Keywords + +6.14 ‘:if’, ‘:when’, ‘:unless’ +============================== + +You can use the ‘:if’ keyword to predicate the loading and +initialization of modules. + + For example, I only want ‘edit-server’ running for my main, graphical +Emacs, not for other Emacsen I may start at the command line: + + (use-package edit-server + :if window-system + :init + (add-hook 'after-init-hook 'server-start t) + (add-hook 'after-init-hook 'edit-server-start t)) + + In another example, we can load things conditional on the operating +system: + + (use-package exec-path-from-shell + :if (memq window-system '(mac ns)) + :ensure t + :config + (exec-path-from-shell-initialize)) + + Note that ‘:when’ is provided as an alias for ‘:if’, and ‘:unless +foo’ means the same thing as ‘:if (not foo)’. + + +File: use-package.info, Node: load-path, Next: mode interpreter, Prev: if when unless, Up: Keywords + +6.15 ‘:load-path’ +================= + +If your package needs a directory added to the ‘load-path’ in order to +load, use ‘:load-path’. This takes a symbol, a function, a string or a +list of strings. If the path is relative, it is expanded within +‘user-emacs-directory’: + + (use-package ess-site + :load-path "site-lisp/ess/lisp/" + :commands R) + + Note that when using a symbol or a function to provide a dynamically +generated list of paths, you must inform the byte-compiler of this +definition so the value is available at byte-compilation time. This is +done by using the special form ‘eval-and-compile’ (as opposed to +‘eval-when-compile’). Further, this value is fixed at whatever was +determined during compilation, to avoid looking up the same information +again on each startup: + + (eval-and-compile + (defun ess-site-load-path () + (shell-command "find ~ -path ess/lisp"))) + + (use-package ess-site + :load-path (lambda () (list (ess-site-load-path))) + :commands R) + + +File: use-package.info, Node: mode interpreter, Next: magic magic-fallback, Prev: load-path, Up: Keywords + +6.16 ‘:mode’, ‘:interpreter’ +============================ + +Similar to ‘:bind’, you can use ‘:mode’ and ‘:interpreter’ to establish +a deferred binding within the ‘auto-mode-alist’ and +‘interpreter-mode-alist’ variables. The specifier to either keyword can +be a cons cell, a list of cons cells, or a string or regexp: + + (use-package ruby-mode + :mode "\\.rb\\'" + :interpreter "ruby") + + ;; The package is "python" but the mode is "python-mode": + (use-package python + :mode ("\\.py\\'" . python-mode) + :interpreter ("python" . python-mode)) + + If you aren’t using ‘:commands’, ‘:bind’, ‘:bind*’, ‘:bind-keymap’, +‘:bind-keymap*’, ‘:mode’, or ‘:interpreter’ (all of which imply +‘:defer’; see the docstring for ‘use-package’ for a brief description of +each), you can still defer loading with the ‘:defer’ keyword: + + (use-package ace-jump-mode + :defer t + :init + (autoload 'ace-jump-mode "ace-jump-mode" nil t) + (bind-key "C-." 'ace-jump-mode)) + + This does exactly the same thing as the following: + + (use-package ace-jump-mode + :bind ("C-." . ace-jump-mode)) + + +File: use-package.info, Node: magic magic-fallback, Next: no-require, Prev: mode interpreter, Up: Keywords + +6.17 ‘:magic’, ‘:magic-fallback’ +================================ + +Similar to ‘:mode’ and ‘:interpreter’, you can also use ‘:magic’ and +‘:magic-fallback’ to cause certain function to be run if the beginning +of a file matches a given regular expression. The difference between +the two is that ‘:magic-fallback’ has a lower priority than ‘:mode’. +For example: + + (use-package pdf-tools + :load-path "site-lisp/pdf-tools/lisp" + :magic ("%PDF" . pdf-view-mode) + :config + (pdf-tools-install)) + + This registers an autoloaded command for ‘pdf-view-mode’, defers +loading of ‘pdf-tools’, and runs ‘pdf-view-mode’ if the beginning of a +buffer matches the string ‘"%PDF"’. + + +File: use-package.info, Node: no-require, Next: requires, Prev: magic magic-fallback, Up: Keywords + +6.18 ‘:no-require’ +================== + +Normally, ‘use-package’ will load each package at compile time before +compiling the configuration, to ensure that any necessary symbols are in +scope to satisfy the byte-compiler. At times this can cause problems, +since a package may have special loading requirements, and all that you +want to use ‘use-package’ for is to add a configuration to the +‘eval-after-load’ hook. In such cases, use the ‘:no-require’ keyword: + + (use-package foo + :no-require t + :config + (message "This is evaluated when `foo' is loaded")) + + +File: use-package.info, Node: requires, Prev: no-require, Up: Keywords + +6.19 ‘:requires’ +================ + +While the ‘:after’ keyword delays loading until the dependencies are +loaded, the somewhat simpler ‘:requires’ keyword simply never loads the +package if the dependencies are not available at the time the +‘use-package’ declaration is encountered. By "available" in this +context it means that ‘foo’ is available of ‘(featurep 'foo)’ evaluates +to a non-nil value. For example: + + (use-package abbrev + :requires foo) + + This is the same as: + + (use-package abbrev + :if (featurep 'foo)) + + As a convenience, a list of such packages may be specified: + + (use-package abbrev + :requires (foo bar baz)) + + For more complex logic, such as that supported by ‘:after’, simply +use ‘:if’ and the appropriate Lisp expression. + + +File: use-package.info, Node: Debugging Tools, Prev: Keywords, Up: Top + +7 Debugging Tools +***************** + +TODO + + + +Tag Table: +Node: Top786 +Node: Introduction2828 +Node: Installation3339 +Node: Installing from an Elpa Archive3691 +Node: Installing from the Git Repository4820 +Node: Post-Installation Tasks6363 +Node: Getting Started7051 +Node: Basic Concepts7230 +Node: Issues/Requests8394 +Node: Keywords8531 +Node: after9299 +Node: bind-keymap bind-keymap*11311 +Node: bind bind*12364 +Node: Binding to local keymaps14439 +Node: commands15586 +Node: preface init config15728 +Node: custom17827 +Node: custom-face18267 +Node: defer demand18587 +Node: defines functions19399 +Node: diminish delight20544 +Node: disabled22487 +Node: ensure pin22982 +Node: hook25712 +Node: if when unless27137 +Node: load-path28083 +Node: mode interpreter29229 +Node: magic magic-fallback30540 +Node: no-require31394 +Node: requires32098 +Node: Debugging Tools32985 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/web-server-20210708.2242/dir b/code/elpa/web-server-20210708.2242/dir new file mode 100644 index 0000000..e3d7778 --- /dev/null +++ b/code/elpa/web-server-20210708.2242/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* Web Server: (web-server). Web Server for Emacs. diff --git a/code/elpa/web-server-20210708.2242/doclicense.info b/code/elpa/web-server-20210708.2242/doclicense.info new file mode 100644 index 0000000..30401b7 --- /dev/null +++ b/code/elpa/web-server-20210708.2242/doclicense.info @@ -0,0 +1,489 @@ +This is doclicense.info, produced by makeinfo version 6.7 from +doclicense.texi. + + Version 1.3, 3 November 2008 + + Copyright (C) 2000, 2001, 2002, 2007, 2008, 2009 Free Software Foundation, Inc. + + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document "free" in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of "copyleft", which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. We + recommend this License principally for works whose purpose is + instruction or reference. + + 1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it can + be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + "Document", below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as "you". You accept + the license if you copy, modify or distribute the work in a way + requiring permission under copyright law. + + A "Modified Version" of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A "Secondary Section" is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document's overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The "Invariant Sections" are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in the + notice that says that the Document is released under this License. + If a section does not fit the above definition of Secondary then it + is not allowed to be designated as Invariant. The Document may + contain zero Invariant Sections. If the Document does not identify + any Invariant Sections then there are none. + + The "Cover Texts" are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A "Transparent" copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images composed + of pixels) generic paint programs or (for drawings) some widely + available drawing editor, and that is suitable for input to text + formatters or for automatic translation to a variety of formats + suitable for input to text formatters. A copy made in an otherwise + Transparent file format whose markup, or absence of markup, has + been arranged to thwart or discourage subsequent modification by + readers is not Transparent. An image format is not Transparent if + used for any substantial amount of text. A copy that is not + "Transparent" is called "Opaque". + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and standard-conforming + simple HTML, PostScript or PDF designed for human modification. + Examples of transparent image formats include PNG, XCF and JPG. + Opaque formats include proprietary formats that can be read and + edited only by proprietary word processors, SGML or XML for which + the DTD and/or processing tools are not generally available, and + the machine-generated HTML, PostScript or PDF produced by some word + processors for output purposes only. + + The "Title Page" means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, "Title + Page" means the text near the most prominent appearance of the + work's title, preceding the beginning of the body of the text. + + The "publisher" means any person or entity that distributes copies + of the Document to the public. + + A section "Entitled XYZ" means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + "Acknowledgements", "Dedications", "Endorsements", or "History".) + To "Preserve the Title" of such a section when you modify the + Document means that it remains a section "Entitled XYZ" according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + + 2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow the + conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + + 3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document's license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the title + equally prominent and visible. You may add other material on the + covers in addition. Copying with changes limited to the covers, as + long as they preserve the title of the Document and satisfy these + conditions, can be treated as verbatim copying in other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a machine-readable + Transparent copy along with each Opaque copy, or state in or with + each Opaque copy a computer-network location from which the general + network-using public has access to download using public-standard + network protocols a complete Transparent copy of the Document, free + of added material. If you use the latter option, you must take + reasonably prudent steps, when you begin distribution of Opaque + copies in quantity, to ensure that this Transparent copy will + remain thus accessible at the stated location until at least one + year after the last time you distribute an Opaque copy (directly or + through your agents or retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of copies, + to give them a chance to provide you with an updated version of the + Document. + + 4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with the + Modified Version filling the role of the Document, thus licensing + distribution and modification of the Modified Version to whoever + possesses a copy of it. In addition, you must do these things in + the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of previous + versions (which should, if there were any, be listed in the + History section of the Document). You may use the same title + as a previous version if the original publisher of that + version gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has fewer than five), unless they release you + from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified + Version under the terms of this License, in the form shown in + the Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document's + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled "History", Preserve its Title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on the + Title Page. If there is no section Entitled "History" in the + Document, create one stating the title, year, authors, and + publisher of the Document as given on its Title Page, then add + an item describing the Modified Version as stated in the + previous sentence. + + J. Preserve the network location, if any, given in the Document + for public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in the + "History" section. You may omit a network location for a work + that was published at least four years before the Document + itself, or if the original publisher of the version it refers + to gives permission. + + K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section + all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, unaltered + in their text and in their titles. Section numbers or the + equivalent are not considered part of the section titles. + + M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + "Endorsements" or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option designate + some or all of these sections as invariant. To do this, add their + titles to the list of Invariant Sections in the Modified Version's + license notice. These titles must be distinct from any other + section titles. + + You may add a section Entitled "Endorsements", provided it contains + nothing but endorsements of your Modified Version by various + parties--for example, statements of peer review or that the text + has been approved by an organization as the authoritative + definition of a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end of + the list of Cover Texts in the Modified Version. Only one passage + of Front-Cover Text and one of Back-Cover Text may be added by (or + through arrangements made by) any one entity. If the Document + already includes a cover text for the same cover, previously added + by you or by arrangement made by the same entity you are acting on + behalf of, you may not add another; but you may replace the old + one, on explicit permission from the previous publisher that added + the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + + 5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination all + of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + "History" in the various original documents, forming one section + Entitled "History"; likewise combine any sections Entitled + "Acknowledgements", and any sections Entitled "Dedications". You + must delete all sections Entitled "Endorsements." + + 6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the documents + in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow this + License in all other respects regarding verbatim copying of that + document. + + 7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of a + storage or distribution medium, is called an "aggregate" if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation's users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document's Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + + 8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of section + 4. Replacing Invariant Sections with translations requires special + permission from their copyright holders, but you may include + translations of some or all Invariant Sections in addition to the + original versions of these Invariant Sections. You may include a + translation of this License, and all the license notices in the + Document, and any Warranty Disclaimers, provided that you also + include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled "Acknowledgements", + "Dedications", or "History", the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + + 9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, or distribute it is void, + and will automatically terminate your rights under this License. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, receipt of a copy of some or all of the + same material does not give you any rights to use it. + + 10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. See + . + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If the + Document does not specify a version number of this License, you may + choose any version ever published (not as a draft) by the Free + Software Foundation. If the Document specifies that a proxy can + decide which future versions of this License can be used, that + proxy's public statement of acceptance of a version permanently + authorizes you to choose that version for the Document. + + 11. RELICENSING + + "Massive Multiauthor Collaboration Site" (or "MMC Site") means any + World Wide Web server that publishes copyrightable works and also + provides prominent facilities for anybody to edit those works. A + public wiki that anybody can edit is an example of such a server. + A "Massive Multiauthor Collaboration" (or "MMC") contained in the + site means any set of copyrightable works thus published on the MMC + site. + + "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 + license published by Creative Commons Corporation, a not-for-profit + corporation with a principal place of business in San Francisco, + California, as well as future copyleft versions of that license + published by that same organization. + + "Incorporate" means to publish or republish a Document, in whole or + in part, as part of another Document. + + An MMC is "eligible for relicensing" if it is licensed under this + License, and if all works that were first published under this + License somewhere other than this MMC, and subsequently + incorporated in whole or in part into the MMC, (1) had no cover + texts or invariant sections, and (2) were thus incorporated prior + to November 1, 2008. + + The operator of an MMC Site may republish an MMC contained in the + site under CC-BY-SA on the same site at any time before August 1, + 2009, provided the MMC is eligible for relicensing. + +ADDENDUM: How to use this License for your documents +==================================================== + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and license +notices just after the title page: + + Copyright (C) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + + If you have Invariant Sections, Front-Cover Texts and Back-Cover +Texts, replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with + the Front-Cover Texts being LIST, and with the Back-Cover Texts + being LIST. + + If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + + If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of free +software license, such as the GNU General Public License, to permit +their use in free software. + + + +Tag Table: + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/web-server-20210708.2242/gpl.info b/code/elpa/web-server-20210708.2242/gpl.info new file mode 100644 index 0000000..3879140 --- /dev/null +++ b/code/elpa/web-server-20210708.2242/gpl.info @@ -0,0 +1,721 @@ +This is gpl.info, produced by makeinfo version 6.7 from gpl.texi. + + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies of this + license document, but changing it is not allowed. + +Preamble +======== + +The GNU General Public License is a free, copyleft license for software +and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS +==================== + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public + License. + + "Copyright" also means copyright-like laws that apply to other + kinds of works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this + License. Each licensee is addressed as "you". "Licensees" and + "recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the + work in a fashion requiring copyright permission, other than the + making of an exact copy. The resulting work is called a "modified + version" of the earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work + based on the Program. + + To "propagate" a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on + a computer or modifying a private copy. Propagation includes + copying, distribution (with or without modification), making + available to the public, and in some countries other activities as + well. + + To "convey" a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user + through a computer network, with no transfer of a copy, is not + conveying. + + An interactive user interface displays "Appropriate Legal Notices" + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to + the extent that warranties are provided), that licensees may convey + the work under this License, and how to view a copy of this + License. If the interface presents a list of user commands or + options, such as a menu, a prominent item in the list meets this + criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work + for making modifications to it. "Object code" means any non-source + form of a work. + + A "Standard Interface" means an interface that either is an + official standard defined by a recognized standards body, or, in + the case of interfaces specified for a particular programming + language, one that is widely used among developers working in that + language. + + The "System Libraries" of an executable work include anything, + other than the work as a whole, that (a) is included in the normal + form of packaging a Major Component, but which is not part of that + Major Component, and (b) serves only to enable use of the work with + that Major Component, or to implement a Standard Interface for + which an implementation is available to the public in source code + form. A "Major Component", in this context, means a major + essential component (kernel, window system, and so on) of the + specific operating system (if any) on which the executable work + runs, or a compiler used to produce the work, or an object code + interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts + to control those activities. However, it does not include the + work's System Libraries, or general-purpose tools or generally + available free programs which are used unmodified in performing + those activities but which are not part of the work. For example, + Corresponding Source includes interface definition files associated + with source files for the work, and the source code for shared + libraries and dynamically linked subprograms that the work is + specifically designed to require, such as by intimate data + communication or control flow between those subprograms and other + parts of the work. + + The Corresponding Source need not include anything that users can + regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running + a covered work is covered by this License only if the output, given + its content, constitutes a covered work. This License acknowledges + your rights of fair use or other equivalent, as provided by + copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise + remains in force. You may convey covered works to others for the + sole purpose of having them make modifications exclusively for you, + or provide you with facilities for running those works, provided + that you comply with the terms of this License in conveying all + material for which you do not control copyright. Those thus making + or running the covered works for you must do so exclusively on your + behalf, under your direction and control, on terms that prohibit + them from making any copies of your copyrighted material outside + their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section + 10 makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under + article 11 of the WIPO copyright treaty adopted on 20 December + 1996, or similar laws prohibiting or restricting circumvention of + such measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such + circumvention is effected by exercising rights under this License + with respect to the covered work, and you disclaim any intention to + limit operation or modification of the work as a means of + enforcing, against the work's users, your or third parties' legal + rights to forbid circumvention of technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the + code; keep intact all notices of the absence of any warranty; and + give all recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these + conditions: + + a. The work must carry prominent notices stating that you + modified it, and giving a relevant date. + + b. The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in + section 4 to "keep intact all notices". + + c. You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable + section 7 additional terms, to the whole of the work, and all + its parts, regardless of how they are packaged. This License + gives no permission to license the work in any other way, but + it does not invalidate such permission if you have separately + received it. + + d. If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has + interactive interfaces that do not display Appropriate Legal + Notices, your work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered + work, and which are not combined with it such as to form a larger + program, in or on a volume of a storage or distribution medium, is + called an "aggregate" if the compilation and its resulting + copyright are not used to limit the access or legal rights of the + compilation's users beyond what the individual works permit. + Inclusion of a covered work in an aggregate does not cause this + License to apply to the other parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this + License, in one of these ways: + + a. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that + product model, to give anyone who possesses the object code + either (1) a copy of the Corresponding Source for all the + software in the product that is covered by this License, on a + durable physical medium customarily used for software + interchange, for a price no more than your reasonable cost of + physically performing this conveying of source, or (2) access + to copy the Corresponding Source from a network server at no + charge. + + c. Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, + and only if you received the object code with such an offer, + in accord with subsection 6b. + + d. Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to + the Corresponding Source in the same way through the same + place at no further charge. You need not require recipients + to copy the Corresponding Source along with the object code. + If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by + you or a third party) that supports equivalent copying + facilities, provided you maintain clear directions next to the + object code saying where to find the Corresponding Source. + Regardless of what server hosts the Corresponding Source, you + remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + + e. Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the + general public at no charge under subsection 6d. + + A separable portion of the object code, whose source code is + excluded from the Corresponding Source as a System Library, need + not be included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means + any tangible personal property which is normally used for personal, + family, or household purposes, or (2) anything designed or sold for + incorporation into a dwelling. In determining whether a product is + a consumer product, doubtful cases shall be resolved in favor of + coverage. For a particular product received by a particular user, + "normally used" refers to a typical or common use of that class of + product, regardless of the status of the particular user or of the + way in which the particular user actually uses, or expects or is + expected to use, the product. A product is a consumer product + regardless of whether the product has substantial commercial, + industrial or non-consumer uses, unless such uses represent the + only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, + procedures, authorization keys, or other information required to + install and execute modified versions of a covered work in that + User Product from a modified version of its Corresponding Source. + The information must suffice to ensure that the continued + functioning of the modified object code is in no case prevented or + interfered with solely because modification has been made. + + If you convey an object code work under this section in, or with, + or specifically for use in, a User Product, and the conveying + occurs as part of a transaction in which the right of possession + and use of the User Product is transferred to the recipient in + perpetuity or for a fixed term (regardless of how the transaction + is characterized), the Corresponding Source conveyed under this + section must be accompanied by the Installation Information. But + this requirement does not apply if neither you nor any third party + retains the ability to install modified object code on the User + Product (for example, the work has been installed in ROM). + + The requirement to provide Installation Information does not + include a requirement to continue to provide support service, + warranty, or updates for a work that has been modified or installed + by the recipient, or for the User Product in which it has been + modified or installed. Access to a network may be denied when the + modification itself materially and adversely affects the operation + of the network or violates the rules and protocols for + communication across the network. + + Corresponding Source conveyed, and Installation Information + provided, in accord with this section must be in a format that is + publicly documented (and with an implementation available to the + public in source code form), and must require no special password + or key for unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of + this License by making exceptions from one or more of its + conditions. Additional permissions that are applicable to the + entire Program shall be treated as though they were included in + this License, to the extent that they are valid under applicable + law. If additional permissions apply only to part of the Program, + that part may be used separately under those permissions, but the + entire Program remains governed by this License without regard to + the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part + of it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material + you add to a covered work, you may (if authorized by the copyright + holders of that material) supplement the terms of this License with + terms: + + a. Disclaiming warranty or limiting liability differently from + the terms of sections 15 and 16 of this License; or + + b. Requiring preservation of specified reasonable legal notices + or author attributions in that material or in the Appropriate + Legal Notices displayed by works containing it; or + + c. Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked + in reasonable ways as different from the original version; or + + d. Limiting the use for publicity purposes of names of licensors + or authors of the material; or + + e. Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f. Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified + versions of it) with contractual assumptions of liability to + the recipient, for any liability that these contractual + assumptions directly impose on those licensors and authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as + you received it, or any part of it, contains a notice stating that + it is governed by this License along with a term that is a further + restriction, you may remove that term. If a license document + contains a further restriction but permits relicensing or conveying + under this License, you may add to a covered work material governed + by the terms of that license document, provided that the further + restriction does not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in + the form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights + under this License (including any patent licenses granted under the + third paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, you do not qualify to receive new licenses + for the same material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer + transmission to receive a copy likewise does not require + acceptance. However, nothing other than this License grants you + permission to propagate or modify any covered work. These actions + infringe copyright if you do not accept this License. Therefore, + by modifying or propagating a covered work, you indicate your + acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not + responsible for enforcing compliance by third parties with this + License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a + covered work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party's predecessor in interest had or + could give under the previous paragraph, plus a right to possession + of the Corresponding Source of the work from the predecessor in + interest, if the predecessor has it or can get it with reasonable + efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you + may not impose a license fee, royalty, or other charge for exercise + of rights granted under this License, and you may not initiate + litigation (including a cross-claim or counterclaim in a lawsuit) + alleging that any patent claim is infringed by making, using, + selling, offering for sale, or importing the Program or any portion + of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. + The work thus licensed is called the contributor's "contributor + version". + + A contributor's "essential patent claims" are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, + permitted by this License, of making, using, or selling its + contributor version, but do not include claims that would be + infringed only as a consequence of further modification of the + contributor version. For purposes of this definition, "control" + includes the right to grant patent sublicenses in a manner + consistent with the requirements of this License. + + Each contributor grants you a non-exclusive, worldwide, + royalty-free patent license under the contributor's essential + patent claims, to make, use, sell, offer for sale, import and + otherwise run, modify and propagate the contents of its contributor + version. + + In the following three paragraphs, a "patent license" is any + express agreement or commitment, however denominated, not to + enforce a patent (such as an express permission to practice a + patent or covenant not to sue for patent infringement). To "grant" + such a patent license to a party means to make such an agreement or + commitment not to enforce a patent against the party. + + If you convey a covered work, knowingly relying on a patent + license, and the Corresponding Source of the work is not available + for anyone to copy, free of charge and under the terms of this + License, through a publicly available network server or other + readily accessible means, then you must either (1) cause the + Corresponding Source to be so available, or (2) arrange to deprive + yourself of the benefit of the patent license for this particular + work, or (3) arrange, in a manner consistent with the requirements + of this License, to extend the patent license to downstream + recipients. "Knowingly relying" means you have actual knowledge + that, but for the patent license, your conveying the covered work + in a country, or your recipient's use of the covered work in a + country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, + modify or convey a specific copy of the covered work, then the + patent license you grant is automatically extended to all + recipients of the covered work and works based on it. + + A patent license is "discriminatory" if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that + are specifically granted under this License. You may not convey a + covered work if you are a party to an arrangement with a third + party that is in the business of distributing software, under which + you make payment to the third party based on the extent of your + activity of conveying the work, and under which the third party + grants, to any of the parties who would receive the covered work + from you, a discriminatory patent license (a) in connection with + copies of the covered work conveyed by you (or copies made from + those copies), or (b) primarily for and in connection with specific + products or compilations that contain the covered work, unless you + entered into that arrangement, or that patent license was granted, + prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement + or otherwise) that contradict the conditions of this License, they + do not excuse you from the conditions of this License. If you + cannot convey a covered work so as to satisfy simultaneously your + obligations under this License and any other pertinent obligations, + then as a consequence you may not convey it at all. For example, + if you agree to terms that obligate you to collect a royalty for + further conveying from those to whom you convey the Program, the + only way you could satisfy both those terms and this License would + be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a + single combined work, and to convey the resulting work. The terms + of this License will continue to apply to the part which is the + covered work, but the special requirements of the GNU Affero + General Public License, section 13, concerning interaction through + a network will apply to the combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new + versions of the GNU General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU + General Public License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that numbered version or of any later version published by the Free + Software Foundation. If the Program does not specify a version + number of the GNU General Public License, you may choose any + version ever published by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that + proxy's public statement of acceptance of a version permanently + authorizes you to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE + COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE + RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. + SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES + AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE + THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA + BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely + approximates an absolute waiver of all civil liability in + connection with the Program, unless a warranty or assumption of + liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS +=========================== + +How to Apply These Terms to Your New Programs +============================================= + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) YEAR NAME OF AUTHOR + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper +mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + PROGRAM Copyright (C) YEAR NAME OF AUTHOR + This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type 'show c' for details. + + The hypothetical commands 'show w' and 'show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + + You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + + The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . + + +Tag Table: + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/web-server-20210708.2242/web-server-autoloads.el b/code/elpa/web-server-20210708.2242/web-server-autoloads.el new file mode 100644 index 0000000..04fec23 --- /dev/null +++ b/code/elpa/web-server-20210708.2242/web-server-autoloads.el @@ -0,0 +1,75 @@ +;;; web-server-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "web-server" "web-server.el" (0 0 0 0)) +;;; Generated autoloads from web-server.el + +(autoload 'ws-start "web-server" "\ +Start a server using HANDLERS and return the server object. + +HANDLERS may be a single function (which is then called on every +request) or a list of conses of the form (MATCHER . FUNCTION), +where the FUNCTION associated with the first successful MATCHER +is called. Handler functions are called with two arguments, the +process and the request object. + +A MATCHER may be either a function (in which case it is called on +the request object) or a cons cell of the form (KEYWORD . STRING) +in which case STRING is matched against the value of the header +specified by KEYWORD. + +Any supplied NETWORK-ARGS are assumed to be keyword arguments for +`make-network-process' to which they are passed directly. + +For example, the following starts a simple hello-world server on +port 8080. + + (ws-start + (lambda (request) + (with-slots (process headers) request + (process-send-string process + \"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\n\\r\\nhello world\"))) + 8080) + +Equivalently, the following starts an identical server using a +function MATCH and the `ws-response-header' convenience +function. + + (ws-start + `(((lambda (_) t) . + (lambda (request) + (with-slots ((proc process)) request + (ws-response-header proc 200 '(\"Content-Type\" . \"text/plain\")) + (process-send-string proc \"hello world\"))))) + 8080) + +\(fn HANDLERS PORT &optional LOG-BUFFER &rest NETWORK-ARGS)" nil nil) + +(register-definition-prefixes "web-server" '("ws-")) + +;;;*** + +;;;### (autoloads nil "web-server-status-codes" "web-server-status-codes.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from web-server-status-codes.el + +(register-definition-prefixes "web-server-status-codes" '("ws-status-codes")) + +;;;*** + +;;;### (autoloads nil nil ("web-server-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; web-server-autoloads.el ends here diff --git a/code/elpa/web-server-20210708.2242/web-server-pkg.el b/code/elpa/web-server-20210708.2242/web-server-pkg.el new file mode 100644 index 0000000..3aa795d --- /dev/null +++ b/code/elpa/web-server-20210708.2242/web-server-pkg.el @@ -0,0 +1,13 @@ +(define-package "web-server" "20210708.2242" "Emacs Web Server" + '((emacs "24.1") + (cl-lib "0.6")) + :commit "6357a1c2d1718778503f7ee0909585094117525b" :authors + '(("Eric Schulte" . "schulte.eric@gmail.com")) + :maintainer + '("Eric Schulte" . "schulte.eric@gmail.com") + :keywords + '("http" "server" "network") + :url "https://github.com/eschulte/emacs-web-server") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/web-server-20210708.2242/web-server-status-codes.el b/code/elpa/web-server-20210708.2242/web-server-status-codes.el new file mode 100644 index 0000000..158b596 --- /dev/null +++ b/code/elpa/web-server-20210708.2242/web-server-status-codes.el @@ -0,0 +1,106 @@ +;;; web-server-status-codes.el --- Emacs Web Server HTML status codes -*- lexical-binding: t; -*- + +;; Copyright (C) 2013-2021 Free Software Foundation, Inc. + +;; This software 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 software is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: +(defvar ws-status-codes + '((100 . "Continue") + (101 . "Switching Protocols") + (102 . "Processing") + (200 . "OK") + (201 . "Created") + (202 . "Accepted") + (203 . "Non-Authoritative Information") + (204 . "No Content") + (205 . "Reset Content") + (206 . "Partial Content") + (207 . "Multi-Status") + (208 . "Already Reported") + (226 . "IM Used") + (300 . "Multiple Choices") + (301 . "Moved Permanently") + (302 . "Found") + (303 . "See Other") + (304 . "Not Modified") + (305 . "Use Proxy") + (306 . "Switch Proxy") + (307 . "Temporary Redirect") + (308 . "Permanent Redirect") + (400 . "Bad Request") + (401 . "Unauthorized") + (402 . "Payment Required") + (403 . "Forbidden") + (404 . "Not Found") + (405 . "Method Not Allowed") + (406 . "Not Acceptable") + (407 . "Proxy Authentication Required") + (408 . "Request Timeout") + (409 . "Conflict") + (410 . "Gone") + (411 . "Length Required") + (412 . "Precondition Failed") + (413 . "Request Entity Too Large") + (414 . "Request-URI Too Long") + (415 . "Unsupported Media Type") + (416 . "Requested Range Not Satisfiable") + (417 . "Expectation Failed") + (418 . "I'm a teapot") + (419 . "Authentication Timeout") + (420 . "Method Failure") + (420 . "Enhance Your Calm") + (422 . "Unprocessable Entity") + (423 . "Locked") + (424 . "Failed Dependency") + (424 . "Method Failure") + (425 . "Unordered Collection") + (426 . "Upgrade Required") + (428 . "Precondition Required") + (429 . "Too Many Requests") + (431 . "Request Header Fields Too Large") + (440 . "Login Timeout") + (444 . "No Response") + (449 . "Retry With") + (450 . "Blocked by Windows Parental Controls") + (451 . "Unavailable For Legal Reasons") + (451 . "Redirect") + (494 . "Request Header Too Large") + (495 . "Cert Error") + (496 . "No Cert") + (497 . "HTTP to HTTPS") + (499 . "Client Closed Request") + (500 . "Internal Server Error") + (501 . "Not Implemented") + (502 . "Bad Gateway") + (503 . "Service Unavailable") + (504 . "Gateway Timeout") + (505 . "HTTP Version Not Supported") + (506 . "Variant Also Negotiates") + (507 . "Insufficient Storage") + (508 . "Loop Detected") + (509 . "Bandwidth Limit Exceeded") + (510 . "Not Extended") + (511 . "Network Authentication Required") + (520 . "Origin Error") + (522 . "Connection timed out") + (523 . "Proxy Declined Request") + (524 . "A timeout occurred") + (598 . "Network read timeout error") + (599 . "Network connect timeout error")) + "Possible HTML status codes with names. +From http://en.wikipedia.org/wiki/List_of_HTTP_status_codes.") + +(provide 'web-server-status-codes) +;;; web-server-status-codes.el ends here diff --git a/code/elpa/web-server-20210708.2242/web-server.el b/code/elpa/web-server-20210708.2242/web-server.el new file mode 100644 index 0000000..de6cb91 --- /dev/null +++ b/code/elpa/web-server-20210708.2242/web-server.el @@ -0,0 +1,749 @@ +;;; web-server.el --- Emacs Web Server -*- lexical-binding: t -*- + +;; Copyright (C) 2013-2014 Free Software Foundation, Inc. + +;; Author: Eric Schulte +;; Maintainer: Eric Schulte +;; Version: 0.1.2 +;; Package-Requires: ((emacs "24.1") (cl-lib "0.6")) +;; Keywords: http, server, network +;; URL: https://github.com/eschulte/emacs-web-server + +;; This software 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 software is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; A web server in Emacs running handlers written in Emacs Lisp. +;; +;; Full support for GET and POST requests including URL-encoded +;; parameters and multipart/form data. Supports web sockets. +;; +;; See the examples/ directory for examples demonstrating the usage of +;; the Emacs Web Server. The following launches a simple "hello +;; world" server. +;; +;; (ws-start +;; '(((lambda (_) t) . ; match every request +;; (lambda (request) ; reply with "hello world" +;; (with-slots (process) request +;; (ws-response-header process 200 '("Content-type" . "text/plain")) +;; (process-send-string process "hello world"))))) +;; 9000) + +;;; Code: +(require 'web-server-status-codes) +(require 'mail-parse) ; to parse multipart data in headers +(require 'mm-encode) ; to look-up mime types for files +(require 'url-util) ; to decode url-encoded params +(require 'eieio) +(require 'cl-lib) + +(defclass ws-server () + ((handlers :initarg :handlers :accessor ws-handlers :initform nil) + (process :initarg :process :accessor ws-process :initform nil) + (port :initarg :port :accessor ws-port :initform nil) + (requests :initarg :requests :accessor ws-requests :initform nil))) + +(defclass ws-request () + ((process :initarg :process :accessor ws-process :initform nil) + (pending :initarg :pending :accessor ws-pending :initform "") + (context :initarg :context :accessor ws-context :initform nil) + (boundary :initarg :boundary :accessor ws-boundary :initform nil) + (index :initarg :index :accessor ws-index :initform 0) + (active :initarg :active :accessor ws-active :initform nil) + (headers :initarg :headers :accessor ws-headers :initform (list nil)) + (body :initarg :body :accessor ws-body :initform ""))) + +(defvar ws-servers nil + "List holding all web servers.") + +(defvar ws-log-time-format "%Y.%m.%d.%H.%M.%S.%N" + "Logging time format passed to `format-time-string'.") + +(defvar ws-guid "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + "This GUID is defined in RFC6455.") + +;;;###autoload +(defun ws-start (handlers port &optional log-buffer &rest network-args) + "Start a server using HANDLERS and return the server object. + +HANDLERS may be a single function (which is then called on every +request) or a list of conses of the form (MATCHER . FUNCTION), +where the FUNCTION associated with the first successful MATCHER +is called. Handler functions are called with two arguments, the +process and the request object. + +A MATCHER may be either a function (in which case it is called on +the request object) or a cons cell of the form (KEYWORD . STRING) +in which case STRING is matched against the value of the header +specified by KEYWORD. + +Any supplied NETWORK-ARGS are assumed to be keyword arguments for +`make-network-process' to which they are passed directly. + +For example, the following starts a simple hello-world server on +port 8080. + + (ws-start + (lambda (request) + (with-slots (process headers) request + (process-send-string process + \"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\n\\r\\nhello world\"))) + 8080) + +Equivalently, the following starts an identical server using a +function MATCH and the `ws-response-header' convenience +function. + + (ws-start + `(((lambda (_) t) . + (lambda (request) + (with-slots ((proc process)) request + (ws-response-header proc 200 '(\"Content-Type\" . \"text/plain\")) + (process-send-string proc \"hello world\"))))) + 8080) + +" + (let ((server (make-instance 'ws-server :handlers handlers :port port)) + (log (when log-buffer (get-buffer-create log-buffer)))) + (setf (ws-process server) + (apply + #'make-network-process + :name "ws-server" + :service (ws-port server) + :filter 'ws-filter + :server t + :nowait (< emacs-major-version 26) + :family 'ipv4 + :coding 'no-conversion + :plist (append (list :server server) + (when log (list :log-buffer log))) + :log (when log + (lambda (proc request message) + (let ((c (process-contact request)) + (buf (plist-get (process-plist proc) :log-buffer))) + (with-current-buffer buf + (goto-char (point-max)) + (insert (format "%s\t%s\t%s\t%s" + (format-time-string ws-log-time-format) + (cl-first c) (cl-second c) message)))))) + network-args)) + (push server ws-servers) + server)) + +(defun ws-stop (server) + "Stop SERVER." + (setq ws-servers (remove server ws-servers)) + (mapc #'delete-process (append (mapcar #'ws-process (ws-requests server)) + (list (ws-process server))))) + +(defun ws-stop-all () + "Stop all servers in `ws-servers'." + (interactive) + (mapc #'ws-stop ws-servers)) + +(defvar ws-http-common-methods '(GET HEAD POST PUT DELETE TRACE) + "HTTP methods from http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html.") + +(defvar ws-http-method-rx + (format "^\\(%s\\) \\([^[:space:]]+\\) \\([^[:space:]]+\\)$" + (mapconcat #'symbol-name ws-http-common-methods "\\|"))) + +(defun ws-parse-query-string (string) + "Thin wrapper around `url-parse-query-string'." + (mapcar (lambda (pair) (cons (cl-first pair) (cl-second pair))) + (url-parse-query-string string nil 'allow-newlines))) + +(defun ws-parse (proc string) + "Parse HTTP headers in STRING reporting errors to PROC." + (cl-flet ((to-keyword (s) (intern (concat ":" (upcase s))))) + (cond + ;; Method + ((string-match ws-http-method-rx string) + (let ((method (to-keyword (match-string 1 string))) + (url (match-string 2 string))) + (if (string-match "?" url) + (cons (cons method (substring url 0 (match-beginning 0))) + (ws-parse-query-string + (url-unhex-string (substring url (match-end 0))))) + (list (cons method url))))) + ;; Authorization + ((string-match "^AUTHORIZATION: \\([^[:space:]]+\\) \\(.*\\)$" string) + (let ((protocol (to-keyword (match-string 1 string))) + (credentials (match-string 2 string))) + (list (cons :AUTHORIZATION + (cons protocol + (cl-case protocol + (:BASIC + (let ((cred (base64-decode-string credentials))) + (if (string-match ":" cred) + (cons (substring cred 0 (match-beginning 0)) + (substring cred (match-end 0))) + (ws-error proc "bad credentials: %S" cred)))) + (t (ws-error proc "un-support protocol: %s" + protocol)))))))) + ;; All other headers + ((string-match "^\\([^[:space:]]+\\): \\(.*\\)$" string) + (list (cons (to-keyword (match-string 1 string)) + (match-string 2 string)))) + (:otherwise (ws-error proc "bad header: %S" string) nil)))) + +(defun ws-trim (string) + (while (and (> (length string) 0) + (or (and (string-match "[\r\n]" (substring string -1)) + (setq string (substring string 0 -1))) + (and (string-match "[\r\n]" (substring string 0 1)) + (setq string (substring string 1)))))) + string) + +(defun ws-parse-multipart/form (proc string) + ;; ignore empty and non-content blocks + (when (string-match "Content-Disposition:[[:space:]]*\\(.*\\)\r\n" string) + (let ((dp (cdr (mail-header-parse-content-disposition + (match-string 1 string)))) + (last-index (match-end 0)) + index) + ;; every line up until the double \r\n is a header + (while (and (setq index (string-match "\r\n" string last-index)) + (not (= index last-index))) + (setcdr (last dp) (ws-parse proc (substring string last-index index))) + (setq last-index (+ 2 index))) + ;; after double \r\n is all content + (cons (cdr (assoc 'name dp)) + (cons (cons 'content (substring string (+ 2 last-index))) + dp))))) + +(defun ws-filter (proc string) + (with-slots (handlers requests) (plist-get (process-plist proc) :server) + (unless (cl-find-if (lambda (c) (equal proc (ws-process c))) requests) + (push (make-instance 'ws-request :process proc) requests)) + (let ((request (cl-find-if (lambda (c) (equal proc (ws-process c))) requests))) + (with-slots (pending) request (setq pending (concat pending string))) + (unless (ws-active request) ; don't re-start if request is being parsed + (setf (ws-active request) t) + (when (not (eq (catch 'close-connection + (if (ws-parse-request request) + (ws-call-handler request handlers) + :keep-alive)) + :keep-alive)) + ;; Properly shut down processes requiring an ending (e.g., chunked) + (let ((ender (plist-get (process-plist proc) :ender))) + (when ender (process-send-string proc ender))) + (setq requests (cl-remove-if (lambda (r) (eql proc (ws-process r))) requests)) + (delete-process proc)))))) + +(defun ws-parse-request (request) + "Parse request STRING from REQUEST with process PROC. +Return non-nil only when parsing is complete." + (catch 'finished-parsing-headers + (with-slots (process pending context boundary headers body index) request + (let ((delimiter (concat "\r\n" (if boundary (concat "--" boundary) ""))) + ;; Track progress through string, always work with the + ;; section of string between INDEX and NEXT-INDEX. + next-index + body-stored) + ;; parse headers and append to request + (while (setq next-index (string-match delimiter pending index)) + (let ((tmp (+ next-index (length delimiter)))) + (if (= index next-index) ; double \r\n ends current run of headers + (progn + ;; Store the body + (unless + ;; Multipart form data has multiple passes - store on + ;; first pass only. + body-stored + (let ((after-headers (substring pending index))) + (when (string-prefix-p "\r\n" after-headers) + (setq body + ;; Trim off the additional CRLF + (substring after-headers 2)))) + (setq body-stored t)) + (cl-case context + ;; Parse URL data. + ;; http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 + (application/x-www-form-urlencoded + (mapc (lambda (pair) (setcdr (last headers) (list pair))) + (ws-parse-query-string + (replace-regexp-in-string + "\\+" " " + (ws-trim (substring pending index))))) + (throw 'finished-parsing-headers t)) + ;; Set custom delimiter for multipart form data. + (multipart/form-data + (setq delimiter (concat "\r\n--" boundary))) + ;; No special context so we're done. + (t (throw 'finished-parsing-headers t)))) + (if (eql context 'multipart/form-data) + (progn + (setcdr (last headers) + (list (ws-parse-multipart/form process + (substring pending index next-index)))) + ;; Boundary suffixed by "--" indicates end of the headers. + (when (and (> (length pending) (+ tmp 2)) + (string= (substring pending tmp (+ tmp 2)) "--")) + (throw 'finished-parsing-headers t))) + ;; Standard header parsing. + (let ((header (ws-parse process (substring pending + index next-index)))) + ;; Content-Type indicates that the next double \r\n + ;; will be followed by a special type of content which + ;; will require special parsing. Thus we will note + ;; the type in the CONTEXT variable for parsing + ;; dispatch above. + (when (and (caar header) (eql (caar header) :CONTENT-TYPE)) + (cl-destructuring-bind (type &rest data) + (mail-header-parse-content-type (cdar header)) + (setq boundary (cdr (assoc 'boundary data))) + (setq context (intern (downcase type))))) + ;; All other headers are collected directly. + (setcdr (last headers) header)))) + (setq index tmp))))) + (setf (ws-active request) nil) + nil)) + +(defun ws-call-handler (request handlers) + (catch 'matched-handler + (when (functionp handlers) + (throw 'matched-handler + (condition-case e (funcall handlers request) + (error (ws-error (ws-process request) "Caught Error: %S" e))))) + (mapc (lambda (handler) + (let ((match (car handler)) + (function (cdr handler))) + (when (or (and (consp match) + (assoc (car match) (ws-headers request)) + (string-match (cdr match) + (cdr (assoc (car match) + (ws-headers request))))) + (and (functionp match) (funcall match request))) + (throw 'matched-handler + (condition-case e (funcall function request) + (error (ws-error (ws-process request) + "Caught Error: %S" e))))))) + handlers) + (ws-error (ws-process request) "no handler matched request: %S" + (ws-headers request)))) + +(defun ws-error (proc msg &rest args) + (let ((buf (plist-get (process-plist proc) :log-buffer)) + (c (process-contact proc))) + (when buf + (with-current-buffer buf + (goto-char (point-max)) + (insert (format "%s\t%s\t%s\tWS-ERROR: %s" + (format-time-string ws-log-time-format) + (cl-first c) (cl-second c) + (apply #'format msg args))))) + (apply #'ws-send-500 proc msg args))) + + +;;; Web Socket +;; Implement to conform to http://tools.ietf.org/html/rfc6455. + +;; The `ws-message' object is used to hold state across multiple calls +;; of the process filter on the websocket network process. The fields +;; play the following roles. +;; process ------ holds the process itself, used for communication +;; pending ------ holds text received from the client but not yet parsed +;; active ------- indicates that parsing is active to avoid re-entry +;; of the `ws-web-socket-parse-messages' function +;; new ---------- indicates that new text was received during parsing +;; and causes `ws-web-socket-parse-messages' to be +;; called again after it terminates +;; data --------- holds the data of parsed messages +;; handler ------ holds the user-supplied function of two arguments +;; called on the process and the data of parsed +;; messages +(defclass ws-message () ; web socket message object + ((process :initarg :process :accessor ws-process :initform "") + (pending :initarg :pending :accessor ws-pending :initform "") + (active :initarg :active :accessor ws-active :initform nil) + (new :initarg :new :accessor ws-new :initform nil) + (data :initarg :data :accessor ws-data :initform "") + (handler :initarg :handler :accessor ws-handler :initform ""))) + +(defun ws-web-socket-connect (request handler) + "Establish a web socket connection with request. +If the connection is successful this function will throw +`:keep-alive' to `close-connection' skipping any remaining code +in the request handler. If no web-socket connection is +established (e.g., because REQUEST is not attempting to establish +a connection) then no actions are taken and nil is returned. + +Second argument HANDLER should be a function of two arguments, +the process and a string, which will be called on all complete +messages as they are received and parsed from the network." + (with-slots (process headers) request + (when (assoc :SEC-WEBSOCKET-KEY headers) + ;; Accept the connection + (ws-response-header process 101 + (cons "Upgrade" "websocket") + (cons "Connection" "upgrade") + (cons "Sec-WebSocket-Accept" + (ws-web-socket-handshake + (cdr (assoc :SEC-WEBSOCKET-KEY headers))))) + ;; Setup the process filter + (set-process-coding-system process 'binary) + (set-process-plist + process (list :message (make-instance 'ws-message + :handler handler :process process))) + (set-process-filter process 'ws-web-socket-filter) + process))) + +(defun ws-web-socket-filter (process string) + (let ((message (plist-get (process-plist process) :message))) + (if (ws-active message) ; don't re-start if message is being parsed + (setf (ws-new message) string) + (setf (ws-pending message) (concat (ws-pending message) string)) + (setf (ws-active message) t) + (ws-web-socket-parse-messages message)) + (setf (ws-active message) nil))) + +(defun ws-web-socket-mask (masking-key data) + (let ((masking-data (apply #'concat (make-list (+ 1 (/ (length data) 4)) + masking-key)))) + (apply #'string (cl-mapcar #'logxor masking-data data)))) + +;; Binary framing protocol +;; from http://tools.ietf.org/html/rfc6455#section-5.2 +;; +;; 0 1 2 3 +;; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +;; +-+-+-+-+-------+-+-------------+-------------------------------+ +;; |F|R|R|R| opcode|M| Payload len | Extended payload length | +;; |I|S|S|S| (4) |A| (7) | (16/64) | +;; |N|V|V|V| |S| | (if payload len==126/127) | +;; | |1|2|3| |K| | | +;; +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + +;; | Extended payload length continued, if payload len == 127 | +;; + - - - - - - - - - - - - - - - +-------------------------------+ +;; | |Masking-key, if MASK set to 1 | +;; +-------------------------------+-------------------------------+ +;; | Masking-key (continued) | Payload Data | +;; +-------------------------------- - - - - - - - - - - - - - - - + +;; : Payload Data continued ... : +;; + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +;; | Payload Data continued ... | +;; +---------------------------------------------------------------+ +;; +(defun ws-web-socket-parse-messages (message) + "Web socket filter to pass whole frames to the client. +See RFC6455." + (with-slots (process active pending data handler new) message + (let ((index 0)) + (cl-labels ((int-to-bits (int size) + (let ((result (make-bool-vector size nil))) + (mapc (lambda (place) + (let ((val (expt 2 place))) + (when (>= int val) + (setq int (- int val)) + (aset result place t)))) + (reverse (number-sequence 0 (- size 1)))) + (reverse (append result nil)))) + (bits-to-int (bits) + (let ((place 0)) + (apply #'+ + (mapcar (lambda (bit) + (prog1 (if bit (expt 2 place) 0) (cl-incf place))) + (reverse bits))))) + (bits (length) + (apply #'append + (mapcar (lambda (int) (int-to-bits int 8)) + (cl-subseq + pending index (cl-incf index length)))))) + (let (fin rsvs opcode mask pl mask-key) + ;; Parse fin bit, rsvs bits and opcode + (let ((byte (bits 1))) + (setq fin (car byte) + rsvs (cl-subseq byte 1 4) + opcode + (let ((it (bits-to-int (cl-subseq byte 4)))) + (cl-case it + (0 :CONTINUATION) + (1 :TEXT) + (2 :BINARY) + ((3 4 5 6 7) :NON-CONTROL) + (8 :CLOSE) + (9 :PING) + (10 :PONG) + ((11 12 13 14 15) :CONTROL) + ;; If an unknown opcode is received, the receiving + ;; endpoint MUST _Fail the WebSocket Connection_. + (t (ws-error process + "Web Socket Fail: bad opcode %d" it)))))) + (unless (cl-every #'null rsvs) + ;; MUST be 0 unless an extension is negotiated that defines + ;; meanings for non-zero values. + (ws-error process "Web Socket Fail: non-zero RSV 1 2 or 3")) + ;; Parse mask and payload length + (let ((byte (bits 1))) + (setq mask (car byte) + pl (bits-to-int (cl-subseq byte 1)))) + (unless (eq mask t) + ;; All frames sent from client to server have this bit set to 1. + (ws-error process "Web Socket Fail: client must mask data")) + (cond + ((= pl 126) (setq pl (bits-to-int (bits 2)))) + ((= pl 127) (setq pl (bits-to-int (bits 8))))) + ;; unmask data + (when mask (setq mask-key (cl-subseq pending index (cl-incf index 4)))) + (setq data (concat data + (ws-web-socket-mask + mask-key (cl-subseq pending index (+ index pl))))) + (if fin + ;; wipe the message state and call the handler + (let ((it data)) + (setq data "" active nil pending "" new nil) + ;; close on a close frame, otherwise call the handler + (if (not (eql opcode :CLOSE)) + (funcall handler process it) + (process-send-string process + (unibyte-string (logior (lsh 1 7) 8) 0)))) + ;; add any remaining un-parsed network data to pending + (when (< (+ index pl) (length pending)) + (setq pending (substring pending (+ index pl))))))) + ;; possibly re-parse any pending input + (when (ws-new message) (ws-web-socket-parse-messages message))))) + +(defun ws-web-socket-frame (string &optional opcode) + "Frame STRING for web socket communication." + (let* ((fin 1) ;; set to 0 if not final frame + (len (length string)) + (opcode (cl-ecase (or opcode :TEXT) (:TEXT 1) (:BINARY 2)))) + ;; Does not do any masking which is only required of client communication + (concat + (cond + ((< len 126) (unibyte-string (logior (lsh fin 7) opcode) len)) + ((< len 65536) (unibyte-string (logior (lsh fin 7) opcode) 126 + ;; extended 16-bit length + (logand (lsh len -8) 255) + (logand len 255))) + (t (unibyte-string (logior (lsh fin 7) opcode) 127 + ;; more extended 64-bit length + (logand (lsh len -56) 255) + (logand (lsh len -48) 255) + (logand (lsh len -40) 255) + (logand (lsh len -32) 255) + (logand (lsh len -24) 255) + (logand (lsh len -16) 255) + (logand (lsh len -8) 255) + (logand len 255)))) + string))) + + +;;; Content and Transfer encoding support +(defvar ws-compress-cmd "compress" + "Command used for the \"compress\" Content or Transfer coding.") + +(defvar ws-deflate-cmd "zlib-flate -compress" + "Command used for the \"deflate\" Content or Transfer coding.") + +(defvar ws-gzip-cmd "gzip" + "Command used for the \"gzip\" Content or Transfer coding.") + +(defmacro ws-encoding-cmd-to-fn (cmd) + "Return a function which applies CMD to strings." + `(lambda (s) + (with-temp-buffer + (insert s) + (shell-command-on-region (point-min) (point-max) ,cmd nil 'replace) + (buffer-string)))) + +(defun ws-chunk (string) + "Convert STRING to a valid chunk for HTTP chunked Transfer-encoding." + (format "%x\r\n%s\r\n" (string-bytes string) string)) + + +;;; Convenience functions to write responses +(defun ws-response-header (proc code &rest headers) + "Send the headers for an HTTP response to PROC. +CODE should be an HTTP status code, see `ws-status-codes' for a +list of known codes. + +When \"Content-Encoding\" or \"Transfer-Encoding\" headers are +supplied any subsequent data written to PROC using `ws-send' will +be encoded appropriately including sending the appropriate data +upon the end of transmission for chunked transfer encoding. + +For example with the header `(\"Content-Encoding\" . \"gzip\")', +any data subsequently written to PROC using `ws-send' will be +compressed using the command specified in `ws-gzip-cmd'." + ;; update process to reflect any Content or Transfer encodings + (let ((content (cdr (assoc "Content-Encoding" headers))) + (transfer (cdr (assoc "Transfer-Encoding" headers)))) + (when content + (set-process-plist proc + (append + (list :content-encoding + (cl-ecase (intern content) + ((compress x-compress) (ws-encoding-cmd-to-fn ws-compress-cmd)) + ((deflate x-deflate) (ws-encoding-cmd-to-fn ws-deflate-cmd)) + ((gzip x-gzip) (ws-encoding-cmd-to-fn ws-gzip-cmd)) + (identity #'identity) + ((exi pack200-zip) + (ws-error proc "`%s' Content-encoding not supported." + content)))) + (process-plist proc)))) + (when transfer + (set-process-plist proc + (append + (when (string= transfer "chunked") (list :ender "0\r\n\r\n")) + (list :transfer-encoding + (cl-ecase (intern transfer) + (chunked #'ws-chunk) + ((compress x-compress) (ws-encoding-cmd-to-fn ws-compress-cmd)) + ((deflate x-deflate) (ws-encoding-cmd-to-fn ws-deflate-cmd)) + ((gzip x-gzip) (ws-encoding-cmd-to-fn ws-gzip-cmd)))) + (process-plist proc))))) + (let ((headers + (cons + (format "HTTP/1.1 %d %s" code (cdr (assoc code ws-status-codes))) + (mapcar (lambda (h) (format "%s: %s" (car h) (cdr h))) headers)))) + (setcdr (last headers) (list "" "")) + (process-send-string proc (mapconcat #'identity headers "\r\n")))) + +(defun ws-send (proc string) + "Send STRING to process PROC. +If any Content or Transfer encodings are in use, apply them to +STRING before sending." + (let + ((cc (or (plist-get (process-plist proc) :content-encoding) #'identity)) + (tc (or (plist-get (process-plist proc) :transfer-encoding) #'identity))) + (process-send-string proc (funcall tc (funcall cc string))))) + +(defun ws-send-500 (proc &rest msg-and-args) + "Send 500 \"Internal Server Error\" to PROC with an optional message." + (ws-response-header proc 500 + '("Content-type" . "text/plain")) + (process-send-string proc (if msg-and-args + (apply #'format msg-and-args) + "500 Internal Server Error")) + (throw 'close-connection nil)) + +(defun ws-send-404 (proc &rest msg-and-args) + "Send 404 \"Not Found\" to PROC with an optional message." + (ws-response-header proc 404 + '("Content-type" . "text/plain")) + (process-send-string proc (if msg-and-args + (apply #'format msg-and-args) + "404 Not Found")) + (throw 'close-connection nil)) + +(defun ws-send-file (proc path &optional mime-type) + "Send PATH to PROC. +Optionally explicitly set MIME-TYPE, otherwise it is guessed by +`mm-default-file-encoding'." + (let ((mime (or mime-type + (mm-default-file-encoding path) + "application/octet-stream"))) + (process-send-string proc + (with-temp-buffer + (insert-file-contents-literally path) + (ws-response-header proc 200 + (cons "Content-type" mime) + (cons "Content-length" (- (point-max) (point-min)))) + (buffer-string))))) + +(defun ws-send-directory-list (proc directory &optional match) + "Send a listing of files in DIRECTORY to PROC. +Optional argument MATCH is passed to `directory-files' and may be +used to limit the files sent." + (ws-response-header proc 200 (cons "Content-type" "text/html")) + (process-send-string proc + (concat ""))) + +(defun ws-in-directory-p (parent path) + "Check if PATH is under the PARENT directory. +If so return PATH, if not return nil. Note: the PARENT directory +must be full expanded as with `expand-file-name' and should not +contain e.g., \"~\" for a user home directory." + (if (zerop (length path)) + parent + (let ((expanded (expand-file-name path parent))) + (and (>= (length expanded) (length parent)) + (string= parent (substring expanded 0 (length parent))) + expanded)))) + +(defun ws-with-authentication (handler credentials + &optional realm unauth invalid) + "Return a version of HANDLER protected by CREDENTIALS. +HANDLER should be a function as passed to `ws-start', and +CREDENTIALS should be an alist of elements of the form (USERNAME +. PASSWORD). + +Optional argument REALM sets the realm in the authentication +challenge. Optional arguments UNAUTH and INVALID should be +functions which are called on the request when no authentication +information, or invalid authentication information are provided +respectively." + (let ((handler handler) + (credentials credentials) + (realm realm) + (unauth unauth) + (invalid invalid)) + (lambda (request) + (with-slots (process headers) request + (let ((auth (cddr (assoc :AUTHORIZATION headers)))) + (cond + ;; no authentication information provided + ((not auth) + (if unauth + (funcall unauth request) + (ws-response-header process 401 + (cons "WWW-Authenticate" + (format "Basic realm=%S" (or realm "restricted"))) + '("Content-type" . "text/plain")) + (process-send-string process "authentication required"))) + ;; valid authentication information + ((string= (cdr auth) (cdr (assoc (car auth) credentials))) + (funcall handler request)) + ;; invalid authentication information + (t + (if invalid + (funcall invalid request) + (ws-response-header process 403 '("Content-type" . "text/plain")) + (process-send-string process "invalid credentials"))))))))) + +(defun ws-web-socket-handshake (key) + "Perform the handshake defined in RFC6455." + (base64-encode-string (sha1 (concat (ws-trim key) ws-guid) nil nil 'binary))) + +;;; Enable the old accessors without the `ws-' namespace as obsolete. +;;; Lets plan to remove these within a year of the date they were +;;; marked obsolete, so that would be roughly 2021-03-12. +(define-obsolete-function-alias 'active 'ws-active "2020-03-12") +(define-obsolete-function-alias 'body 'ws-body "2020-03-12") +(define-obsolete-function-alias 'boundary 'ws-boundary "2020-03-12") +(define-obsolete-function-alias 'context 'ws-context "2020-03-12") +(define-obsolete-function-alias 'data 'ws-data "2020-03-12") +(define-obsolete-function-alias 'handler 'ws-handler "2020-03-12") +(define-obsolete-function-alias 'handlers 'ws-handlers "2020-03-12") +(define-obsolete-function-alias 'headers 'ws-headers "2020-03-12") +(define-obsolete-function-alias 'index 'ws-index "2020-03-12") +(define-obsolete-function-alias 'new 'ws-new "2020-03-12") +(define-obsolete-function-alias 'pending 'ws-pending "2020-03-12") +(define-obsolete-function-alias 'port 'ws-port "2020-03-12") +(define-obsolete-function-alias 'process 'ws-process "2020-03-12") +(define-obsolete-function-alias 'requests 'ws-requests "2020-03-12") + +(provide 'web-server) +;;; web-server.el ends here diff --git a/code/elpa/web-server-20210708.2242/web-server.info b/code/elpa/web-server-20210708.2242/web-server.info new file mode 100644 index 0000000..3bbb2ad --- /dev/null +++ b/code/elpa/web-server-20210708.2242/web-server.info @@ -0,0 +1,2106 @@ +This is web-server.info, produced by makeinfo version 6.7 from +web-server.texi. + +This file documents the Emacs Web Server (web-server) + + Copyright (C) 2013 Eric Schulte + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with the Invariant Section being “GNU GENERAL PUBLIC + LICENSE,” A copy of the license is included in the section entitled + “GNU Free Documentation License.” +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Web Server: (web-server). Web Server for Emacs. +END-INFO-DIR-ENTRY + + +File: web-server.info, Node: Top, Next: Introduction, Prev: (dir), Up: (dir) + +Emacs Web Server User Manual +**************************** + +This file documents the Emacs Web Server (web-server) + + Copyright (C) 2013 Eric Schulte + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with the Invariant Section being “GNU GENERAL PUBLIC + LICENSE,” A copy of the license is included in the section entitled + “GNU Free Documentation License.” + +* Menu: + +* Introduction:: Overview of the Emacs Web Server +* Handlers:: Handlers respond to HTTP requests +* Requests:: Getting information on HTTP requests +* Usage Examples:: Examples demonstrating usage +* Function Index:: List of Functions + +Appendices + +* Copying:: The GNU General Public License gives + you permission to redistribute GNU Emacs on + certain terms; it also explains that there is + no warranty. +* GNU Free Documentation License:: The license for this documentation. +* Index:: Complete index. + + + + + +File: web-server.info, Node: Introduction, Next: Handlers, Prev: Top, Up: Top + +1 Introduction +************** + +The Emacs Web Server is a Web server implemented entirely in Emacs Lisp. +HTTP requests are matched to handlers (*note Handlers::) which are Emacs +Lisp functions. Handlers receive as their only argument a request +object (*note Requests::) which holds information about the request and +a process holding the HTTP network connection. Handlers write their +responses directly to the network process. + + A number of examples (*note Usage Examples::) demonstrate usage of +the Emacs Web Server. All public functions of the Emacs Web Server are +listed (*note Function Index::). + + +File: web-server.info, Node: Handlers, Next: Requests, Prev: Handlers, Up: Top + +2 Handlers +********** + +The function ‘ws-start’ takes takes two arguments ‘handlers’ and ‘port’. +It starts a server listening on ‘port’ responding to requests with +‘handlers’. ‘Handlers’ may be either a single function or an +association list composed of pairs of matchers and handler functions. +When ‘handlers’ is a single function the given function is used to serve +every request, when it is an association list, the function of the first +matcher to match each request handles that request. + +2.1 Matchers +============ + +Matchers may be a regular expression or a function. Regular expression +matchers consists of an HTTP header and a regular expression. When the +regular expression matches the content of the given header the matcher +succeeds and the associated handler is called. For example the +following matches any ‘GET’ request whose path starts with the substring +“foo”. + + (:GET . "^foo") + + A function matcher is a function which takes the request object +(*note Requests::) and succeeds when the function returns a non-nil +value. For example the following matcher matches every request, + + (lambda (_) t) + + and the following matches only requests in which the supplied +“number” parameter is odd. + + (lambda (request) + (oddp (string-to-number (cdr (assoc "number" request))))) + +2.2 Handler Function +==================== + +Each handler is a function which takes a request object (*note +Requests::) as its only argument. The function may respond to the +request by writing to the network process held in the ‘process’ field of +the request object. For example, the ‘process-send-string’ function may +be used to write string data to a request as in the following. + + (process-send-string (process request) "hello world") + + When the handler function exits the connection is terminated unless +the handler function returns the keyword ‘:keep-alive’. + + +File: web-server.info, Node: Requests, Next: Usage Examples, Prev: Handlers, Up: Top + +3 Requests +********** + +Each HTTP requests is represented using a ‘ws-request’ object (*note +ws-request::). The request object serves two purposes, one internal and +one external. Internally, request objects are used to hold state while +HTTP headers are parsed incrementally as the HTTP request text is +received from the network. Externally, request objects are used to +decide which handler to call, and are then passed as the only argument +to the called handler. + + In addition to fields used internally, each ‘ws-request’ object holds +the network process in the ‘process’ and holds all HTTP headers and +request GET or POST parameters in the ‘headers’ alist. HTML Headers are +keyed using uppercase keywords (e.g., ‘:GET’), and user supplied +parameters are keyed using the string name of the parameter. + + The ‘process’ field may be used by handlers to send data to a client +as in the following example. + + (process-send-string (process request) "hello world") + + The ‘headers’ field may be used to access request information such as +the requested path, + + (cdr (assoc :GET (headers request))) + + or named parameters as from a web form. + + (cdr (assoc "message" (headers request))) + + +File: web-server.info, Node: Usage Examples, Next: Hello World, Prev: Requests, Up: Top + +4 Usage Examples +**************** + +These examples demonstrate usage. +* Menu: + +* Hello World:: Serve “Hello World” to every request +* Hello World UTF8:: Serve “Hello World” w/UTF8 encoding +* Hello World HTML:: Serve “Hello World” in HTML +* File Server:: Serve files from a document root +* URL Parameter Echo:: Echo parameters from a URL query string +* POST Echo:: Echo POST parameters back +* Basic Authentication:: BASIC HTTP authentication +* Org-mode Export:: Export files to HTML and Tex +* File Upload:: Upload files and return their sha1sum +* Web Socket:: Web socket echo server +* Gzipped Transfer Encoding:: Gzip content encoding +* Chunked Transfer Encoding:: Chunked transfer encoding + + +File: web-server.info, Node: Hello World, Next: Hello World UTF8, Prev: Usage Examples, Up: Usage Examples + +4.1 Hello World +=============== + +The simplest possible “hello world” example. The handler consists of a +single (matcher . handler) pair. The function matcher matches _every_ +incoming HTTP request. The handler responds by setting the content type +to ‘text/plain’, and then sending the string “hello world”. When the +handler exits the network connection of the request is closed. + +;;; hello-world.el --- simple hello world server using Emacs Web Server +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + (lambda (request) + (with-slots (process headers) request + (ws-response-header process 200 '("Content-type" . "text/plain")) + (process-send-string process "hello world"))) + 9000) + + +File: web-server.info, Node: Hello World UTF8, Next: Hello World HTML, Prev: Hello World, Up: Usage Examples + +4.2 Hello World UTF8 +==================== + +This example only differs from the previous in that the “Content-type” +indicates UTF8 encoded data, and the hello world sent is selected at +random from a list of different languages. + +;;; hello-world-utf8.el --- utf8 hello world server using Emacs Web Server +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + (lambda (request) + (with-slots (process headers) request + (let ((hellos '("こんにちは" + "안녕하세요" + "góðan dag" + "Grüßgott" + "hyvää päivää" + "yá'át'ééh" + "Γεια σας" + "Вiтаю" + "გამარჯობა" + "नमस्ते" + "你好"))) + (ws-response-header process 200 + '("Content-type" . "text/plain; charset=utf-8")) + (process-send-string process + (concat (nth (random (length hellos)) hellos) " world"))))) + 9001) + + +File: web-server.info, Node: Hello World HTML, Next: File Server, Prev: Hello World UTF8, Up: Usage Examples + +4.3 Hello World HTML +==================== + +;;; hello-world-html.el --- html hello world server using Emacs Web Server +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + (lambda (request) + (with-slots (process headers) request + (ws-response-header process 200 '("Content-type" . "text/html")) + (process-send-string process " + + Hello World + + + hello world + +"))) + 9002) + + This variation of the “hello world” example sends a ‘text/html’ +response instead of a simple ‘text/plain’ response. + + +File: web-server.info, Node: File Server, Next: URL Parameter Echo, Prev: Hello World HTML, Up: Usage Examples + +4.4 File Server +=============== + +The following example implements a file server which will serve files +from the ‘docroot’ document root set to the current working directory in +this example. Four helper functions are used; ‘ws-in-directory-p’ is +used to check if the requested path is within the document root. If not +then ‘ws-send-404’ is used to send a default “File Not Found”. If so +then the file is served with ‘ws-send-file’ (which appropriately sets +the mime-type of the response based on the extension of the file) if it +is a file or is served with ‘ws-send-directory-list’ if it is a +directory. + +;;; file-server.el --- serve any files using Emacs Web Server +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(lexical-let ((docroot default-directory)) + (ws-start + (lambda (request) + (with-slots (process headers) request + (let ((path (substring (cdr (assoc :GET headers)) 1))) + (if (ws-in-directory-p docroot path) + (if (file-directory-p path) + (ws-send-directory-list process + (expand-file-name path docroot) "^[^\.]") + (ws-send-file process (expand-file-name path docroot))) + (ws-send-404 process))))) + 9003)) + + +File: web-server.info, Node: URL Parameter Echo, Next: POST Echo, Prev: File Server, Up: Usage Examples + +4.5 URL Parameter Echo +====================== + +This example demonstrates access of URL-encoded parameters in a ‘GET’ +request. For example the following URL + will render as the +following HTML table. + +foo bar +baz qux + +;;; url-param-echo.el --- echo back url-paramed message using Emacs Web Server +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + '(((:GET . ".*") . + (lambda (request) + (with-slots (process headers) request + (ws-response-header process 200 '("Content-type" . "text/html")) + (process-send-string process + (concat "URL Parameters:
" + (mapconcat (lambda (pair) + (format "" + (car pair) (cdr pair))) + (cl-remove-if-not (lambda (el) (stringp (car el))) + headers) + "") + "
%s%s
")))))) + 9004) + + +File: web-server.info, Node: POST Echo, Next: Basic Authentication, Prev: URL Parameter Echo, Up: Usage Examples + +4.6 POST Echo +============= + +The following example echos back the content of the “message” field in a +‘POST’ request. + +;;; post-echo.el --- echo back posted message using Emacs Web Server +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + '(((:POST . ".*") . + (lambda (request) + (with-slots (process headers) request + (let ((message (cdr (assoc "message" headers)))) + (ws-response-header process 200 '("Content-type" . "text/plain")) + (process-send-string process + (if message + (format "you said %S\n" (cdr (assoc 'content message))) + "This is a POST request, but it has no \"message\".\n")))))) + ((:GET . ".*") . + (lambda (request) + (with-slots (process) request + (ws-response-header process 200 '("Content-type" . "text/plain")) + (process-send-string process + "This is a GET request not a POST request.\n"))))) + 9005) + + +File: web-server.info, Node: Basic Authentication, Next: Org-mode Export, Prev: POST Echo, Up: Usage Examples + +4.7 Basic Authentication +======================== + +The following example demonstrates BASIC HTTP authentication. The +handler prompts an unauthenticated client for authentication by sending +a “WWW-Authenticate” header. + + (ws-response-header process 401 + '("WWW-Authenticate" . "Basic realm=\"example\"") + '("Content-type" . "text/plain")) + + The client replies by setting the “Authorization” HTTP header which +is parsed into a list of the form ‘(PROTOCOL USERNAME . PASSWORD)’. +Currently only BASIC HTTP authentication is supported. + +Note: BASIC HTTP authentication passes user credentials in plain text +between the client and the server and should generally only be used with +HTTPS network encryption. While the Emacs web server currently doesn’t +support HTTPS network encryption it may be run behind an HTTPS proxy +server (e.g., Apache or Nginx) with HTTPS support. + +;;; basic-authentication.el --- basic authentication +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(lexical-let ((users '(("foo" . "bar") + ("baz" . "qux")))) + (ws-start + (ws-with-authentication + (lambda (request) + (with-slots (process headers) request + (let ((user (caddr (assoc :AUTHORIZATION headers)))) + (ws-response-header process 200 '("Content-type" . "text/plain")) + (process-send-string process (format "welcome %s" user))))) + users) + 9006)) + + +File: web-server.info, Node: Org-mode Export, Next: File Upload, Prev: Basic Authentication, Up: Usage Examples + +4.8 Org-mode Export +=================== + +The following example exports a directory of Org-mode files as either +text, HTML or LaTeX. The Org-mode export engine is used to export files +on-demand as they are requested. + +;;; org-mode-file-server.el --- serve on-demand exported Org-mode files +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(lexical-let ((docroot "/tmp/")) + (ws-start + (lambda (request) + (with-slots (process headers) request + (let ((path (ws-in-directory-p ; check if path is in docroot + docroot (substring (cdr (assoc :GET headers)) 1)))) + (unless path (ws-send-404 process)) ; send 404 if not in docroot + (if (file-directory-p path) + (progn ;; send directory listing, convert org files to html/tex/txt + (ws-response-header proc 200 (cons "Content-type" "text/html")) + (process-send-string proc + (concat "
"))) + ;; Export the file as requested and return the result + (let* ((base (file-name-sans-extension path)) + (type (case (intern (downcase (file-name-extension path))) + (html 'html) + (tex 'latex) + (txt 'ascii) + (t (ws-error process "%S export not supported" + (file-name-extension path))))) + (orig (concat base ".org"))) + (unless (file-exists-p orig) (ws-send-404 process)) + (save-window-excursion (find-file orig) + (org-export-to-file type path)) + (ws-send-file process path)))))) + 9007)) + + +File: web-server.info, Node: File Upload, Next: Web Socket, Prev: Org-mode Export, Up: Usage Examples + +4.9 File Upload +=============== + +The following example demonstrates accessing an uploaded file. This +simple server accesses the file named “file” and returns it’s sha1sum +and file name. + +;;; file-upload.el --- use an uploaded file +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + '(((:POST . ".*") . + (lambda (request) + (with-slots (process headers) request + (ws-response-header process 200 '("Content-type" . "text/plain")) + (let ((file (cdr (assoc "file" headers)))) + (process-send-string process + (concat (sha1 (cdr (assoc 'content file))) " " + (cdr (assoc 'filename file)) "\n"))))))) + 9008) + + A file may be uploaded from an HTML form, or using the ‘curl’ program +as in the following example. + + $ curl -s -F file=usr/share/emacs/24.3/etc/COPYING localhost:9008 + 8624bcdae55baeef00cd11d5dfcfa60f68710a02 COPYING + $ sha1sum /usr/share/emacs/24.3/etc/COPYING + 8624bcdae55baeef00cd11d5dfcfa60f68710a02 /usr/share/emacs/24.3/etc/COPYING + + +File: web-server.info, Node: Web Socket, Next: Chunked Transfer Encoding, Prev: File Upload, Up: Usage Examples + +4.10 Web Socket +=============== + +Example demonstrating the use of web sockets for full duplex +communication between clients and the server. Handlers may use the +‘ws-web-socket-connect’ function (*note ws-web-socket-connect::) to +check for and respond to a web socket upgrade request sent by the client +(as demonstrated with the ‘new WebSocket’ JavaScript code in the +example). Upon successfully initializing a web socket connection the +call to ‘ws-web-socket-connect’ will return the web socket network +process. This process may then be used by the server to communicate +with the client over the web socket using the ‘process-send-string’ and +‘ws-web-socket-frame’ functions. All web socket communication must be +wrapped in frames using the ‘ws-web-socket-frame’ function. + + The handler must pass a function as the second argument to +‘ws-web-socket-connect’. This function will be called on every web +socket message received from the client. + +Note: in order to keep the web socket connection alive the request +handler from which ‘ws-web-socket-connect’ is called must return the +‘:keep-alive’ keyword, as demonstrated in the example. + +;;; web-sockets.el --- communicate via web-sockets +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(lexical-let* ((web-socket-port 9009) + (web-socket-page + (format " + + + + +
    + +
  1. Press \"connect\" to initialize the web socket connection to + the server. The server will complete the web socket + handshake at which point you'll see an alert with the text + \"connected\".
  2. + +
  3. Press \"message\" to send the string \"foo\" to the server. + The server will reply with the text \"you said: foo\" which + you will see in an alert as \"server: you said: foo\".
  4. + +
  5. Press \"close\" to close the connection. After the server + responds with a close frame you will see an alert with the + text \"connection closed\".
  6. + +
+
connect +message +close + +" web-socket-port))) + (ws-start + (lambda (request) + (with-slots (process headers) request + ;; if a web-socket request, then connect and keep open + (if (ws-web-socket-connect request + (lambda (proc string) + (process-send-string proc + (ws-web-socket-frame (concat "you said: " string))))) + (prog1 :keep-alive (setq my-connection process)) + ;; otherwise send the index page + (ws-response-header process 200 '("Content-type" . "text/html")) + (process-send-string process web-socket-page)))) + web-socket-port)) + + +File: web-server.info, Node: Gzipped Transfer Encoding, Next: Chunked Transfer Encoding, Prev: Web Socket, Up: Usage Examples + +4.11 Gzipped Transfer Encoding +============================== + +HTTP Responses may be compressed by setting the “gzip” (or “compress” or +“deflate”) content- or transfer-encoding HTTP headers in +‘ws-response-header’. Any further data sent to the process using +‘ws-send’ will automatically be appropriately compressed. + +;;; content-encoding-gzip.el -- gzip content encoding +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + (lambda (request) + (with-slots (process headers) request + (ws-response-header process 200 + '("Content-type" . "text/plain; charset=utf-8") + '("Content-Encoding" . "x-gzip")) + (let ((s "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec +hendrerit tempor tellus. Donec pretium posuere tellus. Proin quam +nisl, tincidunt et, mattis eget, convallis nec, purus. Cum sociis +natoque penatibus et magnis dis parturient montes, nascetur +ridiculus mus. Nulla posuere. Donec vitae dolor. Nullam tristique +diam non turpis. Cras placerat accumsan nulla. Nullam rutrum. Nam +vestibulum accumsan nisl. + +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec +hendrerit tempor tellus. Donec pretium posuere tellus. Proin quam +nisl, tincidunt et, mattis eget, convallis nec, purus. Cum sociis +natoque penatibus et magnis dis parturient montes, nascetur +ridiculus mus. Nulla posuere. Donec vitae dolor. Nullam tristique +diam non turpis. Cras placerat accumsan nulla. Nullam rutrum. Nam +vestibulum accumsan nisl.")) + (ws-send process s)))) + 9016) + + +File: web-server.info, Node: Chunked Transfer Encoding, Next: Function Index, Prev: Web Socket, Up: Usage Examples + +4.12 Chunked Transfer Encoding +============================== + +Similarly, HTTP Responses may be sent using the “chunked” transfer +encoding by passing the appropriate HTTP header to ‘ws-response-header’. +Any further data sent to the process using ‘ws-send’ will automatically +be appropriately encoded for chunked transfer. + +;;; transfer-encoding-chunked.el -- chunked transfer encoding +;; Copyright (C) 2014 Free Software Foundation, Inc. + +(ws-start + (lambda (request) + (let ((s " +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec +hendrerit tempor tellus. Donec pretium posuere tellus. Proin quam +nisl, tincidunt et, mattis eget, convallis nec, purus. Cum sociis +natoque penatibus et magnis dis parturient montes, nascetur +ridiculus mus. Nulla posuere. Donec vitae dolor. Nullam tristique +diam non turpis. Cras placerat accumsan nulla. Nullam rutrum. Nam +vestibulum accumsan nisl. + +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec +hendrerit tempor tellus. Donec pretium posuere tellus. Proin quam +nisl, tincidunt et, mattis eget, convallis nec, purus. Cum sociis +natoque penatibus et magnis dis parturient montes, nascetur +ridiculus mus. Nulla posuere. Donec vitae dolor. Nullam tristique +diam non turpis. Cras placerat accumsan nulla. Nullam rutrum. Nam +vestibulum accumsan nisl. +")) + (with-slots (process headers) request + (ws-response-header process 200 + '("Content-type" . "text/plain; charset=utf-8") + '("Transfer-Encoding" . "chunked")) + (ws-send process s) (sit-for 0.5) + (ws-send process s) (sit-for 0.5) + (ws-send process s) (sit-for 0.5) + (ws-send process s)))) + 9017) + + +File: web-server.info, Node: Function Index, Next: Copying, Prev: Usage Examples, Up: Top + +5 Function Index +**************** + +The following functions implement the Emacs Web Server public API. + +5.1 Objects +=========== + +The following objects represent web servers and requests. + + -- Class: ws-server handlers process port requests + Every Emacs web server is an instance of the ‘ws-server’ class. + Each instance includes the ‘handlers’ association list and ‘port’ + passed to ‘ws-start’, as well as the server network ‘process’ and a + list of all active ‘requests’. + + -- Class: ws-request process pending context boundary index active + headers + The ‘ws-request’ class represents an active web request. The + ‘process’ field holds the network process of the client and may be + used by handlers to respond to requests. The ‘headers’ field holds + an alist of information on the request for use by handlers. The + remaining ‘pending’, ‘context’, ‘boundary’, ‘index’ and ‘active’ + fields are used to maintain header parsing information across calls + to the ‘ws-filter’ function. + +5.2 Starting and Stopping Servers +================================= + +The following functions start and stop Emacs web servers. The +‘ws-servers’ list holds all running servers. + + -- Function: ws-start handlers port &optional log-buffer &rest + network-args + ‘ws-start’ starts a server listening on ‘port’ using ‘handlers’ + (*note Handlers::) to match and respond to requests. An instance + of the ‘ws-server’ class is returned. + + -- Variable: ws-servers + The ‘ws-servers’ list holds all active Emacs web servers. + + -- Function: ws-stop server + ‘ws-stop’ stops ‘server’ deletes all related processes, and frees + the server’s port. Evaluate the following to stop all emacs web + servers. + (mapc #'ws-stop ws-servers) + + -- Function: ws-stop-all + ‘ws-stop-all’ stops all emacs web servers by mapping ‘ws-stop’ over + ‘ws-servers’. + +5.3 Convenience Functions +========================= + +The following convenience functions automate many common tasks +associated with responding to HTTP requests. + + -- Function: ws-response-header process code &rest headers + Send the headers required to start an HTTP response to ‘proc’. + ‘proc’ should be a ‘ws-request’ ‘proc’ of an active request. + + For example start a standard 200 “OK” HTML response with the + following. + + (ws-response-header process 200 '("Content-type" . "text/html")) + + The encoding may optionally be set in the HTTP header. Send a UTF8 + encoded response with the following. + + (ws-response-header process 200 + '("Content-type" . "text/plain; charset=utf-8")) + + Additionally, when “Content-Encoding” or “Transfer-Encoding” + headers are supplied any subsequent data written to ‘proc’ using + ‘ws-send’ will be encoded appropriately including sending the + appropriate data upon the end of transmission for chunked transfer + encoding. + + For example with the header ‘("Content-Encoding" . "gzip")’, any + data subsequently written to ‘proc’ using ‘ws-send’ will be + compressed using the command specified in ‘ws-gzip-cmd’. See *note + Gzipped Transfer Encoding:: and *note Chunked Transfer Encoding:: + for more complete examples. + + -- Function: ws-send proc string + Send ‘string’ to process ‘proc’. If any Content or Transfer + encodings are in use, apply them to ‘string’ before sending. + + -- Function: ws-send-500 process &rest msg-and-args + ‘ws-send-500’ sends a default 500 “Internal Server Error” response + to ‘process’. + + -- Function: ws-send-404 process &rest msg-and-args + ‘ws-send-500’ sends a default 404 “File Not Found” response to + ‘process’. + + -- Function: ws-send-file process path &optional mime-type + ‘ws-send-file’ sends the file located at ‘path’ to ‘process’. If + the optional ‘mime-type’ is not set, then the mime-type is + determined by calling ‘mm-default-file-encoding’ on ‘path’ or is + set to “application/octet-stream” if no mime-type can be + determined. + + -- Function: ws-send-directory-list process directory &optional match + ‘ws-send-directory-list’ sends the a listing of the files located + in ‘directory’ to ‘process’. The list is sent as an HTML list of + links to the files. Optional argument ‘match’ may be set to a + regular expression, in which case only those files that match are + listed. + + -- Function: ws-in-directory-p parent path + Check if ‘path’ is under the ‘parent’ directory. + + (ws-in-directory-p "/tmp/" "pics") + ⇒ "/tmp/pics" + + (ws-in-directory-p "/tmp/" "..") + ⇒ nil + + (ws-in-directory-p "/tmp/" "~/pics") + ⇒ nil + + -- Function: ws-with-authentication handler credentials &optional realm + unauth invalid + Return a version of ‘handler’ which is protected by ‘credentials’. + Handler should be a normal handler function (*note Handlers::) and + ‘credentials’ should be an association list of usernames and + passwords. + + For example, a server running the following handlers, + + (list (cons '(:GET . ".*") 'view-handler) + (cons '(:POST . ".*") 'edit-handler)) + + could have authorization added by changing the handlers to the + following. + + (list (cons '(:GET . ".*") view-handler) + (cons '(:POST . ".*") (ws-with-authentication + 'org-ehtml-edit-handler + '(("admin" . "password"))))) + + -- Function: ws-web-socket-connect request handler + If ‘request’ is a web socket upgrade request (indicated by the + presence of the ‘:SEC-WEBSOCKET-KEY’ header argument) establish a + web socket connection to the client. Call ‘handler’ on web socket + messages received from the client. + + (ws-web-socket-connect request + (lambda (proc string) + (process-send-string proc + (ws-web-socket-frame (concat "you said: " string))))) + ⇒ #> + +5.4 Customization Variables +=========================== + +The following variables may be changed to control the behavior of the +web server. Specifically the ‘ws-*-cmd’ variables specify the command +lines used to compress data according to content and or transfer +encoding HTTP headers passed to *note ws-response-header::. + + -- Variable: ws-compress-cmd + Command used for the “compress” Content or Transfer coding. + + -- Variable: ws-deflate-cmd + Command used for the “deflate” Content or Transfer coding. + + -- Variable: ws-gzip-cmd + Command used for the “gzip” Content or Transfer coding. + + +File: web-server.info, Node: Copying, Next: GNU Free Documentation License, Prev: Function Index, Up: Top + +Appendix A GNU GENERAL PUBLIC LICENSE +************************************* + + Version 3, 29 June 2007 + + Copyright © 2007 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies of this + license document, but changing it is not allowed. + +Preamble +======== + +The GNU General Public License is a free, copyleft license for software +and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program—to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers’ and authors’ protection, the GPL clearly explains +that there is no warranty for this free software. For both users’ and +authors’ sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users’ freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS +==================== + + 0. Definitions. + + “This License” refers to version 3 of the GNU General Public + License. + + “Copyright” also means copyright-like laws that apply to other + kinds of works, such as semiconductor masks. + + “The Program” refers to any copyrightable work licensed under this + License. Each licensee is addressed as “you”. “Licensees” and + “recipients” may be individuals or organizations. + + To “modify” a work means to copy from or adapt all or part of the + work in a fashion requiring copyright permission, other than the + making of an exact copy. The resulting work is called a “modified + version” of the earlier work or a work “based on” the earlier work. + + A “covered work” means either the unmodified Program or a work + based on the Program. + + To “propagate” a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on + a computer or modifying a private copy. Propagation includes + copying, distribution (with or without modification), making + available to the public, and in some countries other activities as + well. + + To “convey” a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user + through a computer network, with no transfer of a copy, is not + conveying. + + An interactive user interface displays “Appropriate Legal Notices” + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to + the extent that warranties are provided), that licensees may convey + the work under this License, and how to view a copy of this + License. If the interface presents a list of user commands or + options, such as a menu, a prominent item in the list meets this + criterion. + + 1. Source Code. + + The “source code” for a work means the preferred form of the work + for making modifications to it. “Object code” means any non-source + form of a work. + + A “Standard Interface” means an interface that either is an + official standard defined by a recognized standards body, or, in + the case of interfaces specified for a particular programming + language, one that is widely used among developers working in that + language. + + The “System Libraries” of an executable work include anything, + other than the work as a whole, that (a) is included in the normal + form of packaging a Major Component, but which is not part of that + Major Component, and (b) serves only to enable use of the work with + that Major Component, or to implement a Standard Interface for + which an implementation is available to the public in source code + form. A “Major Component”, in this context, means a major + essential component (kernel, window system, and so on) of the + specific operating system (if any) on which the executable work + runs, or a compiler used to produce the work, or an object code + interpreter used to run it. + + The “Corresponding Source” for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts + to control those activities. However, it does not include the + work’s System Libraries, or general-purpose tools or generally + available free programs which are used unmodified in performing + those activities but which are not part of the work. For example, + Corresponding Source includes interface definition files associated + with source files for the work, and the source code for shared + libraries and dynamically linked subprograms that the work is + specifically designed to require, such as by intimate data + communication or control flow between those subprograms and other + parts of the work. + + The Corresponding Source need not include anything that users can + regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running + a covered work is covered by this License only if the output, given + its content, constitutes a covered work. This License acknowledges + your rights of fair use or other equivalent, as provided by + copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise + remains in force. You may convey covered works to others for the + sole purpose of having them make modifications exclusively for you, + or provide you with facilities for running those works, provided + that you comply with the terms of this License in conveying all + material for which you do not control copyright. Those thus making + or running the covered works for you must do so exclusively on your + behalf, under your direction and control, on terms that prohibit + them from making any copies of your copyrighted material outside + their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section + 10 makes it unnecessary. + + 3. Protecting Users’ Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under + article 11 of the WIPO copyright treaty adopted on 20 December + 1996, or similar laws prohibiting or restricting circumvention of + such measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such + circumvention is effected by exercising rights under this License + with respect to the covered work, and you disclaim any intention to + limit operation or modification of the work as a means of + enforcing, against the work’s users, your or third parties’ legal + rights to forbid circumvention of technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program’s source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the + code; keep intact all notices of the absence of any warranty; and + give all recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these + conditions: + + a. The work must carry prominent notices stating that you + modified it, and giving a relevant date. + + b. The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in + section 4 to “keep intact all notices”. + + c. You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable + section 7 additional terms, to the whole of the work, and all + its parts, regardless of how they are packaged. This License + gives no permission to license the work in any other way, but + it does not invalidate such permission if you have separately + received it. + + d. If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has + interactive interfaces that do not display Appropriate Legal + Notices, your work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered + work, and which are not combined with it such as to form a larger + program, in or on a volume of a storage or distribution medium, is + called an “aggregate” if the compilation and its resulting + copyright are not used to limit the access or legal rights of the + compilation’s users beyond what the individual works permit. + Inclusion of a covered work in an aggregate does not cause this + License to apply to the other parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this + License, in one of these ways: + + a. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that + product model, to give anyone who possesses the object code + either (1) a copy of the Corresponding Source for all the + software in the product that is covered by this License, on a + durable physical medium customarily used for software + interchange, for a price no more than your reasonable cost of + physically performing this conveying of source, or (2) access + to copy the Corresponding Source from a network server at no + charge. + + c. Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, + and only if you received the object code with such an offer, + in accord with subsection 6b. + + d. Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to + the Corresponding Source in the same way through the same + place at no further charge. You need not require recipients + to copy the Corresponding Source along with the object code. + If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by + you or a third party) that supports equivalent copying + facilities, provided you maintain clear directions next to the + object code saying where to find the Corresponding Source. + Regardless of what server hosts the Corresponding Source, you + remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + + e. Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the + general public at no charge under subsection 6d. + + A separable portion of the object code, whose source code is + excluded from the Corresponding Source as a System Library, need + not be included in conveying the object code work. + + A “User Product” is either (1) a “consumer product”, which means + any tangible personal property which is normally used for personal, + family, or household purposes, or (2) anything designed or sold for + incorporation into a dwelling. In determining whether a product is + a consumer product, doubtful cases shall be resolved in favor of + coverage. For a particular product received by a particular user, + “normally used” refers to a typical or common use of that class of + product, regardless of the status of the particular user or of the + way in which the particular user actually uses, or expects or is + expected to use, the product. A product is a consumer product + regardless of whether the product has substantial commercial, + industrial or non-consumer uses, unless such uses represent the + only significant mode of use of the product. + + “Installation Information” for a User Product means any methods, + procedures, authorization keys, or other information required to + install and execute modified versions of a covered work in that + User Product from a modified version of its Corresponding Source. + The information must suffice to ensure that the continued + functioning of the modified object code is in no case prevented or + interfered with solely because modification has been made. + + If you convey an object code work under this section in, or with, + or specifically for use in, a User Product, and the conveying + occurs as part of a transaction in which the right of possession + and use of the User Product is transferred to the recipient in + perpetuity or for a fixed term (regardless of how the transaction + is characterized), the Corresponding Source conveyed under this + section must be accompanied by the Installation Information. But + this requirement does not apply if neither you nor any third party + retains the ability to install modified object code on the User + Product (for example, the work has been installed in ROM). + + The requirement to provide Installation Information does not + include a requirement to continue to provide support service, + warranty, or updates for a work that has been modified or installed + by the recipient, or for the User Product in which it has been + modified or installed. Access to a network may be denied when the + modification itself materially and adversely affects the operation + of the network or violates the rules and protocols for + communication across the network. + + Corresponding Source conveyed, and Installation Information + provided, in accord with this section must be in a format that is + publicly documented (and with an implementation available to the + public in source code form), and must require no special password + or key for unpacking, reading or copying. + + 7. Additional Terms. + + “Additional permissions” are terms that supplement the terms of + this License by making exceptions from one or more of its + conditions. Additional permissions that are applicable to the + entire Program shall be treated as though they were included in + this License, to the extent that they are valid under applicable + law. If additional permissions apply only to part of the Program, + that part may be used separately under those permissions, but the + entire Program remains governed by this License without regard to + the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part + of it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material + you add to a covered work, you may (if authorized by the copyright + holders of that material) supplement the terms of this License with + terms: + + a. Disclaiming warranty or limiting liability differently from + the terms of sections 15 and 16 of this License; or + + b. Requiring preservation of specified reasonable legal notices + or author attributions in that material or in the Appropriate + Legal Notices displayed by works containing it; or + + c. Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked + in reasonable ways as different from the original version; or + + d. Limiting the use for publicity purposes of names of licensors + or authors of the material; or + + e. Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f. Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified + versions of it) with contractual assumptions of liability to + the recipient, for any liability that these contractual + assumptions directly impose on those licensors and authors. + + All other non-permissive additional terms are considered “further + restrictions” within the meaning of section 10. If the Program as + you received it, or any part of it, contains a notice stating that + it is governed by this License along with a term that is a further + restriction, you may remove that term. If a license document + contains a further restriction but permits relicensing or conveying + under this License, you may add to a covered work material governed + by the terms of that license document, provided that the further + restriction does not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in + the form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights + under this License (including any patent licenses granted under the + third paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, you do not qualify to receive new licenses + for the same material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer + transmission to receive a copy likewise does not require + acceptance. However, nothing other than this License grants you + permission to propagate or modify any covered work. These actions + infringe copyright if you do not accept this License. Therefore, + by modifying or propagating a covered work, you indicate your + acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not + responsible for enforcing compliance by third parties with this + License. + + An “entity transaction” is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a + covered work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party’s predecessor in interest had or + could give under the previous paragraph, plus a right to possession + of the Corresponding Source of the work from the predecessor in + interest, if the predecessor has it or can get it with reasonable + efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you + may not impose a license fee, royalty, or other charge for exercise + of rights granted under this License, and you may not initiate + litigation (including a cross-claim or counterclaim in a lawsuit) + alleging that any patent claim is infringed by making, using, + selling, offering for sale, or importing the Program or any portion + of it. + + 11. Patents. + + A “contributor” is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. + The work thus licensed is called the contributor’s “contributor + version”. + + A contributor’s “essential patent claims” are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, + permitted by this License, of making, using, or selling its + contributor version, but do not include claims that would be + infringed only as a consequence of further modification of the + contributor version. For purposes of this definition, “control” + includes the right to grant patent sublicenses in a manner + consistent with the requirements of this License. + + Each contributor grants you a non-exclusive, worldwide, + royalty-free patent license under the contributor’s essential + patent claims, to make, use, sell, offer for sale, import and + otherwise run, modify and propagate the contents of its contributor + version. + + In the following three paragraphs, a “patent license” is any + express agreement or commitment, however denominated, not to + enforce a patent (such as an express permission to practice a + patent or covenant not to sue for patent infringement). To “grant” + such a patent license to a party means to make such an agreement or + commitment not to enforce a patent against the party. + + If you convey a covered work, knowingly relying on a patent + license, and the Corresponding Source of the work is not available + for anyone to copy, free of charge and under the terms of this + License, through a publicly available network server or other + readily accessible means, then you must either (1) cause the + Corresponding Source to be so available, or (2) arrange to deprive + yourself of the benefit of the patent license for this particular + work, or (3) arrange, in a manner consistent with the requirements + of this License, to extend the patent license to downstream + recipients. “Knowingly relying” means you have actual knowledge + that, but for the patent license, your conveying the covered work + in a country, or your recipient’s use of the covered work in a + country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, + modify or convey a specific copy of the covered work, then the + patent license you grant is automatically extended to all + recipients of the covered work and works based on it. + + A patent license is “discriminatory” if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that + are specifically granted under this License. You may not convey a + covered work if you are a party to an arrangement with a third + party that is in the business of distributing software, under which + you make payment to the third party based on the extent of your + activity of conveying the work, and under which the third party + grants, to any of the parties who would receive the covered work + from you, a discriminatory patent license (a) in connection with + copies of the covered work conveyed by you (or copies made from + those copies), or (b) primarily for and in connection with specific + products or compilations that contain the covered work, unless you + entered into that arrangement, or that patent license was granted, + prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others’ Freedom. + + If conditions are imposed on you (whether by court order, agreement + or otherwise) that contradict the conditions of this License, they + do not excuse you from the conditions of this License. If you + cannot convey a covered work so as to satisfy simultaneously your + obligations under this License and any other pertinent obligations, + then as a consequence you may not convey it at all. For example, + if you agree to terms that obligate you to collect a royalty for + further conveying from those to whom you convey the Program, the + only way you could satisfy both those terms and this License would + be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a + single combined work, and to convey the resulting work. The terms + of this License will continue to apply to the part which is the + covered work, but the special requirements of the GNU Affero + General Public License, section 13, concerning interaction through + a network will apply to the combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new + versions of the GNU General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU + General Public License “or any later version” applies to it, you + have the option of following the terms and conditions either of + that numbered version or of any later version published by the Free + Software Foundation. If the Program does not specify a version + number of the GNU General Public License, you may choose any + version ever published by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that + proxy’s public statement of acceptance of a version permanently + authorizes you to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE + COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE + RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. + SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES + AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE + THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA + BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely + approximates an absolute waiver of all civil liability in + connection with the Program, unless a warranty or assumption of + liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS +=========================== + +How to Apply These Terms to Your New Programs +============================================= + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least the +“copyright” line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) YEAR NAME OF AUTHOR + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper +mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + PROGRAM Copyright (C) YEAR NAME OF AUTHOR + This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’. + This is free software, and you are welcome to redistribute it + under certain conditions; type ‘show c’ for details. + + The hypothetical commands ‘show w’ and ‘show c’ should show the +appropriate parts of the General Public License. Of course, your +program’s commands might be different; for a GUI interface, you would +use an “about box”. + + You should also get your employer (if you work as a programmer) or +school, if any, to sign a “copyright disclaimer” for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + + The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . + + +File: web-server.info, Node: GNU Free Documentation License, Next: Index, Prev: Copying, Up: Top + +Appendix B GNU Free Documentation License +***************************************** + + Version 1.3, 3 November 2008 + + Copyright © 2000, 2001, 2002, 2007, 2008, 2009 Free Software Foundation, Inc. + + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document “free” in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of “copyleft”, which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. We + recommend this License principally for works whose purpose is + instruction or reference. + + 1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it can + be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + “Document”, below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as “you”. You accept + the license if you copy, modify or distribute the work in a way + requiring permission under copyright law. + + A “Modified Version” of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A “Secondary Section” is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document’s overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The “Invariant Sections” are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in the + notice that says that the Document is released under this License. + If a section does not fit the above definition of Secondary then it + is not allowed to be designated as Invariant. The Document may + contain zero Invariant Sections. If the Document does not identify + any Invariant Sections then there are none. + + The “Cover Texts” are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A “Transparent” copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images composed + of pixels) generic paint programs or (for drawings) some widely + available drawing editor, and that is suitable for input to text + formatters or for automatic translation to a variety of formats + suitable for input to text formatters. A copy made in an otherwise + Transparent file format whose markup, or absence of markup, has + been arranged to thwart or discourage subsequent modification by + readers is not Transparent. An image format is not Transparent if + used for any substantial amount of text. A copy that is not + “Transparent” is called “Opaque”. + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and standard-conforming + simple HTML, PostScript or PDF designed for human modification. + Examples of transparent image formats include PNG, XCF and JPG. + Opaque formats include proprietary formats that can be read and + edited only by proprietary word processors, SGML or XML for which + the DTD and/or processing tools are not generally available, and + the machine-generated HTML, PostScript or PDF produced by some word + processors for output purposes only. + + The “Title Page” means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, “Title + Page” means the text near the most prominent appearance of the + work’s title, preceding the beginning of the body of the text. + + The “publisher” means any person or entity that distributes copies + of the Document to the public. + + A section “Entitled XYZ” means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) + To “Preserve the Title” of such a section when you modify the + Document means that it remains a section “Entitled XYZ” according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + + 2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow the + conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + + 3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document’s license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the title + equally prominent and visible. You may add other material on the + covers in addition. Copying with changes limited to the covers, as + long as they preserve the title of the Document and satisfy these + conditions, can be treated as verbatim copying in other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a machine-readable + Transparent copy along with each Opaque copy, or state in or with + each Opaque copy a computer-network location from which the general + network-using public has access to download using public-standard + network protocols a complete Transparent copy of the Document, free + of added material. If you use the latter option, you must take + reasonably prudent steps, when you begin distribution of Opaque + copies in quantity, to ensure that this Transparent copy will + remain thus accessible at the stated location until at least one + year after the last time you distribute an Opaque copy (directly or + through your agents or retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of copies, + to give them a chance to provide you with an updated version of the + Document. + + 4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with the + Modified Version filling the role of the Document, thus licensing + distribution and modification of the Modified Version to whoever + possesses a copy of it. In addition, you must do these things in + the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of previous + versions (which should, if there were any, be listed in the + History section of the Document). You may use the same title + as a previous version if the original publisher of that + version gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has fewer than five), unless they release you + from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified + Version under the terms of this License, in the form shown in + the Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document’s + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled “History”, Preserve its Title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on the + Title Page. If there is no section Entitled “History” in the + Document, create one stating the title, year, authors, and + publisher of the Document as given on its Title Page, then add + an item describing the Modified Version as stated in the + previous sentence. + + J. Preserve the network location, if any, given in the Document + for public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in the + “History” section. You may omit a network location for a work + that was published at least four years before the Document + itself, or if the original publisher of the version it refers + to gives permission. + + K. For any section Entitled “Acknowledgements” or “Dedications”, + Preserve the Title of the section, and preserve in the section + all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, unaltered + in their text and in their titles. Section numbers or the + equivalent are not considered part of the section titles. + + M. Delete any section Entitled “Endorsements”. Such a section + may not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + “Endorsements” or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option designate + some or all of these sections as invariant. To do this, add their + titles to the list of Invariant Sections in the Modified Version’s + license notice. These titles must be distinct from any other + section titles. + + You may add a section Entitled “Endorsements”, provided it contains + nothing but endorsements of your Modified Version by various + parties—for example, statements of peer review or that the text has + been approved by an organization as the authoritative definition of + a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end of + the list of Cover Texts in the Modified Version. Only one passage + of Front-Cover Text and one of Back-Cover Text may be added by (or + through arrangements made by) any one entity. If the Document + already includes a cover text for the same cover, previously added + by you or by arrangement made by the same entity you are acting on + behalf of, you may not add another; but you may replace the old + one, on explicit permission from the previous publisher that added + the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + + 5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination all + of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + “History” in the various original documents, forming one section + Entitled “History”; likewise combine any sections Entitled + “Acknowledgements”, and any sections Entitled “Dedications”. You + must delete all sections Entitled “Endorsements.” + + 6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the documents + in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow this + License in all other respects regarding verbatim copying of that + document. + + 7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of a + storage or distribution medium, is called an “aggregate” if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation’s users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document’s Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + + 8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of section + 4. Replacing Invariant Sections with translations requires special + permission from their copyright holders, but you may include + translations of some or all Invariant Sections in addition to the + original versions of these Invariant Sections. You may include a + translation of this License, and all the license notices in the + Document, and any Warranty Disclaimers, provided that you also + include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled “Acknowledgements”, + “Dedications”, or “History”, the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + + 9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, or distribute it is void, + and will automatically terminate your rights under this License. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, receipt of a copy of some or all of the + same material does not give you any rights to use it. + + 10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. See + . + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License “or any later version” applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If the + Document does not specify a version number of this License, you may + choose any version ever published (not as a draft) by the Free + Software Foundation. If the Document specifies that a proxy can + decide which future versions of this License can be used, that + proxy’s public statement of acceptance of a version permanently + authorizes you to choose that version for the Document. + + 11. RELICENSING + + “Massive Multiauthor Collaboration Site” (or “MMC Site”) means any + World Wide Web server that publishes copyrightable works and also + provides prominent facilities for anybody to edit those works. A + public wiki that anybody can edit is an example of such a server. + A “Massive Multiauthor Collaboration” (or “MMC”) contained in the + site means any set of copyrightable works thus published on the MMC + site. + + “CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 + license published by Creative Commons Corporation, a not-for-profit + corporation with a principal place of business in San Francisco, + California, as well as future copyleft versions of that license + published by that same organization. + + “Incorporate” means to publish or republish a Document, in whole or + in part, as part of another Document. + + An MMC is “eligible for relicensing” if it is licensed under this + License, and if all works that were first published under this + License somewhere other than this MMC, and subsequently + incorporated in whole or in part into the MMC, (1) had no cover + texts or invariant sections, and (2) were thus incorporated prior + to November 1, 2008. + + The operator of an MMC Site may republish an MMC contained in the + site under CC-BY-SA on the same site at any time before August 1, + 2009, provided the MMC is eligible for relicensing. + +ADDENDUM: How to use this License for your documents +==================================================== + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and license +notices just after the title page: + + Copyright (C) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + + If you have Invariant Sections, Front-Cover Texts and Back-Cover +Texts, replace the “with...Texts.” line with this: + + with the Invariant Sections being LIST THEIR TITLES, with + the Front-Cover Texts being LIST, and with the Back-Cover Texts + being LIST. + + If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + + If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of free +software license, such as the GNU General Public License, to permit +their use in free software. + + +File: web-server.info, Node: Index, Prev: GNU Free Documentation License, Up: Top + +Index +***** + +[index] +* Menu: + +* content type: Function Index. (line 60) +* function index: Function Index. (line 6) +* handler function: Handlers. (line 41) +* handlers: Handlers. (line 6) +* introduction: Introduction. (line 6) +* matchers: Handlers. (line 17) +* requests: Requests. (line 6) +* start and stop: Function Index. (line 32) +* usage examples: Usage Examples. (line 6) +* ws-compress-cmd: Function Index. (line 165) +* ws-deflate-cmd: Function Index. (line 168) +* ws-gzip-cmd: Function Index. (line 171) +* ws-in-directory-p: Function Index. (line 113) +* ws-request: Function Index. (line 19) +* ws-response-header: Function Index. (line 60) +* ws-send: Function Index. (line 87) +* ws-send-404: Function Index. (line 95) +* ws-send-500: Function Index. (line 91) +* ws-send-directory-list: Function Index. (line 106) +* ws-send-file: Function Index. (line 99) +* ws-server: Function Index. (line 13) +* ws-servers: Function Index. (line 41) +* ws-start: Function Index. (line 35) +* ws-stop: Function Index. (line 44) +* ws-stop-all: Function Index. (line 50) +* ws-web-socket-connect: Function Index. (line 145) +* ws-with-authentication: Function Index. (line 125) + + + +Tag Table: +Node: Top709 +Node: Introduction2088 +Node: Handlers2778 +Node: Requests4796 +Node: Usage Examples6114 +Node: Hello World7048 +Node: Hello World UTF87885 +Node: Hello World HTML9065 +Node: File Server9780 +Node: URL Parameter Echo11143 +Node: POST Echo12377 +Node: Basic Authentication13446 +Node: Org-mode Export14987 +Node: File Upload17729 +Node: Web Socket18885 +Node: Gzipped Transfer Encoding22129 +Node: Chunked Transfer Encoding23797 +Node: Function Index25592 +Ref: ws-server25876 +Ref: ws-request26202 +Ref: ws-start26966 +Ref: ws-servers27253 +Ref: ws-stop27346 +Ref: ws-stop-all27579 +Ref: ws-response-header27872 +Ref: ws-send29112 +Ref: ws-send-50029291 +Ref: ws-send-40429448 +Ref: ws-send-file29598 +Ref: ws-send-directory-list29971 +Ref: ws-in-directory-p30353 +Ref: ws-with-authentication30672 +Ref: ws-web-socket-connect31508 +Ref: ws-compress-cmd32395 +Ref: ws-deflate-cmd32495 +Ref: ws-gzip-cmd32593 +Node: Copying32685 +Node: GNU Free Documentation License70476 +Node: Index95831 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/code/elpa/websocket-20210110.17/websocket-autoloads.el b/code/elpa/websocket-20210110.17/websocket-autoloads.el new file mode 100644 index 0000000..c68dedb --- /dev/null +++ b/code/elpa/websocket-20210110.17/websocket-autoloads.el @@ -0,0 +1,22 @@ +;;; websocket-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "websocket" "websocket.el" (0 0 0 0)) +;;; Generated autoloads from websocket.el + +(register-definition-prefixes "websocket" '("websocket-")) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; websocket-autoloads.el ends here diff --git a/code/elpa/websocket-20210110.17/websocket-pkg.el b/code/elpa/websocket-20210110.17/websocket-pkg.el new file mode 100644 index 0000000..0f86021 --- /dev/null +++ b/code/elpa/websocket-20210110.17/websocket-pkg.el @@ -0,0 +1,2 @@ +;;; Generated package description from websocket.el -*- no-byte-compile: t -*- +(define-package "websocket" "20210110.17" "Emacs WebSocket client and server" '((cl-lib "0.5")) :commit "82b370602fa0158670b1c6c769f223159affce9b" :authors '(("Andrew Hyatt" . "ahyatt@gmail.com")) :maintainer '("Andrew Hyatt" . "ahyatt@gmail.com") :keywords '("communication" "websocket" "server") :url "https://github.com/ahyatt/emacs-websocket") diff --git a/code/elpa/websocket-20210110.17/websocket.el b/code/elpa/websocket-20210110.17/websocket.el new file mode 100644 index 0000000..94552dc --- /dev/null +++ b/code/elpa/websocket-20210110.17/websocket.el @@ -0,0 +1,1052 @@ +;;; websocket.el --- Emacs WebSocket client and server -*- lexical-binding:t -*- + +;; Copyright (c) 2013, 2016-2017 Free Software Foundation, Inc. + +;; Author: Andrew Hyatt +;; Homepage: https://github.com/ahyatt/emacs-websocket +;; Keywords: Communication, Websocket, Server +;; Package-Version: 20210110.17 +;; Package-Commit: 82b370602fa0158670b1c6c769f223159affce9b +;; Version: 1.13 +;; Package-Requires: ((cl-lib "0.5")) +;; +;; 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 2 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 GNU Emacs. If not, see . + +;;; Commentary: +;; This implements RFC 6455, which can be found at +;; http://tools.ietf.org/html/rfc6455. +;; +;; This library contains code to connect Emacs as a client to a +;; websocket server, and for Emacs to act as a server for websocket +;; connections. +;; +;; Websockets clients are created by calling `websocket-open', which +;; returns a `websocket' struct. Users of this library use the +;; websocket struct, and can call methods `websocket-send-text', which +;; sends text over the websocket, or `websocket-send', which sends a +;; `websocket-frame' struct, enabling finer control of what is sent. +;; A callback is passed to `websocket-open' that will retrieve +;; websocket frames called from the websocket. Websockets are +;; eventually closed with `websocket-close'. +;; +;; Server functionality is similar. A server is started with +;; `websocket-server' called with a port and the callbacks to use, +;; which returns a process. The process can later be closed with +;; `websocket-server-close'. A `websocket' struct is also created +;; for every connection, and is exposed through the callbacks. + +(require 'bindat) +(require 'url-parse) +(require 'url-cookie) +(require 'seq) +(eval-when-compile (require 'cl-lib)) + +;;; Code: + +(cl-defstruct (websocket + (:constructor nil) + (:constructor websocket-inner-create)) + "A websocket structure. +This follows the W3C Websocket API, except translated to elisp +idioms. The API is implemented in both the websocket struct and +additional methods. Due to how defstruct slots are accessed, all +API methods are prefixed with \"websocket-\" and take a websocket +as an argument, so the distrinction between the struct API and +the additional helper APIs are not visible to the caller. + +A websocket struct is created with `websocket-open'. + +`ready-state' contains one of `connecting', `open', or +`closed', depending on the state of the websocket. + +The W3C API \"bufferedAmount\" call is not currently implemented, +since there is no elisp API to get the buffered amount from the +subprocess. There may, in fact, be output data buffered, +however, when the `on-message' or `on-close' callbacks are +called. + +`on-open', `on-message', `on-close', and `on-error' are described +in `websocket-open'. + +The `negotiated-extensions' slot lists the extensions accepted by +both the client and server, and `negotiated-protocols' does the +same for the protocols." + ;; API + (ready-state 'connecting) + client-data + on-open + on-message + on-close + on-error + negotiated-protocols + negotiated-extensions + (server-p nil :read-only t) + + ;; Other data - clients should not have to access this. + (url (cl-assert nil) :read-only t) + (protocols nil :read-only t) + (extensions nil :read-only t) + (conn (cl-assert nil) :read-only t) + ;; Only populated for servers, this is the server connection. + server-conn + accept-string + (inflight-input nil)) + +(defvar websocket-version "1.12" + "Version numbers of this version of websocket.el.") + +(defvar websocket-debug nil + "Set to true to output debugging info to a per-websocket buffer. +The buffer is ` *websocket URL debug*' where URL is the +URL of the connection.") + +(defconst websocket-guid "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + "The websocket GUID as defined in RFC 6455. +Do not change unless the RFC changes.") + +(defvar websocket-callback-debug-on-error nil + "If true, when an error happens in a client callback, invoke the debugger. +Having this on can cause issues with missing frames if the debugger is +exited by quitting instead of continuing, so it's best to have this set +to nil unless it is especially needed.") + +(defmacro websocket-document-function (function docstring) + "Document FUNCTION with DOCSTRING. Use this for defstruct accessor etc." + (declare (indent defun) + (doc-string 2)) + `(put ',function 'function-documentation ,docstring)) + +(websocket-document-function websocket-on-open + "Accessor for websocket on-open callback. +See `websocket-open' for details. + +\(fn WEBSOCKET)") + +(websocket-document-function websocket-on-message + "Accessor for websocket on-message callback. +See `websocket-open' for details. + +\(fn WEBSOCKET)") + +(websocket-document-function websocket-on-close + "Accessor for websocket on-close callback. +See `websocket-open' for details. + +\(fn WEBSOCKET)") + +(websocket-document-function websocket-on-error + "Accessor for websocket on-error callback. +See `websocket-open' for details. + +\(fn WEBSOCKET)") + +(defun websocket-genbytes (nbytes) + "Generate NBYTES random bytes." + (let ((s (make-string nbytes ?\s))) + (dotimes (i nbytes) + (aset s i (random 256))) + s)) + +(defun websocket-try-callback (websocket-callback callback-type websocket + &rest rest) + "Invoke function WEBSOCKET-CALLBACK with WEBSOCKET and REST args. +If an error happens, it is handled according to +`websocket-callback-debug-on-error'." + ;; This looks like it should be able to done more efficiently, but + ;; I'm not sure that's the case. We can't do it as a macro, since + ;; we want it to change whenever websocket-callback-debug-on-error + ;; changes. + (let ((args rest) + (debug-on-error websocket-callback-debug-on-error)) + (push websocket args) + (if websocket-callback-debug-on-error + (condition-case err + (apply (funcall websocket-callback websocket) args) + ((debug error) (funcall (websocket-on-error websocket) + websocket callback-type err))) + (condition-case err + (apply (funcall websocket-callback websocket) args) + (error (funcall (websocket-on-error websocket) websocket + callback-type err)))))) + +(defun websocket-genkey () + "Generate a key suitable for the websocket handshake." + (base64-encode-string (websocket-genbytes 16))) + +(defun websocket-calculate-accept (key) + "Calculate the expect value of the accept header. +This is based on the KEY from the Sec-WebSocket-Key header." + (base64-encode-string + (sha1 (concat key websocket-guid) nil nil t))) + +(defun websocket-get-bytes (s n) + "From string S, retrieve the value of N bytes. +Return the value as an unsigned integer. The value N must be a +power of 2, up to 8. + +We support getting frames up to 536870911 bytes (2^29 - 1), +approximately 537M long." + (if (= n 8) + (let* ((32-bit-parts + (bindat-get-field (bindat-unpack '((:val vec 2 u32)) s) :val)) + (cval + (logior (lsh (aref 32-bit-parts 0) 32) (aref 32-bit-parts 1)))) + (if (and (= (aref 32-bit-parts 0) 0) + (= (lsh (aref 32-bit-parts 1) -29) 0)) + cval + (signal 'websocket-unparseable-frame + (list "Frame value found too large to parse!")))) + ;; n is not 8 + (bindat-get-field + (condition-case _ + (bindat-unpack + `((:val + ,(cond ((= n 1) 'u8) + ((= n 2) 'u16) + ((= n 4) 'u32) + ;; This is an error with the library, + ;; not a user-facing, meaningful error. + (t (error + "websocket-get-bytes: Unknown N: %S" n))))) + s) + (args-out-of-range (signal 'websocket-unparseable-frame + (list (format "Frame unexpectedly short: %s" s))))) + :val))) + +(defun websocket-to-bytes (val nbytes) + "Encode the integer VAL in NBYTES of data. +NBYTES much be a power of 2, up to 8. + +This supports encoding values up to 536870911 bytes (2^29 - 1), +approximately 537M long." + (when (and (< nbytes 8) + (> val (expt 2 (* 8 nbytes)))) + ;; not a user-facing error, this must be caused from an error in + ;; this library + (error "websocket-to-bytes: Value %d could not be expressed in %d bytes" + val nbytes)) + (if (= nbytes 8) + (progn + (let* ((hi-32bits (lsh val -32)) + ;; This is just VAL on systems that don't have >= 32 bits. + (low-32bits (- val (lsh hi-32bits 32)))) + (when (or (> hi-32bits 0) (> (lsh low-32bits -29) 0)) + (signal 'websocket-frame-too-large (list val))) + (bindat-pack `((:val vec 2 u32)) + `((:val . [,hi-32bits ,low-32bits]))))) + (bindat-pack + `((:val ,(cond ((= nbytes 1) 'u8) + ((= nbytes 2) 'u16) + ((= nbytes 4) 'u32) + ;; Library error, not system error + (t (error "websocket-to-bytes: Unknown NBYTES: %S" nbytes))))) + `((:val . ,val))))) + +(defun websocket-get-opcode (s) + "Retrieve the opcode from first byte of string S." + (websocket-ensure-length s 1) + (let ((opcode (logand #xf (aref s 0)))) + (cond ((= opcode 0) 'continuation) + ((= opcode 1) 'text) + ((= opcode 2) 'binary) + ((= opcode 8) 'close) + ((= opcode 9) 'ping) + ((= opcode 10) 'pong)))) + +(defun websocket-get-payload-len (s) + "Parse out the payload length from the string S. +We start at position 0, and return a cons of the payload length and how +many bytes were consumed from the string." + (websocket-ensure-length s 1) + (let* ((initial-val (logand 127 (aref s 0)))) + (cond ((= initial-val 127) + (websocket-ensure-length s 9) + (cons (websocket-get-bytes (substring s 1) 8) 9)) + ((= initial-val 126) + (websocket-ensure-length s 3) + (cons (websocket-get-bytes (substring s 1) 2) 3)) + (t (cons initial-val 1))))) + +(cl-defstruct websocket-frame opcode payload length completep) + +(defun websocket-frame-text (frame) + "Given FRAME, return the payload as a utf-8 encoded string." + (cl-assert (websocket-frame-p frame)) + (decode-coding-string (websocket-frame-payload frame) 'utf-8)) + +(defun websocket-mask (key data) + "Using string KEY, mask string DATA according to the RFC. +This is used to both mask and unmask data." + ;; Returning the string as unibyte is important here. Because we set the + ;; string byte by byte, this results in a unibyte string. + (cl-loop + with result = (make-string (length data) ?x) + for i from 0 below (length data) + do (setf (seq-elt result i) (logxor (aref key (mod i 4)) (seq-elt data i))) + finally return result)) + +(defun websocket-ensure-length (s n) + "Ensure the string S has at most N bytes. +Otherwise we throw the error `websocket-incomplete-frame'." + (when (< (length s) n) + (throw 'websocket-incomplete-frame nil))) + +(defun websocket-encode-frame (frame should-mask) + "Encode the FRAME struct to the binary representation. +We mask the frame or not, depending on SHOULD-MASK." + (let* ((opcode (websocket-frame-opcode frame)) + (payload (websocket-frame-payload frame)) + (fin (websocket-frame-completep frame)) + (payloadp (and payload + (memq opcode '(continuation ping pong text binary)))) + (mask-key (when should-mask (websocket-genbytes 4)))) + (apply #'unibyte-string + (let ((val (append (list + (logior (pcase opcode + (`continuation 0) + (`text 1) + (`binary 2) + (`close 8) + (`ping 9) + (`pong 10)) + (if fin 128 0))) + (when payloadp + (list + (logior + (if should-mask 128 0) + (cond ((< (length payload) 126) (length payload)) + ((< (length payload) 65536) 126) + (t 127))))) + (when (and payloadp (>= (length payload) 126)) + (append (websocket-to-bytes + (length payload) + (cond ((< (length payload) 126) 1) + ((< (length payload) 65536) 2) + (t 8))) nil)) + (when (and payloadp should-mask) + (append mask-key nil)) + (when payloadp + (append (if should-mask (websocket-mask mask-key payload) + payload) + nil))))) + ;; We have to make sure the non-payload data is a full 32-bit frame + (if (= 1 (length val)) + (append val '(0)) val))))) + +(defun websocket-read-frame (s) + "Read from string S a `websocket-frame' struct with the contents. +This only gets complete frames. Partial frames need to wait until +the frame finishes. If the frame is not completed, return NIL." + (catch 'websocket-incomplete-frame + (websocket-ensure-length s 1) + (let* ((opcode (websocket-get-opcode s)) + (fin (logand 128 (aref s 0))) + (payloadp (memq opcode '(continuation text binary ping pong))) + (payload-len (when payloadp + (websocket-get-payload-len (substring s 1)))) + (maskp (and + payloadp + (= 128 (logand 128 (aref s 1))))) + (payload-start (when payloadp (+ (if maskp 5 1) (cdr payload-len)))) + (payload-end (when payloadp (+ payload-start (car payload-len)))) + (unmasked-payload (when payloadp + (websocket-ensure-length s payload-end) + (substring s payload-start payload-end)))) + (make-websocket-frame + :opcode opcode + :payload + (if maskp + (let ((masking-key (substring s (+ 1 (cdr payload-len)) + (+ 5 (cdr payload-len))))) + (websocket-mask masking-key unmasked-payload)) + unmasked-payload) + :length (if payloadp payload-end 1) + :completep (> fin 0))))) + +(defun websocket-format-error (err) + "Format an error message like command level does. +ERR should be a cons of error symbol and error data." + + ;; Formatting code adapted from `edebug-report-error' + (concat (or (get (car err) 'error-message) + (format "peculiar error (%s)" (car err))) + (when (cdr err) + (format ": %s" + (mapconcat #'prin1-to-string + (cdr err) ", "))))) + +(defun websocket-default-error-handler (_websocket type err) + "The default error handler used to handle errors in callbacks." + (display-warning 'websocket + (format "in callback `%S': %s" + type + (websocket-format-error err)) + :error)) + +;; Error symbols in use by the library +(put 'websocket-unsupported-protocol 'error-conditions + '(error websocket-error websocket-unsupported-protocol)) +(put 'websocket-unsupported-protocol 'error-message "Unsupported websocket protocol") +(put 'websocket-wss-needs-emacs-24 'error-conditions + '(error websocket-error websocket-unsupported-protocol + websocket-wss-needs-emacs-24)) +(put 'websocket-wss-needs-emacs-24 'error-message + "wss protocol is not supported for Emacs before version 24.") +(put 'websocket-received-error-http-response 'error-conditions + '(error websocket-error websocket-received-error-http-response)) +(put 'websocket-received-error-http-response 'error-message + "Error response received from websocket server") +(put 'websocket-invalid-header 'error-conditions + '(error websocket-error websocket-invalid-header)) +(put 'websocket-invalid-header 'error-message + "Invalid HTTP header sent") +(put 'websocket-illegal-frame 'error-conditions + '(error websocket-error websocket-illegal-frame)) +(put 'websocket-illegal-frame 'error-message + "Cannot send illegal frame to websocket") +(put 'websocket-closed 'error-conditions + '(error websocket-error websocket-closed)) +(put 'websocket-closed 'error-message + "Cannot send message to a closed websocket") +(put 'websocket-unparseable-frame 'error-conditions + '(error websocket-error websocket-unparseable-frame)) +(put 'websocket-unparseable-frame 'error-message + "Received an unparseable frame") +(put 'websocket-frame-too-large 'error-conditions + '(error websocket-error websocket-frame-too-large)) +(put 'websocket-frame-too-large 'error-message + "The frame being sent is too large for this emacs to handle") + +(defun websocket-intersect (a b) + "Simple list intersection, should function like Common Lisp's `intersection'." + (let ((result)) + (dolist (elem a (nreverse result)) + (when (member elem b) + (push elem result))))) + +(defun websocket-get-debug-buffer-create (websocket) + "Get or create the buffer corresponding to WEBSOCKET." + (let ((buf (get-buffer-create (format "*websocket %s debug*" + (websocket-url websocket))))) + (when (= 0 (buffer-size buf)) + (buffer-disable-undo buf)) + buf)) + +(defun websocket-debug (websocket msg &rest args) + "In the WEBSOCKET's debug buffer, send MSG, with format ARGS." + (when websocket-debug + (let ((buf (websocket-get-debug-buffer-create websocket))) + (save-excursion + (with-current-buffer buf + (goto-char (point-max)) + (insert "[WS] ") + (insert (apply #'format (append (list msg) args))) + (insert "\n")))))) + +(defun websocket-verify-response-code (output) + "Verify that OUTPUT contains a valid HTTP response code. +The only acceptable one to websocket is responce code 101. +A t value will be returned on success, and an error thrown +if not." + (unless (string-match "^HTTP/1.1 \\([[:digit:]]+\\)" output) + (signal 'websocket-invalid-header (list "Invalid HTTP status line"))) + (unless (equal "101" (match-string 1 output)) + (signal 'websocket-received-error-http-response + (list (string-to-number (match-string 1 output))))) + t) + +(defun websocket-parse-repeated-field (output field) + "From header-containing OUTPUT, parse out the list from a +possibly repeated field." + (let ((pos 0) + (extensions)) + (while (and pos + (string-match (format "\r\n%s: \\(.*\\)\r\n" field) + output pos)) + (when (setq pos (match-end 1)) + (setq extensions (append extensions (split-string + (match-string 1 output) ", ?"))))) + extensions)) + +(defun websocket-process-frame (websocket frame) + "Using the WEBSOCKET's filter and connection, process the FRAME. +This returns a lambda that should be executed when all frames have +been processed. If the frame has a payload, the lambda has the frame +passed to the filter slot of WEBSOCKET. If the frame is a ping, +the lambda has a reply with a pong. If the frame is a close, the lambda +has connection termination." + (let ((opcode (websocket-frame-opcode frame))) + (cond ((memq opcode '(continuation text binary)) + (lambda () (websocket-try-callback 'websocket-on-message 'on-message + websocket frame))) + ((eq opcode 'ping) + (lambda () (websocket-send websocket + (make-websocket-frame + :opcode 'pong + :payload (websocket-frame-payload frame) + :completep t)))) + ((eq opcode 'close) + (lambda () (delete-process (websocket-conn websocket)))) + (t (lambda ()))))) + +(defun websocket-process-input-on-open-ws (websocket text) + "This handles input processing for both the client and server filters." + (let ((current-frame) + (processing-queue) + (start-point 0)) + (while (setq current-frame (websocket-read-frame + (substring text start-point))) + (push (websocket-process-frame websocket current-frame) processing-queue) + (cl-incf start-point (websocket-frame-length current-frame))) + (when (> (length text) start-point) + (setf (websocket-inflight-input websocket) + (substring text start-point))) + (dolist (to-process (nreverse processing-queue)) + (funcall to-process)))) + +(defun websocket-send-text (websocket text) + "To the WEBSOCKET, send TEXT as a complete frame." + (websocket-send + websocket + (make-websocket-frame :opcode 'text + :payload (encode-coding-string + text 'raw-text) + :completep t))) + +(defun websocket-check (frame) + "Check FRAME for correctness, returning true if correct." + (or + ;; Text, binary, and continuation frames need payloads + (and (memq (websocket-frame-opcode frame) '(text binary continuation)) + (websocket-frame-payload frame)) + ;; Pings and pongs may optionally have them + (memq (websocket-frame-opcode frame) '(ping pong)) + ;; And close shouldn't have any payload, and should always be complete. + (and (eq (websocket-frame-opcode frame) 'close) + (not (websocket-frame-payload frame)) + (websocket-frame-completep frame)))) + +(defun websocket-send (websocket frame) + "To the WEBSOCKET server, send the FRAME. +This will raise an error if the frame is illegal. + +The error signaled may be of type `websocket-illegal-frame' if +the frame is malformed in some way, also having the condition +type of `websocket-error'. The data associated with the signal +is the frame being sent. + +If the websocket is closed a signal `websocket-closed' is sent, +also with `websocket-error' condition. The data in the signal is +also the frame. + +The frame may be too large for this buid of Emacs, in which case +`websocket-frame-too-large' is returned, with the data of the +size of the frame which was too large to process. This also has +the `websocket-error' condition." + (unless (websocket-check frame) + (signal 'websocket-illegal-frame (list frame))) + (websocket-debug websocket "Sending frame, opcode: %s payload: %s" + (websocket-frame-opcode frame) + (websocket-frame-payload frame)) + (unless (websocket-openp websocket) + (signal 'websocket-closed (list frame))) + (process-send-string (websocket-conn websocket) + ;; We mask only when we're a client, following the spec. + (websocket-encode-frame frame (not (websocket-server-p websocket))))) + +(defun websocket-openp (websocket) + "Check WEBSOCKET and return non-nil if the connection is open." + (and websocket + (not (eq 'close (websocket-ready-state websocket))) + (member (process-status (websocket-conn websocket)) '(open run)))) + +(defun websocket-close (websocket) + "Close WEBSOCKET and erase all the old websocket data." + (websocket-debug websocket "Closing websocket") + (websocket-try-callback 'websocket-on-close 'on-close websocket) + (when (websocket-openp websocket) + (websocket-send websocket + (make-websocket-frame :opcode 'close + :completep t)) + (setf (websocket-ready-state websocket) 'closed)) + (delete-process (websocket-conn websocket))) + +;;;;;;;;;;;;;;;;;;;;;; +;; Websocket client ;; +;;;;;;;;;;;;;;;;;;;;;; + +(cl-defun websocket-open (url &key protocols extensions (on-open 'identity) + (on-message (lambda (_w _f))) (on-close 'identity) + (on-error 'websocket-default-error-handler) + (nowait nil) (custom-header-alist nil)) + "Open a websocket connection to URL, returning the `websocket' struct. +The PROTOCOL argument is optional, and setting it will declare to +the server that this client supports the protocols in the list +given. We will require that the server also has to support that +protocols. + +Similar logic applies to EXTENSIONS, which is a list of conses, +the car of which is a string naming the extension, and the cdr of +which is the list of parameter strings to use for that extension. +The parameter strings are of the form \"key=value\" or \"value\". +EXTENSIONS can be NIL if none are in use. An example value would +be (\"deflate-stream\" . (\"mux\" \"max-channels=4\")). + +Cookies that are set via `url-cookie-store' will be used during +communication with the server, and cookies received from the +server will be stored in the same cookie storage that the +`url-cookie' package uses. + +Optionally you can specify +ON-OPEN, ON-MESSAGE and ON-CLOSE callbacks as well. + +The ON-OPEN callback is called after the connection is +established with the websocket as the only argument. The return +value is unused. + +The ON-MESSAGE callback is called after receiving a frame, and is +called with the websocket as the first argument and +`websocket-frame' struct as the second. The return value is +unused. + +The ON-CLOSE callback is called after the connection is closed, or +failed to open. It is called with the websocket as the only +argument, and the return value is unused. + +The ON-ERROR callback is called when any of the other callbacks +have an error. It takes the websocket as the first argument, and +a symbol as the second argument either `on-open', `on-message', +or `on-close', and the error as the third argument. Do NOT +rethrow the error, or else you may miss some websocket messages. +You similarly must not generate any other errors in this method. +If you want to debug errors, set +`websocket-callback-debug-on-error' to t, but this also can be +dangerous is the debugger is quit out of. If not specified, +`websocket-default-error-handler' is used. + +For each of these event handlers, the client code can store +arbitrary data in the `client-data' slot in the returned +websocket. + +The following errors might be thrown in this method or in +websocket processing, all of them having the error-condition +`websocket-error' in addition to their own symbol: + +`websocket-unsupported-protocol': Data in the error signal is the +protocol that is unsupported. For example, giving a URL starting +with http by mistake raises this error. + +`websocket-wss-needs-emacs-24': Trying to connect wss protocol +using Emacs < 24 raises this error. You can catch this error +also by `websocket-unsupported-protocol'. + +`websocket-received-error-http-response': Data in the error +signal is the integer error number. + +`websocket-invalid-header': Data in the error is a string +describing the invalid header received from the server. + +`websocket-unparseable-frame': Data in the error is a string +describing the problem with the frame. + +`nowait': If NOWAIT is true, return without waiting for the +connection to complete. + +`custom-headers-alist': An alist of custom headers to pass to the +server. The car is the header name, the cdr is the header value. +These are different from the extensions because it is not related +to the websocket protocol. +" + (let* ((name (format "websocket to %s" url)) + (url-struct (url-generic-parse-url url)) + (key (websocket-genkey)) + (coding-system-for-read 'binary) + (coding-system-for-write 'binary) + (conn (if (member (url-type url-struct) '("ws" "wss")) + (let* ((type (if (equal (url-type url-struct) "ws") + 'plain 'tls)) + (port (if (= 0 (url-port url-struct)) + (if (eq type 'tls) 443 80) + (url-port url-struct))) + (host (url-host url-struct))) + (if (eq type 'plain) + (make-network-process :name name :buffer nil :host host + :service port :nowait nowait) + (condition-case-unless-debug nil + (open-network-stream name nil host port :type type :nowait nowait) + (wrong-number-of-arguments + (signal 'websocket-wss-needs-emacs-24 (list "wss")))))) + (signal 'websocket-unsupported-protocol (list (url-type url-struct))))) + (websocket (websocket-inner-create + :conn conn + :url url + :on-open on-open + :on-message on-message + :on-close on-close + :on-error on-error + :protocols protocols + :extensions (mapcar 'car extensions) + :accept-string + (websocket-calculate-accept key)))) + (unless conn (error "Could not establish the websocket connection to %s" url)) + (process-put conn :websocket websocket) + (set-process-filter conn + (lambda (process output) + (let ((websocket (process-get process :websocket))) + (websocket-outer-filter websocket output)))) + (set-process-sentinel + conn + (websocket-sentinel url conn key protocols extensions custom-header-alist nowait)) + (set-process-query-on-exit-flag conn nil) + (websocket-ensure-handshake url conn key protocols extensions custom-header-alist nowait) + websocket)) + +(defun websocket-sentinel (url conn key protocols extensions custom-header-alist nowait) + #'(lambda (process change) + (let ((websocket (process-get process :websocket))) + (websocket-debug websocket "State change to %s" change) + (let ((status (process-status process))) + (when (and nowait (eq status 'open)) + (websocket-ensure-handshake url conn key protocols extensions custom-header-alist nowait)) + + (when (and (member status '(closed failed exit signal)) + (not (eq 'closed (websocket-ready-state websocket)))) + (websocket-try-callback 'websocket-on-close 'on-close websocket)))))) + +(defun websocket-ensure-handshake (url conn key protocols extensions custom-header-alist nowait) + (let ((url-struct (url-generic-parse-url url)) + (websocket (process-get conn :websocket))) + (when (and (eq 'connecting (websocket-ready-state websocket)) + (memq (process-status conn) + (list 'run (if nowait 'connect 'open)))) + (websocket-debug websocket "Sending handshake, key: %s, acceptance: %s" + key (websocket-accept-string websocket)) + (process-send-string conn + (format "GET %s HTTP/1.1\r\n%s" + (let ((path (url-filename url-struct))) + (if (> (length path) 0) path "/")) + (websocket-create-headers + url key protocols extensions custom-header-alist)))))) + +(defun websocket-process-headers (url headers) + "On opening URL, process the HEADERS sent from the server." + (when (string-match "Set-Cookie: \(.*\)\r\n" headers) + ;; The url-current-object is assumed to be set by + ;; url-cookie-handle-set-cookie. + (let ((url-current-object (url-generic-parse-url url))) + (url-cookie-handle-set-cookie (match-string 1 headers))))) + +(defun websocket-outer-filter (websocket output) + "Filter the WEBSOCKET server's OUTPUT. +This will parse headers and process frames repeatedly until there +is no more output or the connection closes. If the websocket +connection is invalid, the connection will be closed." + (websocket-debug websocket "Received: %s" output) + (let ((start-point) + (text (concat (websocket-inflight-input websocket) output)) + (header-end-pos)) + (setf (websocket-inflight-input websocket) nil) + ;; If we've received the complete header, check to see if we've + ;; received the desired handshake. + (when (and (eq 'connecting (websocket-ready-state websocket))) + (if (and (setq header-end-pos (string-match "\r\n\r\n" text)) + (setq start-point (+ 4 header-end-pos))) + (progn + (condition-case err + (progn + (websocket-verify-response-code text) + (websocket-verify-headers websocket text) + (websocket-process-headers (websocket-url websocket) text)) + (error + (websocket-close websocket) + (funcall (websocket-on-error websocket) + websocket 'on-open err))) + (setf (websocket-ready-state websocket) 'open) + (websocket-try-callback 'websocket-on-open 'on-open websocket)) + (setf (websocket-inflight-input websocket) text))) + (when (eq 'open (websocket-ready-state websocket)) + (websocket-process-input-on-open-ws + websocket (substring text (or start-point 0)))))) + +(defun websocket-verify-headers (websocket output) + "Based on WEBSOCKET's data, ensure the headers in OUTPUT are valid. +The output is assumed to have complete headers. This function +will either return t or call `error'. This has the side-effect +of populating the list of server extensions to WEBSOCKET." + (let ((accept-regexp + (concat "Sec-Web[Ss]ocket-Accept: " (regexp-quote (websocket-accept-string websocket))))) + (websocket-debug websocket "Checking for accept header regexp: %s" accept-regexp) + (unless (string-match accept-regexp output) + (signal 'websocket-invalid-header + (list "Incorrect handshake from websocket: is this really a websocket connection?")))) + (let ((case-fold-search t)) + (websocket-debug websocket "Checking for upgrade header") + (unless (string-match "\r\nUpgrade: websocket\r\n" output) + (signal 'websocket-invalid-header + (list "No 'Upgrade: websocket' header found"))) + (websocket-debug websocket "Checking for connection header") + (unless (string-match "\r\nConnection: upgrade\r\n" output) + (signal 'websocket-invalid-header + (list "No 'Connection: upgrade' header found"))) + (when (websocket-protocols websocket) + (dolist (protocol (websocket-protocols websocket)) + (websocket-debug websocket "Checking for protocol match: %s" + protocol) + (let ((protocols + (if (string-match (format "\r\nSec-Websocket-Protocol: %s\r\n" + protocol) + output) + (list protocol) + (signal 'websocket-invalid-header + (list "Incorrect or missing protocol returned by the server."))))) + (setf (websocket-negotiated-protocols websocket) protocols)))) + (let* ((extensions (websocket-parse-repeated-field + output + "Sec-WebSocket-Extensions")) + (extra-extensions)) + (dolist (ext extensions) + (let ((x (cl-first (split-string ext "; ?")))) + (unless (or (member x (websocket-extensions websocket)) + (member x extra-extensions)) + (push x extra-extensions)))) + (when extra-extensions + (signal 'websocket-invalid-header + (list (format "Non-requested extensions returned by server: %S" + extra-extensions)))) + (setf (websocket-negotiated-extensions websocket) extensions))) + t) + +;;;;;;;;;;;;;;;;;;;;;; +;; Websocket server ;; +;;;;;;;;;;;;;;;;;;;;;; + +(defvar websocket-server-websockets nil + "A list of current websockets live on any server.") + +(cl-defun websocket-server (port &rest plist) + "Open a websocket server on PORT. +If the plist contains a `:host' HOST pair, this value will be +used to configure the addresses the socket listens on. The symbol +`local' specifies the local host. If unspecified or nil, the +socket will listen on all addresses. + +This also takes a plist of callbacks: `:on-open', `:on-message', +`:on-close' and `:on-error', which operate exactly as documented +in the websocket client function `websocket-open'. Returns the +connection, which should be kept in order to pass to +`websocket-server-close'." + (let* ((conn (make-network-process + :name (format "websocket server on port %s" port) + :server t + :family 'ipv4 + :noquery t + :filter 'websocket-server-filter + :log 'websocket-server-accept + :filter-multibyte nil + :plist plist + :host (plist-get plist :host) + :service port))) + conn)) + +(defun websocket-server-close (conn) + "Closes the websocket, as well as all open websockets for this server." + (let ((to-delete)) + (dolist (ws websocket-server-websockets) + (when (eq (websocket-server-conn ws) conn) + (if (eq (websocket-ready-state ws) 'closed) + (unless (member ws to-delete) + (push ws to-delete)) + (websocket-close ws)))) + (dolist (ws to-delete) + (setq websocket-server-websockets (remove ws websocket-server-websockets)))) + (delete-process conn)) + +(defun websocket-server-accept (server client _message) + "Accept a new websocket connection from a client." + (let ((ws (websocket-inner-create + :server-conn server + :conn client + :url client + :server-p t + :on-open (or (process-get server :on-open) 'identity) + :on-message (or (process-get server :on-message) (lambda (_ws _frame))) + :on-close (let ((user-method + (or (process-get server :on-close) 'identity))) + (lambda (ws) + (setq websocket-server-websockets + (remove ws websocket-server-websockets)) + (funcall user-method ws))) + :on-error (or (process-get server :on-error) + 'websocket-default-error-handler) + :protocols (process-get server :protocol) + :extensions (mapcar 'car (process-get server :extensions))))) + (unless (member ws websocket-server-websockets) + (push ws websocket-server-websockets)) + (process-put client :websocket ws) + (set-process-coding-system client 'binary 'binary) + (set-process-sentinel client + (lambda (process change) + (let ((websocket (process-get process :websocket))) + (websocket-debug websocket "State change to %s" change) + (when (and + (member (process-status process) '(closed failed exit signal)) + (not (eq 'closed (websocket-ready-state websocket)))) + (websocket-try-callback 'websocket-on-close 'on-close websocket))))))) + +(defun websocket-create-headers (url key protocol extensions custom-headers-alist) + "Create connections headers for the given URL, KEY, PROTOCOL, and EXTENSIONS. +Additionally, the CUSTOM-HEADERS-ALIST is passed from the client. +All these parameters are defined as in `websocket-open'." + (let* ((parsed-url (url-generic-parse-url url)) + (host-port (if (url-port-if-non-default parsed-url) + (format "%s:%s" (url-host parsed-url) (url-port parsed-url)) + (url-host parsed-url))) + (cookie-header (url-cookie-generate-header-lines + host-port (car (url-path-and-query parsed-url)) + (equal (url-type parsed-url) "wss")))) + (format (concat "Host: %s\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: %s\r\n" + "Sec-WebSocket-Version: 13\r\n" + (when protocol + (concat + (mapconcat + (lambda (protocol) + (format "Sec-WebSocket-Protocol: %s" protocol)) + protocol "\r\n") + "\r\n")) + (when extensions + (format "Sec-WebSocket-Extensions: %s\r\n" + (mapconcat + (lambda (ext) + (concat + (car ext) + (when (cdr ext) "; ") + (when (cdr ext) + (mapconcat 'identity (cdr ext) "; ")))) + extensions ", "))) + (when cookie-header cookie-header) + (concat (mapconcat (lambda (cons) (format "%s: %s" (car cons) (cdr cons))) + custom-headers-alist "\r\n") + (when custom-headers-alist "\r\n")) + "\r\n") + host-port + key + protocol))) + +(defun websocket-get-server-response (websocket client-protocols client-extensions) + "Get the websocket response from client WEBSOCKET." + (let ((separator "\r\n")) + (concat "HTTP/1.1 101 Switching Protocols" separator + "Upgrade: websocket" separator + "Connection: Upgrade" separator + "Sec-WebSocket-Accept: " + (websocket-accept-string websocket) separator + (let ((protocols + (websocket-intersect client-protocols + (websocket-protocols websocket)))) + (when protocols + (concat + (mapconcat + (lambda (protocol) (format "Sec-WebSocket-Protocol: %s" + protocol)) protocols separator) + separator))) + (let ((extensions (websocket-intersect + client-extensions + (websocket-extensions websocket)))) + (when extensions + (concat + (mapconcat + (lambda (extension) (format "Sec-Websocket-Extensions: %s" + extension)) extensions separator) + separator))) + separator))) + +(defun websocket-server-filter (process output) + "This acts on all OUTPUT from websocket clients PROCESS." + (let* ((ws (process-get process :websocket)) + (text (concat (websocket-inflight-input ws) output))) + (setf (websocket-inflight-input ws) nil) + (cond ((eq (websocket-ready-state ws) 'connecting) + ;; check for connection string + (let ((end-of-header-pos + (let ((pos (string-match "\r\n\r\n" text))) + (when pos (+ 4 pos))))) + (if end-of-header-pos + (progn + (let ((header-info (websocket-verify-client-headers text))) + (if header-info + (progn (setf (websocket-accept-string ws) + (websocket-calculate-accept + (plist-get header-info :key))) + (process-send-string + process + (websocket-get-server-response + ws (plist-get header-info :protocols) + (plist-get header-info :extensions))) + (setf (websocket-ready-state ws) 'open) + (websocket-try-callback 'websocket-on-open + 'on-open ws)) + (message "Invalid client headers found in: %s" output) + (process-send-string process "HTTP/1.1 400 Bad Request\r\n\r\n") + (websocket-close ws))) + (when (> (length text) (+ 1 end-of-header-pos)) + (websocket-server-filter process (substring + text + end-of-header-pos)))) + (setf (websocket-inflight-input ws) text)))) + ((eq (websocket-ready-state ws) 'open) + (websocket-process-input-on-open-ws ws text)) + ((eq (websocket-ready-state ws) 'closed) + (message "WARNING: Should not have received further input on closed websocket"))))) + +(defun websocket-verify-client-headers (output) + "Verify the headers from the WEBSOCKET client connection in OUTPUT. +Unlike `websocket-verify-headers', this is a quieter routine. We +don't want to error due to a bad client, so we just print out +messages and a plist containing `:key', the websocket key, +`:protocols' and `:extensions'." + (cl-block nil + (let ((case-fold-search t) + (plist)) + (unless (string-match "HTTP/1.1" output) + (message "Websocket client connection: HTTP/1.1 not found") + (cl-return nil)) + (unless (string-match "^Host: " output) + (message "Websocket client connection: Host header not found") + (cl-return nil)) + (unless (string-match "^Upgrade: websocket\r\n" output) + (message "Websocket client connection: Upgrade: websocket not found") + (cl-return nil)) + (if (string-match "^Sec-WebSocket-Key: \\([[:graph:]]+\\)\r\n" output) + (setq plist (plist-put plist :key (match-string 1 output))) + (message "Websocket client connect: No key sent") + (cl-return nil)) + (unless (string-match "^Sec-WebSocket-Version: 13" output) + (message "Websocket client connect: Websocket version 13 not found") + (cl-return nil)) + (when (string-match "^Sec-WebSocket-Protocol:" output) + (setq plist (plist-put plist :protocols (websocket-parse-repeated-field + output + "Sec-Websocket-Protocol")))) + (when (string-match "^Sec-WebSocket-Extensions:" output) + (setq plist (plist-put plist :extensions (websocket-parse-repeated-field + output + "Sec-Websocket-Extensions")))) + plist))) + +(provide 'websocket) + +;;; websocket.el ends here diff --git a/code/elpa/with-editor-20220810.1159/dir b/code/elpa/with-editor-20220810.1159/dir new file mode 100644 index 0000000..c5810e0 --- /dev/null +++ b/code/elpa/with-editor-20220810.1159/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +File: dir, Node: Top This is the top of the INFO tree + + This (the Directory node) gives a menu of major topics. + Typing "q" exits, "H" lists all Info commands, "d" returns here, + "h" gives a primer for first-timers, + "mEmacs" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* With-Editor: (with-editor). Using the Emacsclient as $EDITOR. diff --git a/code/elpa/with-editor-20220810.1159/with-editor-autoloads.el b/code/elpa/with-editor-20220810.1159/with-editor-autoloads.el new file mode 100644 index 0000000..bee6ae8 --- /dev/null +++ b/code/elpa/with-editor-20220810.1159/with-editor-autoloads.el @@ -0,0 +1,111 @@ +;;; with-editor-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "with-editor" "with-editor.el" (0 0 0 0)) +;;; Generated autoloads from with-editor.el + +(autoload 'with-editor-export-editor "with-editor" "\ +Teach subsequent commands to use current Emacs instance as editor. + +Set and export the environment variable ENVVAR, by default +\"EDITOR\". The value is automatically generated to teach +commands to use the current Emacs instance as \"the editor\". + +This works in `shell-mode', `term-mode', `eshell-mode' and +`vterm'. + +\(fn &optional (ENVVAR \"EDITOR\"))" t nil) + +(autoload 'with-editor-export-git-editor "with-editor" "\ +Like `with-editor-export-editor' but always set `$GIT_EDITOR'." t nil) + +(autoload 'with-editor-export-hg-editor "with-editor" "\ +Like `with-editor-export-editor' but always set `$HG_EDITOR'." t nil) + +(defvar shell-command-with-editor-mode nil "\ +Non-nil if Shell-Command-With-Editor mode is enabled. +See the `shell-command-with-editor-mode' command +for a description of this minor mode.") + +(custom-autoload 'shell-command-with-editor-mode "with-editor" nil) + +(autoload 'shell-command-with-editor-mode "with-editor" "\ +Teach `shell-command' to use current Emacs instance as editor. + +This is a minor mode. If called interactively, toggle the +`Shell-Command-With-Editor mode' mode. If the prefix argument is +positive, enable the mode, and if it is zero or negative, disable +the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='shell-command-with-editor-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +Teach `shell-command', and all commands that ultimately call that +command, to use the current Emacs instance as editor by executing +\"EDITOR=CLIENT COMMAND&\" instead of just \"COMMAND&\". + +CLIENT is automatically generated; EDITOR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming no other variable overrides the effect of \"$EDITOR\". +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Alternatively you can use the `with-editor-async-shell-command', +which also allows the use of another variable instead of +\"EDITOR\". + +\(fn &optional ARG)" t nil) + +(autoload 'with-editor-async-shell-command "with-editor" "\ +Like `async-shell-command' but with `$EDITOR' set. + +Execute string \"ENVVAR=CLIENT COMMAND\" in an inferior shell; +display output, if any. With a prefix argument prompt for an +environment variable, otherwise the default \"EDITOR\" variable +is used. With a negative prefix argument additionally insert +the COMMAND's output at point. + +CLIENT is automatically generated; ENVVAR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming it respects ENVVAR as an \"EDITOR\"-like variable. +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Also see `async-shell-command' and `shell-command'. + +\(fn COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER ENVVAR)" t nil) + +(autoload 'with-editor-shell-command "with-editor" "\ +Like `shell-command' or `with-editor-async-shell-command'. +If COMMAND ends with \"&\" behave like the latter, +else like the former. + +\(fn COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER ENVVAR)" t nil) + +(register-definition-prefixes "with-editor" '("server-" "shell-command--shell-command-with-editor-mode" "start-file-process--with-editor-process-filter" "with-editor")) + +;;;*** + +;;;### (autoloads nil nil ("with-editor-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; with-editor-autoloads.el ends here diff --git a/code/elpa/with-editor-20220810.1159/with-editor-pkg.el b/code/elpa/with-editor-20220810.1159/with-editor-pkg.el new file mode 100644 index 0000000..f377b53 --- /dev/null +++ b/code/elpa/with-editor-20220810.1159/with-editor-pkg.el @@ -0,0 +1,13 @@ +(define-package "with-editor" "20220810.1159" "Use the Emacsclient as $EDITOR" + '((emacs "25.1") + (compat "28.1.1.0")) + :commit "1d5860cfd05d6805018bd071b8f9b56493ba11c6" :authors + '(("Jonas Bernoulli" . "jonas@bernoul.li")) + :maintainer + '("Jonas Bernoulli" . "jonas@bernoul.li") + :keywords + '("processes" "terminals") + :url "https://github.com/magit/with-editor") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/code/elpa/with-editor-20220810.1159/with-editor.el b/code/elpa/with-editor-20220810.1159/with-editor.el new file mode 100644 index 0000000..21a6eba --- /dev/null +++ b/code/elpa/with-editor-20220810.1159/with-editor.el @@ -0,0 +1,949 @@ +;;; with-editor.el --- Use the Emacsclient as $EDITOR -*- lexical-binding:t -*- + +;; Copyright (C) 2014-2022 The Magit Project Contributors + +;; Author: Jonas Bernoulli +;; Homepage: https://github.com/magit/with-editor +;; Keywords: processes terminals + +;; Package-Version: 3.2.0-git +;; Package-Requires: ((emacs "25.1") (compat "28.1.1.0")) + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This library makes it possible to reliably use the Emacsclient as +;; the `$EDITOR' of child processes. It makes sure that they know how +;; to call home. For remote processes a substitute is provided, which +;; communicates with Emacs on standard output/input instead of using a +;; socket as the Emacsclient does. + +;; It provides the commands `with-editor-async-shell-command' and +;; `with-editor-shell-command', which are intended as replacements +;; for `async-shell-command' and `shell-command'. They automatically +;; export `$EDITOR' making sure the executed command uses the current +;; Emacs instance as "the editor". With a prefix argument these +;; commands prompt for an alternative environment variable such as +;; `$GIT_EDITOR'. To always use these variants add this to your init +;; file: +;; +;; (define-key (current-global-map) +;; [remap async-shell-command] #'with-editor-async-shell-command) +;; (define-key (current-global-map) +;; [remap shell-command] #'with-editor-shell-command) + +;; Alternatively use the global `shell-command-with-editor-mode', +;; which always sets `$EDITOR' for all Emacs commands which ultimately +;; use `shell-command' to asynchronously run some shell command. + +;; The command `with-editor-export-editor' exports `$EDITOR' or +;; another such environment variable in `shell-mode', `eshell-mode', +;; `term-mode' and `vterm-mode' buffers. Use this Emacs command +;; before executing a shell command which needs the editor set, or +;; always arrange for the current Emacs instance to be used as editor +;; by adding it to the appropriate mode hooks: +;; +;; (add-hook 'shell-mode-hook #'with-editor-export-editor) +;; (add-hook 'eshell-mode-hook #'with-editor-export-editor) +;; (add-hook 'term-exec-hook #'with-editor-export-editor) +;; (add-hook 'vterm-mode-hook #'with-editor-export-editor) + +;; Some variants of this function exist, these two forms are +;; equivalent: +;; +;; (add-hook 'shell-mode-hook +;; (apply-partially #'with-editor-export-editor "GIT_EDITOR")) +;; (add-hook 'shell-mode-hook #'with-editor-export-git-editor) + +;; This library can also be used by other packages which need to use +;; the current Emacs instance as editor. In fact this library was +;; written for Magit and its `git-commit-mode' and `git-rebase-mode'. +;; Consult `git-rebase.el' and the related code in `magit-sequence.el' +;; for a simple example. + +;;; Code: + +(require 'cl-lib) +(require 'compat) +(require 'server) +(require 'shell) +(eval-when-compile (require 'subr-x)) + +(declare-function dired-get-filename "dired" + (&optional localp no-error-if-not-filep)) +(declare-function term-emulate-terminal "term" (proc str)) +(declare-function vterm-send-return "vterm" ()) +(declare-function vterm-send-string "vterm" (string &optional paste-p)) +(defvar eshell-preoutput-filter-functions) +(defvar git-commit-post-finish-hook) +(defvar vterm--process) +(defvar warning-minimum-level) +(defvar warning-minimum-log-level) + +;;; Options + +(defgroup with-editor nil + "Use the Emacsclient as $EDITOR." + :group 'external + :group 'server) + +(defun with-editor-locate-emacsclient () + "Search for a suitable Emacsclient executable." + (or (with-editor-locate-emacsclient-1 + (with-editor-emacsclient-path) + (length (split-string emacs-version "\\."))) + (prog1 nil (display-warning 'with-editor "\ +Cannot determine a suitable Emacsclient + +Determining an Emacsclient executable suitable for the +current Emacs instance failed. For more information +please see https://github.com/magit/magit/wiki/Emacsclient.")))) + +(defun with-editor-locate-emacsclient-1 (path depth) + (let* ((version-lst (cl-subseq (split-string emacs-version "\\.") 0 depth)) + (version-reg (concat "^" (mapconcat #'identity version-lst "\\.")))) + (or (locate-file + (if (equal (downcase invocation-name) "remacs") + "remacsclient" + "emacsclient") + path + (cl-mapcan + (lambda (v) (cl-mapcar (lambda (e) (concat v e)) exec-suffixes)) + (nconc (and (boundp 'debian-emacs-flavor) + (list (format ".%s" debian-emacs-flavor))) + (cl-mapcon (lambda (v) + (setq v (mapconcat #'identity (reverse v) ".")) + (list v (concat "-" v) (concat ".emacs" v))) + (reverse version-lst)) + (list "" "-snapshot" ".emacs-snapshot"))) + (lambda (exec) + (ignore-errors + (string-match-p version-reg + (with-editor-emacsclient-version exec))))) + (and (> depth 1) + (with-editor-locate-emacsclient-1 path (1- depth)))))) + +(defun with-editor-emacsclient-version (exec) + (let ((default-directory (file-name-directory exec))) + (ignore-errors + (cadr (split-string (car (process-lines exec "--version"))))))) + +(defun with-editor-emacsclient-path () + (let ((path exec-path)) + (when invocation-directory + (push (directory-file-name invocation-directory) path) + (let* ((linkname (expand-file-name invocation-name invocation-directory)) + (truename (file-chase-links linkname))) + (unless (equal truename linkname) + (push (directory-file-name (file-name-directory truename)) path))) + (when (eq system-type 'darwin) + (let ((dir (expand-file-name "bin" invocation-directory))) + (when (file-directory-p dir) + (push dir path))) + (when (string-search "Cellar" invocation-directory) + (let ((dir (expand-file-name "../../../bin" invocation-directory))) + (when (file-directory-p dir) + (push dir path)))))) + (cl-remove-duplicates path :test #'equal))) + +(defcustom with-editor-emacsclient-executable (with-editor-locate-emacsclient) + "The Emacsclient executable used by the `with-editor' macro." + :group 'with-editor + :type '(choice (string :tag "Executable") + (const :tag "Don't use Emacsclient" nil))) + +(defcustom with-editor-sleeping-editor "\ +sh -c '\ +printf \"\\nWITH-EDITOR: $$ OPEN $0\\037 IN $(pwd)\\n\"; \ +sleep 604800 & sleep=$!; \ +trap \"kill $sleep; exit 0\" USR1; \ +trap \"kill $sleep; exit 1\" USR2; \ +wait $sleep'" + "The sleeping editor, used when the Emacsclient cannot be used. + +This fallback is used for asynchronous processes started inside +the macro `with-editor', when the process runs on a remote machine +or for local processes when `with-editor-emacsclient-executable' +is nil (i.e. when no suitable Emacsclient was found, or the user +decided not to use it). + +Where the latter uses a socket to communicate with Emacs' server, +this substitute prints edit requests to its standard output on +which a process filter listens for such requests. As such it is +not a complete substitute for a proper Emacsclient, it can only +be used as $EDITOR of child process of the current Emacs instance. + +Some shells do not execute traps immediately when waiting for a +child process, but by default we do use such a blocking child +process. + +If you use such a shell (e.g. `csh' on FreeBSD, but not Debian), +then you have to edit this option. You can either replace \"sh\" +with \"bash\" (and install that), or you can use the older, less +performant implementation: + + \"sh -c '\\ + echo -e \\\"\\nWITH-EDITOR: $$ OPEN $0 IN $(pwd)\\n\\\"; \\ + trap \\\"exit 0\\\" USR1; \\ + trap \\\"exit 1\" USR2; \\ + while true; do sleep 1; done'\" + +Note that the unit separator character () right after the file +name ($0) is required. + +Also note that using this alternative implementation leads to a +delay of up to a second. The delay can be shortened by replacing +\"sleep 1\" with \"sleep 0.01\", or if your implementation does +not support floats, then by using \"nanosleep\" instead." + :package-version '(with-editor . "2.8.0") + :group 'with-editor + :type 'string) + +(defcustom with-editor-finish-query-functions nil + "List of functions called to query before finishing session. + +The buffer in question is current while the functions are called. +If any of them returns nil, then the session is not finished and +the buffer is not killed. The user should then fix the issue and +try again. The functions are called with one argument. If it is +non-nil then that indicates that the user used a prefix argument +to force finishing the session despite issues. Functions should +usually honor that and return non-nil." + :group 'with-editor + :type 'hook) +(put 'with-editor-finish-query-functions 'permanent-local t) + +(defcustom with-editor-cancel-query-functions nil + "List of functions called to query before canceling session. + +The buffer in question is current while the functions are called. +If any of them returns nil, then the session is not canceled and +the buffer is not killed. The user should then fix the issue and +try again. The functions are called with one argument. If it is +non-nil then that indicates that the user used a prefix argument +to force canceling the session despite issues. Functions should +usually honor that and return non-nil." + :group 'with-editor + :type 'hook) +(put 'with-editor-cancel-query-functions 'permanent-local t) + +(defcustom with-editor-mode-lighter " WE" + "The mode-line lighter of the With-Editor mode." + :group 'with-editor + :type '(choice (const :tag "No lighter" "") string)) + +(defvar with-editor-server-window-alist nil + "Alist of filename patterns vs corresponding `server-window'. + +Each element looks like (REGEXP . FUNCTION). Files matching +REGEXP are selected using FUNCTION instead of the default in +`server-window'. + +Note that when a package adds an entry here then it probably +has a reason to disrespect `server-window' and it likely is +not a good idea to change such entries.") + +(defvar with-editor-file-name-history-exclude nil + "List of regexps for filenames `server-visit' should not remember. +When a filename matches any of the regexps, then `server-visit' +does not add it to the variable `file-name-history', which is +used when reading a filename in the minibuffer.") + +(defcustom with-editor-shell-command-use-emacsclient t + "Whether to use the emacsclient when running shell commands. + +This affects `with-editor-shell-command-async' and, if the input +ends with \"&\" `with-editor-shell-command' . + +If `shell-command-with-editor-mode' is enabled, then it also +affects `shell-command-async' and, if the input ends with \"&\" +`shell-command'. + +This is a temporary kludge that lets you choose between two +possible defects, the ones described in the issues #23 and #40. + +When t, then use the emacsclient. This has the disadvantage that +`with-editor-mode' won't be enabled because we don't know whether +this package was involved at all in the call to the emacsclient, +and when it is not, then we really should. The problem is that +the emacsclient doesn't pass along any environment variables to +the server. This will hopefully be fixed in Emacs eventually. + +When nil, then use the sleeping editor. Because in this case we +know that this package is involved, we can enable the mode. But +this makes it necessary that you invoke $EDITOR in shell scripts +like so: + + eval \"$EDITOR\" file + +And some tools that do not handle $EDITOR properly also break." + :package-version '(with-editor . "2.7.1") + :group 'with-editor + :type 'boolean) + +;;; Mode Commands + +(defvar with-editor-pre-finish-hook nil) +(defvar with-editor-pre-cancel-hook nil) +(defvar with-editor-post-finish-hook nil) +(defvar with-editor-post-finish-hook-1 nil) +(defvar with-editor-post-cancel-hook nil) +(defvar with-editor-post-cancel-hook-1 nil) +(defvar with-editor-cancel-alist nil) +(put 'with-editor-pre-finish-hook 'permanent-local t) +(put 'with-editor-pre-cancel-hook 'permanent-local t) +(put 'with-editor-post-finish-hook 'permanent-local t) +(put 'with-editor-post-cancel-hook 'permanent-local t) + +(defvar-local with-editor-show-usage t) +(defvar-local with-editor-cancel-message nil) +(defvar-local with-editor-previous-winconf nil) +(put 'with-editor-cancel-message 'permanent-local t) +(put 'with-editor-previous-winconf 'permanent-local t) + +(defvar-local with-editor--pid nil "For internal use.") +(put 'with-editor--pid 'permanent-local t) + +(defun with-editor-finish (force) + "Finish the current edit session." + (interactive "P") + (when (run-hook-with-args-until-failure + 'with-editor-finish-query-functions force) + (let ((post-finish-hook with-editor-post-finish-hook) + (post-commit-hook (bound-and-true-p git-commit-post-finish-hook)) + (dir default-directory)) + (run-hooks 'with-editor-pre-finish-hook) + (with-editor-return nil) + (accept-process-output nil 0.1) + (with-temp-buffer + (setq default-directory dir) + (setq-local with-editor-post-finish-hook post-finish-hook) + (when post-commit-hook + (setq-local git-commit-post-finish-hook post-commit-hook)) + (run-hooks 'with-editor-post-finish-hook))))) + +(defun with-editor-cancel (force) + "Cancel the current edit session." + (interactive "P") + (when (run-hook-with-args-until-failure + 'with-editor-cancel-query-functions force) + (let ((message with-editor-cancel-message)) + (when (functionp message) + (setq message (funcall message))) + (let ((post-cancel-hook with-editor-post-cancel-hook) + (with-editor-cancel-alist nil) + (dir default-directory)) + (run-hooks 'with-editor-pre-cancel-hook) + (with-editor-return t) + (accept-process-output nil 0.1) + (with-temp-buffer + (setq default-directory dir) + (setq-local with-editor-post-cancel-hook post-cancel-hook) + (run-hooks 'with-editor-post-cancel-hook))) + (message (or message "Canceled by user"))))) + +(defun with-editor-return (cancel) + (let ((winconf with-editor-previous-winconf) + (clients server-buffer-clients) + (dir default-directory) + (pid with-editor--pid)) + (remove-hook 'kill-buffer-query-functions + #'with-editor-kill-buffer-noop t) + (cond (cancel + (save-buffer) + (if clients + (let ((buf (current-buffer))) + (dolist (client clients) + (message "client %S" client) + (ignore-errors + (server-send-string client "-error Canceled by user")) + (delete-process client)) + (when (buffer-live-p buf) + (kill-buffer buf))) + ;; Fallback for when emacs was used as $EDITOR + ;; instead of emacsclient or the sleeping editor. + ;; See https://github.com/magit/magit/issues/2258. + (ignore-errors (delete-file buffer-file-name)) + (kill-buffer))) + (t + (save-buffer) + (if clients + ;; Don't use `server-edit' because we do not want to + ;; show another buffer belonging to another client. + ;; See https://github.com/magit/magit/issues/2197. + (server-done) + (kill-buffer)))) + (when pid + (let ((default-directory dir)) + (process-file "kill" nil nil nil + "-s" (if cancel "USR2" "USR1") pid))) + (when (and winconf (eq (window-configuration-frame winconf) + (selected-frame))) + (set-window-configuration winconf)))) + +;;; Mode + +(defvar with-editor-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-c\C-c" #'with-editor-finish) + (define-key map [remap server-edit] #'with-editor-finish) + (define-key map [remap evil-save-and-close] #'with-editor-finish) + (define-key map [remap evil-save-modified-and-close] #'with-editor-finish) + (define-key map "\C-c\C-k" #'with-editor-cancel) + (define-key map [remap kill-buffer] #'with-editor-cancel) + (define-key map [remap ido-kill-buffer] #'with-editor-cancel) + (define-key map [remap iswitchb-kill-buffer] #'with-editor-cancel) + (define-key map [remap evil-quit] #'with-editor-cancel) + map)) + +(define-minor-mode with-editor-mode + "Edit a file as the $EDITOR of an external process." + :lighter with-editor-mode-lighter + ;; Protect the user from killing the buffer without using + ;; either `with-editor-finish' or `with-editor-cancel', + ;; and from removing the key bindings for these commands. + (unless with-editor-mode + (user-error "With-Editor mode cannot be turned off")) + (add-hook 'kill-buffer-query-functions + #'with-editor-kill-buffer-noop nil t) + ;; `server-execute' displays a message which is not + ;; correct when using this mode. + (when with-editor-show-usage + (with-editor-usage-message))) + +(put 'with-editor-mode 'permanent-local t) + +(defun with-editor-kill-buffer-noop () + ;; We started doing this in response to #64, but it is not safe + ;; to do so, because the client has already been killed, causing + ;; `with-editor-return' (called by `with-editor-cancel') to delete + ;; the file, see #66. The reason we delete the file in the first + ;; place are https://github.com/magit/magit/issues/2258 and + ;; https://github.com/magit/magit/issues/2248. + ;; (if (memq this-command '(save-buffers-kill-terminal + ;; save-buffers-kill-emacs)) + ;; (let ((with-editor-cancel-query-functions nil)) + ;; (with-editor-cancel nil) + ;; t) + ;; ...) + ;; So go back to always doing this instead: + (user-error (substitute-command-keys (format "\ +Don't kill this buffer %S. Instead cancel using \\[with-editor-cancel]" + (current-buffer))))) + +(defvar-local with-editor-usage-message "\ +Type \\[with-editor-finish] to finish, \ +or \\[with-editor-cancel] to cancel") + +(defun with-editor-usage-message () + ;; Run after `server-execute', which is run using + ;; a timer which starts immediately. + (let ((buffer (current-buffer))) + (run-with-timer + 0.05 nil + (lambda () + (with-current-buffer buffer + (message (substitute-command-keys with-editor-usage-message))))))) + +;;; Wrappers + +(defvar with-editor--envvar nil "For internal use.") + +(defmacro with-editor (&rest body) + "Use the Emacsclient as $EDITOR while evaluating BODY. +Modify the `process-environment' for processes started in BODY, +instructing them to use the Emacsclient as $EDITOR. If optional +ENVVAR is a literal string then bind that environment variable +instead. +\n(fn [ENVVAR] BODY...)" + (declare (indent defun) (debug (body))) + `(let ((with-editor--envvar ,(if (stringp (car body)) + (pop body) + '(or with-editor--envvar "EDITOR"))) + (process-environment process-environment)) + (with-editor--setup) + ,@body)) + +(defmacro with-editor* (envvar &rest body) + "Use the Emacsclient as the editor while evaluating BODY. +Modify the `process-environment' for processes started in BODY, +instructing them to use the Emacsclient as editor. ENVVAR is the +environment variable that is exported to do so, it is evaluated +at run-time. +\n(fn [ENVVAR] BODY...)" + (declare (indent defun) (debug (sexp body))) + `(let ((with-editor--envvar ,envvar) + (process-environment process-environment)) + (with-editor--setup) + ,@body)) + +(defun with-editor--setup () + (if (or (not with-editor-emacsclient-executable) + (file-remote-p default-directory)) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + process-environment) + ;; Make sure server-use-tcp's value is valid. + (unless (featurep 'make-network-process '(:family local)) + (setq server-use-tcp t)) + ;; Make sure the server is running. + (unless (process-live-p server-process) + (when (server-running-p server-name) + (setq server-name (format "server%s" (emacs-pid))) + (when (server-running-p server-name) + (server-force-delete server-name))) + (server-start)) + ;; Tell $EDITOR to use the Emacsclient. + (push (concat with-editor--envvar "=" + (shell-quote-argument with-editor-emacsclient-executable) + ;; Tell the process where the server file is. + (and (not server-use-tcp) + (concat " --socket-name=" + (shell-quote-argument + (expand-file-name server-name + server-socket-dir))))) + process-environment) + (when server-use-tcp + (push (concat "EMACS_SERVER_FILE=" + (expand-file-name server-name server-auth-dir)) + process-environment)) + ;; As last resort fallback to the sleeping editor. + (push (concat "ALTERNATE_EDITOR=" with-editor-sleeping-editor) + process-environment))) + +(defun with-editor-server-window () + (or (and buffer-file-name + (cdr (cl-find-if (lambda (cons) + (string-match-p (car cons) buffer-file-name)) + with-editor-server-window-alist))) + server-window)) + +(defun server-switch-buffer--with-editor-server-window-alist + (fn &optional next-buffer &rest args) + "Honor `with-editor-server-window-alist' (which see)." + (let ((server-window (with-current-buffer + (or next-buffer (current-buffer)) + (when with-editor-mode + (setq with-editor-previous-winconf + (current-window-configuration))) + (with-editor-server-window)))) + (apply fn next-buffer args))) + +(advice-add 'server-switch-buffer :around + #'server-switch-buffer--with-editor-server-window-alist) + +(defun start-file-process--with-editor-process-filter + (fn name buffer program &rest program-args) + "When called inside a `with-editor' form and the Emacsclient +cannot be used, then give the process the filter function +`with-editor-process-filter'. To avoid overriding the filter +being added here you should use `with-editor-set-process-filter' +instead of `set-process-filter' inside `with-editor' forms. + +When the `default-directory' is located on a remote machine, +then also manipulate PROGRAM and PROGRAM-ARGS in order to set +the appropriate editor environment variable." + (if (not with-editor--envvar) + (apply fn name buffer program program-args) + (when (file-remote-p default-directory) + (unless (equal program "env") + (push program program-args) + (setq program "env")) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + program-args)) + (let ((process (apply fn name buffer program program-args))) + (set-process-filter process #'with-editor-process-filter) + (process-put process 'default-dir default-directory) + process))) + +(advice-add 'start-file-process :around + #'start-file-process--with-editor-process-filter) + +(cl-defun make-process--with-editor-process-filter + (fn &rest keys &key name buffer command coding noquery stop + connection-type filter sentinel stderr file-handler + &allow-other-keys) + "When called inside a `with-editor' form and the Emacsclient +cannot be used, then give the process the filter function +`with-editor-process-filter'. To avoid overriding the filter +being added here you should use `with-editor-set-process-filter' +instead of `set-process-filter' inside `with-editor' forms. + +When the `default-directory' is located on a remote machine and +FILE-HANDLER is non-nil, then also manipulate COMMAND in order +to set the appropriate editor environment variable." + (if (or (not file-handler) (not with-editor--envvar)) + (apply fn keys) + (when (file-remote-p default-directory) + (unless (equal (car command) "env") + (push "env" command)) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + (cdr command))) + (let* ((filter (if filter + (lambda (process output) + (funcall filter process output) + (with-editor-process-filter process output t)) + #'with-editor-process-filter)) + (process (funcall fn + :name name + :buffer buffer + :command command + :coding coding + :noquery noquery + :stop stop + :connection-type connection-type + :filter filter + :sentinel sentinel + :stderr stderr + :file-handler file-handler))) + (process-put process 'default-dir default-directory) + process))) + +(advice-add #'make-process :around #'make-process--with-editor-process-filter) + +(defun with-editor-set-process-filter (process filter) + "Like `set-process-filter' but keep `with-editor-process-filter'. +Give PROCESS the new FILTER but keep `with-editor-process-filter' +if that was added earlier by the advised `start-file-process'. + +Do so by wrapping the two filter functions using a lambda, which +becomes the actual filter. It calls FILTER first, which may or +may not insert the text into the PROCESS's buffer. Then it calls +`with-editor-process-filter', passing t as NO-STANDARD-FILTER." + (set-process-filter + process + (if (eq (process-filter process) 'with-editor-process-filter) + `(lambda (proc str) + (,filter proc str) + (with-editor-process-filter proc str t)) + filter))) + +(defvar with-editor-filter-visit-hook nil) + +(defconst with-editor-sleeping-editor-regexp + "^WITH-EDITOR: \\([0-9]+\\) OPEN \\([^]+?\\)\\(?: IN \\([^\r]+?\\)\\)?\r?$") + +(defvar with-editor--max-incomplete-length 1000) + +(defun with-editor-sleeping-editor-filter (process string) + (when-let ((incomplete (and process (process-get process 'incomplete)))) + (setq string (concat incomplete string))) + (save-match-data + (cond + ((and process (not (string-suffix-p "\n" string))) + (let ((length (length string))) + (when (> length with-editor--max-incomplete-length) + (setq string + (substring string + (- length with-editor--max-incomplete-length))))) + (process-put process 'incomplete string) + nil) + ((string-match with-editor-sleeping-editor-regexp string) + (when process + (process-put process 'incomplete nil)) + (let ((pid (match-string 1 string)) + (file (match-string 2 string)) + (dir (match-string 3 string))) + (unless (file-name-absolute-p file) + (setq file (expand-file-name file dir))) + (when default-directory + (setq file (concat (file-remote-p default-directory) file))) + (with-current-buffer (find-file-noselect file) + (with-editor-mode 1) + (setq with-editor--pid pid) + (setq with-editor-previous-winconf + (current-window-configuration)) + (run-hooks 'with-editor-filter-visit-hook) + (funcall (or (with-editor-server-window) #'switch-to-buffer) + (current-buffer)) + (kill-local-variable 'server-window))) + nil) + (t string)))) + +(defun with-editor-process-filter + (process string &optional no-default-filter) + "Listen for edit requests by child processes." + (let ((default-directory (process-get process 'default-dir))) + (with-editor-sleeping-editor-filter process string)) + (unless no-default-filter + (internal-default-process-filter process string))) + +(advice-add 'server-visit-files :after + #'server-visit-files--with-editor-file-name-history-exclude) + +(defun server-visit-files--with-editor-file-name-history-exclude + (files _proc &optional _nowait) + (pcase-dolist (`(,file . ,_) files) + (when (cl-find-if (lambda (regexp) + (string-match-p regexp file)) + with-editor-file-name-history-exclude) + (setq file-name-history (delete file file-name-history))))) + +;;; Augmentations + +;;;###autoload +(cl-defun with-editor-export-editor (&optional (envvar "EDITOR")) + "Teach subsequent commands to use current Emacs instance as editor. + +Set and export the environment variable ENVVAR, by default +\"EDITOR\". The value is automatically generated to teach +commands to use the current Emacs instance as \"the editor\". + +This works in `shell-mode', `term-mode', `eshell-mode' and +`vterm'." + (interactive (list (with-editor-read-envvar))) + (cond + ((derived-mode-p 'comint-mode 'term-mode) + (when-let ((process (get-buffer-process (current-buffer)))) + (goto-char (process-mark process)) + (process-send-string + process (format " export %s=%s\n" envvar + (shell-quote-argument with-editor-sleeping-editor))) + (while (accept-process-output process 0.1)) + (if (derived-mode-p 'term-mode) + (with-editor-set-process-filter process #'with-editor-emulate-terminal) + (add-hook 'comint-output-filter-functions #'with-editor-output-filter + nil t)))) + ((derived-mode-p 'eshell-mode) + (add-to-list 'eshell-preoutput-filter-functions + #'with-editor-output-filter) + (setenv envvar with-editor-sleeping-editor)) + ((derived-mode-p 'vterm-mode) + (if with-editor-emacsclient-executable + (let ((with-editor--envvar envvar) + (process-environment process-environment)) + (with-editor--setup) + (while (accept-process-output vterm--process 0.1)) + (when-let ((v (getenv envvar))) + (vterm-send-string (format " export %s=%S" envvar v)) + (vterm-send-return)) + (when-let ((v (getenv "EMACS_SERVER_FILE"))) + (vterm-send-string (format " export EMACS_SERVER_FILE=%S" v)) + (vterm-send-return)) + (vterm-send-string "clear") + (vterm-send-return)) + (error "Cannot use sleeping editor in this buffer"))) + (t + (error "Cannot export environment variables in this buffer"))) + (message "Successfully exported %s" envvar)) + +;;;###autoload +(defun with-editor-export-git-editor () + "Like `with-editor-export-editor' but always set `$GIT_EDITOR'." + (interactive) + (with-editor-export-editor "GIT_EDITOR")) + +;;;###autoload +(defun with-editor-export-hg-editor () + "Like `with-editor-export-editor' but always set `$HG_EDITOR'." + (interactive) + (with-editor-export-editor "HG_EDITOR")) + +(defun with-editor-output-filter (string) + "Handle edit requests on behalf of `comint-mode' and `eshell-mode'." + (with-editor-sleeping-editor-filter nil string)) + +(defun with-editor-emulate-terminal (process string) + "Like `term-emulate-terminal' but also handle edit requests." + (let ((with-editor-sleeping-editor-regexp + (substring with-editor-sleeping-editor-regexp 1))) + (with-editor-sleeping-editor-filter process string)) + (term-emulate-terminal process string)) + +(defvar with-editor-envvars '("EDITOR" "GIT_EDITOR" "HG_EDITOR")) + +(cl-defun with-editor-read-envvar + (&optional (prompt "Set environment variable") + (default "EDITOR")) + (let ((reply (completing-read (if default + (format "%s (%s): " prompt default) + (concat prompt ": ")) + with-editor-envvars nil nil nil nil default))) + (if (string= reply "") (user-error "Nothing selected") reply))) + +;;;###autoload +(define-minor-mode shell-command-with-editor-mode + "Teach `shell-command' to use current Emacs instance as editor. + +Teach `shell-command', and all commands that ultimately call that +command, to use the current Emacs instance as editor by executing +\"EDITOR=CLIENT COMMAND&\" instead of just \"COMMAND&\". + +CLIENT is automatically generated; EDITOR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming no other variable overrides the effect of \"$EDITOR\". +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Alternatively you can use the `with-editor-async-shell-command', +which also allows the use of another variable instead of +\"EDITOR\"." + :global t) + +;;;###autoload +(defun with-editor-async-shell-command + (command &optional output-buffer error-buffer envvar) + "Like `async-shell-command' but with `$EDITOR' set. + +Execute string \"ENVVAR=CLIENT COMMAND\" in an inferior shell; +display output, if any. With a prefix argument prompt for an +environment variable, otherwise the default \"EDITOR\" variable +is used. With a negative prefix argument additionally insert +the COMMAND's output at point. + +CLIENT is automatically generated; ENVVAR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming it respects ENVVAR as an \"EDITOR\"-like variable. +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Also see `async-shell-command' and `shell-command'." + (interactive (with-editor-shell-command-read-args "Async shell command: " t)) + (let ((with-editor--envvar envvar)) + (with-editor + (async-shell-command command output-buffer error-buffer)))) + +;;;###autoload +(defun with-editor-shell-command + (command &optional output-buffer error-buffer envvar) + "Like `shell-command' or `with-editor-async-shell-command'. +If COMMAND ends with \"&\" behave like the latter, +else like the former." + (interactive (with-editor-shell-command-read-args "Shell command: ")) + (if (string-match "&[ \t]*\\'" command) + (with-editor-async-shell-command + command output-buffer error-buffer envvar) + (shell-command command output-buffer error-buffer))) + +(defun with-editor-shell-command-read-args (prompt &optional async) + (let ((command (read-shell-command + prompt nil nil + (let ((filename (or buffer-file-name + (and (eq major-mode 'dired-mode) + (dired-get-filename nil t))))) + (and filename (file-relative-name filename)))))) + (list command + (if (or async (setq async (string-match-p "&[ \t]*\\'" command))) + (< (prefix-numeric-value current-prefix-arg) 0) + current-prefix-arg) + shell-command-default-error-buffer + (and async current-prefix-arg (with-editor-read-envvar))))) + +(defun shell-command--shell-command-with-editor-mode + (fn command &optional output-buffer error-buffer) + ;; `shell-mode' and its hook are intended for buffers in which an + ;; interactive shell is running, but `shell-command' also turns on + ;; that mode, even though it only runs the shell to run a single + ;; command. The `with-editor-export-editor' hook function is only + ;; intended to be used in buffers in which an interactive shell is + ;; running, so it has to be removed here. + (let ((shell-mode-hook (remove 'with-editor-export-editor shell-mode-hook))) + (cond ((or (not (or with-editor--envvar shell-command-with-editor-mode)) + (not (string-suffix-p "&" command))) + (funcall fn command output-buffer error-buffer)) + ((and with-editor-shell-command-use-emacsclient + with-editor-emacsclient-executable + (not (file-remote-p default-directory))) + (with-editor (funcall fn command output-buffer error-buffer))) + (t + (funcall fn (format "%s=%s %s" + (or with-editor--envvar "EDITOR") + (shell-quote-argument with-editor-sleeping-editor) + command) + output-buffer error-buffer) + (ignore-errors + (let ((process (get-buffer-process + (or output-buffer + (get-buffer "*Async Shell Command*"))))) + (set-process-filter + process (lambda (proc str) + (comint-output-filter proc str) + (with-editor-process-filter proc str t))) + process)))))) + +(advice-add 'shell-command :around + #'shell-command--shell-command-with-editor-mode) + +;;; _ + +(defun with-editor-debug () + "Debug configuration issues. +See info node `(with-editor)Debugging' for instructions." + (interactive) + (require 'warnings) + (with-current-buffer (get-buffer-create "*with-editor-debug*") + (pop-to-buffer (current-buffer)) + (erase-buffer) + (ignore-errors (with-editor)) + (insert + (format "with-editor: %s\n" (locate-library "with-editor.el")) + (format "emacs: %s (%s)\n" + (expand-file-name invocation-name invocation-directory) + emacs-version) + "system:\n" + (format " system-type: %s\n" system-type) + (format " system-configuration: %s\n" system-configuration) + (format " system-configuration-options: %s\n" system-configuration-options) + "server:\n" + (format " server-running-p: %s\n" (server-running-p)) + (format " server-process: %S\n" server-process) + (format " server-use-tcp: %s\n" server-use-tcp) + (format " server-name: %s\n" server-name) + (format " server-socket-dir: %s\n" server-socket-dir)) + (if (and server-socket-dir (file-accessible-directory-p server-socket-dir)) + (dolist (file (directory-files server-socket-dir nil "^[^.]")) + (insert (format " %s\n" file))) + (insert (format " %s: not an accessible directory\n" + (if server-use-tcp "WARNING" "ERROR")))) + (insert (format " server-auth-dir: %s\n" server-auth-dir)) + (if (file-accessible-directory-p server-auth-dir) + (dolist (file (directory-files server-auth-dir nil "^[^.]")) + (insert (format " %s\n" file))) + (insert (format " %s: not an accessible directory\n" + (if server-use-tcp "ERROR" "WARNING")))) + (let ((val with-editor-emacsclient-executable) + (def (default-value 'with-editor-emacsclient-executable)) + (fun (let ((warning-minimum-level :error) + (warning-minimum-log-level :error)) + (with-editor-locate-emacsclient)))) + (insert "with-editor-emacsclient-executable:\n" + (format " value: %s (%s)\n" val + (and val (with-editor-emacsclient-version val))) + (format " default: %s (%s)\n" def + (and def (with-editor-emacsclient-version def))) + (format " funcall: %s (%s)\n" fun + (and fun (with-editor-emacsclient-version fun))))) + (insert "path:\n" + (format " $PATH: %S\n" (getenv "PATH")) + (format " exec-path: %s\n" exec-path)) + (insert (format " with-editor-emacsclient-path:\n")) + (dolist (dir (with-editor-emacsclient-path)) + (insert (format " %s (%s)\n" dir (car (file-attributes dir)))) + (when (file-directory-p dir) + ;; Don't match emacsclientw.exe, it makes popup windows. + (dolist (exec (directory-files dir t "emacsclient\\(?:[^w]\\|\\'\\)")) + (insert (format " %s (%s)\n" exec + (with-editor-emacsclient-version exec)))))))) + +(defconst with-editor-font-lock-keywords + '(("(\\(with-\\(?:git-\\)?editor\\)\\_>" (1 'font-lock-keyword-face)))) +(font-lock-add-keywords 'emacs-lisp-mode with-editor-font-lock-keywords) + +(provide 'with-editor) +;; Local Variables: +;; indent-tabs-mode: nil +;; End: +;;; with-editor.el ends here diff --git a/code/elpa/with-editor-20220810.1159/with-editor.info b/code/elpa/with-editor-20220810.1159/with-editor.info new file mode 100644 index 0000000..16a479f --- /dev/null +++ b/code/elpa/with-editor-20220810.1159/with-editor.info @@ -0,0 +1,382 @@ +This is with-editor.info, produced by makeinfo version 6.7 from +with-editor.texi. + + Copyright (C) 2015-2022 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* With-Editor: (with-editor). Using the Emacsclient as $EDITOR. +END-INFO-DIR-ENTRY + + +File: with-editor.info, Node: Top, Next: Using the With-Editor package, Up: (dir) + +With-Editor User Manual +*********************** + +The library ‘with-editor’ makes it easy to use the Emacsclient as the +‘$EDITOR’ of child processes, making sure they know how to call home. +For remote processes a substitute is provided, which communicates with +Emacs on standard output instead of using a socket as the Emacsclient +does. + + This library was written because Magit has to be able to do the above +to allow the user to edit commit messages gracefully and to edit rebase +sequences, which wouldn’t be possible at all otherwise. + + Because other packages can benefit from such functionality, this +library is made available as a separate package. It also defines some +additional functionality which makes it useful even for end-users, who +don’t use Magit or another package which uses it internally. + +This manual is for With-Editor version 3.2.0-git. + + Copyright (C) 2015-2022 Jonas Bernoulli + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +* Menu: + +* Using the With-Editor package:: +* Using With-Editor as a library:: +* Debugging:: +* Function and Command Index:: +* Variable Index:: + +— The Detailed Node Listing — + +Using the With-Editor package + +* Configuring With-Editor:: +* Using With-Editor commands:: + + + +File: with-editor.info, Node: Using the With-Editor package, Next: Using With-Editor as a library, Prev: Top, Up: Top + +1 Using the With-Editor package +******************************* + +The ‘With-Editor’ package is used internally by Magit when editing +commit messages and rebase sequences. It also provides some commands +and features which are useful by themselves, even if you don’t use +Magit. + + For information about using this library in you own package, see +*note Using With-Editor as a library::. + +* Menu: + +* Configuring With-Editor:: +* Using With-Editor commands:: + + +File: with-editor.info, Node: Configuring With-Editor, Next: Using With-Editor commands, Up: Using the With-Editor package + +1.1 Configuring With-Editor +=========================== + +With-Editor tries very hard to locate a suitable ‘emacsclient’ +executable, so ideally you should never have to customize the option +‘with-editor-emacsclient-executable’. When it fails to do so, then the +most likely reason is that someone found yet another way to package +Emacs (most likely on macOS) without putting the executable on ‘$PATH’, +and we have to add another kludge to find it anyway. + + -- User Option: with-editor-emacsclient-executable + The ‘emacsclient’ executable used as the editor by child process of + this Emacs instance. By using this executable, child processes can + call home to their parent process. + + This option is automatically set at startup by looking in + ‘exec-path’, and other places where the executable could be + installed, to find the ‘emacsclient’ executable most suitable for + the current Emacs instance. + + You should *not* customize this option permanently. If you have to + do it, then you should consider that a temporary kludge and inform + the Magit maintainer as described in *note Debugging::. + + If With-Editor fails to find a suitable ‘emacsclient’ on you + system, then this should be fixed for all users at once, by + teaching ‘with-editor-locate-emacsclient’ how to do so on your + system and system like yours. Doing it this way has the advantage, + that you won’t have do it again every time you update Emacs, and + that other users who have installed Emacs the same way as you have, + won’t have to go through the same trouble. + + Note that there also is a nuclear option; setting this variable to + ‘nil’ causes the "sleeping editor" described below to be used even + for local child processes. Obviously we don’t recommend that you + use this except in "emergencies", i.e. before we had a change to + add a kludge appropriate for you setup. + + -- Function: with-editor-locate-emacsclient + The function used to set the initial value of the option + ‘with-editor-emacsclient-executable’. There’s a lot of voodoo + here. + + The ‘emacsclient’ cannot be used when using Tramp to run a process on +a remote machine. (Theoretically it could, but that would be hard to +setup, very fragile, and rather insecure). + + With-Editor provides an alternative "editor" which can be used by +remote processes in much the same way as local processes use an +‘emacsclient’ executable. This alternative is known as the "sleeping +editor" because it is implemented as a shell script which sleeps until +it receives a signal. + + -- User Option: with-editor-sleeping-editor + The sleeping editor is a shell script used as the editor of child + processes when the ‘emacsclient’ executable cannot be used. + + This fallback is used for asynchronous process started inside the + macro ‘with-editor’, when the process runs on a remote machine or + for local processes when ‘with-editor-emacsclient-executable’ is + ‘nil’. + + Where the latter uses a socket to communicate with Emacs’ server, + this substitute prints edit requests to its standard output on + which a process filter listens for such requests. As such it is + not a complete substitute for a proper ‘emacsclient’, it can only + be used as ‘$EDITOR’ of child process of the current Emacs + instance. + + Some shells do not execute traps immediately when waiting for a + child process, but by default we do use such a blocking child + process. + + If you use such a shell (e.g. ‘csh’ on FreeBSD, but not Debian), + then you have to edit this option. You can either replace ‘sh’ + with ‘bash’ (and install that), or you can use the older, less + performant implementation: + + "sh -c '\ + echo \"WITH-EDITOR: $$ OPEN $0 IN $(pwd)\"; \ + trap \"exit 0\" USR1; \ + trap \"exit 1\" USR2; \ + while true; do sleep 1; done'" + + Note that the unit separator character () right after the file name + ($0) is required. + + Also note that using this alternative implementation leads to a + delay of up to a second. The delay can be shortened by replacing + ‘sleep 1’ with ‘sleep 0.01’, or if your implementation does not + support floats, then by using ‘nanosleep’ instead. + + +File: with-editor.info, Node: Using With-Editor commands, Prev: Configuring With-Editor, Up: Using the With-Editor package + +1.2 Using With-Editor commands +============================== + +This section describes how to use the ‘with-editor’ library _outside_ of +Magit. You don’t need to know any of this just to create commits using +Magit. + + The commands ‘with-editor-async-shell-command’ and +‘with-editor-shell-command’ are intended as drop in replacements for +‘async-shell-command’ and ‘shell-command’. They automatically export +‘$EDITOR’ making sure the executed command uses the current Emacs +instance as "the editor". With a prefix argument these commands prompt +for an alternative environment variable such as ‘$GIT_EDITOR’. + + -- Command: with-editor-async-shell-command + This command is like ‘async-shell-command’, but it runs the shell + command with the current Emacs instance exported as ‘$EDITOR’. + + -- Command: with-editor-shell-command + This command is like ‘shell-command’, but if the shell command ends + with ‘&’ and is therefore run asynchronously, then the current + Emacs instance is exported as ‘$EDITOR’. + + To always use these variants add this to you init file: + + (define-key (current-global-map) + [remap async-shell-command] 'with-editor-async-shell-command) + (define-key (current-global-map) + [remap shell-command] 'with-editor-shell-command) + + Alternatively use the global ‘shell-command-with-editor-mode’. + + -- Variable: shell-command-with-editor-mode + When this mode is active, then ‘$EDITOR’ is exported whenever + ultimately ‘shell-command’ is called to asynchronously run some + shell command. This affects most variants of that command, whether + they are defined in Emacs or in some third-party package. + + The command ‘with-editor-export-editor’ exports ‘$EDITOR’ or another +such environment variable in ‘shell-mode’, ‘eshell-mode’, ‘term-mode’ +and ‘vterm-mode’ buffers. Use this Emacs command before executing a +shell command which needs the editor set, or always arrange for the +current Emacs instance to be used as editor by adding it to the +appropriate mode hooks: + + (add-hook 'shell-mode-hook 'with-editor-export-editor) + (add-hook 'eshell-mode-hook 'with-editor-export-editor) + (add-hook 'term-exec-hook 'with-editor-export-editor) + (add-hook 'vterm-exec-hook 'with-editor-export-editor) + + Some variants of this function exist; these two forms are equivalent: + + (add-hook 'shell-mode-hook + (apply-partially 'with-editor-export-editor "GIT_EDITOR")) + (add-hook 'shell-mode-hook 'with-editor-export-git-editor) + + -- Command: with-editor-export-editor + When invoked in a ‘shell-mode’, ‘eshell-mode’, ‘term-mode’ or + ‘vterm-mode’ buffer, this command teaches shell commands to use the + current Emacs instance as the editor, by exporting ‘$EDITOR’. + + -- Command: with-editor-export-git-editor + This command is like ‘with-editor-export-editor’ but exports + ‘$GIT_EDITOR’. + + -- Command: with-editor-export-hg-editor + This command is like ‘with-editor-export-editor’ but exports + ‘$HG_EDITOR’. + + +File: with-editor.info, Node: Using With-Editor as a library, Next: Debugging, Prev: Using the With-Editor package, Up: Top + +2 Using With-Editor as a library +******************************** + +This section describes how to use the ‘with-editor’ library _outside_ of +Magit to teach another package how to have its child processes call +home, just like Magit does. You don’t need to know any of this just to +create commits using Magit. You can also ignore this if you use +‘with-editor’ outside of Magit, but only as an end-user. + + For information about interactive use and options that affect both +interactive and non-interactive use, see *note Using the With-Editor +package::. + + -- Macro: with-editor &rest body + This macro arranges for the ‘emacsclient’ or the sleeping editor to + be used as the editor of child processes, effectively teaching them + to call home to the current Emacs instance when they require that + the user edits a file. + + This is done by establishing a local binding for + ‘process-environment’ and changing the value of the ‘EDITOR’ + environment variable in that scope. This affects all + (asynchronous) processes started by forms (dynamically) inside + BODY. + + If BODY begins with a literal string, then that variable is set + instead of ‘EDITOR’. + + -- Macro: with-editor envvar &rest body + This macro is like ‘with-editor’ instead that the ENVVAR argument + is required and that it is evaluated at run-time. + + -- Function: with-editor-set-process-filter process filter + This function is like ‘set-process-filter’ but ensures that adding + the new FILTER does not remove the ‘with-editor-process-filter’. + This is done by wrapping the two filter functions using a lambda, + which becomes the actual filter. It calls FILTER first, which may + or may not insert the text into the PROCESS’s buffer. Then it + calls ‘with-editor-process-filter’, passing t as + NO-STANDARD-FILTER. + + +File: with-editor.info, Node: Debugging, Next: Function and Command Index, Prev: Using With-Editor as a library, Up: Top + +3 Debugging +*********** + +With-Editor tries very hard to locate a suitable ‘emacsclient’ +executable, and then sets option ‘with-editor-emacsclient-executable’ +accordingly. In very rare cases this fails. When it does fail, then +the most likely reason is that someone found yet another way to package +Emacs (most likely on macOS) without putting the executable on ‘$PATH’, +and we have to add another kludge to find it anyway. + + If you are having problems using ‘with-editor’, e.g. you cannot +commit in Magit, then please open a new issue at + and provide information +about your Emacs installation. Most importantly how did you install +Emacs and what is the output of ‘M-x with-editor-debug RET’. + + +File: with-editor.info, Node: Function and Command Index, Next: Variable Index, Prev: Debugging, Up: Top + +Appendix A Function and Command Index +************************************* + +[index] +* Menu: + +* with-editor: Using With-Editor as a library. + (line 16) +* with-editor <1>: Using With-Editor as a library. + (line 31) +* with-editor-async-shell-command: Using With-Editor commands. + (line 17) +* with-editor-export-editor: Using With-Editor commands. + (line 59) +* with-editor-export-git-editor: Using With-Editor commands. + (line 64) +* with-editor-export-hg-editor: Using With-Editor commands. + (line 68) +* with-editor-locate-emacsclient: Configuring With-Editor. + (line 41) +* with-editor-set-process-filter: Using With-Editor as a library. + (line 35) +* with-editor-shell-command: Using With-Editor commands. + (line 21) + + +File: with-editor.info, Node: Variable Index, Prev: Function and Command Index, Up: Top + +Appendix B Variable Index +************************* + +[index] +* Menu: + +* shell-command-with-editor-mode: Using With-Editor commands. + (line 35) +* with-editor-emacsclient-executable: Configuring With-Editor. + (line 13) +* with-editor-sleeping-editor: Configuring With-Editor. + (line 56) + + + +Tag Table: +Node: Top773 +Node: Using the With-Editor package2567 +Node: Configuring With-Editor3153 +Node: Using With-Editor commands7699 +Node: Using With-Editor as a library10984 +Node: Debugging13009 +Node: Function and Command Index13901 +Node: Variable Index15399 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: