My personal .emacs.d folder

init.el 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. ;;;; Copyright © 2013-2022 Lily Carpenter
  2. ;;;; All rights reserved.
  3. ;;;; Web: https://azrazalea.net
  4. ;;;; Email: azra-license@azrazalea.net
  5. ;;;; This config is free software: you can redistribute it and/or modify
  6. ;;;; it under the terms of the GNU Lesser General Public License as published by
  7. ;;;; the Free Software Foundation, either version 3 of the License, or
  8. ;;;; (at your option) any later version.
  9. ;;;; This config is distributed in the hope that it will be useful,
  10. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;;;; GNU Lesser General Public License for more details.
  13. ;;;; You should have received a copy of the GNU Lesser General Public License
  14. ;;;; along with this config. If not, see <http://www.gnu.org/licenses/>.
  15. ;;;;
  16. (package-initialize)
  17. (server-start)
  18. (require 'misc)
  19. (let ((third-party "~/.emacs.d/third-party/"))
  20. (add-to-list 'load-path third-party)
  21. (mapcar #'(lambda (path)
  22. (add-to-list 'load-path (concat third-party path))
  23. (byte-recompile-directory (concat third-party path)))
  24. '("diminish" "lfe-mode"))
  25. (byte-recompile-directory third-party))
  26. (add-hook 'after-init-hook (lambda ()
  27. (require 'use-package)
  28. (require 'use-package-ensure)
  29. (require 'bind-key)
  30. (require 'diminish)
  31. (require 'lfe-start)
  32. (load "~/.emacs.d/packages")
  33. (diminish 'whitespace-mode)
  34. (diminish 'auto-revert-mode)
  35. (light-colors)
  36. (scroll-bar-mode -1)))
  37. (add-hook 'emacs-startup-hook (lambda ()
  38. (when (file-exists-p "~/.emacs.d/local_vars.el")
  39. (message "Loading local vars")
  40. (load "~/.emacs.d/local_vars"))))
  41. (put 'erase-buffer 'disabled nil)
  42. (require 'recentf)
  43. ;;;; Load other configuration files
  44. ;;;; May be able to switch to a directory based load if I determine order doesn't matter
  45. (load "~/.emacs.d/config.d/custom")
  46. (load "~/.emacs.d/config.d/keys")
  47. (load "~/.emacs.d/config.d/mac-compat")
  48. (load "~/.emacs.d/config.d/fira-code")
  49. (put 'scroll-left 'disabled nil)
  50. (setenv "PATH"
  51. (concat (getenv "HOME") "/bin:" (getenv "PATH")))
  52. (column-number-mode 1)
  53. (blink-cursor-mode 0)
  54. (setenv "MANWIDTH" (number-to-string (fixed-buffer-width)))
  55. (setenv "TERM" "xterm-color")
  56. (defun dark-colors (&optional frame)
  57. (interactive)
  58. (package-install? 'solarized-theme)
  59. (disable-theme 'solarized-light)
  60. (disable-theme 'cyberpunk)
  61. (disable-theme 'leuven)
  62. (load-theme 'solarized-dark))
  63. (defun presentation-font ()
  64. (interactive)
  65. (set-frame-font "Fira Code 24"))
  66. (defun dark-colors-presentation (&optional frame)
  67. (interactive)
  68. (package-install? 'reverse-theme)
  69. (disable-theme 'solarized-light)
  70. (disable-theme 'solarized-dark)
  71. (disable-theme 'leuven)
  72. (load-theme 'reverse))
  73. (defun light-colors (&optional frame)
  74. (interactive)
  75. (package-install? 'solarized-theme)
  76. (disable-theme 'solarized-dark)
  77. (disable-theme 'reverse)
  78. (disable-theme 'leuven)
  79. (load-theme 'solarized-light))
  80. (defun light-colors-presentation (&optional frame)
  81. (interactive)
  82. (package-install? 'leuven-theme)
  83. (disable-theme 'solarized-dark)
  84. (disable-theme 'solarized-light)
  85. (disable-theme 'reverse)
  86. (load-theme 'leuven))
  87. (defun ssh (ssh-to)
  88. (interactive "sSSH to: ")
  89. (let ((multi-term-program "ssh")
  90. (multi-term-buffer-name ssh-to)
  91. (multi-term-program-switches ssh-to))
  92. (multi-term)))
  93. (defun ssh-proxy (ssh-to port)
  94. (interactive "sSSH to: \nsPort[9999]: ")
  95. (if (string= "" port)
  96. (setq port "9999"))
  97. (make-comint-in-buffer "ssh-proxy" nil "ssh" nil "-CND" port ssh-to))
  98. (defun ssh-copy-id (ssh-to)
  99. (interactive "sSSH to: ")
  100. (make-comint-in-buffer "ssh-copy-id" nil "ssh-copy-id" nil ssh-to))
  101. (defvar hexcolour-keywords
  102. '(("#[[:xdigit:]]\\{6\\}"
  103. (0 (put-text-property (match-beginning 0)
  104. (match-end 0)
  105. 'face (list :background
  106. (match-string-no-properties 0)))))))
  107. (defun hexcolour-add-to-font-lock ()
  108. (font-lock-add-keywords nil hexcolour-keywords))
  109. (add-hook 'less-css-mode-hook 'hexcolour-add-to-font-lock)
  110. (require 'erc-services)
  111. (erc-services-mode 1)
  112. (add-to-list 'erc-modules 'log 'scrolltobottom)
  113. (erc-update-modules)
  114. (erc-spelling-mode 1)
  115. ;;; TeX and LaTeX
  116. (add-to-list 'auto-mode-alist '("\\.latex$" . latex-mode))
  117. ;;; Git
  118. (add-to-list 'auto-mode-alist '("\\.gitconfig" . conf-mode))
  119. ;;; Buffers
  120. (put 'downcase-region 'disabled nil)
  121. (put 'narrow-to-region 'disabled nil)
  122. ;;; GPG stuff.
  123. (require 'epa-file)
  124. ;; With this, you can find-file something.gpg and it will just work.
  125. (epa-file-enable)
  126. ;;; Whitespace mode
  127. (require 'whitespace)
  128. (global-whitespace-mode 0)
  129. (mapc (lambda (mode-hook)
  130. (add-hook mode-hook 'whitespace-mode))
  131. '(c-mode-hook
  132. c++-mode-hook
  133. emacs-lisp-mode-hook
  134. lisp-mode-hook
  135. python-mode-hook
  136. ruby-mode-hook))
  137. (define-key global-map (kbd "RET") 'newline-and-indent)
  138. (add-hook 'before-save-hook 'delete-trailing-whitespace)
  139. (add-hook 'prog-mode-hook
  140. (lambda ()
  141. (font-lock-add-keywords nil
  142. '(("\\<\\(FIXME\\|TODO\\|BUG\\|XXX\\):" 1 font-lock-warning-face prepend)))))
  143. (defun kill-url (url &rest _ignore)
  144. "Append URL to kill ring, so that user can take appropriate action."
  145. (interactive)
  146. (kill-new url))
  147. ;;; Custom Functions
  148. ;; Originally from stevey, adapted to support moving to a new directory.
  149. ;; <http://stackoverflow.com/a/1834038/693712>
  150. (defun rename-file-and-buffer (new-name)
  151. "Renames both current buffer and file it's visiting to NEW-NAME."
  152. (interactive
  153. (progn
  154. (if (not (buffer-file-name))
  155. (error "Buffer '%s' is not visiting a file!" (buffer-name)))
  156. (list (read-file-name (format "Rename %s to: " (file-name-nondirectory
  157. (buffer-file-name)))))))
  158. (if (equal new-name "")
  159. (error "Aborted rename"))
  160. (setq new-name (if (file-directory-p new-name)
  161. (expand-file-name (file-name-nondirectory
  162. (buffer-file-name))
  163. new-name)
  164. (expand-file-name new-name)))
  165. ;; If the file isn't saved yet, skip the file rename, but still update the
  166. ;; buffer name and visited file.
  167. (if (file-exists-p (buffer-file-name))
  168. (rename-file (buffer-file-name) new-name 1))
  169. (let ((was-modified (buffer-modified-p)))
  170. ;; This also renames the buffer, and works with uniquify
  171. (set-visited-file-name new-name)
  172. (if was-modified
  173. (save-buffer)
  174. ;; Clear buffer-modified flag caused by set-visited-file-name
  175. (set-buffer-modified-p nil))
  176. (message "Renamed to %s." new-name)))
  177. ;; From http://emacsredux.com/blog/2013/04/03/delete-file-and-buffer/
  178. ;; License unknown
  179. (defun delete-file-and-buffer ()
  180. "Kill the current buffer and deletes the file it is visiting."
  181. (interactive)
  182. (let ((filename (buffer-file-name)))
  183. (when filename
  184. (if (vc-backend filename)
  185. (vc-delete-file filename)
  186. (progn
  187. (delete-file filename)
  188. (message "Deleted file %s" filename)
  189. (kill-buffer))))))
  190. ;; http://www.emacswiki.org/emacs/ElispCookbook
  191. ;; Under Trim Whitespace
  192. (defun chomp (str)
  193. "Chomp leading and tailing whitespace from STR."
  194. (replace-regexp-in-string (rx (or (: bos (* (any " \t\n")))
  195. (: (* (any " \t\n")) eos)))
  196. ""
  197. str))
  198. ;; Gets current git top dir
  199. (defun git-top-dir ()
  200. (projectile-project-root))
  201. ;; Run rspecs at root of git
  202. (defun git-run-rspecs ()
  203. (interactive)
  204. (let ((default-directory (git-top-dir)))
  205. (if (and default-directory (file-directory-p default-directory))
  206. (let ((buffer-name "*rspec output*"))
  207. (if (get-buffer buffer-name)
  208. (with-current-buffer buffer-name
  209. (erase-buffer)))
  210. (start-process "git-rspec" buffer-name "rspec")
  211. (display-buffer buffer-name))
  212. (error "No valid top git directory found"))))
  213. ;; Rebase all whitespace seperated branches on top of each other,
  214. ;; from left to right. Current branch will be on the bottom
  215. (defun git-tree-rebase (branches)
  216. (interactive "sWhitespace seperated list of branches: ")
  217. (let ((branches (split-string branches))
  218. (prev-branch (magit-get-current-branch)))
  219. (dolist (cur-branch branches)
  220. (let ((rc (shell-command (format "git checkout %s; git rebase %s" cur-branch prev-branch))))
  221. (if (not (equal rc 0))
  222. (error "Failed to rebase %s on %s" cur-branch prev-branch)))
  223. (setq prev-branch cur-branch))))
  224. ;; Force update all whitespace seperated branches
  225. (defun git-fix-tree (branches)
  226. (interactive "sWhitespace seperated list of branches: ")
  227. (let ((branches (split-string branches)))
  228. (dolist (branch branches)
  229. (let ((rc (shell-command (format "git push origin :%s; git push origin %s" branch branch))))
  230. (if (not (equal rc 0))
  231. (error "Failed to fix branch %s" branch))))))
  232. ;; Get current branch
  233. (defun git-current-branch ()
  234. (let ((default-directory (git-top-dir)))
  235. (if default-directory
  236. (chomp (shell-command-to-string "git rev-parse --abbrev-ref HEAD"))
  237. (error "Failed to get top directory of repo"))))
  238. ;; Set experimental to current branch and push
  239. (defun git-set-experimental ()
  240. (interactive)
  241. (let* ((current-branch (git-current-branch))
  242. (rc (shell-command (format "git branch -f experimental %s;" current-branch))))
  243. (if (not (equal rc 0))
  244. (error "Failed to move branch experimental to %s" current-branch)))
  245. (git-fix-tree "experimental"))
  246. ;; Check entire git repo's ruby syntax
  247. (require 'find-lisp)
  248. (defun git-check-ruby ()
  249. (interactive)
  250. (let ((default-directory (git-top-dir)))
  251. (if (and default-directory (file-directory-p default-directory))
  252. (let ((buffer-name "*syntax checker*"))
  253. (if (get-buffer buffer-name)
  254. (with-current-buffer buffer-name
  255. (erase-buffer)))
  256. (dolist (file (find-lisp-find-files default-directory "\\.rb$"))
  257. (start-process "ruby-syntax-check" buffer-name "ruby" "-c" file))
  258. (display-buffer buffer-name))
  259. (error "Syntax check failed."))))
  260. ;; Check whether or not a git repo is dirty
  261. (defun git-dirty? (directory)
  262. (let* ((default-directory directory)
  263. (status (shell-command-to-string "git status -s"))
  264. (good-status '("" " M .gitmodules\n" "M .gitmodules\n"))
  265. (result t))
  266. (if (member status good-status)
  267. (setq result nil))
  268. result))
  269. ;; Update a git repo
  270. ;; TODO: Make this work with main or master not just master
  271. (defun update-git (directory)
  272. (let* ((default-directory directory)
  273. (orig-branch (magit-get-current-branch))
  274. rc)
  275. (setq rc (shell-command "git checkout master && git pull"))
  276. (if (not (equal rc 0))
  277. (error "Error: Failed to update repo %s" directory))
  278. (setq rc (shell-command (format "git checkout %s" orig-branch)))
  279. (if (not (equal rc 0))
  280. (error "Error: Failed to checkout original branch %s in repo %s" orig-branch directory))))
  281. ;; Update all repos in the home directory
  282. (defun update-all-repos ()
  283. (interactive)
  284. (let* ((directory (concat (getenv "HOME") "/git-repos"))
  285. (files (directory-files-and-attributes directory t)))
  286. (dolist (file files)
  287. (cond
  288. ;; Ignore . and ..
  289. ((or (string-match "\\.$" (car file)) (string-match "\\.\\.$" (car file))))
  290. ;; Directories with non dirty git need to be checked for git info
  291. ((and (eq t (car (cdr file))) (not (git-dirty? (format "%s/" (car file)))))
  292. (update-git (format "%s/" (car file))))))))
  293. (defun kill-dired-buffers ()
  294. (interactive)
  295. (mapc (lambda (buffer)
  296. (when (eq 'dired-mode (buffer-local-value 'major-mode buffer))
  297. (kill-buffer buffer)))
  298. (buffer-list)))
  299. (defun kill-tramp-buffers ()
  300. (interactive)
  301. (tramp-cleanup-all-connections)
  302. (tramp-cleanup-all-buffers))
  303. (defun git-all-files ()
  304. (let ((default-directory (git-top-dir)))
  305. (split-string (shell-command-to-string "git ls-tree -r --name-only --full-name HEAD"))))
  306. ;; A proper projectile replace with regex support
  307. (defun git-replace-regexp (from to)
  308. (interactive "sFrom regexp: \nsTo regexp: ")
  309. (let ((files '(delq nil (mapcar (lambda (directory)
  310. (let ((file (concat (projectile-project-root) directory)))
  311. (if (file-writable-p file)
  312. file)))
  313. (git-all-files)))))
  314. (tags-query-replace from to nil files)))
  315. ;; From http://stackoverflow.com/a/7250027/693712
  316. (defun smart-line-beginning ()
  317. "Move point to the beginning of text on the current line; if that is already
  318. the current position of point, then move it to the beginning of the line."
  319. (interactive)
  320. (let ((pt (point)))
  321. (beginning-of-line-text)
  322. (when (eq pt (point))
  323. (beginning-of-line))))
  324. (add-hook 'prog-mode-hook (lambda () (local-set-key (kbd "C-a") 'smart-line-beginning)))
  325. ;;; Eshell Functions
  326. ;; Taken from <http://www.emacswiki.org/emacs/EshellFunctions>
  327. (defun eshell/emacs (&rest args)
  328. "Open a file in emacs. Some habits die hard."
  329. (if (null args)
  330. ;; If I just ran "emacs", I probably expect to be launching
  331. ;; Emacs, which is rather silly since I'm already in Emacs.
  332. ;; So just pretend to do what I ask.
  333. (bury-buffer)
  334. ;; We have to expand the file names or else naming a directory in an
  335. ;; argument causes later arguments to be looked for in that directory,
  336. ;; not the starting directory
  337. (mapc #'find-file (mapcar #'expand-file-name (eshell-flatten-list (reverse args))))))
  338. (defun format-commands (&rest commands)
  339. (mapcar (lambda (command)
  340. (shell-command (apply 'format command)))
  341. commands))
  342. (defun eshell/git-branch-here (branch)
  343. (format-commands `("git push origin :%s" ,branch)
  344. `("git branch -d %s" ,branch)
  345. `("git checkout -b %s" ,branch)
  346. `("git push origin %s:%s" ,branch ,branch)))
  347. (defun eshell/git-experimental-here ()
  348. (eshell/git-branch-here "experimental"))
  349. ;;; Spell Checking
  350. (add-hook 'prog-mode-hook 'flyspell-prog-mode)
  351. (mapcar #'(lambda (mode-hook)
  352. (add-hook mode-hook 'flyspell-mode))
  353. '(latex-mode-hook
  354. magit-log-edit-mode-hook
  355. org-mode-hook
  356. message-mode-hook))
  357. ;;; Maxima
  358. (add-to-list 'load-path "/usr/share/maxima/5.32.1/emacs/")
  359. (autoload 'maxima-mode "maxima" "Maxima mode" t)
  360. (autoload 'imaxima "imaxima" "Frontend for maxima with Image support" t)
  361. (autoload 'maxima "maxima" "Maxima interaction" t)
  362. (autoload 'imath-mode "imath" "Imath mode for math formula input" t)
  363. (setq imaxima-use-maxima-mode-flag t
  364. imaxima-pt-size 12
  365. imaxima-fnt-size "large"
  366. imaxima-max-scale nil
  367. imaxima-linearize-flag nil)
  368. (add-to-list 'auto-mode-alist '("\\.ma[cx]" . maxima-mode))
  369. (global-prettify-symbols-mode)
  370. (defun bf-pretty-print-xml-region (begin end)
  371. "Pretty format XML markup in region. You need to have nxml-mode
  372. http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
  373. this. The function inserts linebreaks to separate tags that have
  374. nothing but whitespace between them. It then indents the markup
  375. by using nxml's indentation rules."
  376. (interactive "r")
  377. (save-excursion
  378. (nxml-mode)
  379. (goto-char begin)
  380. (while (search-forward-regexp "\>[ \\t]*\<" nil t)
  381. (backward-char) (insert "\n"))
  382. (indent-region begin end))
  383. (message "Ah, much better!"))
  384. ;; Only should be used in init, use use-package elsewhere
  385. (defun package-install? (package-name)
  386. (when (not (package-installed-p package-name))
  387. (package-install package-name)))
  388. (defun insert-random-uuid ()
  389. (interactive)
  390. (lexical-let ((string (replace-regexp-in-string "\n" "" (shell-command-to-string "uuidgen"))))
  391. (insert string)
  392. (kill-new string)))
  393. (defun eslint-autofix ()
  394. (interactive)
  395. (let ((default-directory (flycheck-eslint--find-working-directory nil)))
  396. (flycheck-call-checker-process 'javascript-eslint nil nil nil "--fix" buffer-file-name)))
  397. (add-hook 'js-mode-hook
  398. (lambda ()
  399. (add-hook 'after-save-hook #'eslint-autofix nil 'make-it-local)))
  400. (add-hook 'web-mode-hook
  401. (lambda ()
  402. (when (string-equal "tsx" (file-name-extension buffer-file-name))
  403. (add-hook 'after-save-hook #'eslint-autofix nil 'make-it-local))))
  404. (add-hook 'typescript-mode-hook
  405. (lambda ()
  406. (add-hook 'after-save-hook #'eslint-autofix nil 'make-it-local)))