My personal .emacs.d folder

init.el 16KB

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