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.
 
 
 

15 KiB

Editing

IEdit mode

  (use-package iedit
    :bind ("C-;" . iedit-mode))

Spellcheck

  (global-set-key (kbd "C-!") 'ispell-buffer)

Undo tree

  (use-package undo-tree
    :config
    (global-undo-tree-mode))

Added functionality

Multiline sexp with symbol

Jump to symbol, go up list, lispy-multiline. Great for diff-friendly custom

  (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)))

Output matches

Run command for each matching exe and see if output-p is true when fed the command output

  (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))

Save buffer-output to file

This handy function is a customized ripoff of custom-save-all

  (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))))))

Save/load

Backup/auto-save

  (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)))

On save

  (add-hook 'before-save-hook 'delete-trailing-whitespace)

Recent files mode

  (use-package recentf
    :config
    (recentf-mode 1))

Platform dependent

Windows

  (when (string-equal system-type "windows-nt")
    (progn
      (defun rlbr/quote-exe (path)
	(w32-short-file-name path))
      (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)))))))

Tramp configuration

Tramp append plist to connection properties

  (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))))))

Android

  (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)))

Major modes

Java

JavaScript

  (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))

Magit

  (use-package magit
    :bind (("C-x g" . magit-status))
    :config
    (use-package git-commit
      :hook (git-commit-setup . git-commit-turn-on-flyspell)))

Python

Platform specific

  (setq elpy-rpc-python-command
	(cond
	 ((string-equal system-type "gnu/linux")
	  "python3")
	 ((string-equal system-type "windows-nt")
	  "pythonw.exe")))

custom feature

  (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/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/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 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
	  (unless (member "emacs-default-venv" (pyvenv-virtualenv-list))
	    (pyvenv-create venv-name (rlbr/get-python-executable)))
	  (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)
      (unless
	  (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
			   (elpy-rpc-get-or-create-virtualenv)
			   (pyvenv-workon-home)))))
	(cond
	 ((and pyvenv-workon (not (member pyvenv-workon (pyvenv-virtualenv-list))))
	  (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)
	  (setq-local
	   pyvenv-workon
	   (rlbr/init-python-venv-in-library-root))))
	(pyvenv-workon pyvenv-workon))))

bindings/settings

  (use-package python
    :hook ((python-mode . blacken-mode)
	   (python-mode . pyvenv-mode)
	   (hack-local-variables . rlbr/init-venv))
    :config
    (use-package elpy
      :bind (("C-=" . elpy-goto-assignment))
      :config (when (require 'flycheck nil t)
		(setq elpy-modules (delq 'elpy-module-flymake elpy-modules))))
    (elpy-enable))

SSH config mode

  (use-package ssh-config-mode
    :mode "~/.ssh/config\\'")

Tramp

Webmode

  (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)))

YAML

  (use-package yaml-mode
    :mode "\\.yml\\'")

Minor modes/misc

Kill the things

Buffer

(global-set-key (kbd "C-x k") 'kill-this-buffer)

Emacs

(global-set-key (kbd "C-x C-k C-x C-k") 'kill-emacs)

Lispy

  (use-package lispy
    :hook ((emacs-lisp-mode) . lispy-mode))

Custom custom

  (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")))

Navigation/auto-completion

Ace window

  (use-package ace-window
    :bind (("M-Q" . ace-window)))

Hippie expand

  (use-package hippie-exp
    :bind ("M-/" . hippie-expand))

IBuffer mode

  (use-package ibbufer-vc
    :hook ((ibuffer-mode . ibuffer-vc-set-filter-groups-by-vc-root)))
  (use-package ibuffer
    :bind (("C-x C-b" . ibuffer))
    :config
    (define-ibuffer-column size-h
      ;; Use human readable Size column instead of original one
      (: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))))))

Ivy

  (use-package ivy
    :config
    (use-package swiper
      :bind ("C-s" . swiper))
    (ivy-mode))

Look and feel

Line numbers

  (global-display-line-numbers-mode)

Mode line bell

  (use-package mode-line-bell
    :config
    (mode-line-bell-mode))

Spaceline

  (use-package spaceline-config
    :config
    (use-package winum
      :init
      (setq winum-keymap
	    (let ((map (make-sparse-keymap)))
	      (define-key map (kbd "M-0") 'winum-select-window-0-or-10)
	      (define-key map (kbd "M-1") 'winum-select-window-1)
	      (define-key map (kbd "M-2") 'winum-select-window-2)
	      (define-key map (kbd "M-3") 'winum-select-window-3)
	      (define-key map (kbd "M-4") 'winum-select-window-4)
	      (define-key map (kbd "M-5") 'winum-select-window-5)
	      (define-key map (kbd "M-6") 'winum-select-window-6)
	      (define-key map (kbd "M-7") 'winum-select-window-7)
	      (define-key map (kbd "M-8") 'winum-select-window-8)
	      map)))
    (spaceline-spacemacs-theme)
    (winum-mode))