init.el 106 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830
  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/-kkp-after-terminal-setup ()
  386. ;; ;; Make tab and backtab work properly
  387. ;; (define-key input-decode-map [(control ?i)] [tab])
  388. ;; (define-key input-decode-map [(control ?I)] [backtab])
  389. ;; (define-key input-decode-map [(control ?m)] [return]))
  390. ;; (defun my/-kkp-after-terminal-teardown (term)
  391. ;; (with-selected-frame (car (frames-on-display-list term))
  392. ;; (define-key input-decode-map [(control ?i)] nil t)
  393. ;; (define-key input-decode-map [(control ?I)] nil t)
  394. ;; (define-key input-decode-map [(control ?m)] nil t)))
  395. ;; (advice-add 'kkp--terminal-setup :after 'my/-kkp-after-terminal-setup)
  396. ;; (advice-add 'kkp--terminal-teardown :after 'my/-kkp-after-terminal-teardown)
  397. (defun my/quoted-insert (arg)
  398. "Insert the next character using read-key, not read-char."
  399. (interactive "*p")
  400. ;; Source: https://github.com/benjaminor/kkp/issues/11
  401. (let ((char (read-key)))
  402. ;; Ensure char is treated as a character code for insertion
  403. (unless (characterp char)
  404. (user-error "%s is not a valid character"
  405. (key-description (vector char))))
  406. (when (numberp char)
  407. (while (> arg 0)
  408. (insert-and-inherit char)
  409. (setq arg (1- arg))))))
  410. (keymap-global-set "C-q" #'my/quoted-insert)
  411. (defun my/-kkp-fix-map-y-or-n-p (oldfun &rest args)
  412. "Fix `map-y-or-n-p' when used in a terminal with kkp enabled."
  413. (let ((status (kkp--terminal-has-active-kkp-p)))
  414. (condition-case err
  415. (progn
  416. (when status (kkp-disable-in-terminal))
  417. (apply oldfun args))
  418. (quit
  419. ;; We won't die in this case, so just re-enable kkp
  420. (when (and status (not (kkp--terminal-has-active-kkp-p)))
  421. (kkp-enable-in-terminal))
  422. (signal 'quit nil))
  423. (t
  424. (when (and status (not (kkp--terminal-has-active-kkp-p)))
  425. ;; this does async stuff that will make kitty send characters after
  426. ;; Emacs exits. We prevent that by not re-enabling if this frame (or
  427. ;; Emacs) is about to die
  428. (let ((will-die))
  429. (mapbacktrace
  430. (lambda (_evald func _args _flags)
  431. (when (or (eq func 'save-buffers-kill-emacs)
  432. (eq func 'server-save-buffers-kill-terminal))
  433. (setq will-die t))))
  434. (unless will-die
  435. (kkp-enable-in-terminal))))
  436. (when err
  437. (signal (car err) (cdr err)))))))
  438. (advice-add #'map-y-or-n-p :around
  439. #'my/-kkp-fix-map-y-or-n-p))
  440. ;; mozc
  441. (require 'mozc nil t)
  442. (setq default-input-method "japanese-mozc")
  443. ;; migemo
  444. (use-package migemo
  445. :config
  446. (setq migemo-dictionary "/usr/share/migemo/utf-8/migemo-dict"
  447. migemo-coding-system 'utf-8-unix
  448. migemo-regex-dictionary nil
  449. migemo-user-dictionary nil
  450. migemo-isearch-enable-p t)
  451. (keymap-global-set "C-c w" 'isearch-forward)
  452. (keymap-global-set "C-c W" 'isearch-backward)
  453. (migemo-init))
  454. ;; undo-tree
  455. (use-package undo-tree
  456. :defer nil
  457. :hook (undo-tree-visualizer-mode . my/-undo-tree-visualizer-mode-setup)
  458. :config
  459. (defun my/-undo-tree-visualizer-mode-setup ()
  460. (visual-line-mode -1)
  461. (setq truncate-lines t))
  462. (global-undo-tree-mode))
  463. ;; evil
  464. (use-package evil
  465. :init
  466. (setq evil-want-integration t
  467. evil-want-C-d-scroll nil
  468. evil-want-keybinding nil
  469. evil-undo-system 'undo-tree
  470. evil-search-module 'isearch
  471. evil-respect-visual-line-mode t)
  472. :config
  473. (evil-mode 1)
  474. (evil-define-key '(normal visual motion) proced-mode-map
  475. "u" #'proced-unmark)
  476. (evil-define-key '(normal visual motion) dired-mode-map
  477. "u" #'dired-unmark)
  478. (evil-define-key '(normal visual motion) profiler-report-mode-map
  479. (kbd "TAB") #'profiler-report-toggle-entry)
  480. (eldoc-add-command 'evil-insert
  481. 'evil-append
  482. 'evil-insert-line
  483. 'evil-append-line))
  484. (use-package evil-collection
  485. :after evil
  486. :diminish evil-collection-unimpaired-mode
  487. :config
  488. (evil-collection-init))
  489. (use-package evil-surround
  490. :after evil
  491. :config
  492. (evil-define-key 'operator evil-surround-mode-map
  493. "z" #'evil-surround-edit
  494. "Z" #'evil-Surround-edit)
  495. (evil-define-key 'visual evil-surround-mode-map
  496. "gz" #'evil-surround-region
  497. "gZ" #'evil-Surround-region)
  498. (global-evil-surround-mode 1))
  499. (use-package evil-terminal-cursor-changer
  500. :after evil
  501. :config
  502. (evil-terminal-cursor-changer-activate))
  503. (use-package evil-numbers
  504. :after evil
  505. :bind (("C-c =" . evil-numbers/inc-at-pt)
  506. ("C-c +" . evil-numbers/inc-at-pt)
  507. ("C-c -" . evil-numbers/dec-at-pt)
  508. ("C-c C-=" . evil-numbers/inc-at-pt-incremental)
  509. ("C-c C-+" . evil-numbers/inc-at-pt-incremental)
  510. ("C-c C--" . evil-numbers/dec-at-pt-incremental)))
  511. (use-package evil-cleverparens
  512. :hook ((prog-mode . my/-enable-evil-cleverparens)
  513. (evil-cleverparens-mode . paredit-mode))
  514. :bind (:map paredit-mode-map
  515. ("C-<return>" . paredit-RET)
  516. ("C-RET" . paredit-RET)
  517. :map evil-cleverparens-mode-map
  518. ("C-c o" . evil-cp-open-below-form))
  519. :custom
  520. (evil-cleverparens-use-s-and-S nil)
  521. (evil-cleverparens-complete-parens-in-yanked-region t)
  522. :config
  523. (eldoc-add-command 'paredit-RET
  524. 'paredit-open-round
  525. 'paredit-open-angled
  526. 'paredit-open-bracket
  527. 'paredit-open-angled
  528. 'paredit-open-parenthesis
  529. 'delete-indentation
  530. 'evil-cp-insert
  531. 'evil-cp-append
  532. 'evil-cp-insert-at-beginning-of-form
  533. 'evil-cp-insert-at-end-of-form)
  534. (keymap-unset evil-cleverparens-mode-map "<normal-state> M-o" t)
  535. (defun my/-enable-evil-cleverparens ()
  536. (if (member major-mode '(lisp-mode emacs-lisp-mode
  537. lisp-interaction-mode))
  538. (evil-cleverparens-mode 1)
  539. (electric-pair-local-mode 1)))
  540. (cl-defun my/range-inside-thing-p (thing beg end &optional no-edge)
  541. "Return non-nil if BEG and END fall inside the bounds of THING.
  542. With NO-EDGE, return nil if beg or end fall on the edge of the range."
  543. (save-excursion
  544. ;; this fixes that fact that `thing-at-point-bounds-of-string-at-point'
  545. ;; errors if called at the end of the buffer
  546. (condition-case nil
  547. (let ((sb (progn (goto-char beg) (bounds-of-thing-at-point thing)))
  548. (eb (progn (goto-char end) (bounds-of-thing-at-point thing))))
  549. (and sb eb (equal sb eb)
  550. (or (not no-edge)
  551. (and (/= beg (car sb))
  552. (< beg (cdr sb))
  553. (/= end (car sb))
  554. (< end (cdr sb))))))
  555. ;; if the error happens, we aren't in a string
  556. (wrong-type-argument nil))))
  557. (defun my/-evil-cp-region-ok-p-no-string (oldfun beg end)
  558. (or
  559. (and (sp-point-in-comment beg)
  560. (sp-point-in-comment end))
  561. (and (sp-point-in-string beg)
  562. (sp-point-in-string end)
  563. (my/range-inside-thing-p 'string beg end t))
  564. (funcall oldfun beg end)))
  565. (defun my/column-num-at-pos (pos)
  566. "Return the column number at POS."
  567. (save-excursion
  568. (goto-char pos)
  569. (current-column)))
  570. (defun my/-evil-cp-block-ok-p-no-string (oldfun beg end)
  571. (when (> beg end) (cl-rotatef beg end))
  572. (or
  573. (save-excursion
  574. (goto-char beg)
  575. (let ((start-off (current-column))
  576. (end-off (my/column-num-at-pos end)))
  577. (cl-block nil
  578. (dotimes (_ (count-lines beg end) t)
  579. (let ((bol (pos-bol)))
  580. (unless (sp-region-ok-p (+ bol start-off)
  581. (+ bol end-off))
  582. (cl-return))
  583. (forward-line))))))
  584. (funcall oldfun beg end)))
  585. (advice-add 'sp-region-ok-p :around 'my/-evil-cp-region-ok-p-no-string)
  586. (advice-add 'evil-cp--balanced-block-p :around 'my/-evil-cp-block-ok-p-no-string))
  587. ;; be (hopefully) safer
  588. (require 'trusted-files)
  589. (keymap-global-set "C-c t" 'trusted-files-map)
  590. (trusted-files-modeline-mode)
  591. ;; better lisp editing
  592. (use-package adjust-parens
  593. :hook (prog-mode . adjust-parens-mode)
  594. :config
  595. (defun my/lisp-indent-adjust-parens ()
  596. "Like `lisp-indent-adjust-parens', but got to first char on line first.
  597. Also, this works even if the region is active (it indents every line in the
  598. region)."
  599. (interactive)
  600. (save-mark-and-excursion
  601. (let ((end (mark t))
  602. (line-count 1)
  603. (indent-cols))
  604. (when (and (region-active-p) end)
  605. (setq mark-active nil
  606. line-count (count-lines (point) end))
  607. (when (> (point) end)
  608. (let ((start (point)))
  609. (goto-char end)
  610. (setq end start))))
  611. ;; find the indentation column of each line
  612. (save-excursion
  613. (dotimes (_ line-count)
  614. (back-to-indentation)
  615. (push (- (point) (pos-bol)) indent-cols)
  616. (forward-line))
  617. (cl-callf nreverse indent-cols))
  618. (cl-loop repeat line-count
  619. for indent-col in indent-cols
  620. for bol = (pos-bol)
  621. do (back-to-indentation)
  622. ;; skip this line if the indentation has changed
  623. when (= (- (point) bol) indent-col) do
  624. (lisp-indent-adjust-parens)
  625. ;; if the indent failed, stop
  626. (when (= (- (point) bol) indent-col)
  627. (cl-return))
  628. do (forward-line)))))
  629. (defun my/lisp-dedent-adjust-parens ()
  630. "Like `lisp-dedent-adjust-parens', but got to first char on line first.
  631. Also, this works even if the region is active (it just jumps to the first line
  632. in the region and indents once)."
  633. (interactive)
  634. (save-mark-and-excursion
  635. (let ((end (mark t)))
  636. (when (and (region-active-p) end)
  637. (setq mark-active nil)
  638. (when (> (point) end)
  639. (goto-char end))))
  640. (back-to-indentation)
  641. (lisp-dedent-adjust-parens)))
  642. (eldoc-add-command 'my/lisp-indent-adjust-parens
  643. 'my/lisp-dedent-adjust-parens
  644. 'lisp-indent-adjust-parens
  645. 'lisp-dedent-adjust-parens)
  646. (evil-define-key '(normal visual) adjust-parens-mode-map
  647. (kbd "<tab>") #'my/lisp-indent-adjust-parens
  648. (kbd "<backtab>") #'my/lisp-dedent-adjust-parens
  649. (kbd "C-c C-i") #'my/lisp-indent-adjust-parens
  650. (kbd "C-c S-<tab>") #'my/lisp-dedent-adjust-parens))
  651. ;; for when the files are just too large
  652. (use-package vlf
  653. :demand t
  654. :config
  655. (require 'vlf-setup))
  656. ;; allow copy from terminal
  657. (use-package xclip
  658. :config
  659. (setq xclip-method 'wl-copy
  660. xclip-program (symbol-name xclip-method))
  661. (xclip-mode 1)
  662. (defun my/-xclip-detect-wl-paste-error (oldfun type)
  663. (if (eq xclip-method 'wl-copy)
  664. ;; Direct from `xclip-get-selection'
  665. (when (and (getenv "WAYLAND_DISPLAY")
  666. (memq type '(clipboard CLIPBOARD primary PRIMARY)))
  667. (let* ((exit-code 0)
  668. (output
  669. (with-output-to-string
  670. (setq exit-code
  671. (apply #'call-process (replace-regexp-in-string
  672. "\\(.*\\)copy" "\\1paste"
  673. xclip-program 'fixedcase)
  674. nil standard-output nil
  675. "-n" (if (memq type '(primary PRIMARY))
  676. '("-p")))))))
  677. (if (zerop exit-code)
  678. output
  679. "")))
  680. (funcall oldfun type)))
  681. (advice-add 'xclip-get-selection :around 'my/-xclip-detect-wl-paste-error))
  682. ;; Set the WAYLAND_DISPLAY environment variable
  683. (require 'xdg)
  684. (defun my/detect-wayland-display ()
  685. "Try to set the WAYLAND_DISPLAY environment variable.
  686. This attempts to detect a running Wayland session and set the WAYLAND_DISPLAY
  687. environment variable accordingly."
  688. (let ((found '(nil . nil)))
  689. (dolist (entry (directory-files-and-attributes
  690. (xdg-runtime-dir) nil nil 'nosort)
  691. (cdr found))
  692. (cl-destructuring-bind (name . attrs) entry
  693. (when-let (((string-match (rx bos "wayland-" (group (+ (any "0-9"))) eos)
  694. name))
  695. (id (string-to-number (match-string 1 name)))
  696. ((or (not (car found)) (< id (car found))))
  697. ;; socket
  698. ((string-prefix-p "s" (file-attribute-modes attrs))))
  699. (setq found (cons id name)))))))
  700. (unless (getenv "WAYLAND_DISPLAY")
  701. (setenv "WAYLAND_DISPLAY" (my/detect-wayland-display)))
  702. ;; which-key
  703. (use-package which-key
  704. :diminish which-key-mode
  705. :config
  706. (which-key-mode 1))
  707. ;; avy
  708. (use-package avy
  709. :bind (("C-c C-j" . avy-resume)
  710. ("M-s s" . evil-avy-goto-char-2)
  711. ("M-s S" . evil-avy-goto-line))
  712. :init
  713. (define-minor-mode my/evil-avy-mode
  714. "A minor mode for binding avy commands to s and S in evil's normal and
  715. visual states."
  716. :keymap (make-sparse-keymap))
  717. (evil-define-key '(normal visual operator motion) my/evil-avy-mode-map
  718. "s" #'evil-avy-goto-char-2
  719. "S" #'evil-avy-goto-line)
  720. (define-globalized-minor-mode my/evil-avy-global-mode my/evil-avy-mode
  721. (lambda () (my/evil-avy-mode 1))
  722. :predicate '((not magit-mode dired-mode
  723. proced-mode mu4e-main-mode
  724. mu4e-view-mode mu4e-headers-mode
  725. ibuffer-mode calc-mode calc-trail-mode
  726. gnus-group-mode) t))
  727. (my/evil-avy-global-mode 1)
  728. :config
  729. (avy-setup-default))
  730. ;; better `replace-regexp'
  731. (use-package visual-regexp
  732. :bind (("C-c q" . vr/replace)
  733. ("C-M-%" . vr/query-replace)))
  734. ;; better `align-regexp'
  735. (use-package ialign
  736. :defer t
  737. :custom
  738. (ialign-initial-repeat t))
  739. ;; ace-window
  740. (use-package ace-window
  741. :diminish ace-window-mode
  742. :bind ("M-o" . ace-window)
  743. :init
  744. (setq aw-scope 'frame
  745. aw-minibuffer-flag t))
  746. ;; savehist
  747. (use-package savehist
  748. :config
  749. (savehist-mode 1))
  750. ;; vertico
  751. (use-package vertico
  752. :bind (:map vertico-map
  753. ("C-RET" . vertico-exit-input)
  754. ("C-<return>" . vertico-exit-input)
  755. ("C-S-k" . kill-line)
  756. ("C-k" . vertico-previous)
  757. ("C-j" . vertico-next)
  758. ("RET" . vertico-directory-enter)
  759. ("DEL" . vertico-directory-delete-char)
  760. ("M-DEL" . vertico-directory-delete-word))
  761. :hook (minibuffer-setup . cursor-intangible-mode)
  762. :init
  763. (defun my/crm-indicator (args)
  764. (cons (format "[CRM%s] %s"
  765. (replace-regexp-in-string
  766. "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
  767. crm-separator)
  768. (car args))
  769. (cdr args)))
  770. (advice-add #'completing-read-multiple :filter-args #'my/crm-indicator)
  771. (setq vertico-cycle t
  772. enable-recursive-minibuffers t
  773. ;; read-extended-command-predicate #'command-completion-default-include-p
  774. read-extended-command-predicate nil
  775. minibuffer-prompt-properties '(read-only t ;; noindent 3
  776. cursor-intangible t
  777. face minibuffer-prompt))
  778. (vertico-mode 1)
  779. ;; for jinx
  780. (require 'vertico-multiform)
  781. (add-to-list 'vertico-multiform-categories
  782. '(jinx grid (vertico-grid-annotate . 20)))
  783. (vertico-multiform-mode 1))
  784. ;; orderless
  785. (use-package orderless
  786. :autoload orderless-define-completion-style
  787. :hook (text-mode . my/-setup-text-mode-completion-styles)
  788. :init
  789. (defun my/-setup-text-mode-completion-styles ()
  790. (setq-local completion-styles '(basic)))
  791. (orderless-define-completion-style my/orderless-with-initialism
  792. (orderless-matching-styles '(orderless-initialism
  793. orderless-regexp)))
  794. (setq orderless-matching-styles '(orderless-regexp)
  795. completion-styles '(orderless basic)
  796. completion-category-defaults nil
  797. completion-category-overrides '((file
  798. (styles basic partial-completion))
  799. (command
  800. (styles my/orderless-with-initialism basic)))))
  801. ;; marginalia
  802. (use-package marginalia
  803. :bind (:map minibuffer-local-map
  804. ("M-a" . marginalia-cycle))
  805. :init
  806. (marginalia-mode 1))
  807. ;; embark
  808. (use-package embark
  809. :bind (("C-," . embark-act)
  810. ("C-;" . embark-dwim)
  811. :map help-map
  812. ("B" . embark-bindings)
  813. :map embark-symbol-map
  814. ("h" . helpful-symbol)
  815. :map embark-become-file+buffer-map
  816. ("b" . consult-buffer)
  817. ("B" . switch-to-buffer))
  818. :init
  819. (setq embark-quit-after-action nil
  820. embark-indicators '(embark-minimal-indicator
  821. embark-isearch-highlight-indicator
  822. embark-highlight-indicator))
  823. :config
  824. (defvar-keymap my/embark-string-map
  825. :doc "Keymap for Embark string actions."
  826. :parent embark-expression-map
  827. "R" 'repunctuate-sentences)
  828. (defun my/embark-target-string ()
  829. "Target the string at point."
  830. (if-let (((not (eobp))) ; prevent next line from causing errors
  831. (bounds (bounds-of-thing-at-point 'string)))
  832. (append (list 'string (buffer-substring-no-properties (car bounds)
  833. (cdr bounds)))
  834. bounds)))
  835. (add-to-list 'embark-around-action-hooks
  836. '(repunctuate-sentences embark--mark-target))
  837. (add-to-list 'embark-keymap-alist
  838. '(string my/embark-string-map))
  839. (add-to-list 'embark-target-finders 'my/embark-target-string)
  840. (add-to-list 'display-buffer-alist
  841. '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
  842. nil
  843. (window-parameters (mode-line-format . none))))
  844. (evil-define-key '(normal motion) org-mode-map
  845. (kbd "C-,") #'embark-act))
  846. ;; consult
  847. (use-package consult
  848. :bind (("C-s" . consult-line)
  849. ("C-x b" . consult-buffer)
  850. ("C-S-s" . consult-ripgrep)
  851. ("C-x C-S-f" . consult-fd)
  852. ("C-x c k" . consult-keep-lines)
  853. ("C-x c f" . consult-focus-lines)
  854. ("C-x c r" . consult-recent-file)
  855. ("C-x c b" . consult-bookmark)
  856. ("C-x c d" . consult-fd)
  857. ("C-c p" . consult-fd)
  858. ("C-x c g" . consult-ripgrep)
  859. ("C-c P" . consult-ripgrep)
  860. ("C-x c y" . consult-yank-from-kill-ring)
  861. ("M-g i" . consult-imenu)
  862. ("M-g I" . consult-imenu-multi)
  863. ("M-g r" . consult-imenu-multi)
  864. :map help-map
  865. ("TAB". consult-info)
  866. ("RET" . consult-man))
  867. :hook (minibuffer-setup . my/consult-setup-minibuffer-completion)
  868. :init
  869. (defun my/consult-setup-minibuffer-completion ()
  870. (setq-local completion-in-region-function #'consult-completion-in-region))
  871. (evil-declare-motion #'consult-line))
  872. (use-package consult-eglot
  873. :commands consult-eglot-symbols)
  874. ;; wgrep
  875. (use-package wgrep)
  876. ;; integration for embark and consult
  877. (use-package embark-consult
  878. :hook (embark-collect-mode . consult-preview-at-point-mode))
  879. ;; corfu (autocomplete)
  880. (use-package corfu
  881. :bind (("M-<tab>" . completion-at-point)
  882. :map corfu-map
  883. ("C-j" . corfu-next)
  884. ("C-k" . corfu-previous)
  885. ("M-SPC" . corfu-insert-separator)
  886. ("M-m" . my/corfu-move-to-minibuffer))
  887. :init
  888. (defun my/corfu-move-to-minibuffer ()
  889. (interactive)
  890. (when completion-in-region--data
  891. (let ((completion-extra-properties corfu--extra)
  892. (completion-cycle-threshold completion-cycling))
  893. (apply #'consult-completion-in-region completion-in-region--data))))
  894. (setq corfu-cycle t
  895. corfu-auto t
  896. corfu-on-exact-match nil
  897. corfu-popupinfo-delay '(1.0 . 0.5)
  898. completion-cycle-threshold nil
  899. global-corfu-minibuffer
  900. ;; only enable corfu in the minibuffer in graphical frames
  901. (lambda ()
  902. (and (display-graphic-p)
  903. (not (eq (current-local-map)
  904. read-passwd-map)))))
  905. (global-corfu-mode 1)
  906. (corfu-popupinfo-mode 1)
  907. :config
  908. (add-to-list 'corfu-continue-commands #'my/corfu-move-to-minibuffer)
  909. (defun my/help-buffer-exists-p ()
  910. "Return if the buffer that `help-buffer' would, or nil if it doesn't exist."
  911. (or (and help-xref-following (derived-mode-p 'help-mode))
  912. (get-buffer "*Help*")))
  913. (defun my/-corfu-popupinfo-close-help-buffer (oldfun &rest args)
  914. (if (derived-mode-p 'emacs-lisp-mode)
  915. (let ((help-buf (my/help-buffer-exists-p)))
  916. (prog1
  917. (apply oldfun args)
  918. (when-let (((not help-buf))
  919. (buf (help-buffer)))
  920. ;; Ensure that, even if `help-buffer' returns nil in the future, we
  921. ;; don't kill the current buffer
  922. (kill-buffer buf))))
  923. (apply oldfun args)))
  924. (advice-add 'corfu-popupinfo--get-documentation :around
  925. 'my/-corfu-popupinfo-close-help-buffer))
  926. (use-package corfu-terminal
  927. :init
  928. (corfu-terminal-mode 1)
  929. :config
  930. (require 'corfu-terminal-popupinfo)
  931. (corfu-terminal-popupinfo-mode 1))
  932. (use-package dabbrev
  933. :ensure nil
  934. :config
  935. (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ")
  936. (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode)
  937. (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)
  938. (add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode))
  939. ;; cape (a bunch of capfs!)
  940. (use-package cape
  941. :bind (([remap dabbrev-expand] . cape-dabbrev)
  942. ("C-c P" . cape-line)
  943. ("C-c f" . cape-file))
  944. :hook (text-mode . my/-cape-setup-text-mode)
  945. :init
  946. (defun my/-cape-setup-text-mode ()
  947. ;; Only run this if we are not in `TeX-mode'
  948. (unless (bound-and-true-p TeX-mode-p)
  949. (setq-local completion-at-point-functions
  950. (append completion-at-point-functions (list 'cape-dict
  951. 'cape-dabbrev))
  952. corfu-auto nil))))
  953. ;; xref
  954. (use-package xref
  955. :init
  956. (evil-define-key '(normal motion) 'global
  957. "gr" #'xref-find-references)
  958. (setq xref-show-xrefs-function #'consult-xref
  959. xref-show-definitions-function #'consult-xref))
  960. ;; popup.el
  961. (use-package popup)
  962. ;; posframe
  963. (use-package posframe
  964. :init
  965. (defun my/posframe-tip (name msg)
  966. "Like `popup-tip', but with a posframe.
  967. NAME should be the buffer name to pass to `posframe-show'. MSG is the message to
  968. display."
  969. (unwind-protect
  970. (progn
  971. (posframe-show name
  972. :string msg
  973. :position (point)
  974. :max-width 80
  975. :border-width 2
  976. :border-color "white")
  977. (clear-this-command-keys)
  978. (push (read-event) unread-command-events)
  979. (posframe-hide name))
  980. (posframe-hide name))))
  981. (defun my/floating-tooltip (name msg)
  982. "If `display-graphic-p', call `my/posframe-tip', otherwise `popup-tip'.
  983. MSG is the message to show in the popup. NAME is the name of the buffer to pass
  984. to `posframe-show' if the display is graphical."
  985. (if (display-graphic-p)
  986. (my/posframe-tip name msg)
  987. (popup-tip msg)))
  988. ;; flymake
  989. (use-package flymake
  990. :config
  991. (require 'consult-flymake))
  992. ;; flycheck
  993. (use-package flycheck
  994. :hook ((sh-mode emacs-lisp-mode) . trusted-files-flycheck-mode-if-safe)
  995. :custom
  996. (flycheck-indication-mode 'left-margin)
  997. :init
  998. (setq flycheck-display-errors-function nil))
  999. (use-package consult-flycheck)
  1000. (defun my/sly-notes-at-point (&optional pos buffer)
  1001. "Return the sly notes at POS in BUFFER.
  1002. If BUFFER is nil, the current buffer is used."
  1003. (with-current-buffer (or buffer (current-buffer))
  1004. (unless pos
  1005. (setq pos (point)))
  1006. (cl-loop for overlay in (overlays-at pos)
  1007. for note = (overlay-get overlay 'sly-note)
  1008. when note
  1009. collect note)))
  1010. (defun my/diagnostic-at-point ()
  1011. "Show the diagnostics under point."
  1012. (interactive)
  1013. (let ((message))
  1014. (when-let (((bound-and-true-p flymake-mode))
  1015. (diag (get-char-property (point) 'flymake-diagnostic)))
  1016. (cl-callf nconc message (string-split (flymake--diag-text diag) "\n" t)))
  1017. (when (bound-and-true-p flycheck-mode)
  1018. (cl-callf nconc message
  1019. (mapcar 'flycheck-error-message (flycheck-overlay-errors-at (point)))))
  1020. ;; sly (lazy-loaded)
  1021. (when (featurep 'sly)
  1022. (cl-callf nconc message (mapcar (lambda (note)
  1023. (plist-get note :message))
  1024. (my/sly-notes-at-point))))
  1025. ;; jinx
  1026. (when-let (((bound-and-true-p jinx-mode))
  1027. (jinx-msg (jinx--get-overlays (point) (1+ (point)))))
  1028. (push "misspelled word" message))
  1029. (when message
  1030. (my/floating-tooltip " *my-diagnostic-posframe*"
  1031. (mapconcat (lambda (msg)
  1032. (concat "•" msg))
  1033. message "\n")))))
  1034. (defconst my/consult-flymake-flycheck-narrow
  1035. '((?e . "Error")
  1036. (?w . "Warning")
  1037. (?i . "Info")
  1038. (?n . "Info")))
  1039. (defun my/-consult-replace-flymake-error-level (candidates)
  1040. "Return CANDIDATES with the flymake error level note replaced with info."
  1041. (cl-loop for cand in candidates
  1042. collect
  1043. (cl-loop
  1044. with start = nil
  1045. for i below (length cand)
  1046. for props = (text-properties-at i cand)
  1047. for face = (plist-get props 'face)
  1048. when (eq face 'compilation-info) do
  1049. (setq start (or start i))
  1050. else when start do
  1051. (setf (substring cand start i)
  1052. (propertize (string-pad "info" (- i start))
  1053. 'face (flycheck-error-level-error-list-face
  1054. 'info)))
  1055. (cl-return cand)
  1056. finally return cand)))
  1057. (defun my/consult-flymake-flycheck-candidates (&optional project)
  1058. "Return combined candidate list for flymake and flycheck.
  1059. With PROJECT, return the candiadeets for that project."
  1060. (let ((had-errors))
  1061. (prog1
  1062. (seq-uniq
  1063. (append
  1064. (when-let (((bound-and-true-p flymake-mode))
  1065. (diags (if project (flymake--project-diagnostics
  1066. project)
  1067. (flymake-diagnostics))))
  1068. (setq had-errors t)
  1069. (my/-consult-replace-flymake-error-level
  1070. (consult-flymake--candidates diags)))
  1071. (when (boundp 'flycheck-mode)
  1072. (if project
  1073. (cl-loop for buf in (project-buffers project)
  1074. append
  1075. (with-current-buffer buf
  1076. (when (and flycheck-mode flycheck-current-errors)
  1077. (setq had-errors t)
  1078. (consult-flycheck--candidates))))
  1079. (when (and flycheck-mode flycheck-current-errors)
  1080. (setq had-errors t)
  1081. (consult-flycheck--candidates))))))
  1082. (unless had-errors
  1083. (user-error "No errors (Flymake: %s | Flycheck: %s)"
  1084. (cond
  1085. ((not (bound-and-true-p flymake-mode))
  1086. "not running")
  1087. ((seq-difference (flymake-running-backends)
  1088. (flymake-reporting-backends))
  1089. "running")
  1090. (t "finished"))
  1091. (if (boundp 'flycheck-last-status-change)
  1092. flycheck-last-status-change
  1093. "not running"))))))
  1094. (defun my/consult-flymake-flycheck (&optional project)
  1095. "Jump to flymake or flycheck error.
  1096. With PROJECT, give diagnostics for all buffers in the current project."
  1097. (interactive "P")
  1098. (consult--read
  1099. (consult--with-increased-gc
  1100. (my/consult-flymake-flycheck-candidates
  1101. (and project (project-current))))
  1102. :prompt "Error: "
  1103. :category 'flymake-flycheck-error
  1104. :history t
  1105. :require-match t
  1106. :sort nil
  1107. :narrow (consult--type-narrow my/consult-flymake-flycheck-narrow)
  1108. :group (consult--type-group my/consult-flymake-flycheck-narrow)
  1109. :lookup #'consult--lookup-candidate
  1110. :state (consult--jump-state)))
  1111. (with-eval-after-load 'flymake
  1112. (keymap-set flymake-mode-map "C-c e" 'my/diagnostic-at-point)
  1113. (keymap-set flymake-mode-map "C-c E" 'my/consult-flymake-flycheck))
  1114. (with-eval-after-load 'flycheck
  1115. (keymap-set flycheck-mode-map "C-c e" 'my/diagnostic-at-point)
  1116. (keymap-set flycheck-mode-map "C-c E" 'my/consult-flymake-flycheck))
  1117. (with-eval-after-load 'jinx
  1118. (keymap-set jinx-mode-map "C-c e" 'my/diagnostic-at-point))
  1119. ;; eldoc
  1120. (use-package eldoc
  1121. :diminish eldoc-mode
  1122. :init
  1123. (setq-default eldoc-echo-area-use-multiline-p nil))
  1124. ;; eglot
  1125. (use-package eglot
  1126. :demand t
  1127. :pin gnu ;; try to force Elpa version to fix warnings
  1128. :hook ((eglot-managed-mode . my/-eglot-setup))
  1129. :init
  1130. ;; (defun my/eglot-in-text-mode-only ()
  1131. ;; (when (eq major-mode 'text-mode)
  1132. ;; (trusted-files-eglot-ensure-if-safe)))
  1133. (defvar my/-eglot-documentation-buffer nil
  1134. "Buffer for showing documentation for `my/eglot-documentation-at-point'.")
  1135. (defun my/eglot-documentation-at-point ()
  1136. "Show documentation for a symbol at point."
  1137. (interactive)
  1138. (if-let (server (eglot-current-server))
  1139. (progn
  1140. (if-let* (((not (buffer-live-p my/-eglot-documentation-buffer)))
  1141. (name (generate-new-buffer-name "*eglot documentation*")))
  1142. (setq my/-eglot-documentation-buffer (generate-new-buffer name)))
  1143. (eglot-hover-eldoc-function
  1144. (lambda (info _ _)
  1145. (if-let (((not (seq-empty-p info)))
  1146. (buff (current-buffer)))
  1147. (with-current-buffer my/-eglot-documentation-buffer
  1148. (read-only-mode -1)
  1149. (erase-buffer)
  1150. (insert info)
  1151. (goto-char (point-min))
  1152. (special-mode)
  1153. (read-only-mode 1)
  1154. (when (not (get-buffer-window my/-eglot-documentation-buffer nil))
  1155. (switch-to-buffer-other-window my/-eglot-documentation-buffer t)
  1156. (switch-to-buffer-other-window buff t)))))))))
  1157. (defun my/-eglot-cleanup-doc-buffer (_server &optional _interactive _timeout
  1158. preserve-buffers)
  1159. (when (and (not preserve-buffers)
  1160. (buffer-live-p my/-eglot-documentation-buffer)
  1161. (cl-every (lambda (buffer)
  1162. (with-current-buffer buffer
  1163. (let ((server (eglot-current-server)))
  1164. (or (not (eglot-lsp-server-p server))
  1165. (eglot--shutdown-requested server)))))
  1166. (buffer-list)))
  1167. (kill-buffer my/-eglot-documentation-buffer)))
  1168. (advice-add 'eglot-shutdown :after 'my/-eglot-cleanup-doc-buffer)
  1169. (defun my/-eglot-setup ()
  1170. "Setup eldoc variables for `eglot-managed-mode-hook'."
  1171. (setq-local evil-lookup-func #'my/eglot-documentation-at-point)
  1172. (evil-define-key '(normal motion) 'local
  1173. "K" #'evil-lookup
  1174. "gR" #'eglot-rename
  1175. "gA" #'eglot-code-actions
  1176. "gs" #'consult-eglot-symbols)
  1177. (eglot-inlay-hints-mode -1))
  1178. (setq eglot-autoshutdown t
  1179. eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider))
  1180. :config
  1181. (add-to-list 'eglot-server-programs
  1182. (cons '(c-mode c-ts-mode c++-mode c++-ts-mode objc-mode)
  1183. '("clangd" "--all-scopes-completion" "--background-index"
  1184. "--clang-tidy" "--completion-style=detailed"
  1185. "--header-insertion=never" "--pch-storage=memory"
  1186. "--function-arg-placeholders"
  1187. "--compile-commands-dir=build"))))
  1188. ;; LTeX (languagetool)
  1189. (require 'ltex-eglot)
  1190. ;; apheleia (code formatter)
  1191. (use-package apheleia
  1192. :defer nil
  1193. :bind ("C-c o" . apheleia-format-buffer)
  1194. :init
  1195. (add-to-list 'auto-mode-alist `(,(rx "/.clang-format" eos) . yaml-ts-mode))
  1196. :config
  1197. (with-eval-after-load 'apheleia
  1198. (setf (alist-get 'java-mode apheleia-mode-alist) 'clang-format
  1199. (alist-get 'java-ts-mode apheleia-mode-alist) 'clang-format))
  1200. (apheleia-global-mode +1))
  1201. ;; gud
  1202. (use-package gud
  1203. :demand t
  1204. :ensure nil
  1205. :after (project evil)
  1206. :bind (:map project-prefix-map
  1207. ("U" . my/project-gdb))
  1208. :config
  1209. (setq gdb-debuginfod-enable-setting t)
  1210. (defvar my/project-gdb-command nil
  1211. "Command to use in `my/project-gdb'.")
  1212. (put 'my/project-gdb-command 'safe-local-variable (lambda (val)
  1213. (stringp val)))
  1214. (defun my/project-gdb (project command-line)
  1215. "Run gdb in the project root"
  1216. (interactive (let* ((project (project-current t))
  1217. (default-directory (project-root project)))
  1218. (list project (gud-query-cmdline 'gdb))))
  1219. (let ((default-directory (project-root project)))
  1220. (gdb command-line)))
  1221. (evil-set-initial-state 'gdb-locals-mode 'motion)
  1222. (evil-collection-inhibit-insert-state 'gdb-locals-mode-map)
  1223. (evil-define-key '(normal motion visual) gdb-locals-mode-map
  1224. (kbd "TAB") (keymap-lookup gdb-locals-mode-map "TAB")
  1225. (kbd "RET") #'gdb-edit-locals-value
  1226. (kbd "<mouse-1>") #'gdb-edit-locals-value
  1227. "q" #'kill-current-buffer)
  1228. (evil-set-initial-state 'gdb-registers-mode 'motion)
  1229. (evil-collection-inhibit-insert-state 'gdb-registers-mode-map)
  1230. (evil-define-key '(normal motion visual) gdb-registers-mode-map
  1231. (kbd "TAB") (keymap-lookup gdb-registers-mode-map "TAB")
  1232. (kbd "RET") #'gdb-edit-register-value
  1233. (kbd "<mouse-1>") #'gdb-edit-register-value
  1234. "q" #'kill-current-buffer
  1235. (kbd "C-c f") #'gdb-registers-toggle-filter
  1236. (kbd "C-c F") (lambda ()
  1237. "Customize the filter for the registers buffer."
  1238. (interactive)
  1239. (customize-option-other-window
  1240. 'gdb-registers-filter-pattern-list)))
  1241. (evil-set-initial-state 'gdb-frames-mode 'motion)
  1242. (evil-collection-inhibit-insert-state 'gdb-frames-mode-map)
  1243. (evil-define-key '(normal motion visual) gdb-frames-mode-map
  1244. "q" #'kill-current-buffer
  1245. (kbd "RET") #'gdb-select-frame)
  1246. (evil-set-initial-state 'gdb-breakpoints-mode 'motion)
  1247. (evil-collection-inhibit-insert-state 'gdb-breakpoints-mode-map)
  1248. (evil-define-key '(normal motion visual) gdb-breakpoints-mode-map
  1249. (kbd "TAB") (keymap-lookup gdb-breakpoints-mode-map "TAB")
  1250. "q" #'gdb-delete-frame-or-window
  1251. "D" #'gdb-delete-breakpoint
  1252. (kbd "RET") #'gdb-goto-breakpoint
  1253. (kbd "<mouse-1>") #'gdb-goto-breakpoint
  1254. (kbd "SPC") #'gdb-toggle-breakpoint)
  1255. (evil-set-initial-state 'gdb-threads-mode 'motion)
  1256. (evil-collection-inhibit-insert-state 'gdb-threads-mode-map)
  1257. (evil-define-key '(normal motion visual) gdb-threads-mode-map
  1258. (kbd "TAB") (keymap-lookup gdb-threads-mode-map "TAB")
  1259. "q" #'gdb-delete-frame-or-window
  1260. "D" #'gdb-frame-disassembly-for-thread
  1261. (kbd "C-c f") #'gdb-display-stack-for-thread
  1262. (kbd "C-c i") #'gdb-interrupt-thread
  1263. (kbd "C-c l") #'gdb-display-locals-for-thread
  1264. (kbd "C-c r") #'gdb-display-registers-for-thread
  1265. (kbd "C-c c") #'gdb-continue-thread
  1266. (kbd "C-c d") #'gdb-display-disassembly-for-thread
  1267. (kbd "C-c s") #'gdb-step-thread
  1268. (kbd "C-c F") #'gdb-frame-stack-for-thread
  1269. (kbd "C-c L") #'gdb-frame-locals-for-thread
  1270. (kbd "C-c R") #'gdb-frame-registers-for-thread
  1271. (kbd "RET") #'gdb-select-thread
  1272. (kbd "<mouse-2>") #'gdb-select-thread))
  1273. ;; dape
  1274. (use-package dape
  1275. :hook ((after-init . dape-breakpoint-load)
  1276. (kill-emacs . dape-breakpoint-save)
  1277. (dape-start . save-some-buffers)
  1278. (dape-display-source . pulse-momentary-highlight-one-line))
  1279. :bind (:map dape-info-parent-mode-map
  1280. ("<tab>" . dape--info-buffer-tab))
  1281. :init
  1282. (setopt dape-default-breakpoints-file (no-littering-expand-var-file-name
  1283. "dape-breakpoints"))
  1284. :config
  1285. (setopt dape-buffer-window-arrangement 'right)
  1286. (dape-breakpoint-global-mode 1))
  1287. ;; dumb-jump
  1288. (use-package dumb-jump
  1289. :init
  1290. (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
  1291. ;; yasnippet
  1292. (use-package yasnippet
  1293. :demand t
  1294. :bind ("C-c s" . yas-expand)
  1295. :config
  1296. (yas-global-mode 1))
  1297. ;; project.el
  1298. (use-package project
  1299. :bind (([remap project-compile] . my/project-compile-or-default)
  1300. :map project-prefix-map
  1301. ("s" . my/project-eshell)
  1302. ("u" . my/project-run))
  1303. :init
  1304. (defvar eshell-buffer-name)
  1305. (defun my/project-eshell (prompt &optional arg)
  1306. "Switch to or create an eshell buffer in the current projects root."
  1307. (interactive (list t current-prefix-arg))
  1308. (if-let ((proj (project-current prompt))
  1309. (default-directory (project-root proj))
  1310. (eshell-buffer-name
  1311. (concat "*eshell for project " default-directory "*")))
  1312. (eshell arg)))
  1313. (defun my/project-eshell-or-default (&optional arg)
  1314. "Open an eshell for the current project, otherwise, open a normal eshell."
  1315. (interactive "P")
  1316. (unless (my/project-eshell nil arg)
  1317. (eshell arg)))
  1318. (defun my/project-compile-or-default ()
  1319. "If in a project, run `project-compile', otherwise run `compile'."
  1320. (interactive)
  1321. (if (project-current)
  1322. (call-interactively 'project-compile)
  1323. (call-interactively 'compile)))
  1324. (defvar my/project-run-command nil
  1325. "Command to run with `my/project-run'.")
  1326. (put 'my/project-run-command 'safe-local-variable (lambda (val)
  1327. (stringp val)))
  1328. (defvar my/project-run-dir nil
  1329. "Directory to run project in with `my/project-run'.")
  1330. (put 'my/project-run-dir 'safe-local-variable (lambda (val)
  1331. (stringp val)))
  1332. (defvar my/-project-run-history '()
  1333. "Commands previously run with `my/project-run'")
  1334. (defvar my/project-root-marker ".project-root"
  1335. "Marker file to look for in non-vc backed projects.")
  1336. (defun my/project-get-root-dir ()
  1337. "Get the root dir for the current project"
  1338. (let* ((proj (project-current nil))
  1339. (default-directory (if proj
  1340. (project-root proj)
  1341. default-directory)))
  1342. (if my/project-run-dir
  1343. (expand-file-name my/project-run-dir)
  1344. default-directory)))
  1345. (defvar my/compile-use-comint t
  1346. "Weather or not to use comint by default for compile buffers.")
  1347. (defun my/-compile-use-comint-by-default (args)
  1348. (if my/compile-use-comint
  1349. (list (car args) t)
  1350. args))
  1351. (advice-add 'compile :filter-args 'my/-compile-use-comint-by-default)
  1352. (defun my/project-run (command comint)
  1353. "Like `project-compile', but for running a project.
  1354. COMMAND and COMINT are like `compile'."
  1355. (interactive
  1356. (list
  1357. (let ((default-directory (my/project-get-root-dir)))
  1358. (read-shell-command "Run Command: "
  1359. (or (car my/-project-run-history)
  1360. my/project-run-command)
  1361. (if (and my/project-run-command
  1362. (equal my/project-run-command
  1363. (car-safe my/-project-run-history)))
  1364. '(my/-project-run-history . 1)
  1365. 'my/-project-run-history)))
  1366. (consp current-prefix-arg)))
  1367. (let* ((default-directory (my/project-get-root-dir))
  1368. (compilation-buffer-name-function (lambda (_)
  1369. (progn "*run project*")))
  1370. (compilation-directory default-directory)
  1371. (compile-history nil)
  1372. (compile-command nil))
  1373. (compile command comint)
  1374. (when (not my/project-run-command)
  1375. (setq my/project-run-command command))))
  1376. :config
  1377. (defun my/project-try-dotfile (dir)
  1378. (if-let (root (locate-dominating-file dir my/project-root-marker))
  1379. (list 'vc nil root)))
  1380. (add-hook 'project-find-functions #'my/project-try-dotfile))
  1381. ;; comint
  1382. (use-package comint
  1383. :ensure nil
  1384. :after evil
  1385. :config
  1386. (evil-set-initial-state 'comint-mode 'normal))
  1387. ;; nxml
  1388. (use-package nxml-mode
  1389. :ensure nil
  1390. :hook (nxml-mode . my/-nxml-setup)
  1391. :init
  1392. (defun my/-nxml-setup ()
  1393. "Setup `nxml-mode'."
  1394. (sgml-electric-tag-pair-mode 1)
  1395. (setq-local completion-at-point-functions
  1396. '(rng-completion-at-point cape-file)))
  1397. (add-to-list 'auto-mode-alist
  1398. `(,(concat
  1399. (regexp-opt '("gschema" "gresource" "ui")) "\\'")
  1400. . nxml-mode)))
  1401. ;; devdocs
  1402. (use-package devdocs
  1403. :bind (("C-h D" . devdocs-lookup)))
  1404. ;; Bibtex (built in)
  1405. (require 'bibtex)
  1406. ;; Better URL highlighting and matching
  1407. (dolist (field '("howpublished" "url"))
  1408. (add-to-list 'bibtex-generate-url-list
  1409. `((,field . ,(rx "http" (? "s") "://" (+ (not "}"))))
  1410. "%s"
  1411. (,field
  1412. ,(rx (? (or "\\url{" "{")) (group "http" (? "s") "://"
  1413. (+ (not "}")))
  1414. (? "}"))
  1415. ,(lambda (field)
  1416. (match-string 1 field))))))
  1417. (defun my/bibtex-in-entry-p (&optional exclude-braces)
  1418. "Return t is point is inside a BibTeX entry.
  1419. When EXCLUDE-BRACES is non-nil, don't count the first and last brace of the
  1420. entry as in the entry. That is, if the point is on the first { or last } of the
  1421. entry, return nil."
  1422. (save-excursion
  1423. (when (and exclude-braces (eq ?\} (char-after)))
  1424. (forward-char))
  1425. ;; go to top level and check if the character at point is {
  1426. (let ((start-pos (point))
  1427. (last-valid (point)))
  1428. (condition-case nil
  1429. (while t
  1430. (backward-up-list 1 t t)
  1431. (setq last-valid (point)))
  1432. (error
  1433. (and
  1434. (eq ?\{ (char-after last-valid))
  1435. (or (not exclude-braces)
  1436. (not (= start-pos last-valid)))))))))
  1437. (defvar my/bibtex-indent-width 4
  1438. "Width to indent for `my/bibtex-calculate-indentation'.")
  1439. (defun my/bibtex-calculate-indentation ()
  1440. "Calculate the column to indent to on the current line."
  1441. (save-excursion
  1442. (back-to-indentation)
  1443. (if (my/bibtex-in-entry-p t)
  1444. my/bibtex-indent-width
  1445. 0)))
  1446. (defun my/bibtex-empty-line-p ()
  1447. "Return t if the current line is only blank characters."
  1448. (save-excursion
  1449. (beginning-of-line)
  1450. (looking-at (rx (* blank) eol))))
  1451. (defun my/bibtex-indent-line ()
  1452. "Indent the current line."
  1453. (interactive)
  1454. (save-excursion
  1455. (beginning-of-line)
  1456. (when (looking-at (rx (+ blank)))
  1457. (delete-region (point) (match-end 0)))
  1458. (indent-to (my/bibtex-calculate-indentation)))
  1459. (when (looking-at (rx (+ blank) eol))
  1460. (end-of-line)))
  1461. (defun my/bibtex-indent-or-find-text ()
  1462. "Either indent the current line or jump to the current fields text.
  1463. If the current line is only whitespace call `my/bibtex-calculate-indentation',
  1464. otherwise, call `bibtex-find-text'."
  1465. (interactive)
  1466. (if (my/bibtex-empty-line-p)
  1467. (my/bibtex-indent-line)
  1468. (bibtex-find-text)))
  1469. (defun my/bibtex-indent-or-find-text-and-insert ()
  1470. "Like `my/bibtex-indent-or-find-text', but enter insert mode after."
  1471. (interactive)
  1472. (my/bibtex-indent-or-find-text)
  1473. (if (my/bibtex-empty-line-p)
  1474. (evil-append 1)
  1475. (evil-insert 1)))
  1476. (defun my/-bibtex-setup-indent ()
  1477. "Set up `bibtex-mode' indentation stuff."
  1478. (setq-local indent-line-function 'my/bibtex-indent-line
  1479. electric-indent-chars '(?\n ?\{ ?\} ?,)))
  1480. (defun my/-bibtex-fix-fill-prefix ()
  1481. "`bivtex-mode' has a bad habbit of messing up `fill-prefix'."
  1482. (when (eq major-mode 'bibtex-mode)
  1483. (setq-local fill-prefix nil)))
  1484. (advice-add 'bibtex-mode :after 'my/-bibtex-fix-fill-prefix)
  1485. (add-hook 'bibtex-mode-hook 'my/-bibtex-setup-indent)
  1486. (keymap-set bibtex-mode-map "RET" 'newline-and-indent)
  1487. (keymap-set bibtex-mode-map "TAB" 'my/bibtex-indent-or-find-text)
  1488. (evil-define-key 'normal bibtex-mode-map
  1489. (kbd "TAB") 'my/bibtex-indent-or-find-text-and-insert)
  1490. ;; Latex help (from elisp file)
  1491. (require 'latex-help)
  1492. ;; AUCTeX
  1493. (use-package auctex
  1494. :hook ((LaTeX-mode . turn-on-reftex)
  1495. (LaTeX-mode . LaTeX-math-mode)
  1496. (LaTeX-mode . my/-setup-LaTeX-mode)
  1497. (LaTeX-mode . trusted-files-flycheck-mode-if-safe))
  1498. :bind (:map TeX-mode-map
  1499. ("C-c ?" . latex-help))
  1500. :init
  1501. (add-to-list 'major-mode-remap-alist '(plain-tex-mode . plain-TeX-mode))
  1502. (add-to-list 'major-mode-remap-alist '(latex-mode . LaTeX-mode))
  1503. (add-to-list 'major-mode-remap-alist '(ams-tex-mode . AmSTeX-mode))
  1504. (add-to-list 'major-mode-remap-alist '(context-mode . ConTeXt-mode))
  1505. (add-to-list 'major-mode-remap-alist '(texinfo-mode . Texinfo-mode))
  1506. (add-to-list 'major-mode-remap-alist '(doctex-mode . docTeX-mode))
  1507. (add-to-list 'auto-mode-alist '("/\\.latexmkrc\\'" . perl-mode))
  1508. (add-to-list 'auto-mode-alist '("\\.[tT]e[xX]\\'" . LaTeX-mode))
  1509. :config
  1510. (defun my/-auctex-texdoc-setup-env (oldfun &rest args)
  1511. (let ((process-environment process-environment)
  1512. (emacs-cmd (concat "emacsclient" (and (not (display-graphic-p)) " -nw"))))
  1513. (setenv "PDFVIEWER_texdoc" "evince")
  1514. (setenv "MDVIEWER_texdoc" emacs-cmd)
  1515. (setenv "PAGER_texdoc" emacs-cmd)
  1516. (apply oldfun args)))
  1517. (advice-add 'TeX-documentation-texdoc :around 'my/-auctex-texdoc-setup-env)
  1518. (defun my/-setup-LaTeX-mode ()
  1519. (setq evil-lookup-func 'latex-help-at-point))
  1520. (setq TeX-auto-save t
  1521. TeX-parse-self t
  1522. reftex-plug-into-AUCTeX t)
  1523. (evil-define-operator my/evil-LaTeX-fill (beg end)
  1524. "Like `evil-fill', but using auctex."
  1525. ;; The code here came straight from `evil-fill'
  1526. :move-point nil
  1527. :type line
  1528. (save-excursion
  1529. (ignore-errors (LaTeX-fill-region beg end))))
  1530. (evil-define-operator my/evil-LaTeX-fill-and-move (beg end)
  1531. "Like `evil-fill-and-move', but using auctex."
  1532. ;; The code here came straight from `evil-fill-and-move'
  1533. :move-point nil
  1534. :type line
  1535. (let ((marker (make-marker)))
  1536. (move-marker marker (1- end))
  1537. (ignore-errors
  1538. (LaTeX-fill-region beg end)
  1539. (goto-char marker)
  1540. (evil-first-non-blank))))
  1541. (evil-define-key 'normal TeX-mode-map
  1542. "gq" 'my/evil-LaTeX-fill-and-move
  1543. "gw" 'my/evil-LaTeX-fill)
  1544. (setq-default TeX-master nil)
  1545. (require 'tex)
  1546. (TeX-global-PDF-mode 1))
  1547. ;; blueprint
  1548. (use-package blueprint-ts-mode
  1549. :hook (blueprint-ts-mode . trusted-files-eglot-ensure-if-safe)
  1550. :after eglot)
  1551. ;; python-ts-mode
  1552. (use-package python-ts-mode
  1553. :ensure nil
  1554. :hook (python-ts-mode . trusted-files-eglot-ensure-if-safe))
  1555. ;; java-ts-mode
  1556. (use-package java-ts-mode
  1557. :hook ((java-ts-mode . trusted-files-eglot-ensure-if-safe)
  1558. (java-ts-mode . my/-setup-java-ts-mode))
  1559. :config
  1560. (defun my/-setup-java-ts-mode ()
  1561. (let ((rules (car treesit-simple-indent-rules)))
  1562. (setcdr rules
  1563. (cons '((and (parent-is "array_initializer")
  1564. (node-is "array_initializer"))
  1565. parent-bol java-ts-mode-indent-offset)
  1566. (nthcdr 1 rules))))))
  1567. ;; c-ts-mode
  1568. (use-package c-ts-mode
  1569. :after evil
  1570. :hook ((c-ts-mode c++-ts-mode) . trusted-files-eglot-ensure-if-safe)
  1571. :init
  1572. (setq-default c-ts-mode-indent-offset 4)
  1573. :config
  1574. (evil-define-key 'normal 'c-ts-mode-map
  1575. "go" #'ff-find-other-file
  1576. "gO" #'ff-find-other-file-other-window)
  1577. (evil-define-key 'normal 'c++-ts-mode-map
  1578. "go" #'ff-find-other-file
  1579. "gO" #'ff-find-other-file-other-window)
  1580. (evil-define-key 'normal 'objc-mode-map
  1581. "go" #'ff-find-other-file
  1582. "gO" #'ff-find-other-file-other-window))
  1583. ;; GLSL
  1584. (use-package glsl-mode)
  1585. ;; php-mode
  1586. (use-package php-mode
  1587. :hook (php-mode . trusted-files-eglot-ensure-if-safe))
  1588. ;; web-mode
  1589. (use-package web-mode
  1590. :hook (web-mode . trusted-files-eglot-ensure-if-safe)
  1591. :init
  1592. (add-to-list 'eglot-server-programs
  1593. '(web-mode . ("vscode-html-language-server" "--stdio"))))
  1594. ;; JavaScript
  1595. (use-package js
  1596. :ensure nil
  1597. :hook (js-ts-mode . trusted-files-eglot-ensure-if-safe))
  1598. ;; TypeScript
  1599. (use-package typescript-ts-mode
  1600. :ensure nil
  1601. :hook (typescript-ts-mode . trusted-files-eglot-ensure-if-safe)
  1602. :init
  1603. (add-to-list 'auto-mode-alist `(,(rx ".ts" eos) . typescript-ts-mode)))
  1604. ;; Polymode
  1605. (use-package polymode
  1606. :config
  1607. (define-hostmode my/poly-web-hostmode
  1608. :mode 'web-mode)
  1609. (define-innermode my/poly-php-innermode
  1610. :mode 'php-mode
  1611. :head-matcher (regexp-quote "<?php")
  1612. :tail-matcher (regexp-quote "?>")
  1613. :head-mode 'body
  1614. :tail-mode 'body)
  1615. (define-polymode my/poly-web-mode
  1616. :hostmode 'my/poly-web-hostmode
  1617. :innermodes '(my/poly-php-innermode))
  1618. (add-to-list 'auto-mode-alist '("\\.php\\|\\.phtml\\'" . my/poly-web-mode)))
  1619. ;; shell-mode
  1620. (use-package sh-script
  1621. :ensure nil
  1622. :hook (sh-mode . my/-setup-sh-mode)
  1623. :init
  1624. (defun my/-setup-sh-mode ()
  1625. (add-to-list 'completion-at-point-functions #'cape-file)))
  1626. ;; go mode
  1627. (use-package go-mode
  1628. :defer nil
  1629. :hook (go-mode . trusted-files-eglot-ensure-if-safe))
  1630. (use-package go-ts-mode
  1631. :ensure nil
  1632. :hook (go-ts-mode . trusted-files-eglot-ensure-if-safe))
  1633. ;; rust
  1634. (use-package rust-mode)
  1635. (use-package rust-ts-mode
  1636. :ensure nil
  1637. :hook (rust-ts-mode . trusted-files-eglot-ensure-if-safe))
  1638. ;; zig
  1639. (use-package zig-mode
  1640. :hook (zig-mode . trusted-files-eglot-ensure-if-safe))
  1641. ;; lua
  1642. (use-package lua-mode
  1643. :hook (lua-mode . trusted-files-eglot-ensure-if-safe))
  1644. ;; markdown
  1645. (use-package markdown-mode
  1646. :hook (markdown-mode . auto-fill-mode))
  1647. ;; groovy
  1648. (use-package groovy-mode)
  1649. ;; cmake
  1650. (require 'cmake-mode)
  1651. (require 'cmake-ts-mode)
  1652. (with-eval-after-load 'cmake-mode
  1653. (setq cmake-ts-mode-indent-offset tab-width))
  1654. ;; kdl
  1655. (require 'kdl-ts-mode)
  1656. ;; json
  1657. (use-package json-ts-mode
  1658. :hook (json-ts-mode . trusted-files-eglot-ensure-if-safe))
  1659. (use-package json-mode)
  1660. ;; csv
  1661. (use-package csv-mode)
  1662. ;; firejail
  1663. (require 'firejail-mode)
  1664. ;; yaml
  1665. (use-package yaml-ts-mode
  1666. :hook ((yaml-ts-mode . trusted-files-eglot-ensure-if-safe)
  1667. (yaml-ts-mode . my/-setup-yaml-ts-mode))
  1668. :init
  1669. (defun my/-setup-yaml-ts-mode ()
  1670. (setq indent-line-function #'yaml-indent-line)))
  1671. (use-package yaml-mode)
  1672. ;; yuck (config language for eww)
  1673. (use-package yuck-mode)
  1674. ;; Some Elisp indentation stuff
  1675. ;; Source: https://github.com/magit/emacsql
  1676. ;; emacsql.el line 394
  1677. (defun my/lisp-inside-plist-p ()
  1678. "Return t if point is inside a plist."
  1679. (save-excursion
  1680. (let ((start (point)))
  1681. (beginning-of-defun)
  1682. (when-let ((sexp (nth 1 (parse-partial-sexp (point) start))))
  1683. (goto-char sexp)
  1684. (looking-at (rx "(" (* (syntax whitespace)) ":"))))))
  1685. (defun my/-calculate-indent-fix-plists (oldfun &rest args)
  1686. "This function is meant to advise `calculate-lisp-indent'.
  1687. It calls OLDFUN with ARGS in such an environment as to prevent the default
  1688. indentation of plists."
  1689. (if (and (eq major-mode 'emacs-lisp-mode)
  1690. (save-excursion
  1691. (beginning-of-line)
  1692. (my/lisp-inside-plist-p)))
  1693. (let ((lisp-indent-offset 1))
  1694. (apply oldfun args))
  1695. (apply oldfun args)))
  1696. (advice-add 'calculate-lisp-indent :around
  1697. 'my/-calculate-indent-fix-plists)
  1698. (defvar my/max-lisp-noindent-comment-search-lines 30
  1699. "Max lines to search for the noindent comment.")
  1700. (defun my/-calculate-lisp-indent-noindent-comment (oldfun &rest args)
  1701. "This function is meant to advise `calculate-lisp-indent'.
  1702. It calls OLDFUN with ARGS, unless the line ends with the comment
  1703. ; noindent [LINES]
  1704. In this case, it just returns the current amount of indentation. LINES is the
  1705. number of lines that this comment affects. This is limited by
  1706. `my/max-lisp-noindent-comment-search-lines'.
  1707. This only works if its on the first or second form in a block. I think this is
  1708. because the indentation code only checks those and then assumes the same
  1709. indentation for every following line in the same block. This is probably OK as
  1710. I can't imagine too many instances where you need to randomly change the indent
  1711. midway through a block, and in those cases you can just stick this on the first
  1712. line in the block and manually deal with indentation."
  1713. (if (and (save-excursion
  1714. (end-of-line)
  1715. (re-search-backward
  1716. (rx (+ ";") (syntax whitespace) "noindent"
  1717. (? (syntax whitespace) (group (+ num)))
  1718. line-end)
  1719. (pos-bol (- my/max-lisp-noindent-comment-search-lines))
  1720. t))
  1721. (save-excursion
  1722. ;; if we are on a blank line, move forward a line
  1723. (when (zerop (length (buffer-substring-no-properties
  1724. (pos-bol) (pos-eol))))
  1725. (beginning-of-line 2))
  1726. (<= (count-lines (match-beginning 0) (pos-eol))
  1727. (if-let ((match (match-string 1)))
  1728. (string-to-number match)
  1729. 1))))
  1730. (save-excursion
  1731. (beginning-of-line)
  1732. (looking-at (rx (* blank)))
  1733. (length (match-string 0)))
  1734. (apply oldfun args)))
  1735. (advice-add 'calculate-lisp-indent :around
  1736. 'my/-calculate-lisp-indent-noindent-comment)
  1737. ;; sly
  1738. (use-package sly
  1739. ;; :hook (lisp-mode . my/-lisp-mode-autoconnect-sly)
  1740. :bind (:map sly-mode-map
  1741. ("C-c e" . my/diagnostic-at-point))
  1742. :autoload sly-connected-p
  1743. :init
  1744. (defun my/-lisp-mode-autoconnect-sly ()
  1745. (unless (sly-connected-p)
  1746. (trusted-files-sly-if-safe)))
  1747. (setq inferior-lisp-program "/usr/bin/sbcl")
  1748. (defun my/-sly-fix-special-buffers ()
  1749. (when (string-match-p (rx bos "*" (* any) "*" eos) (buffer-name))
  1750. (setq-local show-trailing-whitespace nil)))
  1751. (add-hook 'lisp-mode-hook 'my/-sly-fix-special-buffers)
  1752. :config
  1753. (evil-define-key 'insert sly-mrepl-mode-map
  1754. (kbd ",") 'self-insert-command)
  1755. (evil-define-key nil sly-mrepl-mode-map
  1756. (kbd "C-c ,") 'sly-mrepl-shortcut)
  1757. (sly-symbol-completion-mode -1))
  1758. ;; pdf-tools
  1759. (use-package pdf-tools
  1760. :hook (pdf-view-mode . my/setup-pdf-view-mode)
  1761. :init
  1762. (setq pdf-misc-print-program-executable "lp")
  1763. (defun my/setup-pdf-view-mode ()
  1764. (display-line-numbers-mode -1)
  1765. (evil-define-key '(motion normal visual) 'local
  1766. (kbd "C-s") #'isearch-forward
  1767. (kbd "C-r") #'isearch-backward)
  1768. (setq-local cursor-type nil))
  1769. (pdf-tools-install))
  1770. ;; doc view
  1771. (use-package doc-view
  1772. :ensure nil
  1773. :hook (doc-view-mode . my/-setup-doc-view-mode)
  1774. :init
  1775. (defun my/-setup-doc-view-mode ()
  1776. (display-line-numbers-mode -1)
  1777. (evil-define-key '(motion normal visual) 'local
  1778. (kbd "C-s") #'isearch-forward
  1779. (kbd "C-r") #'isearch-backward)))
  1780. ;; calc
  1781. (use-package calc
  1782. :ensure nil
  1783. :bind (("C-c m" . quick-calc)
  1784. :map calc-mode-map
  1785. ("M-<tab>" . calc-roll-up)
  1786. ("M-TAB" . calc-roll-up))
  1787. :hook ((calc-mode calc-trail-mode) . my/setup-calc-calc-trail-mode)
  1788. :init
  1789. (defun my/setup-calc-calc-trail-mode ()
  1790. (setq-local doom-modeline-percent-position '()
  1791. truncate-partial-width-windows nil)
  1792. (visual-line-mode -1)
  1793. (display-line-numbers-mode -1)
  1794. (toggle-truncate-lines 1))
  1795. :config
  1796. (evil-define-key '(normal visual motion) calc-edit-mode-map
  1797. (kbd "RET") 'calc-edit-return
  1798. (kbd "<return>") 'calc-edit-return)
  1799. (defun my/-calc-float-mode-string ()
  1800. (cl-destructuring-bind (mode prec) calc-float-format
  1801. (concat
  1802. (upcase-initials (symbol-name mode))
  1803. (unless (zerop prec)
  1804. (concat ": " (number-to-string prec))))))
  1805. (doom-modeline-def-segment calc
  1806. "Display calculator icons and info."
  1807. (concat
  1808. (doom-modeline-spc)
  1809. (when-let ((icon (doom-modeline-icon 'faicon "nf-fa-calculator" "🖩" "")))
  1810. (concat
  1811. (doom-modeline-display-icon icon)
  1812. (doom-modeline-vspc)))
  1813. (doom-modeline--buffer-simple-name)
  1814. (when (eq major-mode 'calc-mode)
  1815. (concat
  1816. (doom-modeline-spc)
  1817. (number-to-string calc-internal-prec)
  1818. (doom-modeline-spc)
  1819. (upcase-initials (symbol-name calc-angle-mode))
  1820. (doom-modeline-spc)
  1821. (my/-calc-float-mode-string)
  1822. (when calc-prefer-frac
  1823. (concat
  1824. (doom-modeline-spc)
  1825. "Frac"))
  1826. (cond
  1827. (calc-algebraic-mode
  1828. (concat
  1829. (doom-modeline-spc)
  1830. "Alg"))
  1831. (calc-incomplete-algebraic-mode
  1832. (concat
  1833. (doom-modeline-spc)
  1834. "IAlg"))))))))
  1835. ;; sage (for when calc is not enough)
  1836. (use-package sage-shell-mode
  1837. :demand
  1838. :bind ("C-c g" . my/run-sage)
  1839. :hook (sage-shell-mode . my/-setup-sage-shell-mode)
  1840. :init
  1841. (defun my/-setup-sage-shell-mode ()
  1842. (setq-local comint-dynamic-complete-functions
  1843. '(comint-c-a-p-replace-by-expanded-history)))
  1844. :config
  1845. (defun my/run-sage (p)
  1846. "Like `sage-shell:run-sage', but does not ask anything without a prefix
  1847. argument."
  1848. (interactive "P")
  1849. (let ((sage-shell:ask-command-options p))
  1850. (funcall-interactively #'sage-shell:run-sage
  1851. (sage-shell:read-command)))))
  1852. ;; fricas (because I like calculators)
  1853. (add-to-list 'load-path "/usr/lib/fricas/emacs")
  1854. (use-package fricas
  1855. :ensure nil
  1856. :custom
  1857. (fricas-run-command "fricas -nosman")
  1858. :init
  1859. ;; Fix `fricas-mode' messing up `completion-at-point-functions'
  1860. (advice-add #'fricas-mode :around
  1861. #'(lambda (oldfun &rest r)
  1862. (let ((temp-capfs))
  1863. (let ((completion-at-point-functions '(t)))
  1864. (apply oldfun r)
  1865. (setq temp-capfs completion-at-point-functions))
  1866. (setq-local completion-at-point-functions temp-capfs)))
  1867. '((name . "my/-fricas-fix-capfs")))
  1868. :config
  1869. (face-spec-set 'fricas-type-time '((t (:foreground unspecified
  1870. :background unspecified
  1871. :inherit font-lock-type-face))))
  1872. (face-spec-set 'fricas-message '((t (:foreground unspecified
  1873. :background unspecified
  1874. :inherit error))))
  1875. (face-spec-set 'fricas-undefined '((t (:foreground unspecified
  1876. :background unspecified
  1877. :inherit nerd-icons-lblue))))
  1878. (face-spec-set 'fricas-algebra '((t (:foreground unspecified
  1879. :background unspecified
  1880. :weight bold
  1881. :inherit fricas-prompt))))
  1882. (face-spec-set 'fricas-TeX '((t (:foreground "black"
  1883. :background "white"
  1884. :inherit fricas-prompt)))))
  1885. ;; gnuplot (mostly for org-plot)
  1886. (use-package gnuplot)
  1887. ;; eat
  1888. (use-package eat
  1889. :bind (("C-c V" . my/project-eat-or-default)
  1890. :map eat-mode-map
  1891. ("M-o" . ace-window)
  1892. :map eat-semi-char-mode-map
  1893. ("M-o" . ace-window)
  1894. :map eat-eshell-emacs-mode-map
  1895. ("M-o" . ace-window)
  1896. :map eat-eshell-semi-char-mode-map
  1897. ("M-o" . ace-window))
  1898. :config
  1899. (defvar my/project-eat-hash-table (make-hash-table :test 'equal)
  1900. "Hash table that maps project root dirs to eat buffers.")
  1901. (defun my/-eat-shell-for-cwd ()
  1902. "Return a good shell for CWD, or nil if the default shell should be used."
  1903. (when (file-remote-p default-directory)
  1904. "/bin/sh"))
  1905. (defun my/project-eat (prompt &optional arg)
  1906. "Switch to or create a eat buffer in the current projects root."
  1907. (interactive (list t current-prefix-arg))
  1908. (if-let ((proj (project-current prompt))
  1909. (default-directory (project-root proj)))
  1910. (if-let ((eat-buff (gethash default-directory
  1911. my/project-eat-hash-table))
  1912. ((buffer-live-p eat-buff)))
  1913. (switch-to-buffer eat-buff)
  1914. (let ((eat-buffer-name (concat "*eat for project " default-directory
  1915. "*"))
  1916. (eat-term-name (if (file-remote-p default-directory)
  1917. "xterm-256color"
  1918. eat-term-name)))
  1919. (puthash default-directory
  1920. (eat (my/-eat-shell-for-cwd) arg)
  1921. my/project-eat-hash-table)))))
  1922. (defun my/project-eat-or-default (&optional arg)
  1923. "Open an eat for the current project, otherwise, open a normal eat."
  1924. (interactive "P")
  1925. (unless (my/project-eat nil)
  1926. (if-let ((eat-buff (gethash nil my/project-eat-hash-table))
  1927. ((buffer-live-p eat-buff)))
  1928. (switch-to-buffer eat-buff)
  1929. (puthash nil (let ((eat-term-name (if (file-remote-p default-directory)
  1930. "xterm-256color"
  1931. eat-term-name)))
  1932. (eat (my/-eat-shell-for-cwd) arg))
  1933. my/project-eat-hash-table)))))
  1934. ;; eshell stuff
  1935. (use-package eshell
  1936. :ensure nil
  1937. :defer nil
  1938. :hook ((eshell-load . eat-eshell-visual-command-mode)
  1939. (eshell-load . eat-eshell-mode)
  1940. (eshell-mode . my/-eshell-mode-setup)
  1941. (eshell-directory-change . my/-eshell-maybe-setup-remote-aliases))
  1942. :bind (:map eshell-mode-map
  1943. ("TAB" . completion-at-point)
  1944. ("<tab>" . completion-at-point))
  1945. :init
  1946. (defun my/-eshell-filter-alias-list ()
  1947. (cl-remove-if-not (lambda (elt)
  1948. (or (string-match-p
  1949. (rx bos
  1950. (or "clear" "find-file"
  1951. "ls" "la" "git"
  1952. (and "eshell/" (+ (not " "))))
  1953. (or " " eos))
  1954. (cl-second elt))))
  1955. eshell-command-aliases-list))
  1956. (defun my/-eshell-maybe-setup-remote-aliases ()
  1957. (if (file-remote-p default-directory)
  1958. (setq-local eshell-command-aliases-list (my/-eshell-filter-alias-list))
  1959. (kill-local-variable 'eshell-command-aliases-list)))
  1960. (defun my/-eshell-mode-setup ()
  1961. "Setup function run from `eshell-mode-hook'"
  1962. (setq-local corfu-auto nil)
  1963. (my/-eshell-maybe-setup-remote-aliases))
  1964. (setq-default eshell-command-aliases-list
  1965. '(("clear" "clear t")
  1966. ("e" "find-file $1")
  1967. ("n" "find-file $1")
  1968. ("emacs" "find-file $1")
  1969. ("nvim" "find-file $1")
  1970. ("ls" "eza --git -F $*")
  1971. ("la" "ls -a $*")
  1972. ("l" "ls -l $*")
  1973. ("ll" "la -l $*")
  1974. ("gt" "git status $*")
  1975. ("gp" "git push $*")
  1976. ("gu" "git pull $*")
  1977. ("gf" "git fetch $*")
  1978. ("ga" "git add $*")
  1979. ("gcm" "git commit -m ${string-join $* \" \"}")
  1980. ("ldg" "ledger -f \"$HOME/docs/finance/finances.ledger\" $*")
  1981. ("tp" "trash-put $*")
  1982. ("trr" "trash-restore $*")
  1983. ("tre" "trash-empty $*")
  1984. ("tre" "trash-empty $*")
  1985. ("trm" "trash-rm $*")
  1986. ("rm" "echo 'rm: I''m unsafe! Don''t use me.'; false")
  1987. ("\\rm" "eshell/rm")))
  1988. (defvar my/eshell-bm-auto-ls t
  1989. "Weather or not to run ls after `eshell/bm'")
  1990. (defun eshell/bm (&optional name)
  1991. "Change to directory of bookmark NAME.
  1992. If no name is given, list all bookmarks instead."
  1993. (if name
  1994. (progn
  1995. (eshell/cd (bookmark-get-filename name))
  1996. (when my/eshell-bm-auto-ls
  1997. (eshell/ls)))
  1998. (eshell-print (string-join (bookmark-all-names) " ")))))
  1999. (use-package esh-help
  2000. :hook (eshell-mode . my/-setup-eshell-help-func)
  2001. :init
  2002. (defun my/-setup-eshell-help-func ()
  2003. (eldoc-mode 1)
  2004. (setq-local evil-lookup-func #'esh-help-run-help))
  2005. (setup-esh-help-eldoc))
  2006. (use-package eshell-syntax-highlighting
  2007. :init
  2008. (eshell-syntax-highlighting-global-mode 1))
  2009. (use-package eshell-starship
  2010. :ensure nil
  2011. :demand t
  2012. :hook (eshell-prompt-mode . eshell-starship-prompt-mode)
  2013. :config
  2014. (eshell-starship-setup-evil-keybindings)
  2015. (set-face-attribute 'eshell-starship-icon-face nil
  2016. :family "FiraCode Nerd Font"))
  2017. (defun my/open-shell-dwim (&optional arg)
  2018. "Open either an Eshell or eat terminal based on `default-directory'.
  2019. If `default-directory' is remote, call `my/project-eat-or-default'. Otherwise,
  2020. call `my/project-eshell-or-default'. ARG is the same as for either of the above
  2021. functions (only eshell uses it at the time of writing)."
  2022. (interactive "P")
  2023. (if (file-remote-p default-directory)
  2024. (my/project-eat-or-default)
  2025. (my/project-eshell-or-default arg)))
  2026. (keymap-global-set "C-c v" #'my/open-shell-dwim)
  2027. ;; proced
  2028. (use-package proced
  2029. :bind ("C-x j" . proced)
  2030. :init
  2031. (evil-define-key '(motion visual normal) proced-mode-map
  2032. "u" 'proced-unmark)
  2033. (setq proced-auto-update-flag t
  2034. proced-auto-update-interval 1)
  2035. (defun my/-setup-proced-mode ()
  2036. (visual-line-mode -1)
  2037. (setq-local truncate-lines t))
  2038. (add-hook 'proced-mode-hook 'my/-setup-proced-mode))
  2039. ;; dired
  2040. (use-package dired
  2041. :ensure nil
  2042. :custom
  2043. (dired-listing-switches
  2044. "-l --almost-all --human-readable --group-directories-first --no-group")
  2045. (dired-hide-details-hide-symlink-targets nil)
  2046. :init
  2047. (setq-default dired-kill-when-opening-new-dired-buffer t)
  2048. (setq delete-by-moving-to-trash t
  2049. dired-recursive-copies 'always
  2050. dired-recursive-deletes 'always
  2051. dired-dwim-target t
  2052. dired-create-destination-dirs 'ask
  2053. dired-create-destination-dirs-on-trailing-dirsep t
  2054. dired-isearch-filenames 'dwim
  2055. dired-do-revert-buffer (lambda (dir)
  2056. (not (file-remote-p dir)))
  2057. dired-clean-up-buffers-too t
  2058. dired-clean-confirm-killing-deleted-buffers t)
  2059. (evil-define-key '(normal visual motion) dired-mode-map
  2060. "u" #'dired-unmark
  2061. "U" #'dired-unmark-all-marks))
  2062. ;; dirvish
  2063. (use-package dirvish
  2064. :defer nil
  2065. :bind (("C-c b" . dirvish-quick-access)
  2066. :map dirvish-mode-map
  2067. ("<mouse-1>" . dirvish-subtree-toggle-or-open)
  2068. ("<mouse-2>" . dired-mouse-find-file-other-window)
  2069. ("<mouse-3>" . dired-mouse-find-file))
  2070. :hook (((dirvish-directory-view-mode dired-mode dirvish-mode) .
  2071. my/-setup-dirvish-lines)
  2072. ;; ((dirvish-directory-view-mode dired-mode dirvish-mode) .
  2073. ;; auto-revert-mode)
  2074. ((dirvish-mode dired-mode) . my/-setup-dirvish-mouse))
  2075. :custom
  2076. (dirvish-subtree-always-show-state t)
  2077. (dirvish-reuse-session t)
  2078. (dirvish-quick-access-function 'dired)
  2079. :init
  2080. (defun my/-setup-dirvish-lines ()
  2081. (setq-local truncate-lines t))
  2082. (defun my/-setup-dirvish-mouse ()
  2083. (setq-local mouse-1-click-follows-link nil
  2084. mouse-1-click-in-non-selected-windows nil))
  2085. (defvar my/-dirvish-base-quick-access-entries
  2086. `(("h" "~/" "Home")
  2087. ("d" "~/downloads/" "Downloads")
  2088. ("e" ,user-emacs-directory "Emacs user directory")
  2089. ("z" "~/.config/zsh/" "Zsh user directory")
  2090. ("o" "~/docs/" "Documents")
  2091. ("w." "~/workspace/" "Workspace")))
  2092. (defun my/-dirvish-build-quick-access-entries (bookmarks)
  2093. ;; NOTE called from a variable watcher for `bookmark-alist' and so must
  2094. ;; never set that variable
  2095. (let (out)
  2096. (dolist (bme bookmarks
  2097. (append my/-dirvish-base-quick-access-entries
  2098. (sort out
  2099. (lambda (elt1 elt2)
  2100. (string< (car elt1) (car elt2))))))
  2101. (let ((name (car bme)))
  2102. (let-alist (cdr bme)
  2103. (when (and (file-directory-p .filename)
  2104. (string-match (rx bos (group (any "a-z" "A-Z")) "ws" eos)
  2105. name))
  2106. (setf (alist-get (concat "w" (match-string 1 name)) out
  2107. nil nil 'equal)
  2108. (list .filename
  2109. (concat
  2110. (capitalize
  2111. (car (last (string-split .filename "/" t))))
  2112. " Workspace")))))))))
  2113. :config
  2114. (require 'dirvish-extras)
  2115. (defun my/-dirvish-bookmark-alist-watcher (_sym newval oper where)
  2116. (when (and (not where) (memq oper '(set makunbound defvaralias)))
  2117. (setopt dirvish-quick-access-entries
  2118. (my/-dirvish-build-quick-access-entries newval))))
  2119. (add-variable-watcher 'bookmark-alist #'my/-dirvish-bookmark-alist-watcher)
  2120. (defvar-local my/-dirvish-uid-name-cache nil
  2121. "Cons of path and a hash table mapping user ids to their names.")
  2122. (dirvish-define-attribute file-owner-mode
  2123. "The file's owner and mode."
  2124. :index 2
  2125. :when (and (dirvish-prop :root) dired-hide-details-mode
  2126. (> win-width 60))
  2127. (let ((root (dirvish-prop :root))
  2128. (uid (file-attribute-user-id f-attrs)))
  2129. (unless (or (dirvish-prop :remote) (stringp uid))
  2130. (unless (and (equal root (car my/-dirvish-uid-name-cache))
  2131. (hash-table-p (cdr my/-dirvish-uid-name-cache)))
  2132. (setq my/-dirvish-uid-name-cache
  2133. (cons root (make-hash-table :test 'equal))))
  2134. (if-let ((name (gethash uid (cdr my/-dirvish-uid-name-cache))))
  2135. (setq uid name)
  2136. (let* ((new-attrs (file-attributes f-name 'string))
  2137. (new-name (file-attribute-user-id new-attrs)))
  2138. (puthash uid new-name
  2139. (cdr my/-dirvish-uid-name-cache))
  2140. (setq uid new-name))))
  2141. (cons 'right (propertize (format " %s %s" uid (file-attribute-modes f-attrs))
  2142. 'face (or hl-face 'dirvish-file-time)))))
  2143. (let ((cur-val dirvish-ui-setup-items))
  2144. (cl-pushnew '("o" file-owner-mode "File owner and mode")
  2145. cur-val :test 'equal)
  2146. (setopt dirvish-ui-setup-items cur-val))
  2147. (add-to-list 'dirvish-libraries '(dirvish file-owner-mode))
  2148. (setopt dirvish-attributes
  2149. '(vc-state subtree-state nerd-icons file-size file-owner-mode))
  2150. (evil-define-key 'normal dirvish-mode-map
  2151. (kbd "q") #'dirvish-quit
  2152. (kbd "a") #'dirvish-quick-access
  2153. (kbd "f") #'dirvish-file-info-menu
  2154. (kbd "y") #'dirvish-yank-menu
  2155. (kbd "N") #'dirvish-narrow
  2156. (kbd "^") #'dirvish-history-last
  2157. (kbd "h") #'dirvish-history-jump
  2158. (kbd "s") #'dirvish-quicksort
  2159. (kbd "o") #'dirvish-quicksort
  2160. (kbd "v") #'dirvish-vc-menu
  2161. (kbd "TAB") #'dirvish-subtree-toggle
  2162. (kbd "M-f") #'dirvish-history-go-forward
  2163. (kbd "M-b") #'dirvish-history-go-backward
  2164. (kbd "M-l") #'dirvish-history-go-forward
  2165. (kbd "M-h") #'dirvish-history-go-backward
  2166. (kbd "M-l") #'dirvish-ls-switches-menu
  2167. (kbd "M-m") #'dirvish-mark-menu
  2168. (kbd "M-t") #'dirvish-layout-toggle
  2169. (kbd "M-s") #'dirvish-setup-menu
  2170. (kbd "M-e") #'dirvish-emerge-menu
  2171. (kbd "M-j") #'dirvish-fd-jump
  2172. (kbd "/") #'dirvish-fd
  2173. (kbd "?") #'dirvish-dispatch)
  2174. (dirvish-override-dired-mode)
  2175. (dirvish-define-preview eza (file)
  2176. "Use `eza' to generate directory preview."
  2177. :require ("eza")
  2178. (when (file-directory-p file)
  2179. `(shell . ("eza" "-la" "--color=always" "--icons"
  2180. "--group-directories-first" ,file))))
  2181. (add-to-list 'dirvish-preview-dispatchers 'eza))
  2182. ;; ibuffer
  2183. (use-package ibuffer
  2184. :bind ("C-x C-b" . ibuffer))
  2185. ;; magit
  2186. (use-package magit
  2187. :init
  2188. (defvar-keymap my/magit-personal-prefix-map
  2189. :doc "Keymap for some useful Magit commands."
  2190. "s" #'magit-stage
  2191. "d" #'magit-diff-dwim
  2192. "D" #'magit-diff
  2193. "b" #'magit-blame)
  2194. (keymap-global-set "C-c i" my/magit-personal-prefix-map)
  2195. (evil-define-key '(normal visual motion) magit-mode-map
  2196. "s" #'magit-stage-file
  2197. "S" #'magit-stage-modified))
  2198. (use-package forge
  2199. :config
  2200. (add-to-list 'forge-alist '("git.zander.im" "git.zander.im/api/v1"
  2201. "git.zander.im" forge-gitea-repository)))
  2202. ;; org-mode
  2203. (use-package org
  2204. :pin gnu
  2205. :bind (("C-c c" . org-capture)
  2206. ("C-c a" . org-agenda)
  2207. ("C-c l" . org-store-link)
  2208. :map org-mode-map
  2209. ("C-c t" . org-table-create))
  2210. :hook (org-mode . org-table-header-line-mode)
  2211. :init
  2212. (font-lock-add-keywords 'org-mode
  2213. `((,(rx bol (* " ") (group "-") " ")
  2214. (0 (prog1 nil
  2215. (compose-region (match-beginning 1)
  2216. (match-end 1) "•"))))))
  2217. (setq org-directory "~/org"
  2218. org-agenda-files '("~/org/")
  2219. org-log-into-drawer t
  2220. org-log-done 'time
  2221. org-log-redeadline 'time
  2222. org-log-reschedule 'time
  2223. org-preview-latex-default-process 'dvisvgm
  2224. org-highlight-latex-and-related '(native entities)
  2225. org-startup-with-inline-images t
  2226. org-adapt-indentation t
  2227. org-hide-leading-stars t
  2228. org-html-with-latex 'dvisvgm
  2229. org-preview-latex-process-alist
  2230. '((dvisvgm
  2231. :image-input-type "dvi"
  2232. :image-output-type "svg"
  2233. :image-size-adjust (1.7 . 1.5)
  2234. :latex-compiler ("pdflatex -interaction nonstopmode -output-format=dvi -output-directory=%o %f")
  2235. :image-converter ("dvisvgm %o%b.dvi --no-fonts --exact-bbox --scale=%S --output=%O"))))
  2236. (defun my/-org-allow-in-derived-mode (oldfun &rest r)
  2237. "Allow OLDFUN to run, even if `major-mode' is only derived from `org-mode'.
  2238. R is rest of the arguments to OLDFUN."
  2239. (let ((major-mode (if (derived-mode-p 'org-mode)
  2240. 'org-mode
  2241. major-mode)))
  2242. (apply oldfun r)))
  2243. (advice-add 'org-element-at-point :around 'my/-org-allow-in-derived-mode)
  2244. (advice-add 'org-table-header-line-mode :around 'my/-org-allow-in-derived-mode))
  2245. (use-package evil-org
  2246. :after org
  2247. :hook (org-mode . evil-org-mode)
  2248. :init
  2249. (require 'evil-org-agenda)
  2250. (evil-org-agenda-set-keys))
  2251. ;; ledger
  2252. (use-package ledger-mode)
  2253. (use-package flycheck-ledger
  2254. :hook (ledger-mode . trusted-files-flycheck-mode-if-safe))
  2255. ;; khard contacts
  2256. (require 'khard)
  2257. ;; This is also in khard (see above), it's just also here so that if I remove
  2258. ;; that file ever, other things will not break.
  2259. (defun my/message-in-header-p (name &optional testfn)
  2260. "If in field NAME, return the start of the header, otherwise, return nil.
  2261. The name is compared with the field name using TESTFN (defaults to `equal')."
  2262. (save-excursion
  2263. (when (and (message-point-in-header-p)
  2264. (message-beginning-of-header t))
  2265. (beginning-of-line)
  2266. (when (and (looking-at (rx bol (group (+? any)) ":" (? " ")))
  2267. (funcall (or testfn 'equal) (match-string 1) name))
  2268. (match-end 0)))))
  2269. ;; mu4e
  2270. (use-package mu4e
  2271. :ensure nil
  2272. :defer nil
  2273. :hook ((mu4e-index-updated . my/-mu4e-enable-index-messages)
  2274. (mu4e-main-mode . my/-mu4e-setup-main-mode)
  2275. (mu4e-view-mode . my/-mu4e-setup-view-mode)
  2276. (mu4e-compose-mode . my/-mu4e-setup-compose-mode))
  2277. :bind (("C-x C-m" . mu4e)
  2278. :map message-mode-map
  2279. ("C-c k" . khard-insert-email-contact)
  2280. :map mu4e-headers-mode-map
  2281. ([remap mu4e-headers-mark-for-trash] .
  2282. my/mu4e-headers-mark-for-trash)
  2283. :map mu4e-view-mode-map
  2284. ([remap mu4e-view-mark-for-trash] .
  2285. my/mu4e-view-mark-for-trash))
  2286. :init
  2287. (require 'mu4e)
  2288. (evil-define-key '(normal motion) mu4e-main-mode-map "q" #'bury-buffer)
  2289. (evil-define-key '(normal motion) mu4e-view-mode-map "gy" #'mu4e-view-save-url)
  2290. (defun my/-mu4e-setup-view-mode ()
  2291. (setq-local global-hl-line-mode nil))
  2292. (defun my/-mu4e-setup-main-mode ()
  2293. (setq-local default-directory "~/"))
  2294. (defun my/-mu4e-enable-index-messages ()
  2295. (setq mu4e-hide-index-messages nil))
  2296. (defun my/mu4e-update-mail-and-index-silent ()
  2297. "Run `mu4e-update-mail-and-index' without any messages in the background."
  2298. (setq mu4e-hide-index-messages t)
  2299. (mu4e-update-mail-and-index t))
  2300. (defun my/mu4e-headers-mark-for-trash ()
  2301. "Move the message a point to the trash without marking it was deleted
  2302. (trashed)."
  2303. (interactive)
  2304. (when (mu4e-thread-message-folded-p)
  2305. (mu4e-warn "Cannot mark folded messages"))
  2306. (mu4e-mark-at-point 'move mu4e-trash-folder)
  2307. (when mu4e-headers-advance-after-mark
  2308. (mu4e-headers-next)))
  2309. (defun my/mu4e-view-mark-for-trash ()
  2310. "Like `my/mu4e-headers-mark-for-trash', but for `mu4e-view-mode'."
  2311. (interactive)
  2312. (mu4e--view-in-headers-context
  2313. (my/mu4e-headers-mark-for-trash)))
  2314. (defun my/-mu4e-enable-autocomplete-in-header ()
  2315. ;; corfu auto must be t (not the integer returned by
  2316. ;; `my/message-in-header-p'
  2317. (setq-local corfu-auto (and (not (window-minibuffer-p))
  2318. (my/message-in-header-p "To")
  2319. t)))
  2320. (defun my/-mu4e-setup-compose-mode ()
  2321. (add-hook 'post-command-hook 'my/-mu4e-enable-autocomplete-in-header
  2322. nil t)
  2323. (add-to-list
  2324. (make-local-variable 'completion-at-point-functions)
  2325. (cape-capf-super #'mu4e-complete-contact #'khard-message-mode-capf)))
  2326. (defun my/-mu4e-fix-cycle-threshold ()
  2327. (setq-local completion-cycle-threshold nil))
  2328. (advice-add 'mu4e--compose-setup-completion :after
  2329. 'my/-mu4e-fix-cycle-threshold)
  2330. (defvar my/mu4e-interesting-mail-query
  2331. (concat "flag:unread AND NOT flag:trashed AND NOT "
  2332. "maildir:/protonmail/Trash AND NOT maildir:/protonmail/Spam")
  2333. "Flag for mail which will appear as \"unread\" and will be notified.")
  2334. (setq message-kill-buffer-on-exit t
  2335. message-confirm-send t
  2336. message-send-mail-function 'sendmail-send-it
  2337. mu4e-change-filenames-when-moving t
  2338. mu4e-context-policy 'pick-first
  2339. mu4e-attachment-dir "~/downloads/"
  2340. mu4e-last-update-buffer " *mu4e-last-update*"
  2341. mu4e-index-update-error-warning nil
  2342. mu4e-get-mail-command "mbsync protonmail"
  2343. mu4e-completing-read-function #'completing-read-default
  2344. mu4e-compose-context-policy 'ask-if-none
  2345. mu4e-contexts
  2346. (list (make-mu4e-context
  2347. :name "Personal"
  2348. :match-func (lambda (msg)
  2349. (when msg
  2350. (string-match-p "^/protonmail/"
  2351. (mu4e-message-field msg
  2352. :maildir))))
  2353. :vars `((user-mail-address . ,(my/get-private 'mu4e-email))
  2354. (user-full-name . ,(my/get-private 'mu4e-name))
  2355. (message-signature . nil)
  2356. (mu4e-refile-folder . "/protonmail/Archive")
  2357. (mu4e-sent-folder . "/protonmail/Sent")
  2358. (mu4e-drafts-folder . "/protonmail/Drafts")
  2359. (mu4e-trash-folder . "/protonmail/Trash")
  2360. (mu4e-bookmarks
  2361. . ((:name "Inbox"
  2362. :query "maildir:/protonmail/Inbox"
  2363. :key ?i)
  2364. (:name "Unread"
  2365. :query ,my/mu4e-interesting-mail-query
  2366. :key ?u))))))))
  2367. (use-package mu4e-alert
  2368. :after mu4e
  2369. :hook (after-init . mu4e-alert-enable-notifications)
  2370. :init
  2371. (setq mu4e-alert-set-window-urgency nil
  2372. mu4e-alert-interesting-mail-query my/mu4e-interesting-mail-query)
  2373. :config
  2374. (mu4e-alert-set-default-style 'libnotify))
  2375. (mu4e t)
  2376. (mu4e-context-switch nil "Personal")
  2377. ;; mu4e compose HTML messages
  2378. (use-package org-mime)
  2379. (require 'org-mu4e-compose)
  2380. (setq ;; mail-user-agent 'org-mu4e-user-agent
  2381. org-mime-org-html-with-latex-default 'dvisvgm
  2382. org-mime-export-options '(:with-latex dvisvgm :with-footnotes t))
  2383. ;; (evil-define-key '(normal visual) org-mu4e-compose-mode-map
  2384. ;; "G" #'mu4e-compose-goto-bottom
  2385. ;; "gg" #'mu4e-compose-goto-top)
  2386. ;; (evil-define-key 'normal org-mu4e-compose-mode-map
  2387. ;; "ZZ" #'message-send-and-exit
  2388. ;; "ZD" #'message-dont-send
  2389. ;; "ZQ" #'message-kill-buffer
  2390. ;; "ZF" #'mml-attach-file)
  2391. ;; (evil-define-key 'normal mu4e-view-mode-map
  2392. ;; "R" 'org-mu4e-compose-reply
  2393. ;; "cr" 'org-mu4e-compose-reply)
  2394. ;; (evil-define-key 'normal mu4e-headers-mode-map
  2395. ;; "R" 'org-mu4e-compose-reply
  2396. ;; "cr" 'org-mu4e-compose-reply)
  2397. ;; (defun my/-setup-org-mu4e-compose-mode ()
  2398. ;; "Setup up stuff in `org-mu4e-compose' buffers."
  2399. ;; (setq-local ltex-eglot-variable-save-method 'file)
  2400. ;; ;; this should come last so it can pick up the above
  2401. ;; ;; (trusted-files-eglot-ensure-if-safe)
  2402. ;; )
  2403. ;; (add-hook 'org-mu4e-compose-mode-hook #'my/-setup-org-mu4e-compose-mode)
  2404. ;; elfeed
  2405. (use-package elfeed
  2406. :bind (("C-c d" . elfeed))
  2407. :custom
  2408. (elfeed-feeds
  2409. '(("https://archlinux.org/feeds/news/" linux arch)
  2410. ("https://9to5linux.com/feed/atom" linux news)))
  2411. :config
  2412. (setq elfeed-log-buffer-name " *elfeed-log*")
  2413. (evil-define-key '(normal motion) elfeed-search-mode-map
  2414. "r" #'elfeed-search-fetch)
  2415. (elfeed-db-load))
  2416. ;; helpful
  2417. (use-package helpful
  2418. :hook ((emacs-lisp-mode . my/-helpful-setup-emacs-lisp-mode)
  2419. (helpful-mode . my/-setup-helpful-mode))
  2420. :bind (:map help-map
  2421. ("f" . helpful-callable)
  2422. ("v" . helpful-variable)
  2423. ("k" . helpful-key)
  2424. ("o" . helpful-symbol)
  2425. ("x" . helpful-command)
  2426. ("F" . helpful-function)
  2427. :map helpful-mode-map
  2428. ("<mouse-8>" . my/helpful-history-back)
  2429. ("<mouse-9>" . my/helpful-history-forward)
  2430. ("<normal-state><" . my/helpful-history-back)
  2431. ("<normal-state>>" . my/helpful-history-forward))
  2432. :init
  2433. (defun my/-helpful-setup-emacs-lisp-mode ()
  2434. (setq-local evil-lookup-func #'helpful-at-point))
  2435. (defun my/-setup-helpful-mode ()
  2436. (setq-local evil-lookup-func #'helpful-at-point
  2437. tab-width 8))
  2438. (defvar my/helpful-symbol-history-size 50
  2439. "Max size of `my/helpful-symbol-history'.")
  2440. (defvar my/helpful-symbol-history '()
  2441. "History of helpful symbols.")
  2442. (defvar my/-helpful-inhibit-history nil
  2443. "If non-nil, don't add symbols to `my/helpful-symbol-history'.")
  2444. (defvar my/-helpful-last-entry nil
  2445. "Last entry looked up with helpful.")
  2446. (defun my/helpful-history-back (count)
  2447. "Go back COUNT symbols in `my/helpful-symbol-history'. If called
  2448. interactively, COUNT defaults to 1."
  2449. (interactive "p")
  2450. (my/helpful-history-forward (- count)))
  2451. (defun my/helpful-history-forward (count)
  2452. "Move COUNT symbols in `my/helpful-symbol-history'. If COUNT is negative,
  2453. move back. If COUNT is larger than the history, go to the newest entry. Go to
  2454. the oldest entry if -COUNT is larger than the history."
  2455. (interactive "p")
  2456. (when helpful--sym
  2457. (let* ((hist-len (length my/helpful-symbol-history))
  2458. (current-pos (seq-position my/helpful-symbol-history
  2459. (cons helpful--sym
  2460. helpful--callable-p)
  2461. 'equal))
  2462. (new-pos (- current-pos count)))
  2463. (cond
  2464. ;; if already at the newest element, signal an error
  2465. ((and (> count 0) (= current-pos 0))
  2466. (message "%s" "No newer symbol!"))
  2467. ;; if already at the oldest element, signal an error
  2468. ((and (< count 0) (= (1+ current-pos) hist-len))
  2469. (message "%s" "No older symbol!"))
  2470. (t
  2471. (let ((my/-helpful-inhibit-history t)
  2472. (entry (cond
  2473. ((<= new-pos 0)
  2474. (seq-first my/helpful-symbol-history))
  2475. ((>= new-pos hist-len)
  2476. (car (last my/helpful-symbol-history)))
  2477. (t
  2478. (nth new-pos my/helpful-symbol-history)))))
  2479. (if (cdr entry)
  2480. (helpful-callable (car entry))
  2481. (helpful-variable (car entry)))))))))
  2482. (defun my/-helpful-switch-buffer-function (helpful-buf)
  2483. "Like `pop-to-buffer', but kill previous helpful buffers and save the new
  2484. buffers `helpful--sym' to `my/helpful-symbol-history'."
  2485. (cl-loop with window = nil
  2486. for buf in (buffer-list)
  2487. when (and
  2488. (not (eq buf helpful-buf))
  2489. (eq (buffer-local-value 'major-mode buf) 'helpful-mode))
  2490. do
  2491. (when-let (cur-window (get-buffer-window buf nil))
  2492. (setq window cur-window))
  2493. (kill-buffer buf)
  2494. finally
  2495. (let ((entry (cons (buffer-local-value 'helpful--sym helpful-buf)
  2496. (buffer-local-value 'helpful--callable-p
  2497. helpful-buf))))
  2498. (unless my/-helpful-inhibit-history
  2499. (when-let (from-current-hist
  2500. (member my/-helpful-last-entry
  2501. my/helpful-symbol-history))
  2502. (setq my/helpful-symbol-history from-current-hist))
  2503. (cl-pushnew entry my/helpful-symbol-history :test 'equal)
  2504. (setq my/helpful-symbol-history
  2505. (seq-take my/helpful-symbol-history
  2506. my/helpful-symbol-history-size)))
  2507. (setq my/-helpful-last-entry entry))
  2508. (if window
  2509. (window--display-buffer helpful-buf window 'reuse)
  2510. (pop-to-buffer helpful-buf))))
  2511. (setq helpful-switch-buffer-function 'my/-helpful-switch-buffer-function
  2512. helpful-max-buffers 2))
  2513. (defun my/greyify-color (color percent &optional frame)
  2514. "Make COLOR closer to black by PERCENT on FRAME.
  2515. Color can be any color which can be passed to `color-values'."
  2516. (cl-destructuring-bind (&optional r g b)
  2517. (color-name-to-rgb color frame)
  2518. (when (and r g b)
  2519. (let ((scale (- 1.0 (/ percent 100.0))))
  2520. (color-rgb-to-hex (* r scale)
  2521. (* g scale)
  2522. (* b scale))))))
  2523. ;; rainbow-delimiters
  2524. (use-package rainbow-delimiters
  2525. :hook (prog-mode . rainbow-delimiters-mode)
  2526. :config
  2527. ;; generate dark version of the rainbow delimiters faces
  2528. (defun my/-rainbow-delimiters-recalc-dark-faces (&optional frame)
  2529. (unless frame (setq frame (selected-frame)))
  2530. (dotimes (i 9)
  2531. (when-let ((old-face (intern-soft
  2532. (format "rainbow-delimiters-depth-%d-face"
  2533. (1+ i))))
  2534. (new-face
  2535. (intern
  2536. (format "my/rainbow-delimiters-depth-%d-dark-face"
  2537. (1+ i))))
  2538. (old-color (face-attribute old-face :foreground frame))
  2539. (new-color (my/greyify-color old-color 50 frame)))
  2540. (set-face-attribute new-face frame :foreground new-color))))
  2541. (add-hook 'after-make-frame-functions
  2542. #'my/-rainbow-delimiters-recalc-dark-faces)
  2543. (add-hook 'server-after-make-frame-hook
  2544. #'my/-rainbow-delimiters-recalc-dark-faces)
  2545. (defun my/rainbow-delimiters-parinfer-pick-face (depth match loc)
  2546. "Version of `rainbow-delimiters-default-pick-face' that colors closing
  2547. parenthesis darker than opening ones. This function defers to
  2548. `rainbow-delimiters-default-pick-face' and just changes the output if it returns
  2549. one of the normal rainbow-delimiters-depth-N-face faces."
  2550. (save-match-data
  2551. (let* ((base-face (rainbow-delimiters-default-pick-face depth match loc))
  2552. (base-name (symbol-name base-face)))
  2553. (if (and evil-cleverparens-mode
  2554. (eq ?\) (char-syntax
  2555. (elt (buffer-substring-no-properties loc (1+ loc)) 0)))
  2556. (string-match (rx string-start "rainbow-delimiters-depth-"
  2557. (group (+ num))
  2558. "-face" string-end)
  2559. base-name))
  2560. (or (intern-soft (format "my/rainbow-delimiters-depth-%s-dark-face"
  2561. (match-string 1 base-name)))
  2562. base-face)
  2563. base-face))))
  2564. (setopt rainbow-delimiters-pick-face-function
  2565. 'my/rainbow-delimiters-parinfer-pick-face))
  2566. ;; make regexp look nicer
  2567. (use-package easy-escape
  2568. :hook ((emacs-lisp-mode reb-mode) . easy-escape-minor-mode)
  2569. :config
  2570. (face-spec-set 'easy-escape-face
  2571. '((t (:foreground unspecified
  2572. :weight bold
  2573. :inherit 'font-lock-regexp-grouping-backslash)))))
  2574. ;; auto-highlight-symbol
  2575. (use-package auto-highlight-symbol
  2576. :hook (lisp-data-mode . auto-highlight-symbol-mode)
  2577. :init
  2578. (setq ahs-face 'bold
  2579. ahs-face-unfocused 'bold
  2580. ahs-definition-face 'bold
  2581. ahs-definition-face-unfocused 'bold
  2582. ahs-plugin-default-face 'bold
  2583. ahs-plugin-default-face-unfocused 'bold)
  2584. :config
  2585. (keymap-unset auto-highlight-symbol-mode-map "C-x C-a" t))
  2586. ;; Theme (doom-themes)
  2587. (use-package doom-themes
  2588. :config
  2589. (load-theme 'doom-molokai t)
  2590. (doom-themes-org-config))
  2591. ;; solaire-mode
  2592. (use-package solaire-mode
  2593. :config
  2594. (solaire-global-mode 1))
  2595. ;; Highlight todos
  2596. (use-package hl-todo
  2597. :hook (prog-mode . hl-todo-mode))
  2598. (use-package magit-todos
  2599. :after (hl-todo magit)
  2600. :config
  2601. (magit-todos-mode 1))
  2602. ;; icons
  2603. (use-package nerd-icons)
  2604. (use-package nerd-icons-completion
  2605. :config
  2606. (nerd-icons-completion-mode))
  2607. (use-package nerd-icons-dired
  2608. :hook (dired-mode . my/-maybe-enable-nerd-icons-dired)
  2609. :init
  2610. (defun my/-maybe-enable-nerd-icons-dired ()
  2611. (unless (bound-and-true-p dirvish-override-dired-mode)
  2612. (nerd-icons-dired-mode))))
  2613. (use-package kind-icon
  2614. :after corfu
  2615. :init
  2616. (setq kind-icon-default-face 'corfu-default
  2617. kind-icon-default-style
  2618. '(:padding -1 :stroke 0 :margin 0 :radius 0 :height 0.5 :scale 1))
  2619. :config
  2620. (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
  2621. ;; modeline (doom-modeline)
  2622. (use-package doom-modeline
  2623. :init
  2624. (setq doom-modeline-support-imenu t)
  2625. (doom-modeline-mode 1))
  2626. ;; dashboard.el
  2627. (use-package dashboard
  2628. :config
  2629. (defvar-local my/-dashboard-did-fix-image nil
  2630. "Weather or not the dashboard image has been fixed in this buffer.")
  2631. (defun my/-dashboard-fix-image ()
  2632. (unless my/-dashboard-did-fix-image
  2633. (dashboard-refresh-buffer)
  2634. (setq my/-dashboard-did-fix-image t)))
  2635. (defun my/-dashboard-setup-function ()
  2636. (add-hook 'window-configuration-change-hook 'my/-dashboard-fix-image nil t)
  2637. (setq-local display-line-numbers nil))
  2638. (add-hook 'dashboard-mode-hook 'my/-dashboard-setup-function)
  2639. (set-face-background 'dashboard-banner-logo-title nil)
  2640. (dashboard-setup-startup-hook)
  2641. (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))
  2642. dashboard-force-refresh t
  2643. dashboard-display-icons-p t
  2644. dashboard-icon-type 'nerd-icons
  2645. dashboard-set-file-icons t
  2646. dashboard-projects-backend 'project-el
  2647. dashboard-items '((recents . 5)
  2648. (projects . 5)
  2649. (bookmarks . 5))))
  2650. ;; page break lines
  2651. (use-package page-break-lines
  2652. :config
  2653. (global-page-break-lines-mode 1)
  2654. (add-to-list 'page-break-lines-modes 'prog-mode)
  2655. (add-to-list 'page-break-lines-modes 'text-mode)
  2656. (add-to-list 'page-break-lines-modes 'helpful-mode))
  2657. ;; fun!
  2658. (use-package mines)
  2659. ;;; init.el ends here