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.

487 lines
15 KiB

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