#+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 Packages provided by =straight.el= https://github.com/radian-software/straight.el #+BEGIN_SRC emacs-lisp (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) (bootstrap-version 6)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) (straight-use-package 'use-package) (setq straight-use-package-by-default t) (package-initialize) (straight-use-package 'bind-key) #+end_src * Theming Add the nano package from Github #+begin_src emacs-lisp (straight-use-package '(nano-emacs :type git :host github :repo "rougier/nano-emacs")) (setq nano-font-size 12) #+end_src Configure all nano modules #+begin_src emacs-lisp ;; Default layout (optional) (require 'nano-layout) ;; Theming Command line options (this will cancel warning messages) (add-to-list 'command-switch-alist '("-dark" . (lambda (args)))) (add-to-list 'command-switch-alist '("-light" . (lambda (args)))) (add-to-list 'command-switch-alist '("-default" . (lambda (args)))) (add-to-list 'command-switch-alist '("-no-splash" . (lambda (args)))) (add-to-list 'command-switch-alist '("-no-help" . (lambda (args)))) (add-to-list 'command-switch-alist '("-compact" . (lambda (args)))) (cond ((member "-default" command-line-args) t) ((member "-dark" command-line-args) (require 'nano-theme-dark)) (t (require 'nano-theme-light))) ;; Customize support for 'emacs -q' (Optional) ;; You can enable customizations by creating the nano-custom.el file ;; with e.g. `touch nano-custom.el` in the folder containing this file. (let* ((this-file (or load-file-name (buffer-file-name))) (this-dir (file-name-directory this-file)) (custom-path (concat this-dir "nano-custom.el"))) (when (and (eq nil user-init-file) (eq nil custom-file) (file-exists-p custom-path)) (setq user-init-file this-file) (setq custom-file custom-path) (load custom-file))) ;; Theme (require 'nano-faces) (nano-faces) (require 'nano-theme) (nano-theme) ;; Nano session saving (optional) (require 'nano-session) ;; Nano header & mode lines (optional) (require 'nano-modeline) ;; Nano key bindings modification (optional) (require 'nano-bindings) ;; Compact layout (need to be loaded after nano-modeline) (when (member "-compact" command-line-args) (require 'nano-compact)) ;; Nano counsel configuration (optional) ;; Needs "counsel" package to be installed (M-x: package-install) ;; (require 'nano-counsel) ;; Welcome message (optional) (let ((inhibit-message t)) (message "Welcome to GNU Emacs / N Λ N O edition") (message (format "Initialization time: %s" (emacs-init-time)))) ;; Splash (optional) (unless (member "-no-splash" command-line-args) (require 'nano-splash)) ;; Help (optional) (unless (member "-no-help" command-line-args) (require 'nano-help)) #+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 * 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) ;; Show tooltips on hover and not in the echo area. ;; Those are often cut of. (tooltip-mode) ;; 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 ** Org show only current subtree Fold all other subtrees in an org file and show just the current one #+begin_src emacs-lisp (defun lux/org-show-just-me (&rest _) "Fold all other trees, then show entire current subtree." (interactive) (org-overview) (org-reveal) (org-show-subtree)) (bind-key "C-c m" 'lux/org-show-just-me) #+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 * Completion ** Ivy Use Ivy to make minibuf promts better. Adds the ability to sort and filter. #+BEGIN_SRC emacs-lisp (use-package ivy :straight t :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 :straight t :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 :straight t) #+END_SRC ** Smex Sort commands by recency in ivy windows #+BEGIN_SRC emacs-lisp (use-package smex :straight t) #+END_SRC ** Counsel #+BEGIN_SRC emacs-lisp (use-package counsel :straight 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-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 :straight t :config (ido-mode 1) :bind (("C-x f" . ido-find-file))) #+end_src ** Autocompletion #+BEGIN_SRC emacs-lisp (use-package auto-complete :straight t :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 :straight t :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 :straight t :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 :straight t ;; 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 Include images in org files as base64 directly into the HTML #+begin_src emacs-lisp (defun replace-in-string (what with in) (replace-regexp-in-string (regexp-quote what) with in nil 'literal)) (defun org-html--format-image (source attributes info) (progn (setq source (replace-in-string "%20" " " source)) (format "" (or (file-name-extension source) "") (base64-encode-string (with-temp-buffer (insert-file-contents-literally source) (buffer-string))) (file-name-nondirectory source)) )) #+end_src ** Org Mode Bling #+BEGIN_SRC emacs-lisp (use-package org-bullets :straight t :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. "