#+TITLE: luxicks Emacs Configuration 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 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 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 * Theming Apply a nice looking theme. #+BEGIN_SRC emacs-lisp ;; Light Theme (use-package modus-operandi-theme) (load-theme 'modus-operandi t) ;; Syntax highlighing (setq modus-operandi-theme-faint-syntax t) (setq modus-operandi-theme-org-blocks "greyscale") (setq default-frame-alist (append (list '(width . 90) '(height . 50) '(vertical-scroll-bars . nil) '(internal-border-width . 10) '(font . "Roboto Mono Light 10")))) #+END_SRC Use a nice looking modeline package #+BEGIN_SRC emacs-lisp (use-package telephone-line) (telephone-line-mode 1) #+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 :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-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 * 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) ("C-c -" . org-edit-special)) :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-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 #+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 Ellipsis icon: #+BEGIN_SRC emacs-lisp (setq org-ellipsis "▾") #+END_SRC Nice Icons for lists: #+BEGIN_SRC emacs-lisp (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 *** Prettier Timestamps in Exports #+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. "\n" "\n" "\n"))))) (add-hook 'org-export-before-processing-hook 'my-org-inline-css-hook) #+END_SRC ** Export to a subdirectory 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 * Treemacs Treemacs makes navigating folders and files much easier. This is the default config from [[https://github.com/Alexander-Miller/treemacs][the offical repository]] as a base, with slight modifications to suite my config. #+BEGIN_SRC emacs-lisp (use-package treemacs :defer t :init (with-eval-after-load 'winum (define-key winum-keymap (kbd "M-0") #'treemacs-select-window)) :config (progn (setq treemacs-collapse-dirs (if treemacs-python-executable 3 0) treemacs-deferred-git-apply-delay 0.5 treemacs-display-in-side-window t treemacs-eldoc-display t treemacs-file-event-delay 5000 treemacs-file-follow-delay 0.2 treemacs-follow-after-init t treemacs-git-command-pipe "" treemacs-goto-tag-strategy 'refetch-index treemacs-indentation 2 treemacs-indentation-string " " treemacs-is-never-other-window nil treemacs-max-git-entries 5000 treemacs-missing-project-action 'ask treemacs-no-png-images nil treemacs-no-delete-other-windows t treemacs-project-follow-cleanup nil treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory) treemacs-position 'left treemacs-recenter-distance 0.1 treemacs-recenter-after-file-follow nil treemacs-recenter-after-tag-follow nil treemacs-recenter-after-project-jump 'always treemacs-recenter-after-project-expand 'on-distance treemacs-show-cursor nil treemacs-show-hidden-files t treemacs-silent-filewatch nil treemacs-silent-refresh nil treemacs-sorting 'alphabetic-desc treemacs-space-between-root-nodes t treemacs-tag-follow-cleanup t treemacs-tag-follow-delay 1.5 treemacs-width 35) ;; The default width and height of the icons is 22 pixels. If you are ;; using a Hi-DPI display, uncomment this to double the icon size. ;;(treemacs-resize-icons 44) (treemacs-follow-mode t) (treemacs-filewatch-mode t) (treemacs-fringe-indicator-mode t) (treemacs-toggle-show-dotfiles) (pcase (cons (not (null (executable-find "git"))) (not (null treemacs-python-executable))) (`(t . t) (treemacs-git-mode 'deferred)) (`(t . _) (treemacs-git-mode 'simple)))) :bind (:map global-map ("M-0" . treemacs) ("C-x t 1" . treemacs-delete-other-windows) ("C-x t B" . treemacs-bookmark) ("C-x t C-t" . treemacs-find-file) ("C-x t M-t" . treemacs-find-tag))) (use-package treemacs-magit :after treemacs magit :ensure t) #+END_SRC * Elfeed [[https://github.com/skeeto/elfeed][Elfeed]] is an RSS reader for emacs. #+BEGIN_SRC emacs-lisp (use-package elfeed :bind ("C-x w" . 'elfeed)) #+END_SRC ** Hooks elfeed can be extended with various hooks for ease of used *** Auto tag youtube feeds #+BEGIN_SRC emacs-lisp (add-hook 'elfeed-new-entry-hook (elfeed-make-tagger :feed-url "youtube\\.com" :add '(video youtube))) #+END_SRC *** Do not spam unread tag #+BEGIN_SRC emacs-lisp (add-hook 'elfeed-new-entry-hook (elfeed-make-tagger :before "2 weeks ago" :remove 'unread)) #+END_SRC * Additional Package Imports ** All The Icons We want to have some nice looking icons #+BEGIN_SRC emacs-lisp (use-package all-the-icons) #+END_SRC ** Recentf Show recent files in the buffer selection #+BEGIN_SRC emacs-lisp (use-package recentf :init (recentf-mode t) :config (add-to-list 'recentf-exclude "\\.emacs.d") (add-to-list 'recentf-exclude ".+tmp......\\.org")) #+END_SRC ** Rainbow Delimiters We want to have some nicely colored delimiters when reading and writing lisp code #+BEGIN_SRC emacs-lisp (use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode)) #+END_SRC ** Markdown Mode #+BEGIN_SRC emacs-lisp (use-package markdown-mode :mode ("\\.md$" . gfm-mode) :config (when (executable-find "pandoc") (setq markdown-command "pandoc -f markdown -t html"))) #+END_SRC ** Duplicate Thing Quick bind to ~C-c u ~ to duplicate the current line #+BEGIN_SRC emacs-lisp (use-package duplicate-thing :bind (("C-c u" . duplicate-thing))) #+END_SRC ** ACE Window Small package to quickly switch tiled windows. Use ~M-p~ to quickly switch. #+BEGIN_SRC emacs-lisp (use-package ace-window :bind (("M-o" . 'ace-window)) :config (custom-set-faces '(aw-leading-char-face ((t (:inherit ace-jump-face-foreground :height 3.0)))) )) #+END_SRC ** htmlize HTML Exporter for org-mode #+BEGIN_SRC emacs-lisp (use-package htmlize) #+END_SRC ** Autocompletion #+BEGIN_SRC emacs-lisp (use-package company :config (global-company-mode)) #+END_SRC ** vterm [[https://github.com/akermu/emacs-libvterm][vterm]] is a superiour alternative to the integrated eshell, shell or term modes. The packages only works on linux and reuqires that emacs is compiled with module support (the ~module-file-suffix~ variable will be filled). #+BEGIN_SRC emacs-lisp (if (and (string-equal system-type "gnu/linux") (bound-and-true-p module-file-suffix)) (use-package vterm :ensure t :config (setq vterm-kill-buffer-on-exit t) (setq vterm-copy-exclude-prompt t))) #+End_SRC ** Olivetti A simple Emacs minor mode for a nice writing environment. [[https://github.com/rnkn/olivetti][Gihub Link]] #+BEGIN_SRC emacs-lisp (use-package olivetti :config (setq-default olivetti-hide-mode-line t olivetti-body-width 90)) #+END_SRC Auto enable it in text modes #+BEGIN_SRC emacs-lisp (add-hook 'text-mode-hook 'olivetti-mode) #+END_SRC ** Ag Ag.el allows you to search using ~ag~ from inside Emacs. You can filter by file type, edit results inline, or find files. [[https://agel.readthedocs.io/en/latest/index.html][Documentation]] #+BEGIN_SRC emacs-lisp (use-package ag) #+END_SRC * Set Variables ** General Emacs Options #+BEGIN_SRC emacs-lisp (setq compilation-always-kill t ; Never prompt to kill a compilation session. compilation-scroll-output 'first-error ; Always scroll to the bottom. make-backup-files nil ; No backups, thanks. auto-save-default nil ; Or autosaves. What's the difference between autosaves and backups? create-lockfiles nil ; Emacs sure loves to put lockfiles everywhere. default-directory "~/" ; Home sweet home. inhibit-startup-screen t ; No need to see GNU agitprop. kill-whole-line t ; Lets C-k delete the whole line require-final-newline t ; Auto-insert trailing newlines. ring-bell-function 'ignore ; Do not ding. Ever. use-dialog-box nil ; Dialogues always go in the modeline. initial-scratch-message nil ; SHUT UP SHUT UP SHUT UP save-interprogram-paste-before-kill t ; preserve paste to system ring enable-recursive-minibuffers t ; don't fucking freak out if I use the minibuffer twice sentence-end-double-space nil ; are you fucking kidding me with this shit confirm-kill-processes nil ; don't whine at me when I'm quitting. mark-even-if-inactive nil ; prevent really unintuitive undo behavior load-prefer-newer t ; load newest file version available ) #+END_SRC ** Read environment variables from the shell When not running on a windows system, we can use the env variables in emacs #+BEGIN_SRC emacs-lisp (use-package exec-path-from-shell :if (not (eq system-type 'windows-nt)) :config (exec-path-from-shell-initialize)) #+END_SRC ** Show the current filename in titlebar #+BEGIN_SRC emacs-lisp (setq frame-title-format '((:eval user-login-name) "@" (:eval (system-name)) ": " (:eval (if (buffer-file-name) (abbreviate-file-name (buffer-file-name)) "%b")) " [%*]")) #+END_SRC ** Default encoding #+BEGIN_SRC emacs-lisp (prefer-coding-system 'utf-8) #+END_SRC ** Shorten "yes or no" questions #+BEGIN_SRC emacs-lisp (defalias 'yes-or-no-p 'y-or-n-p) #+END_SRC ** Always highlight the current line #+BEGIN_SRC emacs-lisp (global-hl-line-mode t) #+END_SRC ** Always highlight matching braces #+BEGIN_SRC emacs-lisp (show-paren-mode t) #+END_SRC ** Allow selection override #+BEGIN_SRC emacs-lisp (delete-selection-mode t) #+END_SRC ** Behave like a normal text editor Changed: No more CUA, ~C-c~ is used in to many places that are broken by cua-mode #+BEGIN_SRC emacs-lisp (cua-mode t) #+END_SRC ** Set cursor and indet mode #+BEGIN_SRC emacs-lisp (setq-default cursor-type 'bar indent-tabs-mode nil cursor-in-non-selected-windows nil) #+END_SRC ** Set default column width #+BEGIN_SRC emacs-lisp (set-fill-column 95) #+END_SRC * Hooks ** Remove trailing whitespace on file close #+BEGIN_SRC emacs-lisp (add-hook 'before-save-hook 'delete-trailing-whitespace) #+END_SRC ** Elisp Some customization for writing elisp #+BEGIN_SRC emacs-lisp (defun my-elisp-mode-hook () "My elisp customizations." (electric-pair-mode 1) (add-hook 'before-save-hook 'check-parens nil t) (auto-composition-mode nil)) (add-hook 'emacs-lisp-mode-hook 'my-elisp-mode-hook) #+END_SRC * Global Key Bindings #+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 "C-c 5" 'query-replace-regexp) ;; stupid vestigial binding (bind-key "M-/" 'hippie-expand) (bind-key "C-c \\" 'align-regexp) (bind-key "C-c m" 'compile) (bind-key "C-c 3" 'split-right-and-enter) (bind-key "C-c 2" 'split-below-and-enter) (bind-key "M-p" 'switch-to-previous-buffer) (bind-key "C-c /" 'comment-or-uncomment-region) (bind-key "C-c x" 'ESC-prefix) (bind-key "M-i" 'delete-indentation) (bind-key "C-+" 'text-scale-increase) (bind-key "C--" 'text-scale-decrease) (bind-key "C-<" 'beginning-of-buffer) (bind-key "C->" '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 additional files 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 a custom file from the emacs home dir. This file is specific to the machine emacs runs on. It conatins customizations and file locations that are machine dependend. #+BEGIN_SRC emacs-lisp (setq custom-file "~/.emacs.d/custom.el") (load custom-file 'noerror) #+END_SRC