init.el 134 KB

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