init.el 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. ;;; Emacs config
  2. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  3. ;;; Prerequisites
  4. (let ((minver "26.1"))
  5. (when (version< emacs-version minver)
  6. (error "Your Emacs is too old -- this config requires v%s or higher" minver)))
  7. ;;; Speed up init.
  8. ;;; Temporarily reduce garbage collection during startup. Inspect `gcs-done'.
  9. (defun ambrevar/reset-gc-cons-threshold ()
  10. (setq gc-cons-threshold (car (get 'gc-cons-threshold 'standard-value))))
  11. (setq gc-cons-threshold (* 64 1024 1024))
  12. (add-hook 'after-init-hook 'ambrevar/reset-gc-cons-threshold)
  13. ;;; Temporarily disable the file name handler.
  14. (setq default-file-name-handler-alist file-name-handler-alist)
  15. (setq file-name-handler-alist nil)
  16. (defun ambrevar/reset-file-name-handler-alist ()
  17. (setq file-name-handler-alist
  18. (append default-file-name-handler-alist
  19. file-name-handler-alist))
  20. (cl-delete-duplicates file-name-handler-alist :test 'equal))
  21. (add-hook 'after-init-hook 'ambrevar/reset-file-name-handler-alist)
  22. ;;; Avoid the "loaded old bytecode instead of newer source" pitfall.
  23. (setq load-prefer-newer t)
  24. ;;; Store additional config in a 'lisp' subfolder and add it to the load path so
  25. ;;; that `require' can find the files.
  26. ;;; This must be done before moving `user-emacs-directory'.
  27. (add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory))
  28. ;;; Move user-emacs-directory so that user files don't mix with cache files.
  29. (setq user-emacs-directory "~/.cache/emacs/")
  30. ;; Tor / Proxy: set up before package initialization.
  31. (require 'functions) ; Needed for `ambrevar/toggle-proxy'.
  32. (ambrevar/toggle-proxy)
  33. (when (require 'package nil t)
  34. ;; Different Emacs versions have different byte code. If a versioned ELPA
  35. ;; directory is found, use it.
  36. (let ((versioned-dir (format "elpa-%s.%s" emacs-major-version emacs-minor-version)))
  37. (when (member versioned-dir (directory-files (expand-file-name ".." package-user-dir)))
  38. (setq package-user-dir (expand-file-name (concat "../" versioned-dir) package-user-dir))))
  39. (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
  40. (add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t)
  41. (package-initialize))
  42. ;;; Site Lisp folder for local packages and development.
  43. ;; We need to roll it out manually since we want it first in the `load-path',
  44. ;; while `normal-top-level-add-subdirs-to-load-path' appends it to the very end.
  45. (defun ambrevar/package-refresh-load-path (path)
  46. "Add every non-hidden sub-folder of PATH to `load-path'."
  47. (when (file-directory-p path)
  48. (dolist (dir (directory-files path t "^[^\\.]"))
  49. (when (file-directory-p dir)
  50. (setq load-path (add-to-list 'load-path dir))
  51. (dolist (subdir (directory-files dir t "^[^\\.]"))
  52. (when (file-directory-p subdir)
  53. (setq load-path (add-to-list 'load-path subdir))))))))
  54. (let ((site-lisp (expand-file-name "site-lisp/" "~/.local/share/emacs/")))
  55. (add-to-list 'load-path site-lisp)
  56. (ambrevar/package-refresh-load-path site-lisp))
  57. ;;; Local config. See below for an example usage.
  58. (load "local-before" t)
  59. (require 'hook-functions)
  60. (require 'main)
  61. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  62. (add-to-list 'auto-mode-alist (cons "\\.adoc\\'" 'adoc-mode)):w
  63. (add-to-list 'load-path "/usr/share/asymptote")
  64. (autoload 'asy-mode "asy-mode" "Asymptote major mode." t)
  65. (autoload 'lasy-mode "asy-mode" "Hybrid Asymptote/Latex major mode." t)
  66. (autoload 'asy-insinuate-latex "asy-mode" "Asymptote insinuate LaTeX." t)
  67. (add-to-list 'auto-mode-alist '("\\.asy$" . asy-mode))
  68. (setq bibtex-entry-format '(opts-or-alts required-fields numerical-fields whitespace realign last-comma delimiters braces sort-fields))
  69. (setq bibtex-field-delimiters 'double-quotes)
  70. (add-hook 'bibtex-mode-hook 'ambrevar/turn-off-indent-tabs)
  71. ;; TODO: Fix gtk-look to support in-buffer documentation display with eww
  72. ;; instead of just with w3m.
  73. (with-eval-after-load 'cc-mode (require 'init-cc))
  74. (when (require 'company nil t)
  75. (setq company-idle-delay nil))
  76. (with-eval-after-load 'debbugs
  77. (setq debbugs-gnu-all-severities t))
  78. ;;; TODO: In diff-mode, both `[[` and `C-M-a` do not go back to previous index
  79. ;;; once they are at the beginning of an index.
  80. ;; dired-du is mostly superseeded by disk-usage.el
  81. ;; See https://github.com/calancha/dired-du/issues/2.
  82. ;;; Dired is loaded after init.el, so configure it only then.
  83. (with-eval-after-load 'dired (require 'init-dired))
  84. (with-eval-after-load 'emms (require 'init-emms))
  85. (when (require 'engine-mode nil t)
  86. (require 'init-engine))
  87. (setq evil-want-keybinding nil
  88. evil-want-integration t)
  89. (when (require 'evil nil t) (require 'init-evil))
  90. (with-eval-after-load 'eshell (require 'init-eshell))
  91. (with-eval-after-load 'shell (require 'init-shell))
  92. (with-eval-after-load 'eww (require 'init-eww))
  93. (when (require 'expand-region nil t)
  94. (global-set-key (kbd "C-=") 'er/expand-region))
  95. (with-eval-after-load 'gnus (require 'init-gnus))
  96. (with-eval-after-load 'go-mode (require 'init-go))
  97. ;;; Graphviz-dot-mode: The view command is broken but the preview command works
  98. ;;; (it displays the PNG in a new window), which is enough and probably not
  99. ;;; worth a fix.
  100. (with-eval-after-load 'gud (require 'init-gud)) ; Also GDB.
  101. (when (executable-find "guix")
  102. (require 'init-guix))
  103. (when (require 'helm-config nil t) (require 'init-helm))
  104. (when (require 'helpful nil t)
  105. (global-set-key (kbd "C-h f") #'helpful-callable)
  106. (global-set-key (kbd "C-h v") #'helpful-variable)
  107. (global-set-key (kbd "C-h o") #'helpful-at-point)
  108. (global-set-key (kbd "C-h F") #'helpful-function)
  109. (global-set-key (kbd "C-h c") #'helpful-key))
  110. (when (require 'hl-todo nil t)
  111. (add-to-list 'hl-todo-keyword-faces `("REVIEW" . ,(alist-get "TODO" hl-todo-keyword-faces nil nil 'equal)))
  112. (global-hl-todo-mode)
  113. ;; (global-set-key (kbd "M-s M-o") 'hl-todo-occur)
  114. (define-key hl-todo-mode-map (kbd "M-s t") 'hl-todo-occur))
  115. (when (require 'iedit nil t)
  116. (global-set-key (kbd "C-;") 'iedit-mode))
  117. ;;; Image
  118. ;;; TODO: Disable white frame.
  119. ;;; I think it's the cursor.
  120. ;;; Evil-mode reverts cursor changes.
  121. ;; (set-face-foreground 'cursor "black")
  122. ;;; TODO: Implement other sxiv features:
  123. ;;; - Gamma
  124. ;;; - Marks
  125. ;;; - Gallery
  126. ;;; TODO: Is it possible to display an image fullscreen?
  127. ;;; TODO: Image+: Do no auto-adjust animated files
  128. ;;; https://github.com/mhayashi1120/Emacs-imagex/issues/10
  129. ;;; TODO: Image+: Restore animation state
  130. ;;; https://github.com/mhayashi1120/Emacs-imagex/issues/9
  131. (with-eval-after-load 'image
  132. (setq image-animate-loop t)
  133. (add-hook 'image-mode-hook 'image-toggle-animation)
  134. (require 'image+ nil t))
  135. (when (require 'info-colors nil t)
  136. (add-hook 'Info-selection-hook 'info-colors-fontify-node))
  137. (when (require 'helm-selector nil :noerror)
  138. (global-set-key (kbd "C-h i") 'helm-selector-info))
  139. (add-hook 'js-mode-hook (lambda () (defvaralias 'js-indent-level 'tab-width)))
  140. (with-eval-after-load 'lisp-mode (require 'init-lisp))
  141. (with-eval-after-load 'scheme (require 'init-scheme))
  142. (with-eval-after-load 'racket-mode (require 'init-racket))
  143. (with-eval-after-load 'clojure-mode (require 'init-clojure))
  144. (setq geiser-repl-history-filename (expand-file-name "geiser_history" user-emacs-directory))
  145. (setq geiser-guile-debug-show-bt-p t)
  146. ;; Emacs Lisp
  147. (add-hook 'emacs-lisp-mode-hook 'ambrevar/turn-on-complete-filename)
  148. (add-hook 'emacs-lisp-mode-hook 'ambrevar/turn-on-tab-width-to-8) ; Because some existing code uses tabs.
  149. (add-hook 'emacs-lisp-mode-hook 'ambrevar/turn-off-indent-tabs) ; Should not use tabs.
  150. (add-hook 'emacs-lisp-mode-hook 'ambrevar/init-lispy)
  151. (when (fboundp 'rainbow-delimiters-mode)
  152. (add-hook 'emacs-lisp-mode-hook #'rainbow-delimiters-mode))
  153. (ambrevar/define-keys emacs-lisp-mode-map
  154. "<f5>" 'package-lint-current-buffer
  155. ;; Do not use `recompile' since we want to change the compilation folder for the current buffer.
  156. "<f6>" (lambda () (interactive) (async-byte-recompile-directory (file-name-directory (buffer-file-name)))))
  157. (with-eval-after-load 'lua-mode (require 'init-lua))
  158. ;;; Magit can be loaded just-in-time.
  159. (with-eval-after-load 'magit
  160. (ambrevar/define-keys magit-mode-map
  161. "C-<f6>" 'compile
  162. ;; Do not use `recompile' since we want to change the compilation folder for the current buffer.
  163. "<f6>" 'ambrevar/compile-last-command)
  164. (setq auto-revert-mode-text "")
  165. (set-face-foreground 'magit-branch-remote "orange red")
  166. (setq git-commit-summary-max-length fill-column)
  167. ;; Customize what to fold by default.
  168. ;; (push (cons [* commitbuf] 'hide) magit-section-initial-visibility-alist)
  169. ;; Avoid conflict with WM.
  170. (define-key magit-mode-map (kbd "s-<tab>") nil)
  171. (setq magit-diff-refine-hunk 'all)
  172. (setq magit-repository-directories '(("~/.password-store") ; TODO: Sync with homesync / homeinit?
  173. ("~/common-lisp" . 1)
  174. ("~/projects" . 1)
  175. ("~/.local/share/emacs/site-lisp" . 1)))
  176. ;; magit-todos can be slow on big projects. Use it manually.
  177. ;; (when (require 'magit-todos nil 'noerror)
  178. ;; (magit-todos-mode))
  179. (require 'forge nil 'noerror))
  180. (when (fboundp 'magit-status)
  181. (global-set-key (kbd "C-x g") 'magit-status))
  182. (with-eval-after-load 'orgit
  183. (setq orgit-store-repository-id t))
  184. ;;; Mail
  185. ;; (with-eval-after-load 'mu4e
  186. ;; ;; mu4e-conversation must be enabled here.
  187. ;; ;; REVIEW: https://github.com/djcb/mu/issues/1258
  188. ;; (when (require 'mu4e-conversation nil t)
  189. ;; (global-mu4e-conversation-mode)
  190. ;; ;; (setq mu4e-debug t)
  191. ;; (setq mu4e-headers-show-threads nil
  192. ;; mu4e-headers-include-related nil)
  193. ;; ;; Tree is better to detect thread-jacks.
  194. ;; (setq mu4e-conversation-print-function 'mu4e-conversation-print-tree)
  195. ;; (add-hook 'mu4e-conversation-hook 'flyspell-mode)
  196. ;; (defun ambrevar/mu4e-conversation-sync ()
  197. ;; (let ((mu4e-get-mail-command "mbsync mail-sent atlas-sent"))
  198. ;; (mu4e-update-mail-and-index 'run-in-background)))
  199. ;; (add-hook 'mu4e-conversation-after-send-hook #'ambrevar/mu4e-conversation-sync)
  200. ;; (add-hook 'mu4e-view-mode-hook 'auto-fill-mode))
  201. ;; (require 'init-mu4e))
  202. ;; (autoload 'helm-mu4e-switch "mu4e")
  203. (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
  204. (with-eval-after-load 'nov (require 'init-nov))
  205. (with-eval-after-load 'notmuch (require 'init-notmuch))
  206. (with-eval-after-load 'make-mode (require 'init-makefile))
  207. (with-eval-after-load 'markdown-mode (require 'init-markdown))
  208. ;; (add-to-list 'auto-mode-alist '("\\.m\\'" . octave-mode)) ; matlab, but conflicts with obj-c.
  209. (defun ambrevar/octave-set-comment-start ()
  210. "Set comment character to '%' to be Matlab-compatible."
  211. (set (make-local-variable 'comment-start) "% "))
  212. (add-hook 'octave-mode-hook 'ambrevar/octave-set-comment-start)
  213. (autoload 'maxima-mode "maxima" "Maxima mode" t)
  214. (autoload 'maxima "maxima" "Maxima interaction" t)
  215. (add-to-list 'auto-mode-alist '("\\.mac" . maxima-mode))
  216. (add-to-list 'auto-mode-alist '("\\.wiki\\'" . mediawiki-mode))
  217. (with-eval-after-load 'mediawiki (require 'init-mediawiki))
  218. (with-eval-after-load 'elfeed (require 'init-elfeed))
  219. (with-eval-after-load 'org (require 'init-org))
  220. ;;; pdf-tools requires poppler built with cairo support.
  221. ;;; We cannot defer loading as `pdf-tools-install' is required for PDF
  222. ;;; association.
  223. ;;; REVIEW: `save-place' does not seem to work with pdf-tools.
  224. ;;; See https://github.com/politza/pdf-tools/issues/18.
  225. (when (require 'pdf-tools nil t)
  226. ;; (setq pdf-view-midnight-colors '("#ffffff" . "#000000"))
  227. (setq pdf-view-midnight-colors '("#ff9900" . "#0a0a12" )) ; Amber
  228. (add-hook 'pdf-view-mode-hook 'pdf-view-midnight-minor-mode)
  229. (pdf-tools-install t t t))
  230. (when (require 'rainbow-mode nil t)
  231. (dolist (hook '(css-mode-hook html-mode-hook sass-mode-hook))
  232. (add-hook hook 'rainbow-mode)))
  233. (with-eval-after-load 'restclient
  234. (define-key restclient-mode-map (kbd "M-s f") 'helm-restclient)
  235. (add-to-list 'helm-source-names-using-follow "Sources")
  236. (with-eval-after-load 'company-restclient
  237. (add-to-list 'company-backends 'company-restclient)
  238. (add-hook 'restclient-mode-hook 'company-mode)
  239. (define-key restclient-mode-map (kbd "M-<tab>") (if (require 'helm-company nil t)
  240. 'helm-company
  241. 'company-complete))))
  242. ;;; Screencast
  243. (with-eval-after-load 'camcorder
  244. (setq camcorder-output-directory (expand-file-name "Downloads" "~")
  245. camcorder-gif-output-directory camcorder-output-directory)
  246. (setq camcorder-recording-command '("recordmydesktop" " --fps 10 --no-sound --windowid " window-id " -o " file))
  247. (add-to-list 'camcorder-gif-conversion-commands '("ffmpeg-slow" "ffmpeg -i " input-file " -vf 'fps=10,scale=1024:-1:flags=lanczos' " gif-file)))
  248. (with-eval-after-load 'gif-screencast
  249. (define-key gif-screencast-mode-map (kbd "<f8>") 'gif-screencast-toggle-pause)
  250. (define-key gif-screencast-mode-map (kbd "<f9>") 'gif-screencast-stop))
  251. ;;; Shell
  252. (with-eval-after-load 'sh-script (require 'init-sh))
  253. ;;; Srt (subtitles)
  254. (add-to-list 'auto-mode-alist '("\\.srt\\'" . text-mode))
  255. ;;; System packages
  256. (global-set-key (kbd "C-x c #") 'helm-system-packages)
  257. (with-eval-after-load 'term
  258. ;; (require 'init-term)
  259. (setq term-buffer-maximum-size 0))
  260. (with-eval-after-load 'tex (require 'init-tex))
  261. ;; LaTeX is defined in the same file as TeX. To separate the loading, we add it
  262. ;; to the hook.
  263. (add-hook 'latex-mode-hook (lambda () (require 'init-latex)))
  264. (with-eval-after-load 'transmission
  265. ;; `transmission' will fail to start and will not run any hook if the daemon
  266. ;; is not up yet.
  267. ;; We need to advice the function :before to guarantee it starts.
  268. (defun ambrevar/transmission-start-daemon ()
  269. (unless (member "transmission-da"
  270. (mapcar
  271. (lambda (pid) (alist-get 'comm (process-attributes pid)))
  272. (list-system-processes)))
  273. (call-process "transmission-daemon")
  274. (sleep-for 1)))
  275. (advice-add 'transmission :before 'ambrevar/transmission-start-daemon)
  276. (setq transmission-refresh-modes '(transmission-mode transmission-files-mode transmission-info-mode transmission-peers-mode)
  277. transmission-refresh-interval 1))
  278. ;;; Theme
  279. (if (ignore-errors (load-theme 'cyberpunk 'no-confirm))
  280. (progn
  281. ;; REVIEW: Backport unmerged changes. See
  282. ;; https://github.com/n3mo/cyberpunk-theme.el/issues/46.
  283. (set-face-attribute 'lazy-highlight nil
  284. :underline '(:color "yellow")
  285. :foreground 'unspecified
  286. :background 'unspecified)
  287. (with-eval-after-load 'magit
  288. (let ((cyberpunk-green-2 "#006400"))
  289. (set-face-background 'diff-refine-added cyberpunk-green-2)))
  290. (with-eval-after-load 'org
  291. (set-face-attribute 'org-level-1 nil :height 1.1)
  292. (set-face-attribute 'org-level-2 nil :height 1.0)
  293. (set-face-attribute 'org-level-3 nil :height 1.0)))
  294. (require 'theme-ambrevar))
  295. ;;; Translator
  296. (when (require 'google-translate nil t)
  297. (require 'google-translate-default-ui)
  298. ;; (global-set-key "\C-ct" 'google-translate-at-point)
  299. ;; (global-set-key "\C-cT" 'google-translate-query-translate)
  300. (defun ambrevar/google-translate-line ()
  301. "Translate current line and insert result after it, separated by ' = '."
  302. (interactive)
  303. (let* ((langs (google-translate-read-args nil nil))
  304. (source-language (car langs))
  305. (target-language (cadr langs))
  306. text
  307. result)
  308. (end-of-line)
  309. (just-one-space)
  310. (setq text (buffer-substring-no-properties
  311. (line-beginning-position) (line-end-position)))
  312. (setq result (with-temp-buffer
  313. (google-translate-translate
  314. source-language target-language
  315. text
  316. 'current-buffer)
  317. (buffer-string))
  318. (insert "= " result)))))
  319. (when (require 'wgrep nil t)
  320. ;; TODO: wgrep-face is not so pretty.
  321. (set-face-attribute 'wgrep-face nil :inherit 'ediff-current-diff-C :foreground 'unspecified :background 'unspecified :box nil))
  322. ;;; Window manager
  323. (with-eval-after-load 'pulseaudio-control
  324. (setq pulseaudio-control-use-default-sink t
  325. pulseaudio-control-volume-step "2%"))
  326. (with-eval-after-load 'exwm
  327. (require 'init-exwm))
  328. ;;; XML / SGML
  329. (defun ambrevar/sgml-setup ()
  330. (setq sgml-xml-mode t)
  331. ;; (toggle-truncate-lines) ; This seems to slow down Emacs.
  332. (turn-off-auto-fill))
  333. (add-hook 'sgml-mode-hook 'ambrevar/sgml-setup)
  334. (with-eval-after-load 'nxml-mode
  335. (set-face-foreground 'nxml-element-local-name "gold1")
  336. (defvaralias 'nxml-child-indent 'tab-width))
  337. ;;; Because XML is hard to read.
  338. (add-hook 'nxml-mode-hook 'ambrevar/turn-on-tab-width-to-4)
  339. (with-eval-after-load 'youtube-dl
  340. (setq youtube-dl-directory "~/Downloads"))
  341. (with-eval-after-load 'ytdl
  342. (setq ytdl-always-query-default-filename 'yes)
  343. (setq ytdl-max-mini-buffer-download-type-entries 0)
  344. (setq ytdl-download-extra-args '("-f" "best")))
  345. (with-eval-after-load 'ztree
  346. (set-face-foreground 'ztreep-diff-model-add-face "deep sky blue"))
  347. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  348. ;;; Finalization
  349. (load (expand-file-name "hackpool/hacks.el" (getenv "PERSONAL")) :noerror)
  350. ;;; Don't let `customize' clutter my config.
  351. (setq custom-file
  352. (if (boundp 'server-socket-dir)
  353. (expand-file-name "custom.el" server-socket-dir)
  354. (expand-file-name (format "emacs-custom-%s.el" (user-uid)) temporary-file-directory)))
  355. (load custom-file t)
  356. ;;; Local config. You can use it to set system specific variables, such as the
  357. ;;; external web browser or the geographical coordinates:
  358. ;;
  359. ;; (setq calendar-latitude 20.2158)
  360. ;; (setq calendar-longitude 105.938)
  361. (load "local-after" t)