Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

560 lines
18 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. * Editing
  2. ** Imenu
  3. #+BEGIN_SRC emacs-lisp
  4. (use-package imenu
  5. :bind
  6. ("C-S-s" . imenu))
  7. #+END_SRC
  8. ** IEdit mode
  9. #+BEGIN_SRC emacs-lisp
  10. (use-package iedit
  11. :bind ("C-;" . iedit-mode))
  12. #+END_SRC
  13. ** Spellcheck
  14. #+BEGIN_SRC emacs-lisp
  15. (global-set-key (kbd "C-!") 'ispell-buffer)
  16. #+END_SRC
  17. ** Undo tree
  18. #+BEGIN_SRC emacs-lisp
  19. (use-package undo-tree
  20. :config
  21. (global-undo-tree-mode))
  22. #+END_SRC
  23. * Added functionality
  24. ** Multiline sexp with symbol
  25. Jump to symbol, go up list, lispy-multiline. Great for diff-friendly custom
  26. #+BEGIN_SRC emacs-lisp
  27. (require 'isearch)
  28. (require 'lispy)
  29. (defun rlbr/multiline-sexp-with-symbol (symbol-name)
  30. (save-excursion
  31. (beginning-of-buffer)
  32. (search-forward-regexp (isearch-symbol-regexp symbol-name))
  33. (backward-up-list)
  34. (lispy-alt-multiline)))
  35. #+END_SRC
  36. ** Output matches
  37. Run command for each matching exe and see if output-p is true when fed the command output
  38. #+BEGIN_SRC emacs-lisp
  39. (defun rlbr/output-matches (output-matches-p exe args)
  40. "locate the executable whose output satisfies output-matches-p when fed args and return the full-path"
  41. (let ((exec-path exec-path)
  42. (output)
  43. (bad)
  44. (command-output)
  45. (current-exe)
  46. (failed))
  47. (while (not (or output failed))
  48. (setq current-exe
  49. (executable-find exe))
  50. (if current-exe
  51. (progn
  52. (setq command-output
  53. (shell-command-to-string (format "%s %s" (rlbr/quote-exe current-exe) args)))
  54. (if (funcall output-matches-p command-output)
  55. (setq output current-exe)
  56. (progn
  57. (setq bad
  58. (replace-regexp-in-string "/$" "" (file-name-directory current-exe)))
  59. (setq exec-path
  60. (seq-filter (lambda (item) (not (rlbr/case-insensitive-match item bad))) exec-path)))))
  61. (setq failed t)))
  62. output))
  63. #+END_SRC
  64. ** Save buffer-output to file
  65. This handy function is a customized ripoff of custom-save-all
  66. #+BEGIN_SRC emacs-lisp
  67. (defun rlbr/save-buffer-func-to-file (visit-file func args)
  68. "Rip off of custom-save-all"
  69. (let* ((filename visit-file)
  70. (recentf-exclude (if recentf-mode
  71. (append
  72. `(,(concat "\\`" (regexp-quote (recentf-expand-file-name visit-file)) "\\'")
  73. ,(concat "\\`" (regexp-quote (file-truename (recentf-expand-file-name visit-file))) "\\'"))
  74. recentf-exclude)))
  75. (old-buffer (find-buffer-visiting filename))
  76. old-buffer-name)
  77. (with-current-buffer
  78. (let ((find-file-visit-truename t))
  79. (or old-buffer
  80. (let ((delay-mode-hooks t))
  81. (find-file-noselect filename))))
  82. (when old-buffer
  83. (setq old-buffer-name
  84. (buffer-file-name))
  85. (set-visited-file-name
  86. (file-chase-links filename)))
  87. (unless (eq major-mode
  88. 'emacs-lisp-mode)
  89. (delay-mode-hooks
  90. (emacs-lisp-mode)))
  91. (let ((inhibit-read-only t)
  92. (print-length nil)
  93. (print-level nil))
  94. (apply func args))
  95. (let ((file-precious-flag t))
  96. (save-buffer))
  97. (if old-buffer
  98. (progn
  99. (set-visited-file-name
  100. old-buffer-name)
  101. (set-buffer-modified-p nil))
  102. (kill-buffer (current-buffer))))))
  103. #+END_SRC
  104. * Save/load
  105. ** Backup/auto-save
  106. #+BEGIN_SRC emacs-lisp
  107. (let ((backup-dir "~/.emacs.d/backup")
  108. (auto-save-dir "~/.emacs.d/autosave"))
  109. (if (not (file-directory-p backup-dir))
  110. (make-directory backup-dir))
  111. (if (not (file-directory-p
  112. auto-save-dir))
  113. (make-directory auto-save-dir)))
  114. #+END_SRC
  115. ** On save
  116. #+BEGIN_SRC emacs-lisp
  117. (add-hook 'before-save-hook 'delete-trailing-whitespace)
  118. #+END_SRC
  119. ** Recent files mode
  120. #+BEGIN_SRC emacs-lisp
  121. (use-package recentf
  122. :config
  123. (recentf-mode 1))
  124. #+END_SRC
  125. * Platform dependent
  126. ** Windows
  127. #+BEGIN_SRC emacs-lisp
  128. (when (string-equal system-type "windows-nt")
  129. (progn
  130. (defun rlbr/quote-exe (path)
  131. (w32-short-file-name path))
  132. (defun rlbr/start-external-shell ()
  133. (interactive)
  134. (start-process-shell-command (format "cmd(%s)" default-directory) nil "start default.bat"))
  135. (global-set-key (kbd "C-S-C") 'rlbr/start-external-shell)
  136. (defun rlbr/start-windows-explorer-here ()
  137. (interactive)
  138. (start-process-shell-command "explorer" nil (format "explorer %s" (replace-regexp-in-string "/" (regexp-quote "\\") (expand-file-name default-directory)))))
  139. (global-set-key (kbd "C-S-E") 'rlbr/start-windows-explorer-here)
  140. (defun rlbr/case-insensitive-match (string1 string2)
  141. (apply 'string-equal (mapcar 'downcase (list string1 string2))))
  142. (let ((find)
  143. (grep)
  144. (ls))
  145. (progn
  146. (setq find
  147. (rlbr/output-matches
  148. (lambda (output) (string-equal ".\n" output))
  149. "find" "-maxdepth 0"))
  150. (if find
  151. (setq find-program (rlbr/quote-exe find)))
  152. (setq grep (rlbr/output-matches
  153. (lambda (output) (string-match "grep (\\w+ grep)" output))
  154. "grep" "-V"))
  155. (if grep
  156. (setq grep-program
  157. (rlbr/quote-exe grep)))
  158. (setq ls (rlbr/output-matches
  159. (lambda (output) (string-match "ls: .*'\\?/': No such file or directory" output))
  160. "ls" "?/"))
  161. (if ls
  162. (setq insert-directory-program (rlbr/quote-exe ls)))))))
  163. #+END_SRC
  164. * Tramp configuration
  165. ** Tramp append plist to connection properties
  166. #+BEGIN_SRC emacs-lisp
  167. (use-package kv
  168. :config
  169. (defun rlbr/add-config-to-tramp (matches-regexp config-plist)
  170. (let ((config-alist (kvplist->alist config-plist)))
  171. (dolist (pair config-alist)
  172. (let ((config (list
  173. matches-regexp
  174. (car pair)
  175. (cdr pair))))
  176. (add-to-list
  177. 'tramp-connection-properties
  178. config))))))
  179. #+END_SRC
  180. ** Android
  181. #+BEGIN_SRC emacs-lisp
  182. (use-package tramp
  183. :config
  184. (let ((android-config (let ((default-directory "/data/data/com.termux/files"))
  185. (list "tmpdir" (expand-file-name "home/temp/")
  186. "remote-shell" (expand-file-name "usr/bin/sh")
  187. "remote-process-environment" (append (list (concat "PREFIX=" default-directory "usr")) tramp-remote-process-environment)
  188. "remote-path" (append (mapcar 'expand-file-name '("home/.local/bin" "usr/bin" "usr/bin/applets")) '("/sbin" "/vendor/bin" "/system/sbin" "/system/bin" "/system/xbin"))))))
  189. (rlbr/add-config-to-tramp "/ssh:termux.*:" android-config)))
  190. #+END_SRC
  191. * Major modes
  192. ** C
  193. #+BEGIN_SRC emacs-lisp
  194. (use-package cc-mode
  195. :mode ("\\.c\\'" . c-mode)
  196. :hook (c-mode . format-all-mode)
  197. :config
  198. (use-package format-all))
  199. #+END_SRC
  200. ** Java
  201. *** Meghanada
  202. #+BEGIN_SRC emacs-lisp
  203. (use-package autodisass-java-bytecode
  204. :defer t)
  205. (use-package meghanada
  206. :defer t
  207. :init
  208. (add-hook 'java-mode-hook
  209. (lambda ()
  210. (meghanada-mode t)
  211. (flycheck-mode +1)
  212. (add-hook 'before-save-hook 'meghanada-code-beautify-before-save)))
  213. :config
  214. (setq indent-tabs-mode nil)
  215. (setq meghanada-server-remote-debug t)
  216. (setq meghanada-javac-xlint "-Xlint:all,-processing")
  217. (defhydra hydra-meghanada (:hint nil :exit t)
  218. "
  219. ^Edit^ ^Tast or Task^
  220. ^^^^^^-------------------------------------------------------
  221. _f_: meghanada-compile-file _m_: meghanada-restart
  222. _c_: meghanada-compile-project _t_: meghanada-run-task
  223. _o_: meghanada-optimize-import _j_: meghanada-run-junit-test-case
  224. _s_: meghanada-switch-test-case _J_: meghanada-run-junit-class
  225. _v_: meghanada-local-variable _R_: meghanada-run-junit-recent
  226. _i_: meghanada-import-all _r_: meghanada-reference
  227. _g_: magit-status _T_: meghanada-typeinfo
  228. _q_: exit
  229. "
  230. ("f" meghanada-compile-file)
  231. ("m" meghanada-restart)
  232. ("c" meghanada-compile-project)
  233. ("o" meghanada-optimize-import)
  234. ("s" meghanada-switch-test-case)
  235. ("v" meghanada-local-variable)
  236. ("i" meghanada-import-all)
  237. ("g" magit-status)
  238. ("t" meghanada-run-task)
  239. ("T" meghanada-typeinfo)
  240. ("j" meghanada-run-junit-test-case)
  241. ("J" meghanada-run-junit-class)
  242. ("R" meghanada-run-junit-recent)
  243. ("r" meghanada-reference)
  244. ("q" exit)
  245. ("z" nil "leave"))
  246. :bind
  247. (:map meghanada-mode-map
  248. ("C-S-t" . meghanada-switch-testcase)
  249. ("M-RET" . meghanada-local-variable)
  250. ("M-r" . meghanada-reference)
  251. ("M-t" . meghanada-typeinfo)
  252. ("C-z" . hydra-meghanada/body))
  253. :commands
  254. (meghanada-mode))
  255. #+END_SRC
  256. ** JavaScript
  257. #+BEGIN_SRC emacs-lisp
  258. (use-package js2-mode
  259. :mode "\\.js\\'"
  260. :hook ((js2-mode . js2-imenu-extras-mode)
  261. (js2-mode . (lambda () (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t))))
  262. :config
  263. (use-package js2-refactor
  264. :hook (js2-mode . js2-refactor-mode)
  265. :bind
  266. (:map js2-mode-map
  267. ("C-k" . js2r-kill))
  268. :config
  269. (js2r-add-keybindings-with-prefix "C-c C-r"))
  270. (use-package xref-js2
  271. :demand t)
  272. (define-key js-mode-map (kbd "M-.") nil)
  273. (defun rlbr/jump-to-definition ()
  274. "Jump to a definition."
  275. (interactive)
  276. (condition-case-unless-debug nil
  277. (js2-jump-to-definition)
  278. (error
  279. (progn
  280. (ignore-errors
  281. (xref-pop-marker-stack))
  282. (xref-find-definitions (xref-backend-identifier-at-point (xref-find-backend)))))))
  283. (define-key js-mode-map (kbd "M-.") #'rlbr/jump-to-definition))
  284. #+END_SRC
  285. ** Magit
  286. #+BEGIN_SRC emacs-lisp
  287. (use-package magit
  288. :bind (("C-x g" . magit-status))
  289. :config
  290. (use-package git-commit
  291. :hook (git-commit-setup . git-commit-turn-on-flyspell)))
  292. #+END_SRC
  293. ** Python
  294. *** Platform specific
  295. #+BEGIN_SRC emacs-lisp
  296. (setq elpy-rpc-python-command
  297. (cond
  298. ((string-equal system-type "gnu/linux")
  299. "python3")
  300. ((string-equal system-type "windows-nt")
  301. "pythonw.exe")))
  302. #+END_SRC
  303. *** custom feature
  304. #+BEGIN_SRC emacs-lisp
  305. (defun rlbr/join-venv-with-number (number-name)
  306. "Join a list with a name and a number"
  307. (let
  308. ((number (car number-name))
  309. (name (cdr number-name)))
  310. (if (= number 0)
  311. name
  312. (string-join (list name (number-to-string number))
  313. "~"))))
  314. (defun rlbr/split-venv-with-number (name-number)
  315. "Split a virtualenv name with either a ~ seperating the name and the number, or nothing"
  316. (let ((split-result (split-string name-number (regexp-quote "~")))
  317. (ret))
  318. (if (= 1 (length split-result))
  319. (progn
  320. (setq ret (car split-result))
  321. (push 0 ret))
  322. (progn
  323. (setq ret
  324. (string-join
  325. (butlast split-result)
  326. "~"))
  327. (push
  328. (string-to-number
  329. (car (last split-result)))
  330. ret)))
  331. ret))
  332. (defun rlbr/get-venv-name (&optional library-root)
  333. "Generate venv name based off of the base-name of the library root"
  334. (file-name-base
  335. (directory-file-name
  336. (if library-root
  337. library-root
  338. (elpy-library-root)))))
  339. (defun rlbr/handle-name-conflicts (venv-name)
  340. "Deal with potential name conflicts in venv"
  341. (let ((venv-conflicts)
  342. (venv-partition-name))
  343. (setq venv-partition-name (rlbr/split-venv-with-number venv-name))
  344. (setq venv-conflicts
  345. (seq-filter
  346. (lambda (item)
  347. (string-equal (cdr item)
  348. venv-name))
  349. (mapcar #'rlbr/split-venv-with-number (pyvenv-virtualenv-list))))
  350. (when venv-conflicts
  351. (setcar venv-partition-name (1+ (apply 'max (mapcar #'car venv-conflicts)))))
  352. (rlbr/join-venv-with-number venv-partition-name)))
  353. (require 'vc)
  354. (defun rlbr/setup-python-venv-dirlocals (&optional library-root venv-name)
  355. "Setup .dir-locals file in library root and tell vc system to ignore .dir-locals file"
  356. (let* ((library-root (if library-root
  357. library-root
  358. (elpy-library-root)))
  359. (venv-name (if venv-name venv-name (rlbr/get-venv-name library-root)))
  360. (default-directory library-root)
  361. (dir-locals-path (expand-file-name
  362. ".dir-locals.el")))
  363. (rlbr/save-buffer-func-to-file dir-locals-path 'add-dir-local-variable
  364. `(python-mode pyvenv-workon ,venv-name))
  365. (let* ((vc-root (vc-find-root dir-locals-path ".git"))
  366. (vc-ignore-file (vc-call-backend 'Git 'find-ignore-file vc-root)))
  367. (if (apply 'string-equal (mapcar 'directory-file-name (mapcar 'file-truename (list vc-root library-root))))
  368. (progn
  369. (unless (file-exists-p vc-ignore-file)
  370. (with-temp-buffer
  371. (write-file vc-ignore-file)))
  372. (vc-ignore ".dir-locals.el"))
  373. (when (y-or-n-p (format "Ignore .dir-locals.el in repo '%s' ?" vc-root))
  374. (unless (file-exists-p vc-ignore-file)
  375. (with-temp-buffer
  376. (write-file vc-ignore-file)))
  377. (vc-ignore ".dir-locals.el"))))))
  378. (defun rlbr/get-python-executable ()
  379. (read-file-name "Python interpreter to use: " (file-name-directory (executable-find "python"))
  380. nil nil "python"))
  381. (defun rlbr/init-python-venv-in-library-root (&optional library-root)
  382. "Prompt to either create one or use default" (let ((venv-name (rlbr/get-venv-name))
  383. (library-root (if library-root library-root (elpy-library-root))))
  384. (setq venv-name (rlbr/handle-name-conflicts venv-name))
  385. (if (y-or-n-p (format "Create venv '%s'?" venv-name))
  386. (pyvenv-create venv-name (rlbr/get-python-executable))
  387. (progn (unless (member "emacs-default-venv" (pyvenv-virtualenv-list))
  388. (pyvenv-create venv-name (rlbr/get-python-executable)))
  389. (setq venv-name "emacs-default-venv")))
  390. (rlbr/setup-python-venv-dirlocals library-root venv-name)
  391. venv-name))
  392. (require 'dash)
  393. (defun rlbr/init-venv ()
  394. (when (eq major-mode 'python-mode)
  395. (cond ((file-remote-p buffer-file-name)
  396. (setq-local pyvenv-workon "emacs-default-venv"))
  397. ((let ((buffer-file-name (file-truename buffer-file-name)))
  398. (-any (lambda (file-prefix)
  399. (string-prefix-p file-prefix buffer-file-name))
  400. (mapcar 'file-truename (list (elpy-rpc-get-or-create-virtualenv)
  401. (pyvenv-workon-home)))))
  402. nil)
  403. (t (cond ((and pyvenv-workon (not (member pyvenv-workon (pyvenv-virtualenv-list))))
  404. (if (y-or-n-p (format "Venv '%s' is specified but does not exist. Create it?" pyvenv-workon))
  405. (progn (pyvenv-create pyvenv-workon (rlbr/get-python-executable))
  406. (pyvenv-workon pyvenv-workon))
  407. (rlbr/save-buffer-func-to-file (let ((default-directory (elpy-library-root)))
  408. (expand-file-name ".dir-locals.el"))
  409. 'add-dir-local-variable '(python-mode pyvenv-workon "emacs-default-venv"))
  410. (setq-local pyvenv-workon "emacs-default-venv")))
  411. ((not pyvenv-workon)
  412. (setq-local pyvenv-workon (rlbr/init-python-venv-in-library-root))))))
  413. (pyvenv-workon pyvenv-workon)))
  414. #+END_SRC
  415. *** bindings/settings
  416. #+BEGIN_SRC emacs-lisp
  417. (use-package python
  418. :hook ((python-mode . blacken-mode)
  419. (python-mode . pyvenv-mode)
  420. (hack-local-variables . rlbr/init-venv))
  421. :config
  422. (use-package elpy
  423. :bind (("C-=" . elpy-goto-assignment))
  424. :config (when (require 'flycheck nil t)
  425. (setq elpy-modules (delq 'elpy-module-flymake elpy-modules))))
  426. (elpy-enable))
  427. #+END_SRC
  428. ** SSH config mode
  429. #+BEGIN_SRC emacs-lisp
  430. (use-package ssh-config-mode
  431. :mode "~/.ssh/config\\'")
  432. #+END_SRC
  433. ** Tramp
  434. ** Webmode
  435. #+BEGIN_SRC emacs-lisp
  436. (use-package web-mode
  437. :mode
  438. (("\\.phtml\\'" . web-mode)
  439. ("\\.tpl\\.php\\'" . web-mode)
  440. ("\\.[agj]sp\\'" . web-mode)
  441. ("\\.as[cp]x\\'" . web-mode)
  442. ("\\.erb\\'" . web-mode)
  443. ("\\.mustache\\'" . web-mode)
  444. ("\\.djhtml\\'" . web-mode)
  445. ("\\.html?\\'" . web-mode)))
  446. #+END_SRC
  447. ** YAML
  448. #+BEGIN_SRC emacs-lisp
  449. (use-package yaml-mode
  450. :mode "\\.yml\\'")
  451. #+END_SRC
  452. * Minor modes/misc
  453. ** Kill the things
  454. *** Buffer
  455. #+BEGIN_SRC emacs-lisp
  456. (global-set-key (kbd "C-x k") 'kill-this-buffer)
  457. #+END_SRC
  458. *** Emacs
  459. #+BEGIN_SRC emacs-lisp
  460. (global-set-key (kbd "C-x C-k C-x C-k") 'save-buffers-kill-emacs)
  461. #+END_SRC
  462. ** Lispy
  463. #+BEGIN_SRC emacs-lisp
  464. (use-package lispy
  465. :hook ((emacs-lisp-mode) . lispy-mode))
  466. #+END_SRC
  467. ** Custom custom
  468. #+BEGIN_SRC emacs-lisp
  469. (advice-add 'custom-save-faces :after (lambda () (rlbr/multiline-sexp-with-symbol "custom-set-faces")))
  470. (advice-add 'custom-save-variables :after (lambda () (rlbr/multiline-sexp-with-symbol "custom-set-variables")))
  471. #+END_SRC
  472. ** Elmacro
  473. #+BEGIN_SRC emacs-lisp
  474. (use-package elmacro
  475. :demand
  476. :config
  477. (elmacro-mode +1))
  478. #+END_SRC
  479. * Navigation/auto-completion
  480. ** Ace window
  481. #+BEGIN_SRC emacs-lisp
  482. (use-package ace-window
  483. :bind (("M-Q" . ace-window)))
  484. #+END_SRC
  485. ** Hippie expand
  486. #+BEGIN_SRC emacs-lisp
  487. (use-package hippie-exp
  488. :bind ("M-/" . hippie-expand))
  489. #+END_SRC
  490. ** IBuffer mode
  491. #+BEGIN_SRC emacs-lisp
  492. (use-package ibbufer-vc
  493. :hook ((ibuffer-mode . ibuffer-vc-set-filter-groups-by-vc-root)))
  494. (use-package ibuffer
  495. :bind (("C-x C-b" . ibuffer))
  496. :config
  497. (define-ibuffer-column size-h
  498. ;; Use human readable Size column instead of original one
  499. (:name "Size" :inline t)
  500. (cond ((> (buffer-size) 1000000)
  501. (format "%7.1fM" (/ (buffer-size) 1000000.0)))
  502. ((> (buffer-size) 100000)
  503. (format "%7.0fk" (/ (buffer-size) 1000.0)))
  504. ((> (buffer-size) 1000)
  505. (format "%7.1fk" (/ (buffer-size) 1000.0)))
  506. (t
  507. (format "%8d" (buffer-size))))))
  508. #+END_SRC
  509. ** Ivy
  510. #+BEGIN_SRC emacs-lisp
  511. (use-package ivy
  512. :config
  513. (use-package swiper
  514. :bind ("C-s" . swiper))
  515. (ivy-mode))
  516. #+END_SRC
  517. * Look and feel
  518. ** Line numbers
  519. #+BEGIN_SRC emacs-lisp
  520. (global-display-line-numbers-mode)
  521. #+END_SRC
  522. ** Mode line bell
  523. #+BEGIN_SRC emacs-lisp
  524. (use-package mode-line-bell
  525. :config
  526. (mode-line-bell-mode))
  527. #+END_SRC
  528. ** Spaceline
  529. #+BEGIN_SRC emacs-lisp
  530. (use-package spaceline-config
  531. :config
  532. (use-package winum
  533. :init
  534. (setq winum-keymap
  535. (let ((map (make-sparse-keymap)))
  536. (define-key map (kbd "M-0") 'winum-select-window-0-or-10)
  537. (define-key map (kbd "M-1") 'winum-select-window-1)
  538. (define-key map (kbd "M-2") 'winum-select-window-2)
  539. (define-key map (kbd "M-3") 'winum-select-window-3)
  540. (define-key map (kbd "M-4") 'winum-select-window-4)
  541. (define-key map (kbd "M-5") 'winum-select-window-5)
  542. (define-key map (kbd "M-6") 'winum-select-window-6)
  543. (define-key map (kbd "M-7") 'winum-select-window-7)
  544. (define-key map (kbd "M-8") 'winum-select-window-8)
  545. map)))
  546. (spaceline-spacemacs-theme)
  547. (winum-mode))
  548. #+END_SRC