init.el 126 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313
  1. ;;; init.el --- Configuration entry point -*- lexical-binding: t -*-
  2. ;;; Commentary:
  3. ;;; Code:
  4. (require 'cl-lib)
  5. (require 'xdg)
  6. ;; Some other config files
  7. (cl-eval-when (compile load eval)
  8. (add-to-list 'load-path (expand-file-name "elisp" user-emacs-directory))
  9. (add-to-list 'load-path (expand-file-name "third-party" user-emacs-directory)))
  10. ;; Set package dir to follow no-littering conventions
  11. (setq package-user-dir (expand-file-name "var/elpa"
  12. user-emacs-directory))
  13. ;; Use melpa
  14. (require 'package)
  15. (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
  16. (package-initialize)
  17. ;; Ensure use-package is installed
  18. (unless (package-installed-p 'use-package)
  19. (package-refresh-contents)
  20. (package-install 'use-package))
  21. ;; use-package
  22. (eval-when-compile
  23. (require 'use-package)
  24. (setq use-package-always-ensure t
  25. package-user-dir (expand-file-name "var/elpa"
  26. user-emacs-directory)))
  27. ;; no-littering
  28. (use-package no-littering
  29. :autoload (no-littering-theme-backups
  30. no-littering-expand-etc-file-name)
  31. :init
  32. (no-littering-theme-backups)
  33. (setq custom-file (no-littering-expand-etc-file-name "custom.el")))
  34. ;; load things saved with custom
  35. (load custom-file t t)
  36. ;; diminish
  37. (use-package diminish
  38. :config
  39. (diminish 'visual-line-mode)
  40. (diminish 'abbrev-mode))
  41. ;; Private config loading
  42. (require 'private nil t)
  43. (defun my/get-private (key)
  44. "Get the private config variable KEY from the private configuration file."
  45. (alist-get key my/private-config))
  46. ;; basic stuff
  47. (use-package emacs
  48. :hook (;;(emacs-lisp-mode . my/-emacs-lisp-mode-setup-evil-lookup)
  49. ;;(prog-mode . electric-pair-local-mode)
  50. ((text-mode tex-mode prog-mode) . auto-fill-mode)
  51. ((text-mode tex-mode prog-mode) . my/-enable-show-trailing-whitespace))
  52. :init
  53. (with-eval-after-load 'find-func
  54. (when (file-directory-p "~/src/emacs/src/")
  55. (setq find-function-C-source-directory "~/src/emacs/src/")))
  56. (defun my/-enable-show-trailing-whitespace ()
  57. (setq-local show-trailing-whitespace t))
  58. ;; (defun my/-emacs-lisp-mode-setup-evil-lookup ()
  59. ;; (setq-local evil-lookup-func
  60. ;; #'my/describe-symbol-at-point))
  61. (defun my/describe-symbol-at-point ()
  62. "Calls `describe-symbol' on the return value of `symbol-at-point'."
  63. (interactive)
  64. (let ((form (symbol-at-point)))
  65. (if (consp form)
  66. (describe-symbol (cadr form))
  67. (describe-symbol form))))
  68. ;; Increase responsiveness
  69. (setq gc-cons-threshold 80000000
  70. read-process-output-max (* 1024 1024)) ;; 1mb
  71. (global-so-long-mode 1)
  72. ;; Terminal mouse support
  73. (xterm-mouse-mode 1)
  74. ;; Make cursor more visible
  75. (global-hl-line-mode 1)
  76. (blink-cursor-mode -1)
  77. ;; Enable all disabled stuff
  78. (setq disabled-command-function nil)
  79. ;; Stop some annoying stuff
  80. (setq extended-command-suggest-shorter nil
  81. suggest-key-bindings nil)
  82. ;; Better scrolling
  83. (setq mouse-scroll-delay 0
  84. scroll-conservatively 10
  85. scroll-margin 2
  86. scroll-preserve-screen-position t)
  87. ;; Make show paren instant
  88. (setq show-paren-delay 0)
  89. (show-paren-mode 1)
  90. ;; Display line numbers
  91. (global-display-line-numbers-mode 1)
  92. ;; Allow the frame to be any size
  93. (setq frame-resize-pixelwise t)
  94. ;; Don't use a gtk file picker
  95. (setq use-file-dialog nil)
  96. ;; Make yes-or-no-p less verbose (and not use windows)
  97. (setq use-dialog-box nil
  98. use-short-answers t)
  99. ;; Disable startup screen
  100. (setq inhibit-startup-screen t
  101. server-client-instructions nil)
  102. ;; show column numbers
  103. (column-number-mode 1)
  104. ;; Disable the menu and tool bars
  105. (menu-bar-mode -1)
  106. (tool-bar-mode -1)
  107. ;; No scroll bars
  108. (scroll-bar-mode -1)
  109. ;; Visual line mode
  110. (global-visual-line-mode 1)
  111. ;; Make some commands easier to enter multiple times
  112. (repeat-mode 1)
  113. ;; Easier buffer navigation
  114. (keymap-global-set "C-c <" #'previous-buffer)
  115. (keymap-global-set "C-c >" #'next-buffer)
  116. (keymap-global-set "C-c k" #'previous-buffer)
  117. (keymap-global-set "C-c j" #'next-buffer)
  118. ;; Seems useful...
  119. (keymap-global-set "C-c u" #'browse-url)
  120. ;; Set fonts
  121. (add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono-12"))
  122. (add-hook 'server-after-make-frame-hook
  123. (lambda ()
  124. (set-fontset-font t 'japanese-jisx0208 "IPAGothic")))
  125. ;; Enable color in compilation buffers
  126. (add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)
  127. ;; Some settings for programming
  128. (setq-default indent-tabs-mode nil
  129. tab-width 4
  130. fill-column 80
  131. comment-multi-line t
  132. comment-empty-lines 'eol)
  133. (add-to-list 'auto-mode-alist '("\\.[cC][nN][fF]\\'" . conf-mode))
  134. (keymap-set emacs-lisp-mode-map "C-c C-r" #'eval-region)
  135. (defun my/-fix-emacs-lisp-mode-system-files ()
  136. (when (string-prefix-p lisp-directory buffer-file-name)
  137. ;; system Emacs files use tab characters and look weird without this.
  138. (setq-local tab-width 8)))
  139. (add-hook 'emacs-lisp-mode-hook #'my/-fix-emacs-lisp-mode-system-files)
  140. ;; Tree sitter download locations
  141. (setq treesit-language-source-alist
  142. '((c "https://github.com/tree-sitter/tree-sitter-c")
  143. (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
  144. (java "https://github.com/tree-sitter/tree-sitter-java")
  145. ;; (glsl "https://github.com/tree-sitter-grammars/tree-sitter-glsl")
  146. (python "https://github.com/tree-sitter/tree-sitter-python")
  147. (rust "https://github.com/tree-sitter/tree-sitter-rust")
  148. (json "https://github.com/tree-sitter/tree-sitter-json")
  149. (yaml "https://github.com/ikatyang/tree-sitter-yaml")
  150. (css "https://github.com/tree-sitter/tree-sitter-css")
  151. (go "https://github.com/tree-sitter/tree-sitter-go")
  152. (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
  153. (javascript "https://github.com/tree-sitter/tree-sitter-javascript")
  154. (typescript "https://github.com/tree-sitter/tree-sitter-typescript"
  155. nil "typescript/src")
  156. (tsx "https://github.com/tree-sitter/tree-sitter-typescript"
  157. nil "tsx/src")
  158. (bash "https://github.com/tree-sitter/tree-sitter-bash")
  159. (cmake "https://github.com/uyha/tree-sitter-cmake")
  160. (blueprint "https://github.com/huanie/tree-sitter-blueprint")
  161. (kdl "https://github.com/tree-sitter-grammars/tree-sitter-kdl")))
  162. ;; Tree sitter major mode conversions
  163. (dolist (ent '((c-mode . c-ts-mode)
  164. (c++-mode . c++-ts-mode)
  165. (c-or-c++-mode . c-or-c++-ts-mode)
  166. (python-mode . python-ts-mode)
  167. (java-mode . java-ts-mode)
  168. (rust-mode . rust-ts-mode)
  169. (json-mode . json-ts-mode)
  170. (yaml-mode . yaml-ts-mode)
  171. (css-mode . css-ts-mode)
  172. (javascript-mode . js-ts-mode)
  173. (cmake-mode . cmake-ts-mode)))
  174. (add-to-list 'major-mode-remap-alist ent))
  175. (defun my/treesit-compile-all (force)
  176. "Compile all the modules defined in `treesit-language-source-alist'.
  177. If FORCE, recompile all modules, even ones that are already compiled.
  178. Interactively, force the recompile if called with a prefix."
  179. (interactive "P")
  180. (let ((did-build nil))
  181. (dolist (lang treesit-language-source-alist)
  182. (when (or force (not (treesit-language-available-p (car lang))))
  183. (treesit-install-language-grammar (car lang))
  184. (setq did-build t)))
  185. (unless did-build
  186. (message "All defined parsers installed!")))))
  187. (use-package auth-source
  188. :ensure nil
  189. :custom
  190. (auth-sources '("~/.authinfo.gpg")))
  191. (use-package tramp
  192. :ensure nil
  193. :config
  194. (connection-local-set-profile-variables
  195. 'remote-direct-async-process
  196. '((tramp-direct-async-process . t)))
  197. (connection-local-set-profiles
  198. '(:protocol "ssh")
  199. 'remote-direct-async-process))
  200. (use-package midnight
  201. :ensure nil
  202. :config
  203. (add-to-list 'clean-buffer-list-kill-never-buffer-names
  204. "*mu4e-main*")
  205. (add-to-list 'clean-buffer-list-kill-never-buffer-names
  206. "*Async-native-compile-log*")
  207. (add-to-list 'clean-buffer-list-kill-never-buffer-names
  208. "*dashboard*")
  209. (add-to-list 'clean-buffer-list-kill-never-buffer-names
  210. "*elfeed-search*")
  211. (midnight-mode 1))
  212. (defvar my/kill-some-buffers-exclude-names
  213. '("*mu4e-main*" "*Async-native-compile-log*" "*dashboard*" "*elfeed-search*"
  214. "*Messages*" "*scratch*")
  215. "List of literal buffer names that `my/kill-some-buffers' should not kill.")
  216. (defun my/kill-some-buffers-excluded-buffer-p (buffer)
  217. "Return non-nil if BUFFER should be excluded from `my/kill-some-buffers'."
  218. (cl-find (buffer-name buffer) my/kill-some-buffers-exclude-names
  219. :test 'equal))
  220. (defun my/buffer-visible-p (buffer)
  221. "Return non-nil if BUFFER is visible.
  222. BUFFER can be a string or a buffer."
  223. (cond
  224. ((stringp buffer)
  225. (not (string-prefix-p " " buffer)))
  226. ((bufferp buffer)
  227. (and (stringp (buffer-name buffer))
  228. (my/buffer-visible-p (buffer-name buffer))))
  229. (t
  230. (signal 'wrong-type-argument `((or bufferp stringp) ,buffer)))))
  231. (defvar my/kill-some-buffers-default-pred 'my/buffer-visible-p
  232. "Default predicate for `my/kill-some-buffers'.")
  233. (defun my/kill-some-buffers-prompt-for (buffer)
  234. "Generate a prompt for BUFFER."
  235. (let* ((process (get-buffer-process buffer))
  236. (process-p (and (process-live-p process)
  237. (not (process-query-on-exit-flag process))))
  238. (modified-p (and (buffer-file-name buffer)
  239. (buffer-modified-p buffer))))
  240. (format "Buffer \"%s\" %s. Kill? "
  241. (buffer-name buffer)
  242. (cond
  243. ((and process-p modified-p)
  244. "HAS BEEN EDITED AND HAS A LIVE PROCESS")
  245. (modified-p
  246. "HAS BEEN EDITED")
  247. (process-p
  248. "HAS A LIVE PROCESS")
  249. (t "is unmodified")))))
  250. (cl-defun my/kill-some-buffers (&optional auto-unmod pred)
  251. "Improved version of `kill-some-buffers'.
  252. Ask the user weather to kill each visible buffer whose name is not in
  253. `my/kill-some-buffers-exclude-names'.
  254. When AUTO-UNMOD is non-nil, as it is with a prefix argument, automatically kill
  255. unmodified buffers, and then ask about the rest.
  256. When PRED is non-nil, it is a function that will be run in each buffer (not just
  257. visible ones). If it returns t, that buffer will be considered for killing. If
  258. PRED is nil, the value of `my/kill-some-buffers-default-pred' is used."
  259. (interactive "P")
  260. ;; we already ask, no need to do it again
  261. (let ((kill-buffer-query-functions nil)
  262. (all-action (when auto-unmod 'unmod))
  263. (had-valid-buffer)
  264. (ask-again-buffers)
  265. (to-kill))
  266. (cl-flet ((ask-about (buffer allow-unmod)
  267. (unless all-action
  268. (read-answer
  269. (my/kill-some-buffers-prompt-for buffer)
  270. `(("yes" ?y "save and kill this buffer")
  271. ("no" ?n "skip this buffer")
  272. ("all" ?! "save and kill all remaining buffers")
  273. ("nosave" ?l "kill this buffer without saving")
  274. ,@(when allow-unmod
  275. '(("unmod" ?a
  276. "kill unmodified buffers, ask about the rest")))
  277. ("quit" ?q "exit")))))
  278. (act-on (ans buffer allow-unmod)
  279. (when (equal ans "all")
  280. (setq all-action 'all))
  281. (when (and allow-unmod
  282. (equal ans "unmod"))
  283. (setq all-action 'unmod))
  284. (cond
  285. ((and (eq all-action 'unmod)
  286. (buffer-file-name buffer)
  287. (buffer-modified-p buffer))
  288. (push buffer ask-again-buffers))
  289. ((or (eq all-action 'all)
  290. (eq all-action 'unmod)
  291. (equal ans "yes"))
  292. (when (buffer-file-name buffer)
  293. (with-current-buffer buffer
  294. (save-buffer)))
  295. (push buffer to-kill))
  296. ((equal ans "nosave")
  297. (with-current-buffer buffer
  298. (set-buffer-modified-p nil))
  299. (push buffer to-kill))
  300. ;; Skip buffer
  301. ;; ((equal ans "no"))
  302. ((equal ans "quit")
  303. (cl-return-from my/kill-some-buffers)))))
  304. (dolist (buffer (buffer-list))
  305. (when (and (not (my/kill-some-buffers-excluded-buffer-p buffer))
  306. (funcall (or pred my/kill-some-buffers-default-pred) buffer))
  307. (setq had-valid-buffer t)
  308. (act-on (ask-about buffer t) buffer t)))
  309. (unless had-valid-buffer
  310. (message "Nothing to do..."))
  311. (setq all-action nil)
  312. (dolist (buffer ask-again-buffers)
  313. (act-on (ask-about buffer nil) buffer nil))
  314. ;; Do this last so that tty frames don't auto-close half way through
  315. (mapc 'kill-buffer to-kill))))
  316. (keymap-global-set "C-x K" 'my/kill-some-buffers)
  317. (use-package tab-bar
  318. :ensure nil
  319. :init
  320. (setq tab-bar-show 1
  321. tab-bar-tab-hints t
  322. icon-preference '(symbol text image emoji))
  323. (tab-bar-mode 1))
  324. ;; jinx (better flyspell)
  325. (use-package jinx
  326. :hook (emacs-startup . global-jinx-mode)
  327. :config
  328. (evil-define-key 'normal 'global
  329. "z=" #'jinx-correct)
  330. (defun my/jinx-visit-dictionary (language &optional other-window)
  331. "Visit the dictionary file for LANGUAGE in another window.
  332. With OTHER-WINDOW, visit the file in another window. Interactively, use the
  333. current buffer's language, prompting if there is more than one. OTHER-WINDOW is
  334. t with a prefix argument."
  335. (interactive (list
  336. (let ((langs (split-string jinx-languages " ")))
  337. (if (length= langs 1)
  338. (car langs)
  339. (completing-read "Language: " langs nil t)))
  340. current-prefix-arg))
  341. (let* ((config-dir (expand-file-name "enchant" (xdg-config-home)))
  342. (dict-path (expand-file-name (concat language ".dic") config-dir)))
  343. (if other-window
  344. (find-file-other-window dict-path)
  345. (find-file dict-path)))))
  346. ;; recentf
  347. (use-package recentf
  348. :init
  349. (setq recentf-exclude `("^/tmp/.*"
  350. "^~/.mail/[^/]/Drafts/.*"
  351. ,(format "^%svar/dape-breakpoints" user-emacs-directory)
  352. ,(format "^%svar/elpa/.*" user-emacs-directory)
  353. ,(format "^%svar/elfeed/.*" user-emacs-directory)
  354. ,(format "^%svar/gnus/.*" user-emacs-directory)
  355. ,(format "^%svar/ellama-sessions/.*" user-emacs-directory)
  356. ,(format "^%setc/gnus/.*" user-emacs-directory)
  357. ,(format "^%svar/bookmark-default.el" user-emacs-directory)))
  358. :bind ("C-c r" . recentf)
  359. :config
  360. (recentf-mode 1))
  361. ;; bookmarks
  362. (use-package bookmark
  363. :ensure nil
  364. :bind ("C-c B" . my/bookmark-find-file)
  365. :config
  366. (defun my/bookmark-find-file (&optional name)
  367. "Run `find-file' in or on bookmark NAME.
  368. If NAME points to a directory, run `find-file' with `default-directory' in that
  369. directory. Otherwise, run `find-file' on that file."
  370. (interactive (list (bookmark-completing-read
  371. "Find file in" bookmark-current-bookmark)))
  372. (unless name
  373. (error "No bookmark specified"))
  374. (bookmark-maybe-historicize-string name)
  375. (when-let ((file (bookmark-get-filename name)))
  376. (if (file-directory-p file)
  377. (let ((default-directory (file-name-as-directory file)))
  378. (call-interactively 'find-file))
  379. (find-file file)))))
  380. ;; kitty keyboard protocol
  381. (use-package kkp
  382. :defer nil
  383. :config
  384. (global-kkp-mode 1)
  385. (defun my/quoted-insert (arg)
  386. "Insert the next character using read-key, not read-char."
  387. (interactive "*p")
  388. ;; Source: https://github.com/benjaminor/kkp/issues/11
  389. (let ((char (read-key)))
  390. ;; Ensure char is treated as a character code for insertion
  391. (unless (characterp char)
  392. (user-error "%s is not a valid character"
  393. (key-description (vector char))))
  394. (when (numberp char)
  395. (while (> arg 0)
  396. (insert-and-inherit char)
  397. (setq arg (1- arg))))))
  398. (keymap-global-set "C-q" #'my/quoted-insert)
  399. (defun my/kkp-disable-around-advice (oldfun &rest args)
  400. "Run OLDFUN with ARGS with kkp disabled."
  401. (let ((status (kkp--this-terminal-has-active-kkp-p)))
  402. (unwind-protect
  403. (progn
  404. (when status (kkp-disable-in-terminal))
  405. (apply oldfun args))
  406. (when status (kkp-enable-in-terminal))
  407. ;; consume the response from the terminal. If this is not here and this
  408. ;; function is set as advice for `map-y-or-n-p' called from
  409. ;; `save-buffers-kill-terminal', a bunch of extra characters will be
  410. ;; printed for the shell after Emacs exits because Emacs will die before
  411. ;; it can read the terminal's response to `kkp-enable-in-terminal'
  412. (while-no-input
  413. (sleep-for 0.1)))))
  414. (advice-add #'map-y-or-n-p :around #'my/kkp-disable-around-advice))
  415. ;; some eww (status bar) stuff
  416. (defun my/cmdline-for-pid (pid)
  417. "Return the command line arguments passed to PID.
  418. PID can be a string or a number."
  419. (butlast (string-split
  420. (with-temp-buffer
  421. (insert-file-contents-literally
  422. (format "/proc/%s/cmdline" pid))
  423. (buffer-substring-no-properties (point-min)
  424. (point-max)))
  425. "\0")))
  426. (defun my/eww-current-config-dir ()
  427. "Return the configuration directory for a currently running eww process."
  428. ;; This probably only works on Linux
  429. (catch 'found
  430. (dolist (subdir (directory-files "/proc"))
  431. (when (string-match-p (rx bos (+ num) eos) subdir)
  432. (ignore-error permission-denied
  433. (let* ((attrs (file-attributes (format "/proc/%s/exe" subdir)))
  434. (type (file-attribute-type attrs)))
  435. (when (and (stringp type)
  436. (string-match-p (rx (or bos "/") "eww") type))
  437. (cl-maplist (lambda (tail)
  438. (when (equal (car tail) "-c")
  439. (throw 'found (cl-second tail))))
  440. (my/cmdline-for-pid subdir)))))))))
  441. (defun my/eww-update-variables (&rest vars)
  442. "Update the key value pairs in VARS.
  443. Each key should be either a symbol or a string. Each value will have its
  444. printed representation (via `princ') set as the new value for the key."
  445. (let* ((mappings (map-apply
  446. #'(lambda (key val)
  447. (when (symbolp key) (setq key (symbol-name key)))
  448. (when (cl-find ?= key)
  449. (error "Key cannot contain an equal sign (=): %s" key))
  450. (format "%s=%s" key val))
  451. vars))
  452. (args (cons "update" mappings))
  453. (cfg-dir (my/eww-current-config-dir)))
  454. (when cfg-dir
  455. (setq args (nconc (list "-c" cfg-dir) args)))
  456. (apply 'call-process "eww" nil 0 nil args)))
  457. (defun my/eww-poll-variables (&rest vars)
  458. "Poll each variable in VARS, which is a lists of strings or symbols."
  459. (let* ((args (cons "poll" (mapcar #'(lambda (elt) (format "%s" elt)) vars)))
  460. (cfg-dir (my/eww-current-config-dir)))
  461. (when cfg-dir
  462. (setq args (nconc (list "-c" cfg-dir) args)))
  463. (apply 'call-process "eww" nil 0 nil args)))
  464. ;; mozc
  465. (require 'mozc nil t)
  466. (setq default-input-method "japanese-mozc")
  467. (defun my/-set-eww-fcitx-state (enabled)
  468. "Set the fcitx state for eww to ENABLED."
  469. (my/eww-update-variables "fcitx5-state" (if enabled 2 1)))
  470. (defun my/global-toggle-mozc (&optional no-eww)
  471. "Toggle mozc for all buffers.
  472. With NO-EWW, don't update eww's state."
  473. (interactive)
  474. (let ((default-input-method "japanese-mozc"))
  475. (toggle-input-method nil t)
  476. (let ((activate (or (bound-and-true-p evil-input-method)
  477. current-input-method)))
  478. (dolist (buffer (buffer-list))
  479. (with-current-buffer buffer
  480. (if activate
  481. (activate-input-method activate)
  482. (deactivate-input-method)
  483. (mozc-mode -1)
  484. (when (boundp 'evil-input-method)
  485. (setq-local evil-input-method nil)))))
  486. (unless no-eww
  487. (my/-set-eww-fcitx-state activate)))
  488. (force-mode-line-update t)))
  489. (keymap-global-set "C-\\" #'my/global-toggle-mozc)
  490. (defun my/mozc-active-in-buffer-p (&optional buffer)
  491. "Return non-nil if mozc is active in BUFFER."
  492. (unless buffer (setq buffer (current-buffer)))
  493. (with-current-buffer buffer
  494. (or (equal (or (bound-and-true-p evil-input-method) current-input-method)
  495. "japanese-mozc")
  496. (bound-and-true-p mozc-mode))))
  497. (defun my/-fcitx-enabled-p ()
  498. "Return non-nil if fcitx is enabled."
  499. (ignore-errors
  500. (= 2 (dbus-call-method :session "org.fcitx.Fcitx5" "/controller"
  501. "org.fcitx.Fcitx.Controller1" "State"))))
  502. (defun my/-set-fcitx-enabled (enabled)
  503. "If ENABLED is non-nil, enabled fcitx, otherwise disabled it."
  504. (dbus-call-method :session "org.fcitx.Fcitx5" "/controller"
  505. "org.fcitx.Fcitx.Controller1"
  506. (if enabled "Activate" "Deactivate")))
  507. (defun my/-normalize-mozc-state ()
  508. "Normalize the mozc state between Emacs and fcitx."
  509. (if (cl-some 'frame-focus-state (frame-list))
  510. (progn
  511. (when (my/-fcitx-enabled-p)
  512. (my/-set-fcitx-enabled nil)
  513. (when (not (my/mozc-active-in-buffer-p))
  514. (my/global-toggle-mozc t))))
  515. (when (cl-some 'my/mozc-active-in-buffer-p (buffer-list))
  516. (my/global-toggle-mozc t)
  517. (my/-set-fcitx-enabled t)
  518. (my/-set-eww-fcitx-state t))))
  519. (add-function :after after-focus-change-function #'my/-normalize-mozc-state)
  520. ;; fix the helper process failing if mozc is built with debug flags
  521. ;; (with-eval-after-load 'mozc
  522. ;; (defun my/-fixed-mozc-helper-process-filter (proc output-string)
  523. ;; ;; NOTE this is just a modified version of the origial in mozc.el
  524. ;; ;; If proc is no longer active, just throw away the data.
  525. ;; (when (eq proc mozc-helper-process)
  526. ;; (with-temp-buffer
  527. ;; (insert (concat mozc-helper-process-string-buf output-string))
  528. ;; (goto-char (point-min))
  529. ;; (unwind-protect
  530. ;; (while-let ((pos (scan-sexps (point) 1)))
  531. ;; (let* ((msg (buffer-substring-no-properties (point) pos)))
  532. ;; (push msg mozc-helper-process-message-queue)
  533. ;; (goto-char pos)))
  534. ;; (setq mozc-helper-process-string-buf (buffer-substring-no-properties
  535. ;; (point) (point-max)))))))
  536. ;; (advice-add 'mozc-helper-process-filter :override
  537. ;; #'my/-fixed-mozc-helper-process-filter))
  538. ;; migemo
  539. (use-package migemo
  540. :config
  541. (setq migemo-dictionary "/usr/share/migemo/utf-8/migemo-dict"
  542. migemo-coding-system 'utf-8-unix
  543. migemo-regex-dictionary nil
  544. migemo-user-dictionary nil
  545. migemo-isearch-enable-p t)
  546. (keymap-global-set "C-c w" 'isearch-forward)
  547. (keymap-global-set "C-c W" 'isearch-backward)
  548. (migemo-init))
  549. ;; undo-tree
  550. (use-package undo-tree
  551. :defer nil
  552. :hook (undo-tree-visualizer-mode . my/-undo-tree-visualizer-mode-setup)
  553. :config
  554. (defun my/-undo-tree-visualizer-mode-setup ()
  555. (visual-line-mode -1)
  556. (setq truncate-lines t))
  557. (global-undo-tree-mode))
  558. ;; evil
  559. (use-package evil
  560. :init
  561. (setq evil-want-integration t
  562. evil-want-C-d-scroll nil
  563. evil-want-keybinding nil
  564. evil-undo-system 'undo-tree
  565. evil-search-module 'isearch
  566. evil-respect-visual-line-mode t)
  567. :config
  568. (evil-mode 1)
  569. (evil-define-key '(normal visual motion) proced-mode-map
  570. "u" #'proced-unmark)
  571. (evil-define-key '(normal visual motion) dired-mode-map
  572. "u" #'dired-unmark)
  573. (evil-define-key '(normal visual motion) profiler-report-mode-map
  574. (kbd "TAB") #'profiler-report-toggle-entry)
  575. (eldoc-add-command 'evil-insert
  576. 'evil-append
  577. 'evil-insert-line
  578. 'evil-append-line))
  579. (use-package evil-collection
  580. :after evil
  581. :diminish evil-collection-unimpaired-mode
  582. :config
  583. (evil-collection-init))
  584. (use-package evil-surround
  585. :after evil
  586. :config
  587. (evil-define-key 'operator evil-surround-mode-map
  588. "z" #'evil-surround-edit
  589. "Z" #'evil-Surround-edit)
  590. (evil-define-key 'visual evil-surround-mode-map
  591. "gz" #'evil-surround-region
  592. "gZ" #'evil-Surround-region)
  593. (global-evil-surround-mode 1))
  594. (use-package evil-terminal-cursor-changer
  595. :after evil
  596. :config
  597. (evil-terminal-cursor-changer-activate))
  598. (use-package evil-numbers
  599. :after evil
  600. :bind (("C-c =" . evil-numbers/inc-at-pt)
  601. ("C-c +" . evil-numbers/inc-at-pt)
  602. ("C-c -" . evil-numbers/dec-at-pt)
  603. ("C-c C-=" . evil-numbers/inc-at-pt-incremental)
  604. ("C-c C-+" . evil-numbers/inc-at-pt-incremental)
  605. ("C-c C--" . evil-numbers/dec-at-pt-incremental)))
  606. (use-package evil-cleverparens
  607. :hook ((prog-mode . my/-enable-evil-cleverparens)
  608. (evil-cleverparens-mode . paredit-mode))
  609. :bind (:map paredit-mode-map
  610. ("C-<return>" . paredit-RET)
  611. ("C-RET" . paredit-RET)
  612. :map evil-cleverparens-mode-map
  613. ("C-c o" . evil-cp-open-below-form))
  614. :custom
  615. (evil-cleverparens-use-s-and-S nil)
  616. (evil-cleverparens-complete-parens-in-yanked-region t)
  617. :config
  618. (eldoc-add-command 'paredit-RET
  619. 'paredit-open-round
  620. 'paredit-open-angled
  621. 'paredit-open-bracket
  622. 'paredit-open-angled
  623. 'paredit-open-parenthesis
  624. 'delete-indentation
  625. 'evil-cp-insert
  626. 'evil-cp-append
  627. 'evil-cp-insert-at-beginning-of-form
  628. 'evil-cp-insert-at-end-of-form)
  629. (keymap-unset evil-cleverparens-mode-map "<normal-state> M-o" t)
  630. (defun my/-enable-evil-cleverparens ()
  631. (if (member major-mode '(lisp-mode emacs-lisp-mode
  632. lisp-interaction-mode))
  633. (evil-cleverparens-mode 1)
  634. (electric-pair-local-mode 1)))
  635. (cl-defun my/range-inside-thing-p (thing beg end &optional no-edge)
  636. "Return non-nil if BEG and END fall inside the bounds of THING.
  637. With NO-EDGE, return nil if beg or end fall on the edge of the range."
  638. (save-excursion
  639. ;; this fixes that fact that `thing-at-point-bounds-of-string-at-point'
  640. ;; errors if called at the end of the buffer
  641. (condition-case nil
  642. (let ((sb (progn (goto-char beg) (bounds-of-thing-at-point thing)))
  643. (eb (progn (goto-char end) (bounds-of-thing-at-point thing))))
  644. (and sb eb (equal sb eb)
  645. (or (not no-edge)
  646. (and (/= beg (car sb))
  647. (< beg (cdr sb))
  648. (/= end (car sb))
  649. (< end (cdr sb))))))
  650. ;; if the error happens, we aren't in a string
  651. (wrong-type-argument nil))))
  652. (defun my/-evil-cp-region-ok-p-no-string (oldfun beg end)
  653. (or
  654. (and (sp-point-in-comment beg)
  655. (sp-point-in-comment end))
  656. (and (sp-point-in-string beg)
  657. (sp-point-in-string end)
  658. (my/range-inside-thing-p 'string beg end t))
  659. (funcall oldfun beg end)))
  660. (defun my/column-num-at-pos (pos)
  661. "Return the column number at POS."
  662. (save-excursion
  663. (goto-char pos)
  664. (current-column)))
  665. (defun my/-evil-cp-block-ok-p-no-string (oldfun beg end)
  666. (when (> beg end) (cl-rotatef beg end))
  667. (or
  668. (save-excursion
  669. (goto-char beg)
  670. (let ((start-off (current-column))
  671. (end-off (my/column-num-at-pos end)))
  672. (cl-block nil
  673. (dotimes (_ (count-lines beg end) t)
  674. (let ((bol (pos-bol)))
  675. (unless (sp-region-ok-p (+ bol start-off)
  676. (+ bol end-off))
  677. (cl-return))
  678. (forward-line))))))
  679. (funcall oldfun beg end)))
  680. (advice-add 'sp-region-ok-p :around 'my/-evil-cp-region-ok-p-no-string)
  681. (advice-add 'evil-cp--balanced-block-p :around 'my/-evil-cp-block-ok-p-no-string))
  682. ;; be (hopefully) safer
  683. (require 'trusted-files)
  684. (keymap-global-set "C-c t" 'trusted-files-map)
  685. (trusted-files-modeline-mode)
  686. ;; better lisp editing
  687. (use-package adjust-parens
  688. :hook (prog-mode . adjust-parens-mode)
  689. :config
  690. (defun my/lisp-indent-adjust-parens ()
  691. "Like `lisp-indent-adjust-parens', but got to first char on line first.
  692. Also, this works even if the region is active (it indents every line in the
  693. region)."
  694. (interactive)
  695. (save-mark-and-excursion
  696. (let ((end (mark t))
  697. (line-count 1)
  698. (indent-cols))
  699. (when (and (region-active-p) end)
  700. (setq mark-active nil
  701. line-count (count-lines (point) end))
  702. (when (> (point) end)
  703. (let ((start (point)))
  704. (goto-char end)
  705. (setq end start))))
  706. ;; find the indentation column of each line
  707. (save-excursion
  708. (dotimes (_ line-count)
  709. (back-to-indentation)
  710. (push (- (point) (pos-bol)) indent-cols)
  711. (forward-line))
  712. (cl-callf nreverse indent-cols))
  713. (cl-loop repeat line-count
  714. for indent-col in indent-cols
  715. for bol = (pos-bol)
  716. do (back-to-indentation)
  717. ;; skip this line if the indentation has changed
  718. when (= (- (point) bol) indent-col) do
  719. (lisp-indent-adjust-parens)
  720. ;; if the indent failed, stop
  721. (when (= (- (point) bol) indent-col)
  722. (cl-return))
  723. do (forward-line)))))
  724. (defun my/lisp-dedent-adjust-parens ()
  725. "Like `lisp-dedent-adjust-parens', but got to first char on line first.
  726. Also, this works even if the region is active (it just jumps to the first line
  727. in the region and indents once)."
  728. (interactive)
  729. (save-mark-and-excursion
  730. (let ((end (mark t)))
  731. (when (and (region-active-p) end)
  732. (setq mark-active nil)
  733. (when (> (point) end)
  734. (goto-char end))))
  735. (back-to-indentation)
  736. (lisp-dedent-adjust-parens)))
  737. (eldoc-add-command 'my/lisp-indent-adjust-parens
  738. 'my/lisp-dedent-adjust-parens
  739. 'lisp-indent-adjust-parens
  740. 'lisp-dedent-adjust-parens)
  741. (evil-define-key '(normal visual) adjust-parens-mode-map
  742. (kbd "<tab>") #'my/lisp-indent-adjust-parens
  743. (kbd "<backtab>") #'my/lisp-dedent-adjust-parens
  744. (kbd "C-c C-i") #'my/lisp-indent-adjust-parens
  745. (kbd "C-c S-<tab>") #'my/lisp-dedent-adjust-parens))
  746. ;; for when the files are just too large
  747. (use-package vlf
  748. :demand t
  749. :config
  750. (require 'vlf-setup))
  751. ;; allow copy from terminal
  752. (use-package xclip
  753. :config
  754. (setq xclip-method 'wl-copy
  755. xclip-program (symbol-name xclip-method))
  756. (xclip-mode 1)
  757. (defun my/-xclip-detect-wl-paste-error (oldfun type)
  758. (if (eq xclip-method 'wl-copy)
  759. ;; Direct from `xclip-get-selection'
  760. (when (and (getenv "WAYLAND_DISPLAY")
  761. (memq type '(clipboard CLIPBOARD primary PRIMARY)))
  762. (let* ((exit-code 0)
  763. (output
  764. (with-output-to-string
  765. (setq exit-code
  766. (apply #'call-process (replace-regexp-in-string
  767. "\\(.*\\)copy" "\\1paste"
  768. xclip-program 'fixedcase)
  769. nil standard-output nil
  770. "-n" (if (memq type '(primary PRIMARY))
  771. '("-p")))))))
  772. (if (zerop exit-code)
  773. output
  774. "")))
  775. (funcall oldfun type)))
  776. (advice-add 'xclip-get-selection :around 'my/-xclip-detect-wl-paste-error))
  777. ;; Set the WAYLAND_DISPLAY environment variable
  778. (require 'xdg)
  779. (defun my/detect-wayland-display ()
  780. "Try to set the WAYLAND_DISPLAY environment variable.
  781. This attempts to detect a running Wayland session and set the WAYLAND_DISPLAY
  782. environment variable accordingly."
  783. (let ((found '(nil . nil)))
  784. (dolist (entry (directory-files-and-attributes
  785. (xdg-runtime-dir) nil nil 'nosort)
  786. (cdr found))
  787. (cl-destructuring-bind (name . attrs) entry
  788. (when-let (((string-match (rx bos "wayland-" (group (+ (any "0-9"))) eos)
  789. name))
  790. (id (string-to-number (match-string 1 name)))
  791. ((or (not (car found)) (< id (car found))))
  792. ;; socket
  793. ((string-prefix-p "s" (file-attribute-modes attrs))))
  794. (setq found (cons id name)))))))
  795. (unless (getenv "WAYLAND_DISPLAY")
  796. (setenv "WAYLAND_DISPLAY" (my/detect-wayland-display)))
  797. ;; which-key
  798. (use-package which-key
  799. :diminish which-key-mode
  800. :config
  801. (which-key-mode 1))
  802. ;; avy
  803. (use-package avy
  804. :bind (("C-c C-j" . avy-resume)
  805. ("M-s s" . evil-avy-goto-char-2)
  806. ("M-s S" . evil-avy-goto-line))
  807. :init
  808. (define-minor-mode my/evil-avy-mode
  809. "A minor mode for binding avy commands to s and S in evil's normal and
  810. visual states."
  811. :keymap (make-sparse-keymap))
  812. (evil-define-key '(normal visual operator motion) my/evil-avy-mode-map
  813. "s" #'evil-avy-goto-char-2
  814. "S" #'evil-avy-goto-line)
  815. (define-globalized-minor-mode my/evil-avy-global-mode my/evil-avy-mode
  816. (lambda () (my/evil-avy-mode 1))
  817. :predicate '((not magit-mode dired-mode
  818. proced-mode mu4e-main-mode
  819. mu4e-view-mode mu4e-headers-mode
  820. ibuffer-mode calc-mode calc-trail-mode
  821. gnus-group-mode) t))
  822. (my/evil-avy-global-mode 1)
  823. :config
  824. (avy-setup-default))
  825. ;; better `replace-regexp'
  826. (use-package visual-regexp
  827. :bind (("C-c q" . vr/replace)
  828. ("C-M-%" . vr/query-replace)))
  829. ;; better `align-regexp'
  830. (use-package ialign
  831. :defer t
  832. :custom
  833. (ialign-initial-repeat t))
  834. ;; ace-window
  835. (use-package ace-window
  836. :diminish ace-window-mode
  837. :bind ("M-o" . ace-window)
  838. :init
  839. (setq aw-scope 'frame
  840. aw-minibuffer-flag t))
  841. ;; savehist
  842. (use-package savehist
  843. :config
  844. (savehist-mode 1))
  845. ;; vertico
  846. (use-package vertico
  847. :bind (:map vertico-map
  848. ("C-RET" . vertico-exit-input)
  849. ("C-<return>" . vertico-exit-input)
  850. ("C-S-k" . kill-line)
  851. ("C-k" . vertico-previous)
  852. ("C-j" . vertico-next)
  853. ("RET" . vertico-directory-enter)
  854. ("DEL" . vertico-directory-delete-char)
  855. ("M-DEL" . vertico-directory-delete-word))
  856. :hook (minibuffer-setup . cursor-intangible-mode)
  857. :init
  858. (defun my/crm-indicator (args)
  859. (cons (format "[CRM%s] %s"
  860. (replace-regexp-in-string
  861. "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
  862. crm-separator)
  863. (car args))
  864. (cdr args)))
  865. (advice-add #'completing-read-multiple :filter-args #'my/crm-indicator)
  866. (setq vertico-cycle t
  867. enable-recursive-minibuffers t
  868. ;; read-extended-command-predicate #'command-completion-default-include-p
  869. read-extended-command-predicate nil
  870. minibuffer-prompt-properties '(read-only t ;; noindent 3
  871. cursor-intangible t
  872. face minibuffer-prompt))
  873. (vertico-mode 1)
  874. ;; for jinx
  875. (require 'vertico-multiform)
  876. (add-to-list 'vertico-multiform-categories
  877. '(jinx grid (vertico-grid-annotate . 20)))
  878. (vertico-multiform-mode 1))
  879. ;; orderless
  880. (use-package orderless
  881. :autoload orderless-define-completion-style
  882. :hook (text-mode . my/-setup-text-mode-completion-styles)
  883. :init
  884. (defun my/-setup-text-mode-completion-styles ()
  885. (setq-local completion-styles '(basic)))
  886. (orderless-define-completion-style my/orderless-with-initialism
  887. (orderless-matching-styles '(orderless-initialism
  888. orderless-regexp)))
  889. (setq orderless-matching-styles '(orderless-regexp)
  890. completion-styles '(orderless basic)
  891. completion-category-defaults nil
  892. completion-category-overrides '((file
  893. (styles basic partial-completion))
  894. (command
  895. (styles my/orderless-with-initialism basic)))))
  896. ;; marginalia
  897. (use-package marginalia
  898. :bind (:map minibuffer-local-map
  899. ("M-a" . marginalia-cycle))
  900. :init
  901. (marginalia-mode 1))
  902. ;; embark
  903. (use-package embark
  904. :bind (("C-," . embark-act)
  905. ("C-;" . embark-dwim)
  906. :map help-map
  907. ("B" . embark-bindings)
  908. :map embark-symbol-map
  909. ("h" . helpful-symbol)
  910. :map embark-become-file+buffer-map
  911. ("b" . consult-buffer)
  912. ("B" . switch-to-buffer))
  913. :init
  914. (setq embark-quit-after-action nil
  915. embark-indicators '(embark-minimal-indicator
  916. embark-isearch-highlight-indicator
  917. embark-highlight-indicator))
  918. :config
  919. (defvar-keymap my/embark-string-map
  920. :doc "Keymap for Embark string actions."
  921. :parent embark-expression-map
  922. "R" 'repunctuate-sentences)
  923. (defun my/embark-target-string ()
  924. "Target the string at point."
  925. (if-let (((not (eobp))) ; prevent next line from causing errors
  926. (bounds (bounds-of-thing-at-point 'string)))
  927. (append (list 'string (buffer-substring-no-properties (car bounds)
  928. (cdr bounds)))
  929. bounds)))
  930. (add-to-list 'embark-around-action-hooks
  931. '(repunctuate-sentences embark--mark-target))
  932. (add-to-list 'embark-keymap-alist
  933. '(string my/embark-string-map))
  934. (add-to-list 'embark-target-finders 'my/embark-target-string)
  935. (add-to-list 'display-buffer-alist
  936. '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
  937. nil
  938. (window-parameters (mode-line-format . none))))
  939. (evil-define-key '(normal motion) org-mode-map
  940. (kbd "C-,") #'embark-act))
  941. ;; consult
  942. (use-package consult
  943. :bind (("C-s" . consult-line)
  944. ("C-x b" . consult-buffer)
  945. ("C-S-s" . consult-ripgrep)
  946. ("C-x C-S-f" . consult-fd)
  947. ("C-x c k" . consult-keep-lines)
  948. ("C-x c f" . consult-focus-lines)
  949. ("C-x c r" . consult-recent-file)
  950. ("C-x c b" . consult-bookmark)
  951. ("C-x c d" . consult-fd)
  952. ("C-c p" . consult-fd)
  953. ("C-x c g" . consult-ripgrep)
  954. ("C-c P" . consult-ripgrep)
  955. ("C-x c y" . consult-yank-from-kill-ring)
  956. ("M-g i" . consult-imenu)
  957. ("M-g I" . consult-imenu-multi)
  958. ("M-g r" . consult-imenu-multi)
  959. :map help-map
  960. ("TAB". consult-info)
  961. ("RET" . consult-man))
  962. :hook (minibuffer-setup . my/consult-setup-minibuffer-completion)
  963. :init
  964. (defun my/consult-setup-minibuffer-completion ()
  965. (setq-local completion-in-region-function #'consult-completion-in-region))
  966. (evil-declare-motion #'consult-line))
  967. (use-package consult-eglot
  968. :commands consult-eglot-symbols)
  969. ;; wgrep
  970. (use-package wgrep)
  971. ;; integration for embark and consult
  972. (use-package embark-consult
  973. :hook (embark-collect-mode . consult-preview-at-point-mode))
  974. ;; corfu (autocomplete)
  975. (use-package corfu
  976. :bind (("M-<tab>" . completion-at-point)
  977. :map corfu-map
  978. ("C-j" . corfu-next)
  979. ("C-k" . corfu-previous)
  980. ("M-SPC" . corfu-insert-separator)
  981. ("M-m" . my/corfu-move-to-minibuffer))
  982. :init
  983. (defun my/corfu-move-to-minibuffer ()
  984. (interactive)
  985. (when completion-in-region--data
  986. (let ((completion-extra-properties corfu--extra)
  987. (completion-cycle-threshold completion-cycling))
  988. (apply #'consult-completion-in-region completion-in-region--data))))
  989. (setq corfu-cycle t
  990. corfu-auto t
  991. corfu-on-exact-match nil
  992. corfu-popupinfo-delay '(1.0 . 0.5)
  993. completion-cycle-threshold nil
  994. global-corfu-minibuffer
  995. ;; only enable corfu in the minibuffer in graphical frames
  996. (lambda ()
  997. (and (display-graphic-p)
  998. (not (eq (current-local-map)
  999. read-passwd-map)))))
  1000. (global-corfu-mode 1)
  1001. (corfu-popupinfo-mode 1)
  1002. :config
  1003. (add-to-list 'corfu-continue-commands #'my/corfu-move-to-minibuffer)
  1004. (defun my/help-buffer-exists-p ()
  1005. "Return if the buffer that `help-buffer' would, or nil if it doesn't exist."
  1006. (or (and help-xref-following (derived-mode-p 'help-mode))
  1007. (get-buffer "*Help*")))
  1008. (defun my/-corfu-popupinfo-close-help-buffer (oldfun &rest args)
  1009. (if (derived-mode-p 'emacs-lisp-mode)
  1010. (let ((help-buf (my/help-buffer-exists-p)))
  1011. (prog1
  1012. (apply oldfun args)
  1013. (when-let (((not help-buf))
  1014. (buf (help-buffer)))
  1015. ;; Ensure that, even if `help-buffer' returns nil in the future, we
  1016. ;; don't kill the current buffer
  1017. (kill-buffer buf))))
  1018. (apply oldfun args)))
  1019. (advice-add 'corfu-popupinfo--get-documentation :around
  1020. 'my/-corfu-popupinfo-close-help-buffer))
  1021. (use-package corfu-terminal
  1022. :init
  1023. (corfu-terminal-mode 1)
  1024. :config
  1025. (require 'corfu-terminal-popupinfo)
  1026. (corfu-terminal-popupinfo-mode 1))
  1027. (use-package dabbrev
  1028. :ensure nil
  1029. :config
  1030. (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ")
  1031. (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode)
  1032. (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)
  1033. (add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode))
  1034. ;; cape (a bunch of capfs!)
  1035. (use-package cape
  1036. :bind (([remap dabbrev-expand] . cape-dabbrev)
  1037. ("C-c P" . cape-line)
  1038. ("C-c f" . cape-file))
  1039. :hook (text-mode . my/-cape-setup-text-mode)
  1040. :init
  1041. (defun my/-cape-setup-text-mode ()
  1042. ;; Only run this if we are not in `TeX-mode'
  1043. (unless (bound-and-true-p TeX-mode-p)
  1044. (setq-local completion-at-point-functions
  1045. (append completion-at-point-functions (list 'cape-dict
  1046. 'cape-dabbrev))
  1047. corfu-auto nil))))
  1048. ;; xref
  1049. (use-package xref
  1050. :init
  1051. (evil-define-key '(normal motion) 'global
  1052. "gr" #'xref-find-references)
  1053. (setq xref-show-xrefs-function #'consult-xref
  1054. xref-show-definitions-function #'consult-xref))
  1055. ;; popup.el
  1056. (use-package popup)
  1057. ;; posframe
  1058. (use-package posframe
  1059. :init
  1060. (defun my/posframe-tip (name msg)
  1061. "Like `popup-tip', but with a posframe.
  1062. NAME should be the buffer name to pass to `posframe-show'. MSG is the message to
  1063. display."
  1064. (unwind-protect
  1065. (progn
  1066. (posframe-show name
  1067. :string msg
  1068. :position (point)
  1069. :max-width 80
  1070. :border-width 2
  1071. :border-color "white")
  1072. (clear-this-command-keys)
  1073. (push (read-event) unread-command-events)
  1074. (posframe-hide name))
  1075. (posframe-hide name))))
  1076. (defun my/floating-tooltip (name msg)
  1077. "If `display-graphic-p', call `my/posframe-tip', otherwise `popup-tip'.
  1078. MSG is the message to show in the popup. NAME is the name of the buffer to pass
  1079. to `posframe-show' if the display is graphical."
  1080. (if (display-graphic-p)
  1081. (my/posframe-tip name msg)
  1082. (popup-tip msg)))
  1083. ;; flymake
  1084. (use-package flymake
  1085. :config
  1086. (require 'consult-flymake))
  1087. ;; flycheck
  1088. (use-package flycheck
  1089. :hook ((sh-mode emacs-lisp-mode) . trusted-files-flycheck-mode-if-safe)
  1090. :custom
  1091. (flycheck-indication-mode 'left-margin)
  1092. :init
  1093. (setq flycheck-display-errors-function nil))
  1094. (use-package consult-flycheck)
  1095. (defun my/sly-notes-at-point (&optional pos buffer)
  1096. "Return the sly notes at POS in BUFFER.
  1097. If BUFFER is nil, the current buffer is used."
  1098. (with-current-buffer (or buffer (current-buffer))
  1099. (unless pos
  1100. (setq pos (point)))
  1101. (cl-loop for overlay in (overlays-at pos)
  1102. for note = (overlay-get overlay 'sly-note)
  1103. when note
  1104. collect note)))
  1105. (defun my/diagnostic-at-point ()
  1106. "Show the diagnostics under point."
  1107. (interactive)
  1108. (let ((message))
  1109. (when-let (((bound-and-true-p flymake-mode))
  1110. (diag (get-char-property (point) 'flymake-diagnostic)))
  1111. (cl-callf nconc message (string-split (flymake--diag-text diag) "\n" t)))
  1112. (when (bound-and-true-p flycheck-mode)
  1113. (cl-callf nconc message
  1114. (mapcar 'flycheck-error-message (flycheck-overlay-errors-at (point)))))
  1115. ;; sly (lazy-loaded)
  1116. (when (featurep 'sly)
  1117. (cl-callf nconc message (mapcar (lambda (note)
  1118. (plist-get note :message))
  1119. (my/sly-notes-at-point))))
  1120. ;; jinx
  1121. (when-let (((bound-and-true-p jinx-mode))
  1122. (jinx-msg (jinx--get-overlays (point) (1+ (point)))))
  1123. (push "misspelled word" message))
  1124. (when message
  1125. (my/floating-tooltip " *my-diagnostic-posframe*"
  1126. (mapconcat (lambda (msg)
  1127. (concat "•" msg))
  1128. message "\n")))))
  1129. (defconst my/consult-flymake-flycheck-narrow
  1130. '((?e . "Error")
  1131. (?w . "Warning")
  1132. (?i . "Info")
  1133. (?n . "Info")))
  1134. (defun my/-consult-replace-flymake-error-level (candidates)
  1135. "Return CANDIDATES with the flymake error level note replaced with info."
  1136. (cl-loop for cand in candidates
  1137. collect
  1138. (cl-loop
  1139. with start = nil
  1140. for i below (length cand)
  1141. for props = (text-properties-at i cand)
  1142. for face = (plist-get props 'face)
  1143. when (eq face 'compilation-info) do
  1144. (setq start (or start i))
  1145. else when start do
  1146. (setf (substring cand start i)
  1147. (propertize (string-pad "info" (- i start))
  1148. 'face (flycheck-error-level-error-list-face
  1149. 'info)))
  1150. (cl-return cand)
  1151. finally return cand)))
  1152. (defun my/consult-flymake-flycheck-candidates (&optional project)
  1153. "Return combined candidate list for flymake and flycheck.
  1154. With PROJECT, return the candiadeets for that project."
  1155. (let ((had-errors))
  1156. (prog1
  1157. (seq-uniq
  1158. (append
  1159. (when-let (((bound-and-true-p flymake-mode))
  1160. (diags (if project (flymake--project-diagnostics
  1161. project)
  1162. (flymake-diagnostics))))
  1163. (setq had-errors t)
  1164. (my/-consult-replace-flymake-error-level
  1165. (consult-flymake--candidates diags)))
  1166. (when (boundp 'flycheck-mode)
  1167. (if project
  1168. (cl-loop for buf in (project-buffers project)
  1169. append
  1170. (with-current-buffer buf
  1171. (when (and flycheck-mode flycheck-current-errors)
  1172. (setq had-errors t)
  1173. (consult-flycheck--candidates))))
  1174. (when (and flycheck-mode flycheck-current-errors)
  1175. (setq had-errors t)
  1176. (consult-flycheck--candidates))))))
  1177. (unless had-errors
  1178. (user-error "No errors (Flymake: %s | Flycheck: %s)"
  1179. (cond
  1180. ((not (bound-and-true-p flymake-mode))
  1181. "not running")
  1182. ((seq-difference (flymake-running-backends)
  1183. (flymake-reporting-backends))
  1184. "running")
  1185. (t "finished"))
  1186. (if (boundp 'flycheck-last-status-change)
  1187. flycheck-last-status-change
  1188. "not running"))))))
  1189. (defun my/consult-flymake-flycheck (&optional project)
  1190. "Jump to flymake or flycheck error.
  1191. With PROJECT, give diagnostics for all buffers in the current project."
  1192. (interactive "P")
  1193. (consult--read
  1194. (consult--with-increased-gc
  1195. (my/consult-flymake-flycheck-candidates
  1196. (and project (project-current))))
  1197. :prompt "Error: "
  1198. :category 'flymake-flycheck-error
  1199. :history t
  1200. :require-match t
  1201. :sort nil
  1202. :narrow (consult--type-narrow my/consult-flymake-flycheck-narrow)
  1203. :group (consult--type-group my/consult-flymake-flycheck-narrow)
  1204. :lookup #'consult--lookup-candidate
  1205. :state (consult--jump-state)))
  1206. (with-eval-after-load 'flymake
  1207. (keymap-set flymake-mode-map "C-c e" 'my/diagnostic-at-point)
  1208. (keymap-set flymake-mode-map "C-c E" 'my/consult-flymake-flycheck))
  1209. (with-eval-after-load 'flycheck
  1210. (keymap-set flycheck-mode-map "C-c e" 'my/diagnostic-at-point)
  1211. (keymap-set flycheck-mode-map "C-c E" 'my/consult-flymake-flycheck))
  1212. (with-eval-after-load 'jinx
  1213. (keymap-set jinx-mode-map "C-c e" 'my/diagnostic-at-point))
  1214. ;; eldoc
  1215. (use-package eldoc
  1216. :diminish eldoc-mode
  1217. :init
  1218. (setq-default eldoc-echo-area-use-multiline-p nil))
  1219. ;; eglot
  1220. (use-package eglot
  1221. :demand t
  1222. :pin gnu ;; try to force Elpa version to fix warnings
  1223. :hook ((eglot-managed-mode . my/-eglot-setup))
  1224. :init
  1225. ;; (defun my/eglot-in-text-mode-only ()
  1226. ;; (when (eq major-mode 'text-mode)
  1227. ;; (trusted-files-eglot-ensure-if-safe)))
  1228. (defvar my/-eglot-documentation-buffer nil
  1229. "Buffer for showing documentation for `my/eglot-documentation-at-point'.")
  1230. (define-derived-mode my/eglot-documentation-mode special-mode "Eglot-Doc"
  1231. "Major mode for eglot documentation buffers."
  1232. :interactive nil
  1233. (face-remap-add-relative 'nobreak-space 'default))
  1234. (defun my/eglot-documentation-at-point ()
  1235. "Show documentation for a symbol at point."
  1236. (interactive)
  1237. (if-let (server (eglot-current-server))
  1238. (progn
  1239. (unless (buffer-live-p my/-eglot-documentation-buffer)
  1240. (setq my/-eglot-documentation-buffer
  1241. (get-buffer-create "*eglot documentation*")))
  1242. (eglot-hover-eldoc-function
  1243. (lambda (info _ _)
  1244. (if-let (((not (seq-empty-p info)))
  1245. (buff (current-buffer)))
  1246. (with-current-buffer my/-eglot-documentation-buffer
  1247. (let ((inhibit-read-only t))
  1248. (unless (derived-mode-p 'my/eglot-documentation-mode)
  1249. (my/eglot-documentation-mode))
  1250. (erase-buffer)
  1251. (insert info)
  1252. (goto-char (point-min)))
  1253. (when (not (get-buffer-window my/-eglot-documentation-buffer nil))
  1254. (switch-to-buffer-other-window my/-eglot-documentation-buffer t)
  1255. (switch-to-buffer-other-window buff t)))))))))
  1256. (defun my/-eglot-cleanup-doc-buffer (_server &optional _interactive _timeout
  1257. preserve-buffers)
  1258. (when (and (not preserve-buffers)
  1259. (buffer-live-p my/-eglot-documentation-buffer)
  1260. (cl-every (lambda (buffer)
  1261. (with-current-buffer buffer
  1262. (let ((server (eglot-current-server)))
  1263. (or (not (eglot-lsp-server-p server))
  1264. (eglot--shutdown-requested server)))))
  1265. (buffer-list)))
  1266. (kill-buffer my/-eglot-documentation-buffer)))
  1267. (advice-add 'eglot-shutdown :after 'my/-eglot-cleanup-doc-buffer)
  1268. (defun my/-eglot-setup ()
  1269. "Setup eldoc variables for `eglot-managed-mode-hook'."
  1270. (setq-local evil-lookup-func #'my/eglot-documentation-at-point)
  1271. (evil-define-key '(normal motion) 'local
  1272. "K" #'evil-lookup
  1273. "gR" #'eglot-rename
  1274. "gA" #'eglot-code-actions
  1275. "gs" #'consult-eglot-symbols)
  1276. (eglot-inlay-hints-mode -1))
  1277. (setq eglot-autoshutdown t
  1278. eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider))
  1279. :config
  1280. (add-to-list 'eglot-server-programs
  1281. (cons '(c-mode c-ts-mode c++-mode c++-ts-mode objc-mode)
  1282. '("clangd" "--all-scopes-completion" "--background-index"
  1283. "--clang-tidy" "--completion-style=detailed"
  1284. "--header-insertion=never" "--pch-storage=memory"
  1285. "--function-arg-placeholders"
  1286. "--compile-commands-dir=build"))))
  1287. (use-package eglot-inactive-regions
  1288. :custom
  1289. (eglot-inactive-regions-style 'darken-foreground)
  1290. (eglot-inactive-regions-opacity 0.4)
  1291. :config
  1292. (eglot-inactive-regions-mode 1))
  1293. ;; LTeX (languagetool)
  1294. (require 'ltex-eglot)
  1295. ;; apheleia (code formatter)
  1296. (use-package apheleia
  1297. :defer nil
  1298. :bind ("C-c o" . apheleia-format-buffer)
  1299. :init
  1300. (add-to-list 'auto-mode-alist `(,(rx "/.clang-format" eos) . yaml-ts-mode))
  1301. (defun my/apheleia-disable-in-current-buffer ()
  1302. (setq-local apheleia-inhibit t))
  1303. :config
  1304. (with-eval-after-load 'apheleia
  1305. (setf (alist-get 'java-mode apheleia-mode-alist) 'clang-format
  1306. (alist-get 'java-ts-mode apheleia-mode-alist) 'clang-format))
  1307. (apheleia-global-mode +1))
  1308. ;; awk
  1309. (with-eval-after-load 'cc-mode
  1310. (add-hook 'awk-mode-hook #'my/apheleia-disable-in-current-buffer))
  1311. ;; gud
  1312. (use-package gud
  1313. :demand t
  1314. :ensure nil
  1315. :after (project evil)
  1316. :bind (:map project-prefix-map
  1317. ("U" . my/project-gdb))
  1318. :config
  1319. (setq gdb-debuginfod-enable-setting t)
  1320. (defvar my/project-gdb-command nil
  1321. "Command to use in `my/project-gdb'.")
  1322. (put 'my/project-gdb-command 'safe-local-variable (lambda (val)
  1323. (stringp val)))
  1324. (defun my/project-gdb (project command-line)
  1325. "Run gdb in the project root"
  1326. (interactive (let* ((project (project-current t))
  1327. (default-directory (project-root project)))
  1328. (list project (gud-query-cmdline 'gdb))))
  1329. (let ((default-directory (project-root project)))
  1330. (gdb command-line)))
  1331. (evil-set-initial-state 'gdb-locals-mode 'motion)
  1332. (evil-collection-inhibit-insert-state 'gdb-locals-mode-map)
  1333. (evil-define-key '(normal motion visual) gdb-locals-mode-map
  1334. (kbd "TAB") (keymap-lookup gdb-locals-mode-map "TAB")
  1335. (kbd "RET") #'gdb-edit-locals-value
  1336. (kbd "<mouse-1>") #'gdb-edit-locals-value
  1337. "q" #'kill-current-buffer)
  1338. (evil-set-initial-state 'gdb-registers-mode 'motion)
  1339. (evil-collection-inhibit-insert-state 'gdb-registers-mode-map)
  1340. (evil-define-key '(normal motion visual) gdb-registers-mode-map
  1341. (kbd "TAB") (keymap-lookup gdb-registers-mode-map "TAB")
  1342. (kbd "RET") #'gdb-edit-register-value
  1343. (kbd "<mouse-1>") #'gdb-edit-register-value
  1344. "q" #'kill-current-buffer
  1345. (kbd "C-c f") #'gdb-registers-toggle-filter
  1346. (kbd "C-c F") (lambda ()
  1347. "Customize the filter for the registers buffer."
  1348. (interactive)
  1349. (customize-option-other-window
  1350. 'gdb-registers-filter-pattern-list)))
  1351. (evil-set-initial-state 'gdb-frames-mode 'motion)
  1352. (evil-collection-inhibit-insert-state 'gdb-frames-mode-map)
  1353. (evil-define-key '(normal motion visual) gdb-frames-mode-map
  1354. "q" #'kill-current-buffer
  1355. (kbd "RET") #'gdb-select-frame)
  1356. (evil-set-initial-state 'gdb-breakpoints-mode 'motion)
  1357. (evil-collection-inhibit-insert-state 'gdb-breakpoints-mode-map)
  1358. (evil-define-key '(normal motion visual) gdb-breakpoints-mode-map
  1359. (kbd "TAB") (keymap-lookup gdb-breakpoints-mode-map "TAB")
  1360. "q" #'gdb-delete-frame-or-window
  1361. "D" #'gdb-delete-breakpoint
  1362. (kbd "RET") #'gdb-goto-breakpoint
  1363. (kbd "<mouse-1>") #'gdb-goto-breakpoint
  1364. (kbd "SPC") #'gdb-toggle-breakpoint)
  1365. (evil-set-initial-state 'gdb-threads-mode 'motion)
  1366. (evil-collection-inhibit-insert-state 'gdb-threads-mode-map)
  1367. (evil-define-key '(normal motion visual) gdb-threads-mode-map
  1368. (kbd "TAB") (keymap-lookup gdb-threads-mode-map "TAB")
  1369. "q" #'gdb-delete-frame-or-window
  1370. "D" #'gdb-frame-disassembly-for-thread
  1371. (kbd "C-c f") #'gdb-display-stack-for-thread
  1372. (kbd "C-c i") #'gdb-interrupt-thread
  1373. (kbd "C-c l") #'gdb-display-locals-for-thread
  1374. (kbd "C-c r") #'gdb-display-registers-for-thread
  1375. (kbd "C-c c") #'gdb-continue-thread
  1376. (kbd "C-c d") #'gdb-display-disassembly-for-thread
  1377. (kbd "C-c s") #'gdb-step-thread
  1378. (kbd "C-c F") #'gdb-frame-stack-for-thread
  1379. (kbd "C-c L") #'gdb-frame-locals-for-thread
  1380. (kbd "C-c R") #'gdb-frame-registers-for-thread
  1381. (kbd "RET") #'gdb-select-thread
  1382. (kbd "<mouse-2>") #'gdb-select-thread))
  1383. ;; dape
  1384. (use-package dape
  1385. :hook ((after-init . dape-breakpoint-load)
  1386. (kill-emacs . dape-breakpoint-save)
  1387. (dape-start . save-some-buffers)
  1388. (dape-display-source . pulse-momentary-highlight-one-line))
  1389. :bind (:map dape-info-parent-mode-map
  1390. ("<tab>" . dape--info-buffer-tab))
  1391. :init
  1392. (setopt dape-default-breakpoints-file (no-littering-expand-var-file-name
  1393. "dape-breakpoints"))
  1394. :config
  1395. (setopt dape-buffer-window-arrangement 'right)
  1396. (dape-breakpoint-global-mode 1))
  1397. ;; dumb-jump
  1398. (use-package dumb-jump
  1399. :init
  1400. (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
  1401. ;; yasnippet
  1402. (use-package yasnippet
  1403. :demand t
  1404. :bind ("C-c s" . yas-expand)
  1405. :config
  1406. (yas-global-mode 1))
  1407. ;; project.el
  1408. (use-package project
  1409. :bind (([remap project-compile] . my/project-compile-or-default)
  1410. :map project-prefix-map
  1411. ("s" . my/project-eshell)
  1412. ("u" . my/project-run))
  1413. :init
  1414. (defvar eshell-buffer-name)
  1415. (defun my/project-eshell (prompt &optional arg)
  1416. "Switch to or create an eshell buffer in the current projects root."
  1417. (interactive (list t current-prefix-arg))
  1418. (if-let ((proj (project-current prompt))
  1419. (default-directory (project-root proj))
  1420. (eshell-buffer-name
  1421. (concat "*eshell for project " default-directory "*")))
  1422. (eshell arg)))
  1423. (defun my/project-eshell-or-default (&optional arg)
  1424. "Open an eshell for the current project, otherwise, open a normal eshell."
  1425. (interactive "P")
  1426. (unless (my/project-eshell nil arg)
  1427. (eshell arg)))
  1428. (defun my/project-compile-or-default ()
  1429. "If in a project, run `project-compile', otherwise run `compile'."
  1430. (interactive)
  1431. (if (project-current)
  1432. (call-interactively 'project-compile)
  1433. (call-interactively 'compile)))
  1434. (defvar my/project-run-command nil
  1435. "Command to run with `my/project-run'.")
  1436. (put 'my/project-run-command 'safe-local-variable (lambda (val)
  1437. (stringp val)))
  1438. (defvar my/project-run-dir nil
  1439. "Directory to run project in with `my/project-run'.")
  1440. (put 'my/project-run-dir 'safe-local-variable (lambda (val)
  1441. (stringp val)))
  1442. (defvar my/-project-run-history '()
  1443. "Commands previously run with `my/project-run'")
  1444. (defvar my/project-root-marker ".project-root"
  1445. "Marker file to look for in non-vc backed projects.")
  1446. (defun my/project-get-root-dir ()
  1447. "Get the root dir for the current project"
  1448. (let* ((proj (project-current nil))
  1449. (default-directory (if proj
  1450. (project-root proj)
  1451. default-directory)))
  1452. (if my/project-run-dir
  1453. (expand-file-name my/project-run-dir)
  1454. default-directory)))
  1455. (defvar my/compile-use-comint t
  1456. "Weather or not to use comint by default for compile buffers.")
  1457. (defun my/-compile-use-comint-by-default (args)
  1458. (if my/compile-use-comint
  1459. (list (car args) t)
  1460. args))
  1461. (advice-add 'compile :filter-args 'my/-compile-use-comint-by-default)
  1462. (defun my/project-run (command comint)
  1463. "Like `project-compile', but for running a project.
  1464. COMMAND and COMINT are like `compile'."
  1465. (interactive
  1466. (list
  1467. (let ((default-directory (my/project-get-root-dir)))
  1468. (read-shell-command "Run Command: "
  1469. (or (car my/-project-run-history)
  1470. my/project-run-command)
  1471. (if (and my/project-run-command
  1472. (equal my/project-run-command
  1473. (car-safe my/-project-run-history)))
  1474. '(my/-project-run-history . 1)
  1475. 'my/-project-run-history)))
  1476. (consp current-prefix-arg)))
  1477. (let* ((default-directory (my/project-get-root-dir))
  1478. (compilation-buffer-name-function (lambda (_)
  1479. (progn "*run project*")))
  1480. (compilation-directory default-directory)
  1481. (compile-history nil)
  1482. (compile-command nil))
  1483. (compile command comint)
  1484. (when (not my/project-run-command)
  1485. (setq my/project-run-command command))))
  1486. :config
  1487. (defun my/project-try-dotfile (dir)
  1488. (if-let (root (locate-dominating-file dir my/project-root-marker))
  1489. (list 'vc nil root)))
  1490. (add-hook 'project-find-functions #'my/project-try-dotfile))
  1491. ;; comint
  1492. (use-package comint
  1493. :ensure nil
  1494. :after evil
  1495. :config
  1496. (evil-set-initial-state 'comint-mode 'normal))
  1497. ;; nxml
  1498. (use-package nxml-mode
  1499. :ensure nil
  1500. :hook (nxml-mode . my/-nxml-setup)
  1501. :init
  1502. (defun my/-nxml-setup ()
  1503. "Setup `nxml-mode'."
  1504. (sgml-electric-tag-pair-mode 1)
  1505. (setq-local completion-at-point-functions
  1506. '(rng-completion-at-point cape-file)))
  1507. (add-to-list 'auto-mode-alist
  1508. `(,(concat
  1509. (regexp-opt '("gschema" "gresource" "ui")) "\\'")
  1510. . nxml-mode)))
  1511. ;; devdocs
  1512. (use-package devdocs
  1513. :bind (("C-h D" . devdocs-lookup)))
  1514. ;; Bibtex (built in)
  1515. (require 'bibtex)
  1516. ;; Better URL highlighting and matching
  1517. (dolist (field '("howpublished" "url"))
  1518. (add-to-list 'bibtex-generate-url-list
  1519. `((,field . ,(rx "http" (? "s") "://" (+ (not "}"))))
  1520. "%s"
  1521. (,field
  1522. ,(rx (? (or "\\url{" "{")) (group "http" (? "s") "://"
  1523. (+ (not "}")))
  1524. (? "}"))
  1525. ,(lambda (field)
  1526. (match-string 1 field))))))
  1527. (defun my/bibtex-in-entry-p (&optional exclude-braces)
  1528. "Return t is point is inside a BibTeX entry.
  1529. When EXCLUDE-BRACES is non-nil, don't count the first and last brace of the
  1530. entry as in the entry. That is, if the point is on the first { or last } of the
  1531. entry, return nil."
  1532. (cl-destructuring-bind (depth &rest r) (syntax-ppss)
  1533. (if (zerop depth)
  1534. (and (not exclude-braces) (eql (char-after) ?\{))
  1535. (or (not exclude-braces) (not (eql (char-after) ?\}))))))
  1536. (defvar my/bibtex-indent-width 4
  1537. "Width to indent for `my/bibtex-calculate-indentation'.")
  1538. (defun my/bibtex-calculate-indentation ()
  1539. "Calculate the column to indent to on the current line."
  1540. (save-excursion
  1541. (back-to-indentation)
  1542. (if (my/bibtex-in-entry-p t)
  1543. my/bibtex-indent-width
  1544. 0)))
  1545. (defun my/bibtex-empty-line-p ()
  1546. "Return t if the current line is only blank characters."
  1547. (save-excursion
  1548. (beginning-of-line)
  1549. (looking-at (rx (* blank) eol))))
  1550. (defun my/bibtex-indent-line ()
  1551. "Indent the current line."
  1552. (interactive)
  1553. (save-excursion
  1554. (beginning-of-line)
  1555. (when (looking-at (rx (+ blank)))
  1556. (delete-region (point) (match-end 0)))
  1557. (indent-to (my/bibtex-calculate-indentation)))
  1558. (when (looking-at (rx (+ blank) eol))
  1559. (end-of-line)))
  1560. (defun my/bibtex-indent-or-find-text ()
  1561. "Either indent the current line or jump to the current fields text.
  1562. If the current line is only whitespace call `my/bibtex-calculate-indentation',
  1563. otherwise, call `bibtex-find-text'."
  1564. (interactive)
  1565. (if (my/bibtex-empty-line-p)
  1566. (my/bibtex-indent-line)
  1567. (bibtex-find-text)))
  1568. (defun my/bibtex-indent-or-find-text-and-insert ()
  1569. "Like `my/bibtex-indent-or-find-text', but enter insert mode after."
  1570. (interactive)
  1571. (my/bibtex-indent-or-find-text)
  1572. (if (my/bibtex-empty-line-p)
  1573. (evil-append 1)
  1574. (evil-insert 1)))
  1575. (defun my/-bibtex-setup-indent ()
  1576. "Set up `bibtex-mode' indentation stuff."
  1577. (setq-local indent-line-function 'my/bibtex-indent-line
  1578. electric-indent-chars '(?\n ?\{ ?\} ?,)))
  1579. (defun my/-bibtex-fix-fill-prefix ()
  1580. "`bivtex-mode' has a bad habbit of messing up `fill-prefix'."
  1581. (when (eq major-mode 'bibtex-mode)
  1582. (setq-local fill-prefix nil)))
  1583. (advice-add 'bibtex-mode :after 'my/-bibtex-fix-fill-prefix)
  1584. (add-hook 'bibtex-mode-hook 'my/-bibtex-setup-indent)
  1585. (keymap-set bibtex-mode-map "RET" 'newline-and-indent)
  1586. (keymap-set bibtex-mode-map "TAB" 'my/bibtex-indent-or-find-text)
  1587. (evil-define-key 'normal bibtex-mode-map
  1588. (kbd "TAB") 'my/bibtex-indent-or-find-text-and-insert)
  1589. ;; Latex help (from elisp file)
  1590. (require 'latex-help)
  1591. ;; AUCTeX
  1592. (use-package auctex
  1593. :hook ((LaTeX-mode . turn-on-reftex)
  1594. (LaTeX-mode . LaTeX-math-mode)
  1595. (LaTeX-mode . my/-setup-LaTeX-mode)
  1596. (LaTeX-mode . trusted-files-flycheck-mode-if-safe))
  1597. :bind (:map TeX-mode-map
  1598. ("C-c ?" . latex-help))
  1599. :init
  1600. (add-to-list 'major-mode-remap-alist '(plain-tex-mode . plain-TeX-mode))
  1601. (add-to-list 'major-mode-remap-alist '(latex-mode . LaTeX-mode))
  1602. (add-to-list 'major-mode-remap-alist '(ams-tex-mode . AmSTeX-mode))
  1603. (add-to-list 'major-mode-remap-alist '(context-mode . ConTeXt-mode))
  1604. (add-to-list 'major-mode-remap-alist '(texinfo-mode . Texinfo-mode))
  1605. (add-to-list 'major-mode-remap-alist '(doctex-mode . docTeX-mode))
  1606. (add-to-list 'auto-mode-alist '("/\\.latexmkrc\\'" . perl-mode))
  1607. (add-to-list 'auto-mode-alist '("\\.[tT]e[xX]\\'" . LaTeX-mode))
  1608. :config
  1609. (defun my/-auctex-texdoc-setup-env (oldfun &rest args)
  1610. (let ((process-environment process-environment)
  1611. (emacs-cmd (concat "emacsclient" (and (not (display-graphic-p)) " -nw"))))
  1612. (setenv "PDFVIEWER_texdoc" "evince")
  1613. (setenv "MDVIEWER_texdoc" emacs-cmd)
  1614. (setenv "PAGER_texdoc" emacs-cmd)
  1615. (apply oldfun args)))
  1616. (advice-add 'TeX-documentation-texdoc :around 'my/-auctex-texdoc-setup-env)
  1617. (defun my/-setup-LaTeX-mode ()
  1618. (setq evil-lookup-func 'latex-help-at-point))
  1619. (setq TeX-auto-save t
  1620. TeX-parse-self t
  1621. reftex-plug-into-AUCTeX t)
  1622. (evil-define-operator my/evil-LaTeX-fill (beg end)
  1623. "Like `evil-fill', but using auctex."
  1624. ;; The code here came straight from `evil-fill'
  1625. :move-point nil
  1626. :type line
  1627. (save-excursion
  1628. (ignore-errors (LaTeX-fill-region beg end))))
  1629. (evil-define-operator my/evil-LaTeX-fill-and-move (beg end)
  1630. "Like `evil-fill-and-move', but using auctex."
  1631. ;; The code here came straight from `evil-fill-and-move'
  1632. :move-point nil
  1633. :type line
  1634. (let ((marker (make-marker)))
  1635. (move-marker marker (1- end))
  1636. (ignore-errors
  1637. (LaTeX-fill-region beg end)
  1638. (goto-char marker)
  1639. (evil-first-non-blank))))
  1640. (evil-define-key 'normal TeX-mode-map
  1641. "gq" 'my/evil-LaTeX-fill-and-move
  1642. "gw" 'my/evil-LaTeX-fill)
  1643. (setq-default TeX-master nil)
  1644. (require 'tex)
  1645. (TeX-global-PDF-mode 1))
  1646. ;; blueprint
  1647. (use-package blueprint-ts-mode
  1648. :hook (blueprint-ts-mode . trusted-files-eglot-ensure-if-safe)
  1649. :after eglot)
  1650. ;; python-ts-mode
  1651. (use-package python-ts-mode
  1652. :ensure nil
  1653. :hook (python-ts-mode . trusted-files-eglot-ensure-if-safe))
  1654. ;; python virtual environments
  1655. (use-package pyvenv)
  1656. (use-package pyenv-mode)
  1657. ;; My dev environment for ROS2
  1658. (require 'arch-ros2)
  1659. (require 'inferior-jshell)
  1660. ;; java-ts-mode
  1661. (use-package java-ts-mode
  1662. :hook ((java-ts-mode . trusted-files-eglot-ensure-if-safe)
  1663. (java-ts-mode . my/-setup-java-ts-mode))
  1664. :bind (:map java-ts-mode-map
  1665. ("C-x C-e" . jshell-eval-expression)
  1666. ("C-M-x" . jshell-eval-defun)
  1667. ("C-c C-r" . jshell-eval-region)
  1668. ("C-c C-b" . jshell-eval-buffer))
  1669. :config
  1670. (defun my/-setup-java-ts-mode ()
  1671. (let ((rules (car treesit-simple-indent-rules)))
  1672. (setcdr rules
  1673. (cons '((and (parent-is "array_initializer")
  1674. (node-is "array_initializer"))
  1675. parent-bol java-ts-mode-indent-offset)
  1676. (nthcdr 1 rules))))))
  1677. ;; c-ts-mode
  1678. (use-package c-ts-mode
  1679. :after evil
  1680. :hook ((c-ts-mode c++-ts-mode) . trusted-files-eglot-ensure-if-safe)
  1681. :init
  1682. (setq-default c-ts-mode-indent-offset 4)
  1683. :config
  1684. (evil-define-key 'normal 'c-ts-mode-map
  1685. "go" #'ff-find-other-file
  1686. "gO" #'ff-find-other-file-other-window)
  1687. (evil-define-key 'normal 'c++-ts-mode-map
  1688. "go" #'ff-find-other-file
  1689. "gO" #'ff-find-other-file-other-window)
  1690. (evil-define-key 'normal 'objc-mode-map
  1691. "go" #'ff-find-other-file
  1692. "gO" #'ff-find-other-file-other-window))
  1693. ;; GLSL
  1694. (use-package glsl-mode)
  1695. ;; php-mode
  1696. (use-package php-mode
  1697. :hook (php-mode . trusted-files-eglot-ensure-if-safe))
  1698. ;; web-mode
  1699. (use-package web-mode
  1700. :hook (web-mode . trusted-files-eglot-ensure-if-safe)
  1701. :init
  1702. (add-to-list 'eglot-server-programs
  1703. '(web-mode . ("vscode-html-language-server" "--stdio"))))
  1704. ;; JavaScript
  1705. (use-package js
  1706. :ensure nil
  1707. :hook (js-ts-mode . trusted-files-eglot-ensure-if-safe))
  1708. (use-package js-comint
  1709. :bind (:map js-ts-mode-map
  1710. ("C-x C-e" . js-send-last-sexp)
  1711. ("C-c C-b" . js-send-buffer)
  1712. ("C-c C-r" . js-send-region)
  1713. ("C-M-x" . my/js-send-defun))
  1714. :hook (js-comint-mode . my/-setup-js-comint-mode)
  1715. :config
  1716. (defun my/-setup-js-comint-mode ()
  1717. (setq-local comint-highlight-input nil))
  1718. (defun my/js-send-defun ()
  1719. "Send the defun under point to the inferior JavaScript process."
  1720. (interactive)
  1721. (if-let ((code (thing-at-point 'defun)))
  1722. (js-comint-send-string code)
  1723. (user-error "No defun under point"))))
  1724. ;; TypeScript
  1725. (use-package typescript-ts-mode
  1726. :ensure nil
  1727. :hook (typescript-ts-mode . trusted-files-eglot-ensure-if-safe)
  1728. :init
  1729. (add-to-list 'auto-mode-alist `(,(rx ".ts" eos) . typescript-ts-mode)))
  1730. ;; Polymode
  1731. (use-package polymode
  1732. :config
  1733. (define-hostmode my/poly-web-hostmode
  1734. :mode 'web-mode)
  1735. (define-innermode my/poly-php-innermode
  1736. :mode 'php-mode
  1737. :head-matcher (regexp-quote "<?php")
  1738. :tail-matcher (regexp-quote "?>")
  1739. :head-mode 'body
  1740. :tail-mode 'body)
  1741. (define-polymode my/poly-web-mode
  1742. :hostmode 'my/poly-web-hostmode
  1743. :innermodes '(my/poly-php-innermode))
  1744. (add-to-list 'auto-mode-alist '("\\.php\\|\\.phtml\\'" . my/poly-web-mode)))
  1745. ;; shell-mode
  1746. (use-package sh-script
  1747. :ensure nil
  1748. :hook (sh-mode . my/-setup-sh-mode)
  1749. :init
  1750. (defun my/-setup-sh-mode ()
  1751. (add-hook 'completion-at-point-functions #'cape-file nil t)))
  1752. ;; go mode
  1753. (use-package go-mode
  1754. :defer nil
  1755. :hook (go-mode . trusted-files-eglot-ensure-if-safe))
  1756. (use-package go-ts-mode
  1757. :ensure nil
  1758. :hook (go-ts-mode . trusted-files-eglot-ensure-if-safe))
  1759. ;; rust
  1760. (use-package rust-mode)
  1761. (use-package rust-ts-mode
  1762. :ensure nil
  1763. :hook (rust-ts-mode . trusted-files-eglot-ensure-if-safe))
  1764. ;; zig
  1765. (use-package zig-mode
  1766. :hook (zig-mode . trusted-files-eglot-ensure-if-safe))
  1767. ;; lua
  1768. (use-package lua-mode
  1769. :hook (lua-mode . trusted-files-eglot-ensure-if-safe))
  1770. ;; markdown
  1771. (use-package markdown-mode
  1772. :hook (markdown-mode . auto-fill-mode))
  1773. ;; groovy
  1774. (use-package groovy-mode)
  1775. ;; cmake
  1776. (require 'cmake-mode)
  1777. (require 'cmake-ts-mode)
  1778. (with-eval-after-load 'cmake-mode
  1779. (setq cmake-ts-mode-indent-offset tab-width))
  1780. ;; kdl
  1781. (require 'kdl-ts-mode)
  1782. (with-eval-after-load 'kdl-ts-mode
  1783. (setq kdl-ts-mode-indent-offset 4))
  1784. ;; json
  1785. (use-package json-ts-mode
  1786. :hook (json-ts-mode . trusted-files-eglot-ensure-if-safe)
  1787. :custom
  1788. (json-ts-mode-indent-offset 4)
  1789. :init
  1790. (add-to-list 'auto-mode-alist '("\\.jsonc\\'" . json-ts-mode)))
  1791. (use-package json-mode)
  1792. ;; csv
  1793. (use-package csv-mode)
  1794. ;; firejail
  1795. (require 'firejail-mode)
  1796. ;; yaml
  1797. (use-package yaml-ts-mode
  1798. :hook ((yaml-ts-mode . trusted-files-eglot-ensure-if-safe)
  1799. (yaml-ts-mode . my/-setup-yaml-ts-mode))
  1800. :init
  1801. (defun my/-setup-yaml-ts-mode ()
  1802. (setq indent-line-function #'yaml-indent-line)))
  1803. (use-package yaml-mode)
  1804. ;; yuck (config language for eww)
  1805. (use-package yuck-mode)
  1806. ;; Some Elisp indentation stuff
  1807. ;; Source: https://github.com/magit/emacsql
  1808. ;; emacsql.el line 394
  1809. (defun my/lisp-inside-plist-p ()
  1810. "Return t if point is inside a plist."
  1811. (save-excursion
  1812. (let ((start (point)))
  1813. (beginning-of-defun)
  1814. (when-let ((sexp (nth 1 (parse-partial-sexp (point) start))))
  1815. (goto-char sexp)
  1816. (looking-at (rx "(" (* (syntax whitespace)) ":"))))))
  1817. (defun my/-calculate-indent-fix-plists (oldfun &rest args)
  1818. "This function is meant to advise `calculate-lisp-indent'.
  1819. It calls OLDFUN with ARGS in such an environment as to prevent the default
  1820. indentation of plists."
  1821. (if (and (eq major-mode 'emacs-lisp-mode)
  1822. (save-excursion
  1823. (beginning-of-line)
  1824. (my/lisp-inside-plist-p)))
  1825. (let ((lisp-indent-offset 1))
  1826. (apply oldfun args))
  1827. (apply oldfun args)))
  1828. (advice-add 'calculate-lisp-indent :around
  1829. 'my/-calculate-indent-fix-plists)
  1830. (defvar my/max-lisp-noindent-comment-search-lines 30
  1831. "Max lines to search for the noindent comment.")
  1832. (defun my/-calculate-lisp-indent-noindent-comment (oldfun &rest args)
  1833. "This function is meant to advise `calculate-lisp-indent'.
  1834. It calls OLDFUN with ARGS, unless the line ends with the comment
  1835. ; noindent [LINES]
  1836. In this case, it just returns the current amount of indentation. LINES is the
  1837. number of lines that this comment affects. This is limited by
  1838. `my/max-lisp-noindent-comment-search-lines'.
  1839. This only works if its on the first or second form in a block. I think this is
  1840. because the indentation code only checks those and then assumes the same
  1841. indentation for every following line in the same block. This is probably OK as
  1842. I can't imagine too many instances where you need to randomly change the indent
  1843. midway through a block, and in those cases you can just stick this on the first
  1844. line in the block and manually deal with indentation."
  1845. (if (and (save-excursion
  1846. (end-of-line)
  1847. (re-search-backward
  1848. (rx (+ ";") (syntax whitespace) "noindent"
  1849. (? (syntax whitespace) (group (+ num)))
  1850. line-end)
  1851. (pos-bol (- my/max-lisp-noindent-comment-search-lines))
  1852. t))
  1853. (save-excursion
  1854. ;; if we are on a blank line, move forward a line
  1855. (when (= (pos-bol) (pos-eol))
  1856. (beginning-of-line 2))
  1857. (<= (count-lines (match-beginning 0) (pos-eol))
  1858. (if-let ((match (match-string 1)))
  1859. (string-to-number match)
  1860. 1))))
  1861. (save-excursion
  1862. (beginning-of-line)
  1863. (looking-at (rx (* blank)))
  1864. (length (match-string 0)))
  1865. (apply oldfun args)))
  1866. (advice-add 'calculate-lisp-indent :around
  1867. 'my/-calculate-lisp-indent-noindent-comment)
  1868. ;; sly
  1869. (use-package sly
  1870. ;; :hook (lisp-mode . my/-lisp-mode-autoconnect-sly)
  1871. :bind (:map sly-mode-map
  1872. ("C-c e" . my/diagnostic-at-point))
  1873. :autoload sly-connected-p
  1874. :init
  1875. (defun my/-lisp-mode-autoconnect-sly ()
  1876. (unless (sly-connected-p)
  1877. (trusted-files-sly-if-safe)))
  1878. (setq inferior-lisp-program "/usr/bin/sbcl")
  1879. (defun my/-sly-fix-special-buffers ()
  1880. (when (string-match-p (rx bos "*" (* any) "*" eos) (buffer-name))
  1881. (setq-local show-trailing-whitespace nil)))
  1882. (add-hook 'lisp-mode-hook 'my/-sly-fix-special-buffers)
  1883. :config
  1884. (evil-define-key 'insert sly-mrepl-mode-map
  1885. (kbd ",") 'self-insert-command)
  1886. (evil-define-key nil sly-mrepl-mode-map
  1887. (kbd "C-c ,") 'sly-mrepl-shortcut)
  1888. (sly-symbol-completion-mode -1)
  1889. (setq common-lisp-hyperspec-root
  1890. (concat "file://" (expand-file-name "~/src/clhs/HyperSpec/")))
  1891. (defun my/-hyperspec-loopup-in-eww (oldfun &rest r)
  1892. (let ((browse-url-browser-function #'eww-browse-url))
  1893. (apply oldfun r)))
  1894. (advice-add 'common-lisp-hyperspec :around #'my/-hyperspec-loopup-in-eww)
  1895. (defvar-local my/-sly-fontification-buffer nil
  1896. "The fontification buffer for the current sly buffer.")
  1897. (defun my/-sly-get-fontification-buffer ()
  1898. "Return the sly fontification buffer."
  1899. (if (buffer-live-p my/-sly-fontification-buffer)
  1900. my/-sly-fontification-buffer
  1901. (let ((buffer (generate-new-buffer
  1902. (format " %s-fontification-buffer" (buffer-name)))))
  1903. (with-current-buffer buffer
  1904. (unless (derived-mode-p 'c++-mode)
  1905. (let ((delayed-mode-hooks nil))
  1906. (delay-mode-hooks
  1907. (lisp-mode)
  1908. (rainbow-delimiters-mode 1))))
  1909. (let ((inhibit-message t))
  1910. (indent-tabs-mode -1))
  1911. (unless font-lock-mode
  1912. (font-lock-mode 1)))
  1913. (setq-local my/-sly-fontification-buffer buffer))))
  1914. (defmacro my/-sly-with-font-lock-buffer (&rest body)
  1915. "Execute BODY in the sly indirect buffer.
  1916. Note that this erases the buffer before doing anything."
  1917. `(with-current-buffer (my/-sly-get-fontification-buffer)
  1918. (erase-buffer)
  1919. ,@body))
  1920. (defun my/-sly-fontify-current-input ()
  1921. "Function called from `post-command-hook' to fontify the current input."
  1922. (when-let ((proc (get-buffer-process (current-buffer)))
  1923. (start (process-mark proc))
  1924. (end (point-max))
  1925. (input (buffer-substring-no-properties start end))
  1926. (fontified (my/-sly-with-font-lock-buffer
  1927. (insert input)
  1928. (font-lock-ensure)
  1929. (buffer-string)))
  1930. (len (length fontified))
  1931. (i 0))
  1932. ;; mostly from:
  1933. ;; `python-shell-font-lock-post-command-hook'
  1934. (while (not (= i len))
  1935. (let* ((props (text-properties-at i fontified))
  1936. (change-i (or (next-property-change i fontified)
  1937. len)))
  1938. (when-let ((face (plist-get props 'face)))
  1939. (setf (plist-get props 'face) nil
  1940. (plist-get props 'font-lock-face) face))
  1941. (set-text-properties (+ start i) (+ start change-i) props)
  1942. (setq i change-i)))))
  1943. (defun my/-sly-cleanup-fontification-buffer ()
  1944. (when (buffer-live-p my/-sly-fontification-buffer)
  1945. (kill-buffer my/-sly-fontification-buffer)))
  1946. (defun my/-sly-mrepl-enable-fontification ()
  1947. (setq-local comint-highlight-input nil)
  1948. (add-hook 'post-command-hook #'my/-sly-fontify-current-input
  1949. nil t)
  1950. (add-hook 'kill-buffer-hook #'my/-sly-cleanup-fontification-buffer
  1951. nil t))
  1952. (add-hook 'sly-mrepl-mode-hook #'my/-sly-mrepl-enable-fontification))
  1953. ;; jupyter
  1954. (use-package jupyter
  1955. :hook (jupyter-repl-mode . my/-setup-jupyter-mode)
  1956. :init
  1957. (defun my/-jupyter-dont-use-ts-modes (retval)
  1958. "Prevent `jupyter-kernel-language-mode-properties' from selecting TS modes."
  1959. (cl-destructuring-bind (mode syntax-table) retval
  1960. (if-let (((string-suffix-p "-ts-mode" (symbol-name mode)))
  1961. (non-ts (car (rassq mode major-mode-remap-alist)))
  1962. ((not (string-suffix-p "-ts-mode" (symbol-name non-ts)))))
  1963. (list non-ts
  1964. (if-let ((table-sym (intern-soft (format "%s-syntax-table"
  1965. non-ts))))
  1966. (symbol-value table-sym)
  1967. syntax-table))
  1968. retval)))
  1969. (advice-add 'jupyter-kernel-language-mode-properties
  1970. :filter-return #'my/-jupyter-dont-use-ts-modes)
  1971. :config
  1972. (face-spec-set 'jupyter-repl-traceback
  1973. '((default . (:background unspecified)))
  1974. 'face-override-spec)
  1975. (defun company-doc-buffer (&optional string)
  1976. "Emulate company's `company-doc-buffer'."
  1977. (with-current-buffer (get-buffer-create "*company-documentation*")
  1978. (erase-buffer)
  1979. (fundamental-mode)
  1980. (when string
  1981. (save-excursion
  1982. (insert string)
  1983. (visual-line-mode)))
  1984. (current-buffer)))
  1985. (defun my/-jupyter-kick-use-back-to-cell ()
  1986. "Kick the point out of the invisible read only area at the start of cells."
  1987. (let ((props (text-properties-at (point))))
  1988. (when (and (plist-get props 'invisible)
  1989. (plist-get props 'read-only))
  1990. (forward-char))))
  1991. (defun my/-setup-jupyter-mode ()
  1992. "Setup `jupyter-repl-mode'."
  1993. (display-line-numbers-mode -1)
  1994. (add-hook 'post-command-hook #'my/-jupyter-kick-use-back-to-cell
  1995. nil t))
  1996. (cl-defmethod jupyter-indent-line (&context (jupyter-lang c++))
  1997. (let ((res (syntax-ppss (pos-bol))))
  1998. ;; no paren depth
  1999. (if (zerop (cl-first res))
  2000. (save-excursion
  2001. (back-to-indentation)
  2002. (delete-region (pos-bol) (point)))
  2003. (indent-for-tab-command)))))
  2004. ;; C/C++ and jupyter
  2005. (defvar my/jupyter-extra-language-associations
  2006. '(("c" . "c++")))
  2007. (defun my/-find-jupyter-buffer-for-lang (lang)
  2008. "Find a Jupyter buffer supporint LANG."
  2009. (let ((res (cl-find-if (lambda (buf)
  2010. (with-current-buffer buf
  2011. (and (derived-mode-p 'jupyter-repl-mode)
  2012. jupyter-current-client
  2013. (cl-equalp lang
  2014. (symbol-name
  2015. (jupyter-kernel-language
  2016. jupyter-current-client))))))
  2017. (buffer-list))))
  2018. (when-let (((not res))
  2019. (real (alist-get lang my/jupyter-extra-language-associations nil
  2020. nil #'cl-equalp)))
  2021. (setq res (my/-find-jupyter-buffer-for-lang real)))
  2022. res))
  2023. (defun my/-jupyter-buffer-for-major-mode (&optional mode)
  2024. "Return a Jupyter buffer that can evaluate the code MODE is editing.
  2025. MODE defaults to `major-mode'."
  2026. (when-let ((name (symbol-name (or mode major-mode)))
  2027. ((string-match (rx bos (group (+? any)) (? "-ts") "-mode" eos)
  2028. name)))
  2029. (my/-find-jupyter-buffer-for-lang (match-string 1 name))))
  2030. (defun my/-jupyter-find-proper-buffer ()
  2031. "Find the buffer for `my/jupyter-eval-in-proper-buffer'."
  2032. (if (and (derived-mode-p 'jupyter-repl-mode)
  2033. jupyter-current-client)
  2034. (current-buffer)
  2035. (my/-jupyter-buffer-for-major-mode major-mode)))
  2036. (defun my/jupyter-eval-in-proper-buffer (code &optional no-error)
  2037. "Eval CODE a buffer suitable for `major-mode'.
  2038. If NO-ERROR is non-nil, signal an error if a buffer fails to be found. If the
  2039. current buffer is a Jupyter buffer, just use that."
  2040. (interactive (list (if-let ((buffer (my/-jupyter-find-proper-buffer)))
  2041. (with-current-buffer buffer
  2042. (jupyter-read-expression))
  2043. (user-error "No Jupyter buffer found for mode: %s"
  2044. major-mode))))
  2045. (if-let ((buffer (my/-jupyter-find-proper-buffer)))
  2046. (with-current-buffer buffer
  2047. (let ((jupyter-repl-echo-eval-p t))
  2048. (jupyter-eval-string code)))
  2049. (unless no-error (user-error "No Jupyter buffer found for mode: %s"
  2050. major-mode))))
  2051. (defun my/jupyter-eval-defun ()
  2052. "Eval the defun under point by sending it to a Jupyter repl."
  2053. (interactive)
  2054. (if-let ((code (thing-at-point 'defun)))
  2055. (progn
  2056. (my/jupyter-eval-in-proper-buffer code)
  2057. (message "Evaluated defun"))
  2058. (user-error "Nothing to evaluate under point")))
  2059. (defun my/jupyter-eval-buffer ()
  2060. "Eval the current buffer by sending it to a Jupyter repl."
  2061. (interactive)
  2062. (my/jupyter-eval-in-proper-buffer (buffer-substring-no-properties
  2063. (point-min) (point-max)))
  2064. (message "Evaluated buffer"))
  2065. (defun my/c++-jupyter-eval-region (start end)
  2066. "Send the current buffer between START and END to a Jupyter repl."
  2067. (interactive "r")
  2068. (let ((code (buffer-substring-no-properties start end)))
  2069. (when (string-suffix-p ";" code)
  2070. (setq code (substring code 0 (1- (length code)))))
  2071. (my/jupyter-eval-in-proper-buffer code)
  2072. (message "Evaluated region")))
  2073. (defun my/c++-ts-jupyter-eval-expression ()
  2074. "Eval the expression under point by sending it to a Jupyter repl."
  2075. (interactive nil c-ts-mode c++-ts-mode)
  2076. (save-excursion
  2077. (let ((start (point)))
  2078. (back-to-indentation)
  2079. (unless (> (point) start)
  2080. (goto-char start)))
  2081. (if-let ((thing (treesit-thing-at-point "_" 'nested))
  2082. (code (treesit-node-text thing)))
  2083. (progn
  2084. (when (string-suffix-p ";" code)
  2085. (setq code (substring code 0 (1- (length code)))))
  2086. (my/jupyter-eval-in-proper-buffer code)
  2087. (message "Evaluated: %s" code))
  2088. (user-error "Nothing to evaluate under point"))))
  2089. (defun my/rust-jupyter-eval-region (start end)
  2090. "Send the current buffer between START and END to a Jupyter repl."
  2091. (interactive "r")
  2092. (let ((code (buffer-substring-no-properties start end)))
  2093. (my/jupyter-eval-in-proper-buffer code)
  2094. (message "Evaluated region")))
  2095. (defun my/rust-ts-jupyter-eval-expression ()
  2096. "Eval the expression under point by sending it to a Jupyter repl."
  2097. (interactive nil rust-ts-mode)
  2098. (save-excursion
  2099. (let ((start (point)))
  2100. (back-to-indentation)
  2101. (unless (> (point) start)
  2102. (goto-char start)))
  2103. (if-let ((thing (treesit-thing-at-point "_" 'nested))
  2104. (code (treesit-node-text thing)))
  2105. (progn
  2106. (my/jupyter-eval-in-proper-buffer code)
  2107. (message "Evaluated: %s" code))
  2108. (user-error "Nothing to evaluate under point"))))
  2109. (with-eval-after-load 'c-ts-mode
  2110. (keymap-set c-ts-base-mode-map
  2111. "C-M-x" #'my/jupyter-eval-defun)
  2112. (keymap-set c-ts-base-mode-map
  2113. "C-x C-e" #'my/c++-ts-jupyter-eval-expression)
  2114. (keymap-set c-ts-base-mode-map
  2115. "C-c C-r" #'my/c++-jupyter-eval-region)
  2116. (keymap-set c-ts-base-mode-map
  2117. "C-c C-b" #'my/jupyter-eval-buffer))
  2118. (with-eval-after-load 'rust-ts-mode
  2119. (keymap-set rust-ts-mode-map
  2120. "C-M-x" #'my/jupyter-eval-defun)
  2121. (keymap-set rust-ts-mode-map
  2122. "C-x C-e" #'my/rust-ts-jupyter-eval-expression)
  2123. (keymap-set rust-ts-mode-map
  2124. "C-c C-r" #'my/rust-jupyter-eval-region)
  2125. (keymap-set rust-ts-mode-map
  2126. "C-c C-b" #'my/jupyter-eval-buffer))
  2127. ;; pdf-tools
  2128. (use-package pdf-tools
  2129. :hook (pdf-view-mode . my/setup-pdf-view-mode)
  2130. :init
  2131. (setq pdf-misc-print-program-executable "lp")
  2132. (defun my/setup-pdf-view-mode ()
  2133. (display-line-numbers-mode -1)
  2134. (evil-define-key '(motion normal visual) 'local
  2135. (kbd "C-s") #'isearch-forward
  2136. (kbd "C-r") #'isearch-backward)
  2137. (setq-local cursor-type nil))
  2138. (pdf-tools-install))
  2139. ;; doc view
  2140. (use-package doc-view
  2141. :ensure nil
  2142. :hook (doc-view-mode . my/-setup-doc-view-mode)
  2143. :init
  2144. (defun my/-setup-doc-view-mode ()
  2145. (display-line-numbers-mode -1)
  2146. (evil-define-key '(motion normal visual) 'local
  2147. (kbd "C-s") #'isearch-forward
  2148. (kbd "C-r") #'isearch-backward)))
  2149. ;; calc
  2150. (use-package calc
  2151. :ensure nil
  2152. :bind (("C-c m" . quick-calc)
  2153. :map calc-mode-map
  2154. ("M-<tab>" . calc-roll-up)
  2155. ("M-TAB" . calc-roll-up))
  2156. :hook ((calc-mode calc-trail-mode) . my/setup-calc-calc-trail-mode)
  2157. :init
  2158. (defun my/setup-calc-calc-trail-mode ()
  2159. (setq-local doom-modeline-percent-position '()
  2160. truncate-partial-width-windows nil)
  2161. (visual-line-mode -1)
  2162. (display-line-numbers-mode -1)
  2163. (toggle-truncate-lines 1))
  2164. :config
  2165. (evil-define-key '(normal visual motion) calc-edit-mode-map
  2166. (kbd "RET") 'calc-edit-return
  2167. (kbd "<return>") 'calc-edit-return)
  2168. (defun my/-calc-float-mode-string ()
  2169. (cl-destructuring-bind (mode prec) calc-float-format
  2170. (concat
  2171. (upcase-initials (symbol-name mode))
  2172. (unless (zerop prec)
  2173. (concat ": " (number-to-string prec))))))
  2174. (doom-modeline-def-segment calc
  2175. "Display calculator icons and info."
  2176. (concat
  2177. (doom-modeline-spc)
  2178. (when-let ((icon (doom-modeline-icon 'faicon "nf-fa-calculator" "🖩" "")))
  2179. (concat
  2180. (doom-modeline-display-icon icon)
  2181. (doom-modeline-vspc)))
  2182. (doom-modeline--buffer-simple-name)
  2183. (when (eq major-mode 'calc-mode)
  2184. (concat
  2185. (doom-modeline-spc)
  2186. (number-to-string calc-internal-prec)
  2187. (doom-modeline-spc)
  2188. (upcase-initials (symbol-name calc-angle-mode))
  2189. (doom-modeline-spc)
  2190. (my/-calc-float-mode-string)
  2191. (when calc-prefer-frac
  2192. (concat
  2193. (doom-modeline-spc)
  2194. "Frac"))
  2195. (cond
  2196. (calc-algebraic-mode
  2197. (concat
  2198. (doom-modeline-spc)
  2199. "Alg"))
  2200. (calc-incomplete-algebraic-mode
  2201. (concat
  2202. (doom-modeline-spc)
  2203. "IAlg"))))))))
  2204. ;; sage (for when calc is not enough)
  2205. (use-package sage-shell-mode
  2206. :demand
  2207. :bind ("C-c g" . my/run-sage)
  2208. :hook (sage-shell-mode . my/-setup-sage-shell-mode)
  2209. :init
  2210. (defun my/-setup-sage-shell-mode ()
  2211. (setq-local comint-dynamic-complete-functions
  2212. '(comint-c-a-p-replace-by-expanded-history)))
  2213. :config
  2214. (defun my/run-sage (p)
  2215. "Like `sage-shell:run-sage', but does not ask anything without a prefix
  2216. argument."
  2217. (interactive "P")
  2218. (let ((sage-shell:ask-command-options p))
  2219. (funcall-interactively #'sage-shell:run-sage
  2220. (sage-shell:read-command)))))
  2221. ;; fricas (because I like calculators)
  2222. (add-to-list 'load-path "/usr/lib/fricas/emacs")
  2223. (use-package fricas
  2224. :ensure nil
  2225. :custom
  2226. (fricas-run-command "fricas -nosman")
  2227. :init
  2228. ;; Fix `fricas-mode' messing up `completion-at-point-functions'
  2229. (advice-add #'fricas-mode :around
  2230. #'(lambda (oldfun &rest r)
  2231. (let ((temp-capfs))
  2232. (let ((completion-at-point-functions '(t)))
  2233. (apply oldfun r)
  2234. (setq temp-capfs completion-at-point-functions))
  2235. (setq-local completion-at-point-functions temp-capfs)))
  2236. '((name . "my/-fricas-fix-capfs")))
  2237. :config
  2238. (face-spec-set 'fricas-type-time '((t (:foreground unspecified
  2239. :background unspecified
  2240. :inherit font-lock-type-face))))
  2241. (face-spec-set 'fricas-message '((t (:foreground unspecified
  2242. :background unspecified
  2243. :inherit error))))
  2244. (face-spec-set 'fricas-undefined '((t (:foreground unspecified
  2245. :background unspecified
  2246. :inherit nerd-icons-lblue))))
  2247. (face-spec-set 'fricas-algebra '((t (:foreground unspecified
  2248. :background unspecified
  2249. :weight bold
  2250. :inherit fricas-prompt))))
  2251. (face-spec-set 'fricas-TeX '((t (:foreground "black"
  2252. :background "white"
  2253. :inherit fricas-prompt)))))
  2254. ;; gnuplot (mostly for org-plot)
  2255. (use-package gnuplot)
  2256. ;; eat
  2257. (use-package eat
  2258. :bind (("C-c V" . my/project-eat-or-default)
  2259. :map eat-mode-map
  2260. ("M-o" . ace-window)
  2261. :map eat-semi-char-mode-map
  2262. ("M-o" . ace-window)
  2263. :map eat-eshell-emacs-mode-map
  2264. ("M-o" . ace-window)
  2265. :map eat-eshell-semi-char-mode-map
  2266. ("M-o" . ace-window))
  2267. :init
  2268. (evil-define-key 'insert eat-semi-char-mode-map
  2269. (kbd "<escape>") #'eat-self-input
  2270. (kbd "C-S-n") #'evil-normal-state)
  2271. :config
  2272. ;; The below makes sure that the first time the ESC key is pressed, it does
  2273. ;; what it is supposed to
  2274. (add-hook 'eat--semi-char-mode-hook #'evil-normalize-keymaps)
  2275. (defun my/-evil-disable-cursor-in-eat-buffer (oldfun &rest r)
  2276. "Disable `evil--sw-refresh-cursor' in `eat-mode' buffers."
  2277. (when (or (not (derived-mode-p 'eat-mode))
  2278. (not (eq evil-state 'insert)))
  2279. (apply oldfun r)))
  2280. (advice-add 'evil--sw-refresh-cursor :around
  2281. #'my/-evil-disable-cursor-in-eat-buffer)
  2282. (defun my/-eat-update-cursor-on-tty (&rest r)
  2283. (etcc--evil-set-cursor))
  2284. (advice-add 'eat--set-cursor :after
  2285. #'my/-eat-update-cursor-on-tty)
  2286. (defun my/-eat-disable-evil-in-char-mode ()
  2287. (if eat--char-mode
  2288. (evil-local-mode -1)
  2289. (evil-local-mode 1)))
  2290. (add-hook 'eat--char-mode-hook #'my/-eat-disable-evil-in-char-mode)
  2291. ;; Evil fixes done
  2292. (defvar my/project-eat-hash-table (make-hash-table :test 'equal)
  2293. "Hash table that maps project root dirs to eat buffers.")
  2294. (defun my/-eat-shell-for-cwd ()
  2295. "Return a good shell for CWD, or nil if the default shell should be used."
  2296. (when (file-remote-p default-directory)
  2297. "/bin/sh"))
  2298. (defun my/project-eat (prompt &optional arg)
  2299. "Switch to or create a eat buffer in the current projects root."
  2300. (interactive (list t current-prefix-arg))
  2301. (if-let ((proj (project-current prompt))
  2302. (default-directory (project-root proj)))
  2303. (if-let ((eat-buff (gethash default-directory
  2304. my/project-eat-hash-table))
  2305. ((buffer-live-p eat-buff)))
  2306. (switch-to-buffer eat-buff)
  2307. (let ((eat-buffer-name (concat "*eat for project " default-directory
  2308. "*"))
  2309. (eat-term-name (if (file-remote-p default-directory)
  2310. "xterm-256color"
  2311. eat-term-name)))
  2312. (puthash default-directory
  2313. (eat (my/-eat-shell-for-cwd) arg)
  2314. my/project-eat-hash-table)))))
  2315. (defun my/project-eat-or-default (&optional arg)
  2316. "Open an eat for the current project, otherwise, open a normal eat."
  2317. (interactive "P")
  2318. (unless (my/project-eat nil)
  2319. (if-let ((eat-buff (gethash nil my/project-eat-hash-table))
  2320. ((buffer-live-p eat-buff)))
  2321. (switch-to-buffer eat-buff)
  2322. (puthash nil (let ((eat-term-name (if (file-remote-p default-directory)
  2323. "xterm-256color"
  2324. eat-term-name)))
  2325. (eat (my/-eat-shell-for-cwd) arg))
  2326. my/project-eat-hash-table)))))
  2327. ;; eshell stuff
  2328. (use-package eshell
  2329. :ensure nil
  2330. :defer nil
  2331. :hook ((eshell-load . eat-eshell-visual-command-mode)
  2332. (eshell-mode . eat-eshell-mode)
  2333. (eshell-mode . my/-eshell-mode-setup)
  2334. (eshell-directory-change . my/-eshell-maybe-setup-remote-aliases))
  2335. :bind (:map eshell-mode-map
  2336. ("TAB" . completion-at-point)
  2337. ("<tab>" . completion-at-point))
  2338. :init
  2339. (defun my/-eshell-filter-alias-list ()
  2340. (cl-remove-if-not (lambda (elt)
  2341. (or (string-match-p
  2342. (rx bos
  2343. (or "clear" "find-file"
  2344. "ls" "la" "git"
  2345. (and "eshell/" (+ (not " "))))
  2346. (or " " eos))
  2347. (cl-second elt))))
  2348. eshell-command-aliases-list))
  2349. (defun my/-eshell-maybe-setup-remote-aliases ()
  2350. (if (file-remote-p default-directory)
  2351. (setq-local eshell-command-aliases-list (my/-eshell-filter-alias-list))
  2352. (kill-local-variable 'eshell-command-aliases-list)))
  2353. (defun my/-eshell-mode-setup ()
  2354. "Setup function run from `eshell-mode-hook'"
  2355. (setq-local corfu-auto nil)
  2356. (my/-eshell-maybe-setup-remote-aliases))
  2357. (setq-default eshell-command-aliases-list
  2358. '(("clear" "clear t")
  2359. ("e" "find-file $1")
  2360. ("n" "find-file $1")
  2361. ("emacs" "find-file $1")
  2362. ("nvim" "find-file $1")
  2363. ("ls" "eza --git -F $*")
  2364. ("la" "ls -a $*")
  2365. ("l" "ls -l $*")
  2366. ("ll" "la -l $*")
  2367. ("gt" "git status $*")
  2368. ("gp" "git push $*")
  2369. ("gu" "git pull $*")
  2370. ("gf" "git fetch $*")
  2371. ("ga" "git add $*")
  2372. ("gcm" "git commit -m ${string-join $* \" \"}")
  2373. ("ldg" "ledger -f \"$HOME/docs/finance/finances.ledger\" $*")
  2374. ("tp" "trash-put $*")
  2375. ("trr" "trash-restore $*")
  2376. ("tre" "trash-empty $*")
  2377. ("tre" "trash-empty $*")
  2378. ("trm" "trash-rm $*")
  2379. ("rm" "echo 'rm: I''m unsafe! Don''t use me.'; false")
  2380. ("\\rm" "eshell/rm")))
  2381. (defvar my/eshell-bm-auto-ls t
  2382. "Weather or not to run ls after `eshell/bm'")
  2383. (defun eshell/bm (&optional name)
  2384. "Change to directory of bookmark NAME.
  2385. If no name is given, list all bookmarks instead."
  2386. (if name
  2387. (progn
  2388. (eshell/cd (bookmark-get-filename name))
  2389. (when my/eshell-bm-auto-ls
  2390. (eshell/ls)))
  2391. (eshell-print (string-join (bookmark-all-names) " ")))))
  2392. (use-package esh-help
  2393. :hook (eshell-mode . my/-setup-eshell-help-func)
  2394. :init
  2395. (defun my/-setup-eshell-help-func ()
  2396. (eldoc-mode 1)
  2397. (setq-local evil-lookup-func #'esh-help-run-help))
  2398. (setup-esh-help-eldoc))
  2399. (use-package eshell-syntax-highlighting
  2400. :init
  2401. (eshell-syntax-highlighting-global-mode 1))
  2402. (use-package eshell-starship
  2403. :ensure nil
  2404. :demand t
  2405. :hook (eshell-prompt-mode . eshell-starship-prompt-mode)
  2406. :config
  2407. (eshell-starship-setup-evil-keybindings)
  2408. (set-face-attribute 'eshell-starship-icon-face nil
  2409. :family "FiraCode Nerd Font"))
  2410. (defun my/open-shell-dwim (&optional arg)
  2411. "Open either an Eshell or eat terminal based on `default-directory'.
  2412. If `default-directory' is remote, call `my/project-eat-or-default'. Otherwise,
  2413. call `my/project-eshell-or-default'. ARG is the same as for either of the above
  2414. functions (only eshell uses it at the time of writing)."
  2415. (interactive "P")
  2416. (if (file-remote-p default-directory)
  2417. (my/project-eat-or-default)
  2418. (my/project-eshell-or-default arg)))
  2419. (keymap-global-set "C-c v" #'my/open-shell-dwim)
  2420. ;; proced
  2421. (use-package proced
  2422. :bind ("C-x j" . proced)
  2423. :init
  2424. (evil-define-key '(motion visual normal) proced-mode-map
  2425. "u" 'proced-unmark)
  2426. (setq proced-auto-update-flag t
  2427. proced-auto-update-interval 1)
  2428. (defun my/-setup-proced-mode ()
  2429. (visual-line-mode -1)
  2430. (setq-local truncate-lines t))
  2431. (add-hook 'proced-mode-hook 'my/-setup-proced-mode))
  2432. ;; dired
  2433. (use-package dired
  2434. :ensure nil
  2435. :custom
  2436. (dired-listing-switches
  2437. "-l --almost-all --human-readable --group-directories-first --no-group")
  2438. (dired-hide-details-hide-symlink-targets nil)
  2439. :init
  2440. (setq-default dired-kill-when-opening-new-dired-buffer t)
  2441. (setq delete-by-moving-to-trash t
  2442. dired-recursive-copies 'always
  2443. dired-recursive-deletes 'always
  2444. dired-dwim-target t
  2445. dired-create-destination-dirs 'ask
  2446. dired-create-destination-dirs-on-trailing-dirsep t
  2447. dired-isearch-filenames 'dwim
  2448. dired-do-revert-buffer (lambda (dir)
  2449. (not (file-remote-p dir)))
  2450. dired-clean-up-buffers-too t
  2451. dired-clean-confirm-killing-deleted-buffers t)
  2452. (evil-define-key '(normal visual motion) dired-mode-map
  2453. "u" #'dired-unmark
  2454. "U" #'dired-unmark-all-marks))
  2455. ;; dirvish
  2456. (use-package dirvish
  2457. :defer nil
  2458. :bind (("C-c b" . dirvish-quick-access)
  2459. :map dirvish-mode-map
  2460. ("<mouse-1>" . dirvish-subtree-toggle-or-open)
  2461. ("<mouse-2>" . dired-mouse-find-file-other-window)
  2462. ("<mouse-3>" . dired-mouse-find-file))
  2463. :hook (((dirvish-directory-view-mode dired-mode dirvish-mode) .
  2464. my/-setup-dirvish-lines)
  2465. ;; ((dirvish-directory-view-mode dired-mode dirvish-mode) .
  2466. ;; auto-revert-mode)
  2467. ((dirvish-mode dired-mode) . my/-setup-dirvish-mouse))
  2468. :custom
  2469. (dirvish-subtree-always-show-state t)
  2470. (dirvish-reuse-session t)
  2471. (dirvish-quick-access-function 'dired)
  2472. :init
  2473. (defun my/-setup-dirvish-lines ()
  2474. (setq-local truncate-lines t))
  2475. (defun my/-setup-dirvish-mouse ()
  2476. (setq-local mouse-1-click-follows-link nil
  2477. mouse-1-click-in-non-selected-windows nil))
  2478. (defvar my/-dirvish-base-quick-access-entries
  2479. `(("h" "~/" "Home")
  2480. ("d" "~/downloads/" "Downloads")
  2481. ("e" ,user-emacs-directory "Emacs user directory")
  2482. ("z" "~/.config/zsh/" "Zsh user directory")
  2483. ("o" "~/docs/" "Documents")
  2484. ("w." "~/workspace/" "Workspace")))
  2485. (defun my/-dirvish-build-quick-access-entries (bookmarks)
  2486. ;; NOTE called from a variable watcher for `bookmark-alist' and so must
  2487. ;; never set that variable
  2488. (let (out)
  2489. (dolist (bme bookmarks
  2490. (append my/-dirvish-base-quick-access-entries
  2491. (sort out
  2492. (lambda (elt1 elt2)
  2493. (string< (car elt1) (car elt2))))))
  2494. (let ((name (car bme)))
  2495. (let-alist (cdr bme)
  2496. (when (and (file-directory-p .filename)
  2497. (string-match (rx bos (group (any "a-z" "A-Z")) "ws" eos)
  2498. name))
  2499. (setf (alist-get (concat "w" (match-string 1 name)) out
  2500. nil nil 'equal)
  2501. (list .filename
  2502. (concat
  2503. (capitalize
  2504. (car (last (string-split .filename "/" t))))
  2505. " Workspace")))))))))
  2506. :config
  2507. (require 'dirvish-extras)
  2508. (defun my/-dirvish-bookmark-alist-watcher (_sym newval oper where)
  2509. (when (and (not where) (memq oper '(set makunbound defvaralias)))
  2510. (setopt dirvish-quick-access-entries
  2511. (my/-dirvish-build-quick-access-entries newval))))
  2512. (add-variable-watcher 'bookmark-alist #'my/-dirvish-bookmark-alist-watcher)
  2513. (defvar-local my/-dirvish-uid-name-cache nil
  2514. "Cons of path and a hash table mapping user ids to their names.")
  2515. (dirvish-define-attribute file-owner-mode
  2516. "The file's owner and mode."
  2517. :index 2
  2518. :when (and (dirvish-prop :root) dired-hide-details-mode
  2519. (> win-width 60))
  2520. (let ((root (dirvish-prop :root))
  2521. (uid (file-attribute-user-id f-attrs)))
  2522. (unless (or (dirvish-prop :remote) (stringp uid))
  2523. (unless (and (equal root (car my/-dirvish-uid-name-cache))
  2524. (hash-table-p (cdr my/-dirvish-uid-name-cache)))
  2525. (setq my/-dirvish-uid-name-cache
  2526. (cons root (make-hash-table :test 'equal))))
  2527. (if-let ((name (gethash uid (cdr my/-dirvish-uid-name-cache))))
  2528. (setq uid name)
  2529. (let* ((new-attrs (file-attributes f-name 'string))
  2530. (new-name (file-attribute-user-id new-attrs)))
  2531. (puthash uid new-name
  2532. (cdr my/-dirvish-uid-name-cache))
  2533. (setq uid new-name))))
  2534. (cons 'right (propertize (format " %s %s" uid (file-attribute-modes f-attrs))
  2535. 'face (or hl-face 'dirvish-file-time)))))
  2536. (let ((cur-val dirvish-ui-setup-items))
  2537. (cl-pushnew '("o" file-owner-mode "File owner and mode")
  2538. cur-val :test 'equal)
  2539. (setopt dirvish-ui-setup-items cur-val))
  2540. (add-to-list 'dirvish-libraries '(dirvish file-owner-mode))
  2541. (setopt dirvish-attributes
  2542. '(vc-state subtree-state nerd-icons file-size file-owner-mode))
  2543. (evil-define-key 'normal dirvish-mode-map
  2544. (kbd "q") #'dirvish-quit
  2545. (kbd "a") #'dirvish-quick-access
  2546. (kbd "f") #'dirvish-file-info-menu
  2547. (kbd "y") #'dirvish-yank-menu
  2548. (kbd "N") #'dirvish-narrow
  2549. (kbd "^") #'dirvish-history-last
  2550. (kbd "h") #'dirvish-history-jump
  2551. (kbd "s") #'dirvish-quicksort
  2552. (kbd "o") #'dirvish-quicksort
  2553. (kbd "v") #'dirvish-vc-menu
  2554. (kbd "TAB") #'dirvish-subtree-toggle
  2555. (kbd "M-f") #'dirvish-history-go-forward
  2556. (kbd "M-b") #'dirvish-history-go-backward
  2557. (kbd "M-l") #'dirvish-history-go-forward
  2558. (kbd "M-h") #'dirvish-history-go-backward
  2559. (kbd "M-l") #'dirvish-ls-switches-menu
  2560. (kbd "M-m") #'dirvish-mark-menu
  2561. (kbd "M-t") #'dirvish-layout-toggle
  2562. (kbd "M-s") #'dirvish-setup-menu
  2563. (kbd "M-e") #'dirvish-emerge-menu
  2564. (kbd "M-j") #'dirvish-fd-jump
  2565. (kbd "/") #'dirvish-fd
  2566. (kbd "?") #'dirvish-dispatch)
  2567. (dirvish-override-dired-mode)
  2568. (dirvish-define-preview eza (file)
  2569. "Use `eza' to generate directory preview."
  2570. :require ("eza")
  2571. (when (file-directory-p file)
  2572. `(shell . ("eza" "-la" "--color=always" "--icons"
  2573. "--group-directories-first" ,file))))
  2574. (add-to-list 'dirvish-preview-dispatchers 'eza))
  2575. ;; ibuffer
  2576. (use-package ibuffer
  2577. :bind ("C-x C-b" . ibuffer))
  2578. ;; magit
  2579. (use-package magit
  2580. :init
  2581. (defvar-keymap my/magit-personal-prefix-map
  2582. :doc "Keymap for some useful Magit commands."
  2583. "s" #'magit-stage
  2584. "d" #'magit-diff-dwim
  2585. "D" #'magit-diff
  2586. "b" #'magit-blame)
  2587. (keymap-global-set "C-c i" my/magit-personal-prefix-map)
  2588. (evil-define-key '(normal visual motion) magit-mode-map
  2589. "s" #'magit-stage-file
  2590. "S" #'magit-stage-modified))
  2591. (use-package forge
  2592. :custom
  2593. (forge-add-default-bindings nil)
  2594. :config
  2595. (add-to-list 'forge-alist '("git.zander.im" "git.zander.im/api/v1"
  2596. "git.zander.im" forge-gitea-repository)))
  2597. ;; org-mode
  2598. (use-package org
  2599. :pin gnu
  2600. :bind (("C-c c" . org-capture)
  2601. ("C-c a" . org-agenda)
  2602. ("C-c l" . org-store-link)
  2603. :map org-mode-map
  2604. ("C-c t" . org-table-create))
  2605. :hook (org-mode . org-table-header-line-mode)
  2606. :init
  2607. (font-lock-add-keywords 'org-mode
  2608. `((,(rx bol (* " ") (group "-") " ")
  2609. (0 (prog1 nil
  2610. (compose-region (match-beginning 1)
  2611. (match-end 1) "•"))))))
  2612. (setq org-directory "~/org"
  2613. org-agenda-files '("~/org/")
  2614. org-log-into-drawer t
  2615. org-log-done 'time
  2616. org-log-redeadline 'time
  2617. org-log-reschedule 'time
  2618. org-preview-latex-default-process 'dvisvgm
  2619. org-highlight-latex-and-related '(native entities)
  2620. org-startup-with-inline-images t
  2621. org-adapt-indentation t
  2622. org-hide-leading-stars t
  2623. org-html-with-latex 'dvisvgm
  2624. org-preview-latex-process-alist
  2625. '((dvisvgm
  2626. :image-input-type "dvi"
  2627. :image-output-type "svg"
  2628. :image-size-adjust (1.7 . 1.5)
  2629. :latex-compiler ("pdflatex -interaction nonstopmode -output-format=dvi -output-directory=%o %f")
  2630. :image-converter ("dvisvgm %o%b.dvi --no-fonts --exact-bbox --scale=%S --output=%O"))))
  2631. (defun my/-org-allow-in-derived-mode (oldfun &rest r)
  2632. "Allow OLDFUN to run, even if `major-mode' is only derived from `org-mode'.
  2633. R is rest of the arguments to OLDFUN."
  2634. (let ((major-mode (if (derived-mode-p 'org-mode)
  2635. 'org-mode
  2636. major-mode)))
  2637. (apply oldfun r)))
  2638. (advice-add 'org-element-at-point :around 'my/-org-allow-in-derived-mode)
  2639. (advice-add 'org-table-header-line-mode :around 'my/-org-allow-in-derived-mode))
  2640. (use-package evil-org
  2641. :after org
  2642. :hook (org-mode . evil-org-mode)
  2643. :init
  2644. (require 'evil-org-agenda)
  2645. (evil-org-agenda-set-keys))
  2646. ;; ledger
  2647. (use-package ledger-mode)
  2648. (use-package flycheck-ledger
  2649. :hook (ledger-mode . trusted-files-flycheck-mode-if-safe))
  2650. ;; khard contacts
  2651. (require 'khard)
  2652. ;; This is also in khard (see above), it's just also here so that if I remove
  2653. ;; that file ever, other things will not break.
  2654. (defun my/message-in-header-p (name &optional testfn)
  2655. "If in field NAME, return the start of the header, otherwise, return nil.
  2656. The name is compared with the field name using TESTFN (defaults to `equal')."
  2657. (save-excursion
  2658. (when (and (message-point-in-header-p)
  2659. (message-beginning-of-header t))
  2660. (beginning-of-line)
  2661. (when (and (looking-at (rx bol (group (+? any)) ":" (? " ")))
  2662. (funcall (or testfn 'equal) (match-string 1) name))
  2663. (match-end 0)))))
  2664. ;; mu4e
  2665. (use-package mu4e
  2666. :ensure nil
  2667. :defer nil
  2668. :hook ((mu4e-index-updated . my/-mu4e-enable-index-messages)
  2669. (mu4e-main-mode . my/-mu4e-setup-main-mode)
  2670. (mu4e-view-mode . my/-mu4e-setup-view-mode)
  2671. (mu4e-compose-mode . my/-mu4e-setup-compose-mode))
  2672. :bind (("C-x C-m" . mu4e)
  2673. :map message-mode-map
  2674. ("C-c k" . khard-insert-email-contact)
  2675. :map mu4e-headers-mode-map
  2676. ([remap mu4e-headers-mark-for-trash] .
  2677. my/mu4e-headers-mark-for-trash)
  2678. :map mu4e-view-mode-map
  2679. ([remap mu4e-view-mark-for-trash] .
  2680. my/mu4e-view-mark-for-trash))
  2681. :init
  2682. (require 'mu4e)
  2683. (evil-define-key '(normal motion) mu4e-main-mode-map "q" #'bury-buffer)
  2684. (evil-define-key '(normal motion) mu4e-view-mode-map "gy" #'mu4e-view-save-url)
  2685. (defun my/-mu4e-setup-view-mode ()
  2686. (setq-local global-hl-line-mode nil))
  2687. (defun my/-mu4e-setup-main-mode ()
  2688. (setq-local default-directory "~/"))
  2689. (defun my/-mu4e-enable-index-messages ()
  2690. (setq mu4e-hide-index-messages nil))
  2691. (defun my/mu4e-update-mail-and-index-silent ()
  2692. "Run `mu4e-update-mail-and-index' without any messages in the background."
  2693. (setq mu4e-hide-index-messages t)
  2694. (mu4e-update-mail-and-index t))
  2695. (defun my/mu4e-headers-mark-for-trash ()
  2696. "Move the message a point to the trash without marking it was deleted
  2697. (trashed)."
  2698. (interactive)
  2699. (when (mu4e-thread-message-folded-p)
  2700. (mu4e-warn "Cannot mark folded messages"))
  2701. (mu4e-mark-at-point 'move mu4e-trash-folder)
  2702. (when mu4e-headers-advance-after-mark
  2703. (mu4e-headers-next)))
  2704. (defun my/mu4e-view-mark-for-trash ()
  2705. "Like `my/mu4e-headers-mark-for-trash', but for `mu4e-view-mode'."
  2706. (interactive)
  2707. (mu4e--view-in-headers-context
  2708. (my/mu4e-headers-mark-for-trash)))
  2709. (defun my/-mu4e-enable-autocomplete-in-header ()
  2710. ;; corfu auto must be t (not the integer returned by
  2711. ;; `my/message-in-header-p'
  2712. (setq-local corfu-auto (and (not (window-minibuffer-p))
  2713. (my/message-in-header-p "To")
  2714. t)))
  2715. (defun my/-mu4e-setup-compose-mode ()
  2716. (add-hook 'post-command-hook 'my/-mu4e-enable-autocomplete-in-header
  2717. nil t)
  2718. (add-to-list
  2719. (make-local-variable 'completion-at-point-functions)
  2720. (cape-capf-super #'mu4e-complete-contact #'khard-message-mode-capf)))
  2721. (defun my/-mu4e-fix-cycle-threshold ()
  2722. (setq-local completion-cycle-threshold nil))
  2723. (advice-add 'mu4e--compose-setup-completion :after
  2724. 'my/-mu4e-fix-cycle-threshold)
  2725. (defvar my/mu4e-interesting-mail-query
  2726. (concat "flag:unread AND NOT flag:trashed AND NOT "
  2727. "maildir:/protonmail/Trash AND NOT maildir:/protonmail/Spam")
  2728. "Flag for mail which will appear as \"unread\" and will be notified.")
  2729. (setq message-kill-buffer-on-exit t
  2730. message-confirm-send t
  2731. message-send-mail-function 'sendmail-send-it
  2732. mu4e-change-filenames-when-moving t
  2733. mu4e-context-policy 'pick-first
  2734. mu4e-attachment-dir "~/downloads/"
  2735. mu4e-last-update-buffer " *mu4e-last-update*"
  2736. mu4e-index-update-error-warning nil
  2737. mu4e-get-mail-command "mbsync protonmail"
  2738. mu4e-completing-read-function #'completing-read-default
  2739. mu4e-compose-context-policy 'ask-if-none
  2740. mu4e-contexts
  2741. (list (make-mu4e-context
  2742. :name "Personal"
  2743. :match-func (lambda (msg)
  2744. (when msg
  2745. (string-match-p "^/protonmail/"
  2746. (mu4e-message-field msg
  2747. :maildir))))
  2748. :vars `((user-mail-address . ,(my/get-private 'mu4e-email))
  2749. (user-full-name . ,(my/get-private 'mu4e-name))
  2750. (message-signature . nil)
  2751. (mu4e-refile-folder . "/protonmail/Archive")
  2752. (mu4e-sent-folder . "/protonmail/Sent")
  2753. (mu4e-drafts-folder . "/protonmail/Drafts")
  2754. (mu4e-trash-folder . "/protonmail/Trash")
  2755. (mu4e-bookmarks
  2756. . ((:name "Inbox"
  2757. :query "maildir:/protonmail/Inbox"
  2758. :key ?i)
  2759. (:name "Unread"
  2760. :query ,my/mu4e-interesting-mail-query
  2761. :key ?u))))))))
  2762. (use-package mu4e-alert
  2763. :after mu4e
  2764. :hook (after-init . mu4e-alert-enable-notifications)
  2765. :init
  2766. (setq mu4e-alert-set-window-urgency nil
  2767. mu4e-alert-interesting-mail-query my/mu4e-interesting-mail-query)
  2768. :config
  2769. (mu4e-alert-set-default-style 'libnotify))
  2770. (mu4e t)
  2771. (mu4e-context-switch nil "Personal")
  2772. ;; refresh the eww message count
  2773. (defun my/-mu4e-eww-refresh-unread-count ()
  2774. "Refresh the eww unread message count."
  2775. (my/eww-poll-variables "mu4e"))
  2776. (add-hook 'mu4e-message-changed-hook #'my/-mu4e-eww-refresh-unread-count)
  2777. ;; mu4e compose HTML messages
  2778. (use-package org-mime)
  2779. (require 'org-mu4e-compose)
  2780. (setq ;; mail-user-agent 'org-mu4e-user-agent
  2781. org-mime-org-html-with-latex-default 'dvisvgm
  2782. org-mime-export-options '(:with-latex dvisvgm :with-footnotes t))
  2783. ;; (evil-define-key '(normal visual) org-mu4e-compose-mode-map
  2784. ;; "G" #'mu4e-compose-goto-bottom
  2785. ;; "gg" #'mu4e-compose-goto-top)
  2786. ;; (evil-define-key 'normal org-mu4e-compose-mode-map
  2787. ;; "ZZ" #'message-send-and-exit
  2788. ;; "ZD" #'message-dont-send
  2789. ;; "ZQ" #'message-kill-buffer
  2790. ;; "ZF" #'mml-attach-file)
  2791. ;; (evil-define-key 'normal mu4e-view-mode-map
  2792. ;; "R" 'org-mu4e-compose-reply
  2793. ;; "cr" 'org-mu4e-compose-reply)
  2794. ;; (evil-define-key 'normal mu4e-headers-mode-map
  2795. ;; "R" 'org-mu4e-compose-reply
  2796. ;; "cr" 'org-mu4e-compose-reply)
  2797. ;; (defun my/-setup-org-mu4e-compose-mode ()
  2798. ;; "Setup up stuff in `org-mu4e-compose' buffers."
  2799. ;; (setq-local ltex-eglot-variable-save-method 'file)
  2800. ;; ;; this should come last so it can pick up the above
  2801. ;; ;; (trusted-files-eglot-ensure-if-safe)
  2802. ;; )
  2803. ;; (add-hook 'org-mu4e-compose-mode-hook #'my/-setup-org-mu4e-compose-mode)
  2804. ;; elfeed
  2805. (use-package elfeed
  2806. :bind (("C-c d" . elfeed))
  2807. :custom
  2808. (elfeed-feeds
  2809. '(("https://archlinux.org/feeds/news/" linux arch)
  2810. ("https://9to5linux.com/feed/atom" linux news)))
  2811. :config
  2812. (setq elfeed-log-buffer-name " *elfeed-log*")
  2813. (evil-define-key '(normal motion) elfeed-search-mode-map
  2814. "r" #'elfeed-search-fetch)
  2815. (elfeed-db-load))
  2816. ;; helpful
  2817. (use-package helpful
  2818. :hook ((emacs-lisp-mode . my/-helpful-setup-emacs-lisp-mode)
  2819. (helpful-mode . my/-setup-helpful-mode))
  2820. :bind (:map help-map
  2821. ("f" . helpful-callable)
  2822. ("v" . helpful-variable)
  2823. ("k" . helpful-key)
  2824. ("o" . helpful-symbol)
  2825. ("x" . helpful-command)
  2826. ("F" . helpful-function)
  2827. :map helpful-mode-map
  2828. ("<mouse-8>" . my/helpful-history-back)
  2829. ("<mouse-9>" . my/helpful-history-forward)
  2830. ("<normal-state><" . my/helpful-history-back)
  2831. ("<normal-state>>" . my/helpful-history-forward))
  2832. :init
  2833. (defun my/-helpful-setup-emacs-lisp-mode ()
  2834. (setq-local evil-lookup-func #'helpful-at-point))
  2835. (defun my/-setup-helpful-mode ()
  2836. (setq-local evil-lookup-func #'helpful-at-point
  2837. tab-width 8))
  2838. (defvar my/helpful-symbol-history-size 50
  2839. "Max size of `my/helpful-symbol-history'.")
  2840. (defvar my/helpful-symbol-history '()
  2841. "History of helpful symbols.")
  2842. (defvar my/-helpful-inhibit-history nil
  2843. "If non-nil, don't add symbols to `my/helpful-symbol-history'.")
  2844. (defvar my/-helpful-last-entry nil
  2845. "Last entry looked up with helpful.")
  2846. (defun my/helpful-history-back (count)
  2847. "Go back COUNT symbols in `my/helpful-symbol-history'. If called
  2848. interactively, COUNT defaults to 1."
  2849. (interactive "p")
  2850. (my/helpful-history-forward (- count)))
  2851. (defun my/helpful-history-forward (count)
  2852. "Move COUNT symbols in `my/helpful-symbol-history'. If COUNT is negative,
  2853. move back. If COUNT is larger than the history, go to the newest entry. Go to
  2854. the oldest entry if -COUNT is larger than the history."
  2855. (interactive "p")
  2856. (when helpful--sym
  2857. (let* ((hist-len (length my/helpful-symbol-history))
  2858. (current-pos (seq-position my/helpful-symbol-history
  2859. (cons helpful--sym
  2860. helpful--callable-p)
  2861. 'equal))
  2862. (new-pos (- current-pos count)))
  2863. (cond
  2864. ;; if already at the newest element, signal an error
  2865. ((and (> count 0) (= current-pos 0))
  2866. (message "%s" "No newer symbol!"))
  2867. ;; if already at the oldest element, signal an error
  2868. ((and (< count 0) (= (1+ current-pos) hist-len))
  2869. (message "%s" "No older symbol!"))
  2870. (t
  2871. (let ((my/-helpful-inhibit-history t)
  2872. (entry (cond
  2873. ((<= new-pos 0)
  2874. (seq-first my/helpful-symbol-history))
  2875. ((>= new-pos hist-len)
  2876. (car (last my/helpful-symbol-history)))
  2877. (t
  2878. (nth new-pos my/helpful-symbol-history)))))
  2879. (if (cdr entry)
  2880. (helpful-callable (car entry))
  2881. (helpful-variable (car entry)))))))))
  2882. (defun my/-helpful-switch-buffer-function (helpful-buf)
  2883. "Like `pop-to-buffer', but kill previous helpful buffers and save the new
  2884. buffers `helpful--sym' to `my/helpful-symbol-history'."
  2885. (cl-loop with window = nil
  2886. for buf in (buffer-list)
  2887. when (and
  2888. (not (eq buf helpful-buf))
  2889. (eq (buffer-local-value 'major-mode buf) 'helpful-mode))
  2890. do
  2891. (when-let (cur-window (get-buffer-window buf nil))
  2892. (setq window cur-window))
  2893. (kill-buffer buf)
  2894. finally
  2895. (let ((entry (cons (buffer-local-value 'helpful--sym helpful-buf)
  2896. (buffer-local-value 'helpful--callable-p
  2897. helpful-buf))))
  2898. (unless my/-helpful-inhibit-history
  2899. (when-let (from-current-hist
  2900. (member my/-helpful-last-entry
  2901. my/helpful-symbol-history))
  2902. (setq my/helpful-symbol-history from-current-hist))
  2903. (cl-pushnew entry my/helpful-symbol-history :test 'equal)
  2904. (setq my/helpful-symbol-history
  2905. (seq-take my/helpful-symbol-history
  2906. my/helpful-symbol-history-size)))
  2907. (setq my/-helpful-last-entry entry))
  2908. (if window
  2909. (window--display-buffer helpful-buf window 'reuse)
  2910. (pop-to-buffer helpful-buf))))
  2911. (setq helpful-switch-buffer-function 'my/-helpful-switch-buffer-function
  2912. helpful-max-buffers 2))
  2913. ;; useful for debugging
  2914. (defun my/describe-symbol-plist (symbol)
  2915. "Descrive the plist of SYMBOL in a buffer."
  2916. (interactive (list (intern (completing-read
  2917. "Symbol: "
  2918. (let ((syms))
  2919. (mapatoms (##push (symbol-name %) syms))
  2920. syms)
  2921. nil t))))
  2922. (with-current-buffer (get-buffer-create "*describe-symbol-plist*")
  2923. (unless (derived-mode-p 'special-mode)
  2924. (special-mode))
  2925. (let ((inhibit-read-only t)
  2926. (keys)
  2927. (values))
  2928. (map-do (lambda (k v)
  2929. (push k keys)
  2930. (push v values))
  2931. (symbol-plist symbol))
  2932. (setq keys (nreverse keys)
  2933. values (nreverse values))
  2934. (erase-buffer)
  2935. (insert (propertize "Plist of "
  2936. 'face 'shortdoc-heading))
  2937. (insert (propertize (format "%S" symbol)
  2938. 'face '((:weight normal) shortdoc-heading)))
  2939. (insert "\n\n")
  2940. (with-temp-buffer
  2941. (let ((delayed-mode-hooks nil))
  2942. (delay-mode-hooks
  2943. (lisp-mode))
  2944. (font-lock-mode)
  2945. (show-paren-mode)
  2946. (when (fboundp 'rainbow-delimiters-mode)
  2947. (rainbow-delimiters-mode)))
  2948. (let ((pp-max-width fill-column)
  2949. (pp-use-max-width t))
  2950. (setq values (mapcar (lambda (val)
  2951. (erase-buffer)
  2952. (pp val (current-buffer))
  2953. (font-lock-ensure)
  2954. (buffer-string))
  2955. values))))
  2956. (goto-char (point-max))
  2957. (cl-loop for key in keys
  2958. for value in values
  2959. do
  2960. (insert (propertize (prin1-to-string key)
  2961. 'face 'bold))
  2962. (insert "\n")
  2963. (insert value)
  2964. (insert "\n"))
  2965. (delete-char -1))
  2966. (pop-to-buffer (current-buffer))))
  2967. (defun my/greyify-color (color percent &optional frame)
  2968. "Make COLOR closer to black by PERCENT on FRAME.
  2969. Color can be any color which can be passed to `color-values'."
  2970. (cl-destructuring-bind (&optional r g b)
  2971. (color-name-to-rgb color frame)
  2972. (when (and r g b)
  2973. (let ((scale (- 1.0 (/ percent 100.0))))
  2974. (color-rgb-to-hex (* r scale)
  2975. (* g scale)
  2976. (* b scale))))))
  2977. ;; rainbow-delimiters
  2978. (use-package rainbow-delimiters
  2979. :hook (prog-mode . rainbow-delimiters-mode)
  2980. :config
  2981. ;; generate dark version of the rainbow delimiters faces
  2982. (defun my/-rainbow-delimiters-recalc-dark-faces (&optional frame)
  2983. (unless frame (setq frame (selected-frame)))
  2984. (dotimes (i 9)
  2985. (when-let ((old-face (intern-soft
  2986. (format "rainbow-delimiters-depth-%d-face"
  2987. (1+ i))))
  2988. (new-face
  2989. (intern
  2990. (format "my/rainbow-delimiters-depth-%d-dark-face"
  2991. (1+ i))))
  2992. (old-color (face-attribute old-face :foreground frame))
  2993. (new-color (my/greyify-color old-color 50 frame)))
  2994. (set-face-attribute new-face frame :foreground new-color))))
  2995. (add-hook 'after-make-frame-functions
  2996. #'my/-rainbow-delimiters-recalc-dark-faces)
  2997. (add-hook 'server-after-make-frame-hook
  2998. #'my/-rainbow-delimiters-recalc-dark-faces)
  2999. (defun my/rainbow-delimiters-parinfer-pick-face (depth match loc)
  3000. "Version of `rainbow-delimiters-default-pick-face' that colors closing
  3001. parenthesis darker than opening ones. This function defers to
  3002. `rainbow-delimiters-default-pick-face' and just changes the output if it returns
  3003. one of the normal rainbow-delimiters-depth-N-face faces."
  3004. (save-match-data
  3005. (let* ((base-face (rainbow-delimiters-default-pick-face depth match loc))
  3006. (base-name (symbol-name base-face)))
  3007. (if (and evil-cleverparens-mode
  3008. (eq ?\) (char-syntax
  3009. (elt (buffer-substring-no-properties loc (1+ loc)) 0)))
  3010. (string-match (rx string-start "rainbow-delimiters-depth-"
  3011. (group (+ num))
  3012. "-face" string-end)
  3013. base-name))
  3014. (or (intern-soft (format "my/rainbow-delimiters-depth-%s-dark-face"
  3015. (match-string 1 base-name)))
  3016. base-face)
  3017. base-face))))
  3018. (setopt rainbow-delimiters-pick-face-function
  3019. 'my/rainbow-delimiters-parinfer-pick-face))
  3020. ;; make regexp look nicer
  3021. (use-package easy-escape
  3022. :hook ((emacs-lisp-mode reb-mode) . easy-escape-minor-mode)
  3023. :config
  3024. (face-spec-set 'easy-escape-face
  3025. '((t (:foreground unspecified
  3026. :weight bold
  3027. :inherit 'font-lock-regexp-grouping-backslash)))))
  3028. ;; auto-highlight-symbol
  3029. (use-package auto-highlight-symbol
  3030. :hook (lisp-data-mode . auto-highlight-symbol-mode)
  3031. :init
  3032. (setq ahs-face 'bold
  3033. ahs-face-unfocused 'bold
  3034. ahs-definition-face 'bold
  3035. ahs-definition-face-unfocused 'bold
  3036. ahs-plugin-default-face 'bold
  3037. ahs-plugin-default-face-unfocused 'bold)
  3038. :config
  3039. (keymap-unset auto-highlight-symbol-mode-map "C-x C-a" t))
  3040. ;; Theme (doom-themes)
  3041. (use-package doom-themes
  3042. :config
  3043. (load-theme 'doom-molokai t)
  3044. (doom-themes-org-config))
  3045. ;; solaire-mode
  3046. (use-package solaire-mode
  3047. :config
  3048. (solaire-global-mode 1))
  3049. ;; Highlight todos
  3050. (use-package hl-todo
  3051. :hook (prog-mode . hl-todo-mode))
  3052. (use-package magit-todos
  3053. :after (hl-todo magit)
  3054. :config
  3055. (magit-todos-mode 1))
  3056. ;; icons
  3057. (use-package nerd-icons)
  3058. (use-package nerd-icons-completion
  3059. :config
  3060. (nerd-icons-completion-mode))
  3061. (use-package nerd-icons-dired
  3062. :hook (dired-mode . my/-maybe-enable-nerd-icons-dired)
  3063. :init
  3064. (defun my/-maybe-enable-nerd-icons-dired ()
  3065. (unless (bound-and-true-p dirvish-override-dired-mode)
  3066. (nerd-icons-dired-mode))))
  3067. (use-package kind-icon
  3068. :after corfu
  3069. :init
  3070. (setq kind-icon-default-face 'corfu-default
  3071. kind-icon-default-style
  3072. '(:padding -1 :stroke 0 :margin 0 :radius 0 :height 0.5 :scale 1))
  3073. :config
  3074. (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
  3075. ;; modeline (doom-modeline)
  3076. (use-package doom-modeline
  3077. :init
  3078. (setq doom-modeline-support-imenu t)
  3079. (doom-modeline-mode 1))
  3080. ;; dashboard.el
  3081. (use-package dashboard
  3082. :config
  3083. (defvar-local my/-dashboard-did-fix-image nil
  3084. "Weather or not the dashboard image has been fixed in this buffer.")
  3085. (defun my/-dashboard-fix-image ()
  3086. (unless my/-dashboard-did-fix-image
  3087. (dashboard-refresh-buffer)
  3088. (setq my/-dashboard-did-fix-image t)))
  3089. (defun my/-dashboard-setup-function ()
  3090. (add-hook 'window-configuration-change-hook 'my/-dashboard-fix-image nil t)
  3091. (setq-local display-line-numbers nil))
  3092. (add-hook 'dashboard-mode-hook 'my/-dashboard-setup-function)
  3093. (set-face-background 'dashboard-banner-logo-title nil)
  3094. (dashboard-setup-startup-hook)
  3095. (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))
  3096. dashboard-force-refresh t
  3097. dashboard-display-icons-p t
  3098. dashboard-icon-type 'nerd-icons
  3099. dashboard-set-file-icons t
  3100. dashboard-projects-backend 'project-el
  3101. dashboard-items '((recents . 5)
  3102. (projects . 5)
  3103. (bookmarks . 5))))
  3104. ;; world clocks
  3105. (setq zoneinfo-style-world-list
  3106. '(("America/Los_Angeles" "California")
  3107. ("Asia/Tokyo" "Tokyo")
  3108. ("America/New_York" "New York")
  3109. ("Europe/London" "London")
  3110. ("Europe/Paris" "Paris")
  3111. ("Asia/Calcutta" "Bangalore")))
  3112. ;; page break lines
  3113. (use-package page-break-lines
  3114. :config
  3115. (global-page-break-lines-mode 1)
  3116. (add-to-list 'page-break-lines-modes 'prog-mode)
  3117. (add-to-list 'page-break-lines-modes 'text-mode)
  3118. (add-to-list 'page-break-lines-modes 'helpful-mode))
  3119. ;; fun!
  3120. (use-package mines)
  3121. ;;; init.el ends here