* Editing ** Imenu #+BEGIN_SRC emacs-lisp (use-package imenu :bind ("C-S-s" . imenu)) #+END_SRC ** IEdit mode #+BEGIN_SRC emacs-lisp (use-package iedit :bind ("C-;" . iedit-mode)) #+END_SRC ** Spellcheck #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-!") 'ispell-buffer) #+END_SRC ** Undo tree #+BEGIN_SRC emacs-lisp (use-package undo-tree :config (global-undo-tree-mode)) #+END_SRC * Added functionality ** Multiline sexp with symbol Jump to symbol, go up list, lispy-multiline. Great for diff-friendly custom #+BEGIN_SRC emacs-lisp (require 'isearch) (require 'lispy) (defun rlbr/multiline-sexp-with-symbol (symbol-name) (save-excursion (beginning-of-buffer) (search-forward-regexp (isearch-symbol-regexp symbol-name)) (backward-up-list) (lispy-alt-multiline))) #+END_SRC ** Output matches Run command for each matching exe and see if output-p is true when fed the command output #+BEGIN_SRC emacs-lisp (defun rlbr/output-matches (output-matches-p exe args) "locate the executable whose output satisfies output-matches-p when fed args and return the full-path" (let ((exec-path exec-path) (output) (bad) (command-output) (current-exe) (failed)) (while (not (or output failed)) (setq current-exe (executable-find exe)) (if current-exe (progn (setq command-output (shell-command-to-string (format "%s %s" (rlbr/quote-exe current-exe) args))) (if (funcall output-matches-p command-output) (setq output current-exe) (progn (setq bad (replace-regexp-in-string "/$" "" (file-name-directory current-exe))) (setq exec-path (seq-filter (lambda (item) (not (rlbr/case-insensitive-match item bad))) exec-path))))) (setq failed t))) output)) #+END_SRC ** Save buffer-output to file This handy function is a customized ripoff of custom-save-all #+BEGIN_SRC emacs-lisp (defun rlbr/save-buffer-func-to-file (visit-file func args) "Rip off of custom-save-all" (let* ((filename visit-file) (recentf-exclude (if recentf-mode (append `(,(concat "\\`" (regexp-quote (recentf-expand-file-name visit-file)) "\\'") ,(concat "\\`" (regexp-quote (file-truename (recentf-expand-file-name visit-file))) "\\'")) recentf-exclude))) (old-buffer (find-buffer-visiting filename)) old-buffer-name) (with-current-buffer (let ((find-file-visit-truename t)) (or old-buffer (let ((delay-mode-hooks t)) (find-file-noselect filename)))) (when old-buffer (setq old-buffer-name (buffer-file-name)) (set-visited-file-name (file-chase-links filename))) (unless (eq major-mode 'emacs-lisp-mode) (delay-mode-hooks (emacs-lisp-mode))) (let ((inhibit-read-only t) (print-length nil) (print-level nil)) (apply func args)) (let ((file-precious-flag t)) (save-buffer)) (if old-buffer (progn (set-visited-file-name old-buffer-name) (set-buffer-modified-p nil)) (kill-buffer (current-buffer)))))) #+END_SRC ** Low memeory check #+BEGIN_SRC emacs-lisp (defun rlbr/high-mem () (>= (nth 1 (memory-info)) (expt 1024 2))) #+END_SRC ** Kill this buffer #+BEGIN_SRC emacs-lisp (defun rlbr/kill-this-buffer () (interactive) (kill-buffer (current-buffer))) #+END_SRC * Save/load ** Backup/auto-save #+BEGIN_SRC emacs-lisp (let ((backup-dir "~/.emacs.d/backup") (auto-save-dir "~/.emacs.d/autosave")) (if (not (file-directory-p backup-dir)) (make-directory backup-dir)) (if (not (file-directory-p auto-save-dir)) (make-directory auto-save-dir))) #+END_SRC ** On save #+BEGIN_SRC emacs-lisp (add-hook 'before-save-hook 'delete-trailing-whitespace) #+END_SRC ** Recent files mode #+BEGIN_SRC emacs-lisp (use-package recentf :config (recentf-mode 1)) #+END_SRC * Platform dependent ** Windows #+BEGIN_SRC emacs-lisp (when (string-equal system-type "windows-nt") (progn (defun rlbr/quote-exe (path) (w32-short-file-name path)) (defun rlbr/high-mem () t) (defun rlbr/start-external-shell () (interactive) (start-process-shell-command (format "cmd(%s)" default-directory) nil "start default.bat")) (global-set-key (kbd "C-S-C") 'rlbr/start-external-shell) (defun rlbr/start-windows-explorer-here () (interactive) (start-process-shell-command "explorer" nil (format "explorer %s" (replace-regexp-in-string "/" (regexp-quote "\\") (expand-file-name default-directory))))) (global-set-key (kbd "C-S-E") 'rlbr/start-windows-explorer-here) (defun rlbr/case-insensitive-match (string1 string2) (apply 'string-equal (mapcar 'downcase (list string1 string2)))) (let ((find) (grep) (ls)) (progn (setq find (rlbr/output-matches (lambda (output) (string-equal ".\n" output)) "find" "-maxdepth 0")) (if find (setq find-program (rlbr/quote-exe find))) (setq grep (rlbr/output-matches (lambda (output) (string-match "grep (\\w+ grep)" output)) "grep" "-V")) (if grep (setq grep-program (rlbr/quote-exe grep))) (setq ls (rlbr/output-matches (lambda (output) (string-match "ls: .*'\\?/': No such file or directory" output)) "ls" "?/")) (if ls (setq insert-directory-program (rlbr/quote-exe ls))))))) #+END_SRC * Tramp configuration ** Tramp append plist to connection properties #+BEGIN_SRC emacs-lisp (use-package kv :config (defun rlbr/add-config-to-tramp (matches-regexp config-plist) (let ((config-alist (kvplist->alist config-plist))) (dolist (pair config-alist) (let ((config (list matches-regexp (car pair) (cdr pair)))) (add-to-list 'tramp-connection-properties config)))))) #+END_SRC ** Android #+BEGIN_SRC emacs-lisp (use-package tramp :config (let ((android-config (let ((default-directory "/data/data/com.termux/files")) (list "tmpdir" (expand-file-name "home/temp/") "remote-shell" (expand-file-name "usr/bin/sh") "remote-process-environment" (append (list (concat "PREFIX=" default-directory "usr")) tramp-remote-process-environment) "remote-path" (append (mapcar 'expand-file-name '("home/.local/bin" "usr/bin" "usr/bin/applets")) '("/sbin" "/vendor/bin" "/system/sbin" "/system/bin" "/system/xbin")))))) (rlbr/add-config-to-tramp "/ssh:termux.*:" android-config))) #+END_SRC * Major modes ** C #+BEGIN_SRC emacs-lisp (use-package format-all :if (executable-find "clang-format") :hook (c-mode . format-all-mode)) #+END_SRC ** Java *** Meghanada #+BEGIN_SRC emacs-lisp (use-package autodisass-java-bytecode :defer t) (use-package meghanada :if (rlbr/high-mem) :defer t :init (add-hook 'java-mode-hook (lambda () (meghanada-mode t) (flycheck-mode +1) (add-hook 'before-save-hook 'meghanada-code-beautify-before-save))) :config (setq indent-tabs-mode nil) (setq meghanada-server-remote-debug t) (setq meghanada-javac-xlint "-Xlint:all,-processing") (defhydra hydra-meghanada (:hint nil :exit t) " ^Edit^ ^Tast or Task^ ^^^^^^------------------------------------------------------- _f_: meghanada-compile-file _m_: meghanada-restart _c_: meghanada-compile-project _t_: meghanada-run-task _o_: meghanada-optimize-import _j_: meghanada-run-junit-test-case _s_: meghanada-switch-test-case _J_: meghanada-run-junit-class _v_: meghanada-local-variable _R_: meghanada-run-junit-recent _i_: meghanada-import-all _r_: meghanada-reference _g_: magit-status _T_: meghanada-typeinfo _q_: exit " ("f" meghanada-compile-file) ("m" meghanada-restart) ("c" meghanada-compile-project) ("o" meghanada-optimize-import) ("s" meghanada-switch-test-case) ("v" meghanada-local-variable) ("i" meghanada-import-all) ("g" magit-status) ("t" meghanada-run-task) ("T" meghanada-typeinfo) ("j" meghanada-run-junit-test-case) ("J" meghanada-run-junit-class) ("R" meghanada-run-junit-recent) ("r" meghanada-reference) ("q" exit) ("z" nil "leave")) :bind (:map meghanada-mode-map ("C-S-t" . meghanada-switch-testcase) ("M-RET" . meghanada-local-variable) ("M-r" . meghanada-reference) ("M-t" . meghanada-typeinfo) ("C-z" . hydra-meghanada/body)) :commands (meghanada-mode)) #+END_SRC ** JavaScript #+BEGIN_SRC emacs-lisp (use-package js2-mode :mode "\\.js\\'" :hook ((js2-mode . js2-imenu-extras-mode) (js2-mode . (lambda () (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t)))) :config (use-package js2-refactor :hook (js2-mode . js2-refactor-mode) :bind (:map js2-mode-map ("C-k" . js2r-kill)) :config (js2r-add-keybindings-with-prefix "C-c C-r")) (use-package xref-js2 :demand t) (define-key js-mode-map (kbd "M-.") nil) (defun rlbr/jump-to-definition () "Jump to a definition." (interactive) (condition-case-unless-debug nil (js2-jump-to-definition) (error (progn (ignore-errors (xref-pop-marker-stack)) (xref-find-definitions (xref-backend-identifier-at-point (xref-find-backend))))))) (define-key js-mode-map (kbd "M-.") #'rlbr/jump-to-definition)) #+END_SRC ** Magit #+BEGIN_SRC emacs-lisp (use-package magit :bind (("C-x g" . magit-status)) :config (use-package git-commit :hook (git-commit-setup . git-commit-turn-on-flyspell))) #+END_SRC ** Python *** Platform specific Set python command #+BEGIN_SRC emacs-lisp (setq elpy-rpc-python-command (cond ((string-equal system-type "gnu/linux") "python3") ((string-equal system-type "windows-nt") "pythonw.exe"))) #+END_SRC put executables in elpy-rpc-venv in path #+BEGIN_SRC emacs-lisp (defun rlbr/elpy-append-to-path () (setenv "PATH" (string-join (list (getenv "PATH") (let ((default-directory (elpy-rpc-get-or-create-virtualenv)) (path-entry) (elpy-binpath)) (if (string-equal system-type "windows-nt") (progn (setq elpy-binpath (expand-file-name "Scripts")) (setq path-entry (replace-regexp-in-string (regexp-quote "/") (regexp-quote "\\") elpy-binpath))) (setq elpy-binpath (expand-file-name "bin")) (setq path-entry elpy-binpath)) (nconc exec-path (list elpy-binpath)) elpy-binpath)) path-separator))) #+END_SRC #+BEGIN_SRC emacs-lisp (defun rlbr/fix-for-android () (unless (= 0 (call-process elpy-rpc-python-command nil nil nil "-c" "import multiprocessing;multiprocessing.Pool()")) (setq python-check-command (string-join `(,python-check-command "--jobs=1") " ")))) #+END_SRC *** Custom feature #+BEGIN_SRC emacs-lisp (defun rlbr/join-venv-with-number (number-name) "Join a list with a name and a number" (let ((number (car number-name)) (name (cdr number-name))) (if (= number 0) name (string-join (list name (number-to-string number)) "~")))) (defun rlbr/split-venv-with-number (name-number) "Split a virtualenv name with either a ~ seperating the name and the number, or nothing" (let ((split-result (split-string name-number (regexp-quote "~"))) (ret)) (if (= 1 (length split-result)) (progn (setq ret (car split-result)) (push 0 ret)) (progn (setq ret (string-join (butlast split-result) "~")) (push (string-to-number (car (last split-result))) ret))) ret)) (defun rlbr/get-venv-name (&optional library-root) "Generate venv name based off of the base-name of the library root" (file-name-base (directory-file-name (if library-root library-root (elpy-library-root))))) (defun rlbr/handle-name-conflicts (venv-name) "Deal with potential name conflicts in venv" (let ((venv-conflicts) (venv-partition-name)) (setq venv-partition-name (rlbr/split-venv-with-number venv-name)) (setq venv-conflicts (seq-filter (lambda (item) (string-equal (cdr item) venv-name)) (mapcar #'rlbr/split-venv-with-number (pyvenv-virtualenv-list)))) (when venv-conflicts (setcar venv-partition-name (1+ (apply 'max (mapcar #'car venv-conflicts))))) (rlbr/join-venv-with-number venv-partition-name))) (require 'vc) (defun rlbr/setup-python-venv-dirlocals (&optional library-root venv-name) "Setup .dir-locals file in library root and tell vc system to ignore .dir-locals file" (let* ((library-root (if library-root library-root (elpy-library-root))) (venv-name (if venv-name venv-name (rlbr/get-venv-name library-root))) (default-directory library-root) (dir-locals-path (expand-file-name ".dir-locals.el"))) (rlbr/save-buffer-func-to-file dir-locals-path 'add-dir-local-variable `(python-mode pyvenv-workon ,venv-name)) (let* ((vc-root (vc-find-root dir-locals-path ".git")) (vc-ignore-file (vc-call-backend 'Git 'find-ignore-file vc-root))) (if (apply 'string-equal (mapcar 'directory-file-name (mapcar 'file-truename (list vc-root library-root)))) (progn (unless (file-exists-p vc-ignore-file) (with-temp-buffer (write-file vc-ignore-file))) (vc-ignore ".dir-locals.el")) (when (y-or-n-p (format "Ignore .dir-locals.el in repo '%s' ?" vc-root)) (unless (file-exists-p vc-ignore-file) (with-temp-buffer (write-file vc-ignore-file))) (vc-ignore ".dir-locals.el")))))) (defun rlbr/get-python-executable () (read-file-name "Python interpreter to use: " (file-name-directory (executable-find "python")) nil nil "python")) (defun emacs-default-venv () (unless (member "emacs-default-venv" (pyvenv-virtualenv-list)) (pyvenv-create "emacs-default-venv" (rlbr/get-python-executable))) "emacs-default-venv") (defun rlbr/init-python-venv-in-library-root (&optional library-root) "Prompt to either create one or use default" (let ((venv-name (rlbr/get-venv-name)) (library-root (if library-root library-root (elpy-library-root)))) (setq venv-name (rlbr/handle-name-conflicts venv-name)) (if (y-or-n-p (format "Create venv '%s'?" venv-name)) (pyvenv-create venv-name (rlbr/get-python-executable)) (progn (setq venv-name (emacs-default-venv)))) (rlbr/setup-python-venv-dirlocals library-root venv-name) venv-name)) (require 'dash) (defun rlbr/init-venv () (when (eq major-mode 'python-mode) (cond ((file-remote-p buffer-file-name) ;; If the file is remote, don't try and do anything fancy (setq-local pyvenv-workon (emacs-default-venv))) ((let ((buffer-file-name (file-truename buffer-file-name))) (-any (lambda (file-prefix) (string-prefix-p file-prefix buffer-file-name)) (mapcar 'file-truename (list "/usr/lib/python" "~/.pyenv/versions/" (elpy-rpc-get-or-create-virtualenv) (pyvenv-workon-home))))) ;; Don't change anything if entering a file in a python install's lib (ie xref) nil) (t ;; Upon failing all conditions, prompt to create virtual environment if it doesn't exist (cond ((and pyvenv-workon (not (member pyvenv-workon (pyvenv-virtualenv-list)))) ;; If there is a virtualenv specified and it doesn't exist, prompt to create it or set to default virtual environment (if (y-or-n-p (format "Venv '%s' is specified but does not exist. Create it?" pyvenv-workon)) (progn (pyvenv-create pyvenv-workon (rlbr/get-python-executable)) (pyvenv-workon pyvenv-workon)) (rlbr/save-buffer-func-to-file (let ((default-directory (elpy-library-root))) (expand-file-name ".dir-locals.el")) 'add-dir-local-variable '(python-mode pyvenv-workon (emacs-default-venv))) (setq-local pyvenv-workon (emacs-default-venv)))) ((not pyvenv-workon) ;; If nothing has already set pyvenv-workon, create venv (setq-local pyvenv-workon (rlbr/init-python-venv-in-library-root)))))) (pyvenv-workon pyvenv-workon))) #+END_SRC *** Bindings/settings #+BEGIN_SRC emacs-lisp (use-package python :hook ((python-mode . pyvenv-mode) (python-mode . flycheck-mode) (python-mode . (lambda () (add-hook 'before-save-hook 'elpy-black-fix-code nil 'local)))) :bind (:map python-mode-map (("C-<" . flycheck-previous-error) ("C->" . flycheck-next-error))) :config (use-package elpy :hook (hack-local-variables . rlbr/init-venv) :bind (:map python-mode-map (("C-=" . elpy-goto-assignment))) :config (when (require 'flycheck nil t) (setq elpy-modules (delq 'elpy-module-flymake elpy-modules))) (rlbr/elpy-append-to-path) (rlbr/fix-for-android)) (use-package realgud :bind (:map python-mode-map (("C-c d b" . realgud:pdb)))) (elpy-enable)) #+END_SRC ** SSH config mode #+BEGIN_SRC emacs-lisp (use-package ssh-config-mode :mode "~/.ssh/config\\'") #+END_SRC ** Tramp ** Webmode #+BEGIN_SRC emacs-lisp (use-package web-mode :mode (("\\.phtml\\'" . web-mode) ("\\.tpl\\.php\\'" . web-mode) ("\\.[agj]sp\\'" . web-mode) ("\\.as[cp]x\\'" . web-mode) ("\\.erb\\'" . web-mode) ("\\.mustache\\'" . web-mode) ("\\.djhtml\\'" . web-mode) ("\\.html?\\'" . web-mode))) #+END_SRC ** YAML #+BEGIN_SRC emacs-lisp (use-package yaml-mode :mode "\\.yml\\'") #+END_SRC * Minor modes/misc ** Kill the things *** Buffer #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-x k") 'rlbr/kill-this-buffer) #+END_SRC *** Emacs #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-x C-k C-x C-k") 'save-buffers-kill-emacs) #+END_SRC ** Lispy #+BEGIN_SRC emacs-lisp (use-package lispy :hook ((emacs-lisp-mode) . lispy-mode)) #+END_SRC ** Custom custom #+BEGIN_SRC emacs-lisp (advice-add 'custom-save-faces :after (lambda () (rlbr/multiline-sexp-with-symbol "custom-set-faces"))) (advice-add 'custom-save-variables :after (lambda () (rlbr/multiline-sexp-with-symbol "custom-set-variables"))) #+END_SRC ** Elmacro #+BEGIN_SRC emacs-lisp (use-package elmacro :demand :config (elmacro-mode +1)) #+END_SRC * Navigation/auto-completion ** Ace window #+BEGIN_SRC emacs-lisp (use-package ace-window :bind (("M-Q" . ace-window))) #+END_SRC ** Hippie expand #+BEGIN_SRC emacs-lisp (use-package hippie-exp :bind ("M-/" . hippie-expand)) #+END_SRC ** IBuffer mode #+BEGIN_SRC emacs-lisp (use-package ibbufer-vc :hook ((ibuffer-mode . ibuffer-vc-set-filter-groups-by-vc-root))) ;; Use human readable Size column instead of original one (use-package ibuffer :bind (("C-x C-b" . ibuffer)) :config (define-ibuffer-column size-h (:name "Size" :inline t) (cond ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0))) ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0))) ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0))) (t (format "%8d" (buffer-size)))))) #+END_SRC ** Ivy #+BEGIN_SRC emacs-lisp (use-package ivy :config (use-package swiper :bind ("C-s" . swiper)) (ivy-mode)) #+END_SRC * Look and feel ** Theme #+BEGIN_SRC emacs-lisp (use-package dracula-theme :config (load-theme 'dracula t)) #+END_SRC ** Line numbers #+BEGIN_SRC emacs-lisp (global-display-line-numbers-mode) #+END_SRC ** Mode line bell #+BEGIN_SRC emacs-lisp (use-package mode-line-bell :config (mode-line-bell-mode)) #+END_SRC ** Spaceline #+BEGIN_SRC emacs-lisp (use-package spaceline-config :config (use-package winum :bind (:map winum-keymap (("M-0" . winum-select-window-0-or-10) ("M-1" . winum-select-window-1) ("M-2" . winum-select-window-2) ("M-3" . winum-select-window-3) ("M-4" . winum-select-window-4) ("M-5" . winum-select-window-5) ("M-6" . winum-select-window-6) ("M-7" . winum-select-window-7) ("M-8" . winum-select-window-8)))) (spaceline-spacemacs-theme) (winum-mode)) #+END_SRC