#+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 ~use-package~ 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))) ;; add to the package repos (append-to-list package-archives '(("melpa" . "http://melpa.org/packages/") ("org-elpa" . "https://orgmode.org/elpa/"))) (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 * 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, 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) ;; 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) #+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 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)) (bind-key "C-c 3" 'split-right-and-enter) (defun split-below-and-enter () "Split the window down and enter it." (interactive) (split-window-below) (other-window 1)) (bind-key "C-c 2" 'split-below-and-enter) #+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 * 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 org-hide-emphasis-markers 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) (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 * Misc packages ** 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-o~ 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 * Programming ** 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 * 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