init.el 125 KB

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