init.el 131 KB

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