#+TITLE: luxicks Emacs Configuration #+PROPERTY: header-args :results silent This is my configuration for the emacs editor. Points of intrest: - Personal information (name, email) are stored in a separate ~personal.el~ file. - Machine specific settings are stored in a separate ~custom.el~ file. - Both files are loaded automatically. - When Exporting HTML from ~org-mode~ the style from the ~org-theme.css~ file is inlined automatically. * Set Up use-package Throughout this configuration I will use =use-package= to configure packages from melpa and other sources. [[https://github.com/jwiegley/use-package][Link to GitHub page]] #+BEGIN_SRC emacs-lisp ;; Add melpa to the package repos (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/")) (package-initialize) ;; Install use-package (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package)) (setq use-package-always-ensure t) #+END_SRC Packages that are manually installed should be saved under =.emacs.d/packages= #+begin_src emacs-lisp (add-to-list 'load-path "~/.emacs.d/packages/") #+end_src * Fix Defaults ** Hide UI elements 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 ** Disable file backups Emacs sure loves to clutter directories with backup files. #+BEGIN_SRC emacs-lisp (setq make-backup-files nil) (setq auto-save-default nil) (setq create-lockfiles nil) #+END_SRC ** Other Settings #+begin_src emacs-lisp ;; The default encoding should be utf-8 everywhere (prefer-coding-system 'utf-8) ;; All "Yes or No" questions can be shortend to "y or n". (defalias 'yes-or-no-p 'y-or-n-p) ;; No more startup messages and screens (setq inhibit-startup-screen t) (setq initial-scratch-message nil) (defun display-startup-echo-area-message () (message "Welcome Back!")) ;; Highlight matching braces (show-paren-mode t) ;; cua-mode. Like any other editor (cua-mode t) ;; Configure the cursor (setq-default cursor-type 'bar indent-tabs-mode nil cursor-in-non-selected-windows nil) (blink-cursor-mode 0) ;; Default column with (set-fill-column 95) ;; Start up in the home directory (setq default-directory "~/") ;; Make C-k always kill the whole line (setq kill-whole-line t) ;; Do not ding. Ever. (setq ring-bell-function 'ignore) ;; Dialogues always go in the modeline. (setq use-dialog-box nil) ;; Better line wraping (global-visual-line-mode 1) #+end_src ** Keybindings #+BEGIN_SRC emacs-lisp (bind-key "C-x k" 'kill-buffer-with-prejudice) (bind-key "C-x C-k" 'kill-buffer-and-window) (bind-key "M-p" 'switch-to-previous-buffer) (bind-key "M-i" 'delete-indentation) (bind-key "C-+" 'text-scale-increase) (bind-key "C--" 'text-scale-decrease) ;; buffer-list is not a good default (bind-key "C-x C-b" 'ibuffer) (bind-key "C-c n" 'display-line-numbers-mode) #+END_SRC Unbind useless keys. #+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 * Useful Functions ** Edit This 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 ** Reformating Reindet the whole buffer with ~F12~ #+BEGIN_SRC emacs-lisp (defun lux/indent-buffer () "Reindents the whole buffer" (interactive) (save-excursion (indent-region (point-min) (point-max) nil))) (global-set-key [f12] 'lux/indent-buffer) #+END_SRC ** Window Splitting These are functions for splitting windows and move the cursor over immediately. #+BEGIN_SRC emacs-lisp (defun lux/split-right-and-enter () "Split the window to the right and enter it." (interactive) (split-window-right) (other-window 1)) (bind-key "M-3" 'lux/split-right-and-enter) (defun lux/split-below-and-enter () "Split the window down and enter it." (interactive) (split-window-below) (other-window 1)) (bind-key "M-2" 'lux/split-below-and-enter) #+END_SRC Rebind the default window controls to use "M-*" keys for ease-of-use #+begin_src emacs-lisp (bind-key "M-1" 'delete-other-windows) (bind-key "M-0" 'delete-window) #+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 ** The Links File The link dump is the file to throw all links for later reading in.Where the file is located should be set in either =personal.el= or =custom.el=. #+BEGIN_SRC emacs-lisp (defvar lux/link-dump "") (defun lux/open-link-dump () "Open the file with the links" (interactive) (find-file lux/link-dump)) #+END_SRC ** Variable Pitch Mode This custom version of =variable-pitch-mode= allows to mix variable and fixed fonts in the same buffer. This is very useful for org buffers with code blocks in them. This is a slight variation of [[https://protesilaos.com/dotemacs/][Protesilaos Stavrous]] version. #+begin_src emacs-lisp (use-package face-remap :diminish buffer-face-mode ; the actual mode :commands lux/variable-pitch-mode :hook (text-mode . lux/variable-pitch-mode) :config (define-minor-mode lux/variable-pitch-mode "Toggle `variable-pitch-mode', except for `prog-mode'." :init-value nil :global nil (if lux/variable-pitch-mode (unless (derived-mode-p 'prog-mode) (variable-pitch-mode 1)) (variable-pitch-mode -1)))) #+end_src ** Focused Mode This is a special minor mode that allows focus reading of longer texts. It is a combination of other useful modes to create a distraction free reading environment. This is a minor mode for keeping the cursor at the center of the screen while scrolling #+begin_src emacs-lisp (setq-default scroll-preserve-screen-position t) (setq-default scroll-conservatively 1) (setq-default scroll-margin 0) (define-minor-mode lux/center-scroll-mode "Toggle centred cursor scrolling behaviour." :init-value nil :lighter " S=" :global nil (if lux/center-scroll-mode (setq-local scroll-margin (* (frame-height) 2) scroll-conservatively 0 maximum-scroll-margin 0.5) (dolist (local '(scroll-preserve-screen-position scroll-conservatively maximum-scroll-margin scroll-margin)) (kill-local-variable `,local)))) (bind-key "C-c l" 'lux/center-scroll-mode) #+end_src #+begin_src emacs-lisp (define-minor-mode lux/focus-mode "Creates a distraction free reading environment in the current buffer" :init-value nil :global nil (if lux/focus-mode (progn (olivetti-mode 1) (lux/center-scroll-mode 1)) (progn (olivetti-mode -1) (lux/center-scroll-mode -1)))) (bind-key "C-c f" 'lux/focus-mode) #+end_src * Fonts Set up the fonts to use. I like the [[https://typeof.net/Iosevka/][Iosevka]] font family. #+begin_src emacs-lisp (set-face-attribute 'default nil :font "Iosevka Light-12") (set-face-attribute 'fixed-pitch nil :font "Iosevka Light-12") (set-face-attribute 'variable-pitch nil :font "Iosevka Aile Light-12") #+end_src * Theming *Apply a nice looking theme.* [[https://protesilaos.com/modus-themes/][Source for the themes]] #+BEGIN_SRC emacs-lisp ;; Light Theme (use-package modus-operandi-theme) (use-package modus-vivendi-theme) ;; Configuration for both themes (defmacro modus-themes-format-sexp (sexp &rest objects) `(eval (read (format ,(format "%S" sexp) ,@objects)))) (dolist (theme '("operandi" "vivendi")) (modus-themes-format-sexp (defun modus-%1$s-theme-load () (setq modus-%1$s-theme-slanted-constructs t modus-%1$s-theme-bold-constructs t modus-%1$s-theme-no-link-underline nil modus-%1$s-theme-faint-syntax t modus-%1$s-theme-prompts 'intense modus-%1$s-theme-completions 'moderate modus-%1$s-theme-diffs 'fg-only modus-%1$s-theme-org-blocks 'rainbow modus-%1$s-theme-scale-headings t modus-%1$s-theme-scale-1 1.1 modus-%1$s-theme-scale-2 1.15 modus-%1$s-theme-scale-3 1.21 modus-%1$s-theme-scale-4 1.27 modus-%1$s-theme-scale-5 1.33) (load-theme 'modus-%1$s t)) theme)) #+END_SRC *Allow switching between light and dark mode* #+begin_src emacs-lisp (defun modus-themes-toggle () "Toggle between `modus-operandi' and `modus-vivendi' themes." (interactive) (if (eq (car custom-enabled-themes) 'modus-operandi) (progn (disable-theme 'modus-operandi) (modus-vivendi-theme-load)) (disable-theme 'modus-vivendi) (modus-operandi-theme-load))) #+end_src *Call the swich function once to load the light theme* #+begin_src emacs-lisp (modus-themes-toggle) #+end_src *Use a nice looking modeline package* #+BEGIN_SRC emacs-lisp (use-package telephone-line) (telephone-line-mode 1) #+END_SRC *Set up the default frame look* #+begin_src emacs-lisp (setq default-frame-alist (append (list '(width . 90) '(height . 50) '(vertical-scroll-bars . nil) '(internal-border-width . 5)))) #+end_src * Dired Use the imporoved =dired+= package to extend of the buildin =dired=. #+begin_src emacs-lisp (require 'dired+) ;; Make dired+ reuse a single buffer for visiting directories (diredp-toggle-find-file-reuse-dir 1) ;; Make the mouse click also reuse the buffer (define-key dired-mode-map [mouse-2] 'diredp-mouse-find-file-reuse-dir-buffer) ;; Add better sorting with Dired Sort Menu (require 'dired-sort-menu+) ;; Set defauls for directory listings (setq ls-lisp-use-insert-directory-program nil) (setq ls-lisp-ignore-case t) (setq ls-lisp-dirs-first t) (setq dired-listing-switches "-al --group-directories-first") ;; More dired goodies (require 'dired-x) ;; Make it so that dotfiles are omitted, but not the "." and ".." at the top of the listing (setq dired-omit-files "^\\.\\w.*$") (add-hook 'dired-load-hook (lambda () (load "dired-x") ;; Set dired-x global variables here. For example: ;; (setq dired-guess-shell-gnutar "gtar") ;; (setq dired-x-hands-off-my-keys nil) )) (add-hook 'dired-mode-hook (lambda () ;; Set dired-x buffer-local variables here. ;; Start with hidden files, well, *hidden* (dired-omit-mode 1) (local-set-key (kbd "C-.") 'dired-omit-mode) )) #+end_src * Completion ** Ivy Use Ivy to make minibuf promts better. Adds the ability to sort and filter. #+BEGIN_SRC emacs-lisp (use-package ivy :diminish :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) :bind (("C-x b" . ivy-switch-buffer) ("C-c C-r" . ivy-resume) ("C-s" . swiper))) ;; 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 :after ivy :init (counsel-mode 1) :bind (("C-c ;" . counsel-M-x) ("C-c U" . counsel-unicode-char) ("C-c i" . counsel-imenu) ("C-c y" . counsel-yank-pop) ("C-c r" . counsel-recentf) :map ivy-minibuffer-map ("C-r" . counsel-minibuffer-history)) :diminish) #+END_SRC ** Ido #+begin_src emacs-lisp (use-package ido :config (ido-mode 1) :bind (("C-x f" . ido-find-file))) #+end_src ** Autocompletion #+BEGIN_SRC emacs-lisp (use-package auto-complete :config (ac-config-default)) #+END_SRC * 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 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. :bind (("C-c o c" . org-capture) ("C-c o l" . lux/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 a s" . org-emphasize) ("C-c -" . org-edit-special)) :hook ((org-mode . visual-line-mode) (org-mode . org-indent-mode)) :config (setq org-footnote-section "" org-startup-with-inline-images t org-pretty-entities t org-indent-mode t 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 org-hide-emphasis-markers t ) (setcar (nthcdr 4 org-emphasis-regexp-components) 4)) #+END_SRC ** 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 ** Export Location This snippet will create a sub dir for exports from org-mode. [[https://stackoverflow.com/questions/9559753/emacs-org-mode-export-to-another-directory][See the Stackoverflow question]] #+BEGIN_SRC emacs-lisp (defun org-export-output-file-name-modified (orig-fun extension &optional subtreep pub-dir) (unless pub-dir (setq pub-dir "Exports") (unless (file-directory-p pub-dir) (make-directory pub-dir))) (apply orig-fun extension subtreep pub-dir nil)) (advice-add 'org-export-output-file-name :around #'org-export-output-file-name-modified) #+END_SRC ** Export HTML Auto inline a CSS theme for org HTML exports. This will make sure a self contained single HTML file is created. #+BEGIN_SRC emacs-lisp (defun my-org-inline-css-hook (exporter) "Insert custom inline css" (when (eq exporter 'html) (let* ((dir (ignore-errors (file-name-directory (buffer-file-name)))) (path (concat dir "style.css")) (homestyle (or (null dir) (null (file-exists-p path)))) (final (if homestyle "~/.emacs.d/org-theme.css" path))) ;; <- set your own style file path (setq org-html-head-include-default-style nil) (setq org-html-head (concat "\n"))))) (add-hook 'org-export-before-processing-hook 'my-org-inline-css-hook) #+END_SRC ** Org Mode Bling< #+BEGIN_SRC emacs-lisp (use-package org-bullets :init (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))) (setq org-bullets-bullet-list '("◉" "○" "◆" "✿" "✚" "▶")) ;; Ellipsis icon (setq org-ellipsis "▾") ;; Nice Icons for lists (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))) ;; We also want them in exported HTML files (setq org-html-checkbox-type 'html) ;; Replace dash in bullet lists with unicode symbol (font-lock-add-keywords 'org-mode '(("^ *\\([-]\\) " (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))) ;; Strike out done ckeckbox items (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) ;; Prettier Timestamps in Exports (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. "