#+TITLE: Emacs Configuration This is my configuration for the emacs editor. * Set up UI Remove all those UI elements. They do not look good and waste space. #+BEGIN_SRC emacs-lisp (tool-bar-mode -1) (menu-bar-mode -1) (scroll-bar-mode -1) (tooltip-mode -1) (fringe-mode -1) #+END_SRC * Set up package repositories ** Require package support State that we will need package support and define a macro for adding package repos to the archives #+BEGIN_SRC emacs-lisp (require 'package) (defmacro append-to-list (target suffix) "Append SUFFIX to TARGET in place." `(setq ,target (append ,target ,suffix))) #+END_SRC ** Add package repos #+BEGIN_SRC emacs-lisp (append-to-list package-archives '(("melpa" . "http://melpa.org/packages/") ("org-elpa" . "https://orgmode.org/elpa/"))) #+END_SRC ** Ensure ~use-package~ command is present #+BEGIN_SRC emacs-lisp (package-initialize) (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package)) (require 'use-package) (setq use-package-always-ensure t use-package-verbose t) (use-package gnu-elpa-keyring-update) #+END_SRC * Set Backup Location Emacs, by default clutters the file system with backup files. We do not want them to be right next to the actual file, so we tell emacs to use dedicated directory to store them. While we are at it, we also set rule for how many version emacs should keep as backups. #+BEGIN_SRC emacs-lisp (setq backup-directory-alist `(("." . "~/.emacs-backup"))) (setq backup-by-copying t) (setq delete-old-versions t kept-new-versions 6 kept-old-versions 2 version-control t) #+END_SRC * Define useful functions ** Edit the config file A simple funtion to open this file for quick editing. #+BEGIN_SRC emacs-lisp (defun edit-config () (interactive) (find-file "~/.emacs.d/README.org")) #+END_SRC ** Reformat a whole buffer Reindet the whole buffer with ~F12~ #+BEGIN_SRC emacs-lisp (defun indent-buffer () (interactive) (save-excursion (indent-region (point-min) (point-max) nil))) (global-set-key [f12] 'indent-buffer) #+END_SRC ** Split windows and immediately switch to it #+BEGIN_SRC emacs-lisp (defun split-right-and-enter () "Split the window to the right and enter it." (interactive) (split-window-right) (other-window 1)) (defun split-below-and-enter () "Split the window down and enter it." (interactive) (split-window-below) (other-window 1)) #+END_SRC ** Quick buffer switching #+BEGIN_SRC emacs-lisp (defun switch-to-previous-buffer () "Switch to previously open buffer. Repeated invocations toggle between the two most recently open buffers." (interactive) (switch-to-buffer (other-buffer (current-buffer) 1))) #+END_SRC ** Fold all except the current heading With this all other subtrees in the buffer van be collapsed, leaving only the subtree at the point expanded #+BEGIN_SRC emacs-lisp (defun org-show-current-heading-tidily () (interactive) ;Inteactive "Show next entry, keeping other entries closed." (if (save-excursion (end-of-line) (outline-invisible-p)) (progn (org-show-entry) (show-children)) (outline-back-to-heading) (unless (and (bolp) (org-on-heading-p)) (org-up-heading-safe) (hide-subtree) (error "Boundary reached")) (org-overview) (org-reveal t) (org-show-entry) (show-children))) #+END_SRC And it should be accessible with a quick keystroke: #+BEGIN_SRC emacs-lisp (global-set-key "\M-s" 'org-show-current-heading-tidily) #+END_SRC ** Load file content as string #+BEGIN_SRC emacs-lisp (defun get-string-from-file (filePath) "Return filePath's file content." (with-temp-buffer (insert-file-contents filePath) (buffer-string))) #+END_SRC ** Load all files in a directory #+BEGIN_SRC emacs-lisp (defun load-directory (dir) (let ((load-it (lambda (f) (load-file (concat (file-name-as-directory dir) f))) )) (mapc load-it (directory-files dir nil "\\.el$")))) #+END_SRC * Theming Load font from file. See [[https://github.com/rougier/elegant-emacs]] for more. #+BEGIN_SRC emacs-lisp (load "~/.emacs.d/elegant-emacs/elegant-emacs-common.el") (load "~/.emacs.d/elegant-emacs/elegant-emacs-light-theme.el") #+END_SRC * Ivy Use Ivy to make minibuf promts better. Adds the ability to sort and filter. ** Use Ivy #+BEGIN_SRC emacs-lisp (use-package ivy :init (ivy-mode 1) (unbind-key "S-SPC" ivy-minibuffer-map) (setq ivy-height 30 ivy-use-virtual-buffers t ivy-use-selectable-prompt t) (defun swiper-at-point () (interactive) (swiper (thing-at-point 'word))) :bind (("C-x b" . ivy-switch-buffer) ("C-c C-r" . ivy-resume) ("C-c s" . swiper-at-point) ("C-s" . swiper)) :diminish) ;; ivy-rich makes Ivy look a little bit more like Helm. (use-package ivy-rich :after counsel :custom (ivy-virtual-abbreviate 'full ivy-rich-switch-buffer-align-virtual-buffer t ivy-rich-path-style 'abbrev) :init (ivy-rich-mode)) (use-package ivy-hydra) #+END_SRC ** Smex Sort commands by recency in ivy windows #+BEGIN_SRC emacs-lisp (use-package smex) #+END_SRC * Counsel #+BEGIN_SRC emacs-lisp (use-package counsel :ensure t :after ivy :init (counsel-mode 1) :bind (("C-c ;" . counsel-M-x) ("C-c U" . counsel-unicode-char) ("C-c i" . counsel-imenu) ("C-x f" . counsel-find-file) ("C-c y" . counsel-yank-pop) ("C-c r" . counsel-recentf) :map ivy-minibuffer-map ("C-r" . counsel-minibuffer-history)) :diminish) #+END_SRC * Undo Tree Using the beauty that is undo-tree, we can easily navigate through history of a buffer. This includes obviously going back in edit history, but also branching of end returning to previous states. #+BEGIN_SRC emacs-lisp (use-package undo-tree :bind (("C-x u" . undo-tree-visualize) ("C-z" . undo-tree-undo) ("C-S-z" . undo-tree-redo)) :config (global-undo-tree-mode +1) (unbind-key "M-_" undo-tree-map) :diminish) ;; Trying undo-propose, which seems to offer a better experience, as ;; undo tree is prone to losing data. (use-package undo-propose :disabled :bind (("C-c _" . undo-propose) :map undo-propose-mode-map ("" . undo-only))) #+END_SRC With this we can use ~C-x u~ in any buffer to bring up the tree and navigate it using the arrow key. Once in a state we agree with, just press ~q~ and we are done. * Magit Magit is THE go to package for using git in emacs. #+BEGIN_SRC emacs-lisp (use-package magit :bind (("C-c g" . magit-status)) :diminish magit-auto-revert-mode :diminish auto-revert-mode :custom (magit-remote-set-if-missing t) (magit-diff-refine-hunk t) :config (magit-auto-revert-mode t) (advice-add 'magit-refresh :before #'maybe-unset-buffer-modified) (advice-add 'magit-commit :before #'maybe-unset-buffer-modified) (setq magit-completing-read-function 'ivy-completing-read) (add-to-list 'magit-no-confirm 'stage-all-changes)) (use-package libgit :disabled :after magit) #+END_SRC The ~advice-add~ entries are thereto stop magit from bugging us to save buffers when commiting and refreshing. ** Helper Functions #+BEGIN_SRC emacs-lisp (autoload 'diff-no-select "diff") (defun current-buffer-matches-file-p () "Return t if the current buffer is identical to its associated file." (when (and buffer-file-name (buffer-modified-p)) (diff-no-select buffer-file-name (current-buffer) nil 'noasync) (with-current-buffer "*Diff*" (and (search-forward-regexp "^Diff finished \(no differences\)\." (point-max) 'noerror) t)))) #+END_SRC Clear modified bit on all unmodified buffers #+BEGIN_SRC emacs-lisp (defun maybe-unset-buffer-modified (&optional _) (interactive) (dolist (buf (buffer-list)) (with-current-buffer buf (when (and buffer-file-name (buffer-modified-p) (current-buffer-matches-file-p)) (set-buffer-modified-p nil))))) #+END_SRC Don't prompt to save unmodified buffers on exit. #+BEGIN_SRC emacs-lisp (advice-add 'save-buffers-kill-emacs :before #'maybe-unset-buffer-modified) #+END_SRC #+BEGIN_SRC emacs-lisp (defun kill-buffer-with-prejudice (&optional _) "Kill a buffer, eliding the save dialogue if there are no diffs." (interactive) (when (current-buffer-matches-file-p) (set-buffer-modified-p nil)) (kill-buffer)) #+END_SRC * Org Mode ** Define important files *** The Link Dump I use a single file to dump all links I plan on viewing later. #+BEGIN_SRC emacs-lisp (defvar link-dump "") (defun open-link-dump () (interactive) (find-file link-dump)) #+END_SRC ** Configure org-mode This is the main configuration for the infamous org-mode. The most important parts are configuring key bindings to quickly access the files we have defined above. #+BEGIN_SRC emacs-lisp (use-package org ;; Always get this from the GNU archive. :pin gnu :bind (("C-c o c" . org-capture) ("C-c o l" . open-link-dump) ("C-c o s" . org-store-link) ("C-c o a" . org-agenda) :map org-mode-map ("M-s-" . org-insert-todo-heading) ("M-" . org-insert-heading-respect-content) ("C-c c" . org-mode-insert-code) ("C-c a s" . org-emphasize) ("C-c a r" . org-ref) ("C-c a e" . outline-show-all) ("C-c a t" . unindent-by-four)) :hook ((org-mode . visual-line-mode) (org-mode . variable-pitch-mode) (org-mode . org-indent-mode)) :config (let ((todo-path (expand-file-name "~/Notes/todo.org"))) (when (file-exists-p todo-path) (setq org-agenda-files (list todo-path) org-default-notes-file todo-path))) (setq org-footnote-section "" org-startup-with-inline-images t org-pretty-entities t org-indent-mode t org-ellipsis "⤵" org-footnote-section nil org-hide-leading-stars nil org-link-file-path-type 'relative org-image-actual-width nil ; with this image sizes can be set per image, with an attribute org-display-inline-images t ) (setcar (nthcdr 4 org-emphasis-regexp-components) 4) (defun org-mode-insert-code () (interactive) (org-emphasize ?~))) #+END_SRC ** Set default archive location When archiving items in org files, the default ist to crate a separate file named ~.org_archive~. This clutters up my notes folder quite a bit, as I use a lot of separate files with thier respective archives. All archives should be stored in a single ~.archive~ file per directory. #+BEGIN_SRC emacs-lisp (setq org-archive-location "./.archive::* From %s") #+END_SRC ** Beautify org-mode *** Icons for headline indentation #+BEGIN_SRC emacs-lisp (use-package org-bullets :init (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))) (setq org-bullets-bullet-list '("◉" "○" "◆" "✿" "✚" "▶")) #+END_SRC *** Replace checkmark with unicode icons #+BEGIN_SRC emacs-lisp (use-package pretty-mode :init (global-pretty-mode t)) (add-hook 'org-mode-hook (lambda () "Beautify Org Checkbox Symbol" (push '("[ ]" . "☐") prettify-symbols-alist) (push '("[X]" . "☑" ) prettify-symbols-alist) (push '("[-]" . "❍" ) prettify-symbols-alist) (prettify-symbols-mode))) #+END_SRC We also want them in exported HTML files #+BEGIN_SRC emacs-lisp (setq org-html-checkbox-type 'html) #+END_SRC *** Strike out done ckeckbox items #+BEGIN_SRC emacs-lisp (defface org-checkbox-done-text '((t (:foreground "#71696A" :strike-through t))) "Face for the text part of a checked org-mode checkbox.") (font-lock-add-keywords 'org-mode `(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\[\\(?:X\\|\\([0-9]+\\)/\\2\\)\\][^\n]*\n\\)" 1 'org-checkbox-done-text prepend)) 'append) #+END_SRC *** Replace dash in bullet lists with unicode symbol #+BEGIN_SRC emacs-lisp (font-lock-add-keywords 'org-mode '(("^ *\\([-]\\) " (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))) #+END_SRC ** CSS Themes for Exports When exporting from org-mode (usually to HTML) we want to specify additional styles. #+BEGIN_SRC emacs-lisp (defvar org-theme-css-dir "~/.emacs.d/org-css/") #+END_SRC Pack some ~.css~ files into this directory. They will be available for choosing when exporting. The folowing code will define a function to inline css into a self-contained html file. To use it type ~M-x toggle-org-custom-inline-style~ into an org-mode buffer. When exporting to HTML emacs will ask which css theme to use. #+BEGIN_SRC emacs-lisp (defun org-html-inline-style () (interactive) (let ((hook 'org-export-before-parsing-hook) (fun 'set-org-html-style)) (if (memq fun (eval hook)) (progn (remove-hook hook fun 'buffer-local) (message "Removed %s from %s" (symbol-name fun) (symbol-name hook))) (add-hook hook fun nil 'buffer-local) (message "Added %s to %s" (symbol-name fun) (symbol-name hook))))) (defun org-theme () (interactive) (let* ((cssdir org-theme-css-dir) (css-choices (directory-files cssdir nil ".css$")) (css (completing-read "theme: " css-choices nil t))) (concat cssdir css))) (defun set-org-html-style (&optional backend) (interactive) (when (or (null backend) (eq backend 'html)) (let ((f (or (and (boundp 'org-theme-css) org-theme-css) (org-theme)))) (if (file-exists-p f) (progn (set (make-local-variable 'org-theme-css) f) (set (make-local-variable 'org-html-head) (with-temp-buffer (insert "\n") (buffer-string))) (set (make-local-variable 'org-html-head-include-default-style) nil) (message "Set custom style from %s" f)) (message "Custom header file %s doesnt exist"))))) #+END_SRC ** Prettier Timestamps in Exports The default timestamps look pretty unintuitive, with all the angle brackets and all. Let's make them look better. #+BEGIN_SRC emacs-lisp ;;(add-to-list 'org-export-filter-timestamp-functions ;; #'endless/filter-timestamp) ;;(defun endless/filter-timestamp (trans back _comm) ;; (pcase back ;; ((or `jekyll `html) ;; (replace-regexp-in-string "&[lg]t;" "" trans)) ;; (`latex ;; (replace-regexp-in-string "[<>]" "" trans)))) #+END_SRC Removed for now, this somehow breaks emacs OK, no more brackets. Now for a better formatted display. #+BEGIN_SRC emacs-lisp (setq-default org-display-custom-times t) (setq org-time-stamp-custom-formats '("<%a %d.%m.%Y>" . "<%d.%m.%y %H:%M>")) #+END_SRC ** Templates *** Babel Here we set custom templates to be used for structure expansion. These are used when we type "<" folowed by the shortcut for a template and hit "TAB". e.g. "" 'end-of-buffer) (bind-key "C-x C-b" 'ibuffer) ;; buffer-list is not a good default (bind-key "C-c n" 'line-number-mode) #+END_SRC ** Unbind some default key bindings #+BEGIN_SRC emacs-lisp (unbind-key "C-") ;; prevent switching to tab mode randomly (unbind-key "C-h n") ;; I have never wanted to see emacs news ever (unbind-key "C-h C-n") ;; why on earth is it bound to two keybindings?? (unbind-key "C-x C-d") ;; list-directory is utterly useless given the existence of dired (unbind-key "C-x C-r") ;; as is find-file-read-only #+END_SRC * Load Personal Information All information about the current user should reside in the ~personal.el~ file. This file contains personal information like name, email or other identifying information. This file should contain definitions, that are the same on every device, but sould not be commited to a repository. #+BEGIN_SRC emacs-lisp (setq personal-file "~/.emacs.d/personal.el") (load personal-file 'noerror) #+END_SRC * Load ~custom.el~ Load a custom file from the emacs home dir. This file is specific to the maschine emacs runs on. It conatins customizations and file locations that are maschine dependend. #+BEGIN_SRC emacs-lisp (setq custom-file "~/.emacs.d/custom.el") (load custom-file 'noerror) #+END_SRC