#+COMMENT: -*- mode: org; eval: (add-hook 'after-save-hook '(lambda () (org-gfm-export-to-markdown t nil nil) (org-babel-tangle)) nil t) -*- #+TITLE: Emacs πŸ’• #+TAGS[]: emacs #+MENU: main #+DATE: <2019-04-24 Wed> #+OPTIONS: toc:nil num:nil author:nil timestamp:nil \n:nil #+PROPERTY: header-args:emacs-lisp :tangle readme.el * Prologue This is my Emacs configuration. It is written in [[https://orgmode.org/][Org Mode]] format, which means that I can display a static representation here, but the [[https://github.com/gigawhitlocks/emacs-configs][source repository]] and document ([[https://raw.githubusercontent.com/gigawhitlocks/emacs-configs/refs/heads/master/readme.org][plain text view]]), are interactive when opened in Emacs. It follows the concept of "[[https://en.wikipedia.org/wiki/Literate_programming][literate programming]]" and both defines my Emacs configuration (as well as a few other, related things) and includes my notes about why I made those changes, and what I was doing at the time, as well as whatever other commentary I felt like including at the time (related or otherwise). At least, that's the goal. In reality, it's a messy living document that I use to configure Emacs and to keep track of what I've done. I don't always take the best of notes, but it is sufficient for me to keep moving forward. If you search around, you may find ideas and code that you can repurpose for your own uses. * Entrypoint The source code below is extracted to ~init.el~ by calling ~M-x org-babel-tangle~. The rest of this file is extracted to ~readme.el~ by this entrypoint in ~init.el~. This allows me to only maintain ~readme.org~ as it will be re-extracted at startup every time. If this whole file is tangled to ~init.el~ by ~init.el~, then a bootstrapping problem is introduced. So this part remains static, and the rest of the config can live in its Org file. #+BEGIN_SRC emacs-lisp :tangle init.el (setq dotfiles-dir (file-name-directory (or (buffer-file-name) load-file-name))) (let* ((org-dir (expand-file-name "lisp" (expand-file-name "org" (expand-file-name "src" dotfiles-dir)))) (org-contrib-dir (expand-file-name "lisp" (expand-file-name "contrib" (expand-file-name ".." org-dir)))) (load-path (append (list org-dir org-contrib-dir) (or load-path nil)))) ;; load up Org-mode and Org-babel (require 'ob-tangle)) ;; load up all literate org-mode files in this directory (mapc #'org-babel-load-file (directory-files dotfiles-dir t "\\.org$")) #+END_SRC * Customize Emacs provides a menu-based customization interface that makes configuration files like this one entirely optional, and sometimes Emacs prompts the user for things and saves their preferences to a "custom file." By default, that file is /this/ file, but the auto-generated code is nasty, disposable, and almost always specific to the system where I've made some interactive choice -- for instance to trust local variables set in the header of a file like this one -- and after a long time I've realized it's too troublesome to check in those changes. So this setting tells Customize to write those settings to their own file, and this file is ignored in ~.gitignore~. #+BEGIN_SRC emacs-lisp (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) (load custom-file) #+END_SRC * Package Manager Bootstrap After tangling the source files and loading ~init.el~, the first thing that must be done is to prepare to manage third party packages, because my config is built on top of the work of many third party packages. I like to install and manage all of the packages I use as part of my configuration so that it can be duplicated across computers (more or less) and managed with ~git~, so I use ~use-package~ to ensure that packages are installed from my configuration file. Bootstrap sets up the ELPA, Melpa, and Org Mode repositories, sets up the package manager, configures ~use-package~ and installs a few extra packages that acoutrement ~use-package~ and will be used heavily throughout. It used to install ~use-package~ itself, however, it has since been upstreamed and that step has been removed. πŸŽ‰ #+BEGIN_SRC emacs-lisp (require 'package) (setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/") ("melpa" . "https://melpa.org/packages/") ("org" . "http://orgmode.org/elpa/"))) (package-initialize) ;; set ensure to be the default (require 'use-package-ensure) (setq use-package-always-ensure t) ;; these go in bootstrap because packages installed ;; with use-package use :diminish and :delight (use-package diminish) (use-package delight) #+END_SRC Once this is done I need to install and configure any third party packages that are used in many modes throughout Emacs. Some of these modes fundamentally change the Emacs experience and need to be present before everything can be configured. * Fundamental Package Installation and Configuration First I need to install packages with a large effect and on which other packages are likely to depend. These are packages essential to my workflow. Configuration here should be config that must run early, before variables are set or language-related packages, which will likely rely on these being set. ** Icons Treemacs and Doom themes both rely upon ~all-the-icons~ to look nice #+begin_src emacs-lisp (use-package all-the-icons) #+end_src Along the way nerd-icons also gets installed. On first run or after clearing out elpa/, need to run the following: : M-x nerd-icons-install-fonts : M-x all-the-icons-install-fonts This installs the actual fonts and only needs to be called once. Maybe I'll automate it someday. ** Treemacs Treemacs provides a file browser on the left hand side of Emacs that I have grown to really like. It's great for exploring unfamiliar projects and modules. It's installed early because many things have integrations with it, including some themes. #+begin_src emacs-lisp (use-package treemacs :defer t ) (setq treemacs-no-png-images nil) (use-package treemacs-evil :after (treemacs evil)) (use-package treemacs-projectile :after (treemacs projectile)) (use-package treemacs-magit :after (treemacs magit)) #+end_src ** Theme I'm mainly using the Doom Emacs theme pack. I think they're really nice to look at, especially with ~solaire-mode~. *** Theme packs **** Doom #+begin_src emacs-lisp (use-package doom-themes :config ;; Global settings (defaults) (setq doom-themes-enable-bold t ; if nil, bold is universally disabled doom-themes-enable-italic t ) ; if nil, italics is universally disabled ;; Corrects (and improves) org-mode's native fontification. ;; TODO is this still relevant when also using org-modern? or do ;; they just conflict? (doom-themes-org-config) ) #+end_src **** ef-themes Protesilaos Stavrou has a nice theme pack too: #+begin_src emacs-lisp (use-package ef-themes) #+end_src *** Default theme I prefer to load a theme per-system, but it's nice to have it documented here. Add a line like the following to the appropriate file in ~local/~ #+begin_src emacs-lisp ;; (load-theme 'ef-reverie) #+end_src ** Navigation and Completion, and the Minibuffer The next few packages work closely together to enhance some of the core functionality of Emacs related to navigation, buffer management, and running commands. *** Consult (commands to list, search, and preview files and buffers in the minibuffer) Consult adds search and navigation commands that build upon the built-in completing-read #+begin_src emacs-lisp (use-package consult) #+end_src *** Marginalia (more metadata in completions and the minibuffer) Marginalia enhances the same native Emacs search interface with extra information about whatever is being displayed. It's used by both Vertico and Consult to display extra information about the actions they offer. #+begin_src emacs-lisp ;; Enable rich annotations using the Marginalia package (use-package marginalia ;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding ;; available in the *Completions* buffer, add it to the ;; `completion-list-mode-map'. :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle)) ;; The :init section is always executed. :init ;; Marginalia must be activated in the :init section of use-package such that ;; the mode gets enabled right away. Note that this forces loading the ;; package. (marginalia-mode)) ;; enhance marginalia with icons (use-package nerd-icons-completion :config (nerd-icons-completion-mode)) #+end_src *** Orderless (better interactive matching) Orderless allows pattern matching to be "better." With the default configuration, which is what I have below, the main obvious difference from vanilla Emacs is that now matching works anywhere in the target string and not just the beginning. That's a big win. This is applied everywhere Emacs does matching. #+begin_src emacs-lisp (use-package orderless :ensure t :custom (completion-styles '(orderless basic)) (completion-category-overrides '((file (styles basic partial-completion))))) #+end_src *** Embark (contextual actions) Embark allows you to call commands on whatever the cursor is on (thing "at-point") and shows stuff that is relevant to the context. It has some integrations with consult that seem very powerful and I don't fully understand them yet, but I'm adding them in here so I can figure them out. Lots of searching and matching goodness for working across many files and buffers, I think. #+begin_src emacs-lisp (use-package embark) (use-package embark-consult) #+end_src *** Vertico (minibuffer behavior) Finally, Vertico makes ~M-x~ more featureful, and allows me to display command history when it is invoked. I map ~M-x~ to ~SPC SPC~ due to my historical use of Spacemacs, and Vertico keeps Emacs feeling like home for someone used to Helm. Below is, actually, the default config. I didn't write any of this. It's kind of wild. #+begin_src emacs-lisp ;; Enable vertico (use-package vertico :custom ;; (vertico-scroll-margin 0) ;; Different scroll margin (vertico-count 20) ;; Show more candidates ;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer (vertico-cycle t) ;; Enable cycling for `vertico-next/previous' :init (vertico-mode)) ;; Persist history over Emacs restarts. Vertico sorts by history position. (use-package savehist :init (savehist-mode)) #+end_src *** Tab Completion Corfu handles tab completion outside of the minibuffer, and allows multiple terms separated by spaces, using the rules from completing-read -- in this case, what I've defined in the Orderless section above. #+begin_src emacs-lisp (use-package corfu ;; Optional customizations ;; :custom ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match ;; (corfu-preview-current nil) ;; Disable current candidate preview ;; (corfu-preselect 'prompt) ;; Preselect the prompt ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches ;; Enable Corfu only for certain modes. See also `global-corfu-modes'. ;; :hook ((prog-mode . corfu-mode) ;; (shell-mode . corfu-mode) ;; (eshell-mode . corfu-mode)) :bind ;; Configure SPC for separator insertion (:map corfu-map ("SPC" . corfu-insert-separator)) ;; Recommended: Enable Corfu globally. This is recommended since Dabbrev can ;; be used globally (M-/). See also the customization variable ;; `global-corfu-modes' to exclude certain modes. :init (global-corfu-mode)) #+end_src *** Global Configuration Below is some final, global configuration related to Vertico and Corfu & configure how completion and the minibuffer work. #+begin_src emacs-lisp ;; A few more useful configurations... ;; Support opening new minibuffers from inside existing minibuffers. (setq enable-recursive-minibuffers t) ;; Hide commands in M-x which do not work in the current mode. Vertico ;; commands are hidden in normal buffers. (setq read-extended-command-predicate #'command-completion-default-include-p) ;; Enable indentation+completion using the TAB key. ;; `completion-at-point' is often bound to M-TAB. (setq tab-always-indent 'complete) ;; Emacs 30 and newer: Disable Ispell completion function. ;; Try `cape-dict' as an alternative. ;; (text-mode-ispell-word-completion nil) ;; Add prompt indicator to `completing-read-multiple'. ;; We display [CRM], e.g., [CRM,] if the separator is a comma. (defun crm-indicator (args) (cons (format "[CRM%s] %s" (replace-regexp-in-string "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" crm-separator) (car args)) (cdr args))) (advice-add #'completing-read-multiple :filter-args #'crm-indicator) ;; Do not allow the cursor in the minibuffer prompt (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) #+end_src ** Solaire Mode Also some visual candy that makes "real" buffers more visible by changing the background color slightly vs e.g. *compilation* or magit buffers #+begin_src emacs-lisp (use-package solaire-mode) ;; treemacs got redefined as a normal window at some point (push '(treemacs-window-background-face . solaire-default-face) solaire-mode-remap-alist) (push '(treemacs-hl-line-face . solaire-hl-line-face) solaire-mode-remap-alist) (solaire-global-mode +1) #+end_src ** Doom Modeline The Doom Emacs project also provides a fancy modeline to go along with their themes. #+begin_src emacs-lisp (use-package doom-modeline :config (doom-modeline-def-modeline 'main '(bar matches buffer-info remote-host buffer-position parrot selection-info) '(misc-info minor-modes input-method buffer-encoding major-mode process vcs " ")) :hook (after-init . doom-modeline-mode)) #+end_src ** Emoji πŸ™ Provided by [[https://github.com/iqbalansari/emacs-emojify][emojify]]. Run ~emojify-download-emoji~ #+BEGIN_SRC emacs-lisp ;; πŸ™Œ Emoji! πŸ™Œ (use-package emojify :config (setq emojify-download-emojis-p t) (emojify-set-emoji-styles '(unicode)) (add-hook 'after-init-hook #'global-emojify-mode)) #+END_SRC ** Configure Recent File Tracking Emacs comes with ~recentf-mode~ which helps me remember what I was doing after I restart my session. #+BEGIN_SRC emacs-lisp ;; recent files mode (recentf-mode 1) (setq recentf-max-menu-items 25) (setq recentf-max-saved-items 25) ;; ignore the elpa directory (add-to-list 'recentf-exclude "elpa/*") #+END_SRC ** Install and Configure Projectile [[https://projectile.readthedocs.io/en/latest/][~projectile~]] is a fantastic package that provides all kinds of project context-aware functions for things like: - running grep, but only inside the project - compiling the project from the project root without doing anything - find files within the project, again without having to do anything extra It's great, it gets installed early, can't live without it. πŸ’˜ ~projectile~ #+BEGIN_SRC emacs-lisp (use-package projectile :delight) (use-package treemacs-projectile) (projectile-mode +1) #+END_SRC ** Install and Configure Evil Mode [[https://github.com/emacs-evil/evil][~evil-mode~]] fundamentally changes Emacs so that while editing all of the modes and keybindings from ~vim~ are present. It's controversial but I think modal editing is brilliant and have been using ~vim~ bindings for twenty-odd years now. No going back. #+BEGIN_SRC emacs-lisp (defun setup-evil () "Install and configure evil-mode and related bindings." (use-package evil :init (setq evil-want-keybinding nil) (setq evil-want-integration t) :config (evil-mode 1)) (use-package evil-collection :after evil :config ;; don't let evil-collection manage go-mode ;; it is overriding gd (setq evil-collection-mode-list (delq 'go-mode evil-collection-mode-list)) (evil-collection-init)) ;; the evil-collection overrides the worktree binding :( ;; in magit (general-define-key :states 'normal :keymaps 'magit-status-mode-map "Z" 'magit-worktree) (general-define-key :states 'normal "RET" 'embark-act ) (general-define-key :states 'normal :keymaps 'prog-mode-map "gd" 'evil-goto-definition ) ;; add fd as a remap for esc (use-package evil-escape :delight) (evil-escape-mode 1) (use-package evil-surround :config (global-evil-surround-mode 1)) (use-package evil-snipe) (evil-snipe-override-mode +1) ;; and disable in specific modes (an example below) ;; (push 'python-mode evil-snipe-disabled-modes) (use-package undo-tree :config (global-undo-tree-mode) (evil-set-undo-system 'undo-tree) (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))) ;; add some advice to undo-tree-save-history to suppress messages ;; when it saves its backup files (defun quiet-undo-tree-save-history (undo-tree-save-history &rest args) (let ((message-log-max nil) (inhibit-message t)) (apply undo-tree-save-history args))) (advice-add 'undo-tree-save-history :around 'quiet-undo-tree-save-history) (setq-default evil-escape-key-sequence "fd") ;; unbind RET since it does the same thing as j and in some ;; modes RET is used for other things, and evil conflicts (with-eval-after-load 'evil-maps (define-key evil-motion-state-map (kbd "RET") nil)) ) #+END_SRC ** Install and Configure Keybindings Helper [[https://github.com/noctuid/general.el][General]] provides more consistent and convenient keybindings, especially with ~evil-mode~. It's mostly used below in the [[Global Keybindings][global keybindings]] section. #+BEGIN_SRC emacs-lisp (use-package general :init (setup-evil) :config (general-evil-setup)) #+END_SRC ** Install and Configure Magit [[https://github.com/magit/magit][Magit]] is an incredible integrated ~git~ UI for Emacs. #+BEGIN_SRC emacs-lisp (use-package magit) ;; disable the default emacs vc because git is all I use, ;; for I am a simple man (setq vc-handled-backends nil) #+END_SRC *** Allow magit to interact with git forges, like Github and Gitlab #+begin_src emacs-lisp (use-package forge :after magit) #+end_src ** Install and Configure ~git-timemachine~ ~git-timeline~ lets you step through the history of a file. #+BEGIN_SRC emacs-lisp (use-package git-timemachine) ;; This lets git-timemachine's bindings take precedence over evils' ;; (got lucky and happened to find this while looking for the package name, ha!) ;; @see https://bitbucket.org/lyro/evil/issue/511/let-certain-minor-modes-key-bindings (eval-after-load 'git-timemachine '(progn (evil-make-overriding-map git-timemachine-mode-map 'normal) ;; force update evil keymaps after git-timemachine-mode loaded (add-hook 'git-timemachine-mode-hook #'evil-normalize-keymaps))) #+END_SRC ** Install and Configure ~which-key~ It can be difficult to to remember and discover all of the available shortcuts in Emacs, so [[https://github.com/justbur/emacs-which-key][~which-key~]] pops up a special buffer to show you available shortcuts whenever you pause in the middle of a keyboard shortcut for more than a few seconds. It's really lovely. #+BEGIN_SRC emacs-lisp (use-package which-key :delight :init (which-key-mode) (which-key-setup-minibuffer)) #+END_SRC ** Set up ~pass~ for secrets handling #+begin_src emacs-lisp (use-package pass) #+end_src ** Handle "fancy" output in compilation buffer The external package ~fancy-compilation-mode~ handles colorization and "clever" use of ANSI to create progress bars and stupid shit like that, which show up in things like npm output and Docker output when BuildKit is set to NORMAL. You can, of course, set the BuildKit output style to PLAIN, but sometimes you're eg editing a file where NORMAL is hard-coded in the Makefile target you want to run when using ~compilation-mode~ and fighting project defaults isn't what you want to spend your time on. #+begin_src emacs-lisp (use-package fancy-compilation :commands (fancy-compilation-mode)) (with-eval-after-load 'compile (fancy-compilation-mode)) #+end_src I don't like how fancy-compilation-mode overrides colors by default, but luckily this can be disabled. #+begin_src emacs-lisp (setq fancy-compilation-override-colors nil) #+end_src ** Configure the Startup Splashscreen Following Spacemacs's style, I use the [[https://github.com/emacs-dashboard/emacs-dashboard][~emacs-dashboard~]] project and [[https://github.com/domtronn/all-the-icons.el][~all-the-icons~]] to provide an aesthetically pleasing splash screen with useful links to recently used files on launch. Actually, looking at the project page, the icons don't seem to be working for me. Maybe I need to enable them. I'll investigate later. #+BEGIN_SRC emacs-lisp ;; first disable the default startup screen (setq inhibit-startup-screen t) (use-package dashboard :config (dashboard-setup-startup-hook) (setq dashboard-startup-banner 'logo) (setq dashboard-center-content t) (setq dashboard-items '((recents . 5) (bookmarks . 5) (projects . 5)) ) ) (setq dashboard-set-footer nil) #+END_SRC ** Install templating tool and default snippets YASnippet is really cool and allow fast insertion of boilerplate using templates. I've been meaning to use this more. [[https://www.emacswiki.org/emacs/Yasnippet][Here are the YASnippet docs.]] #+BEGIN_SRC emacs-lisp (use-package yasnippet :delight :config (use-package yasnippet-snippets)) #+end_src Enable yas-mode everywhere #+begin_src emacs-lisp (yas-global-mode 1) #+END_SRC * Extra Packages Packages with a smaller effect on the experience. ** prism colors by indent level It takes over the color theme and I don't know if I want it on all the time but it's interesting and I want to have it installed so that I can turn it on in certain situations, like editing highly nested YAML, where it might be invaluable. If I can remember to use it :) #+begin_src emacs-lisp (use-package prism) #+end_src ** git-gutter shows unstaged changes in the gutter #+BEGIN_SRC emacs-lisp (use-package git-gutter :delight :config (global-git-gutter-mode +1)) #+END_SRC ** Highlight the current line I like to highlight the current line so that it is easy to identify where my cursor is. #+BEGIN_SRC emacs-lisp (global-hl-line-mode) (setq global-hl-line-sticky-flag t) #+END_SRC ** Rainbow delimiters make it easier to identify matching parentheses #+BEGIN_SRC emacs-lisp (use-package rainbow-delimiters :config ;; set up rainbow delimiters for Emacs lisp (add-hook 'emacs-lisp-mode-hook #'rainbow-delimiters-mode) ;; and sql mode too, it's useful there (add-hook 'sql-mode-hook #'rainbow-delimiters-mode) ) #+END_SRC ** restart-emacs does what it says on the tin #+BEGIN_SRC emacs-lisp (use-package restart-emacs) #+END_SRC ** s is a string manipulation utility I use this for a trim() function far down below. I think it gets pulled in as a dependency anyway, but in any case it provides a bunch of helper functions and stuff. [[https://github.com/magnars/s.el][Docs are here.]] #+BEGIN_SRC emacs-lisp (use-package s) #+END_SRC ** a systemd file mode Just provides syntax highlighting in ~.unit~ files. #+BEGIN_SRC emacs-lisp (use-package systemd) #+END_SRC ** Install and Configure Flycheck for Linting [[https://www.flycheck.org/en/latest/][Flycheck]] is an on-the-fly checker that hooks into most language backends. #+BEGIN_SRC emacs-lisp ;; linter (use-package flycheck :delight ;; enable it everywhere :init (global-flycheck-mode)) (add-hook 'flycheck-error-list-mode-hook 'visual-line-mode) #+END_SRC ** Install ~exec-path-from-shell~ to manage the PATH [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] mirrors PATH in zsh or Bash in macOS or Linux into Emacs so that the PATH in the shell and the PATH when calling commands from Emacs are the same. #+BEGIN_SRC emacs-lisp (use-package exec-path-from-shell :config (exec-path-from-shell-initialize)) #+END_SRC ** ace-window provides an ace-jump experience for switching windows #+BEGIN_SRC emacs-lisp (use-package ace-window) #+END_SRC ** Install a mode for drawing indentation guides This mode adds subtle coloration to indentation whitespace for whitespace-delimited languages like YAML where sometimes it can be difficult to see the nesting level of a given headline in deeply-nested configuration. #+begin_src emacs-lisp (use-package highlight-indent-guides) #+end_src ** Quick buffer switcher #+begin_quote PC style quick buffer switcher for Emacs This switches Emacs buffers according to most-recently-used/least-recently-used order using C-tab and C-S-tab keys. It is similar to window or tab switchers that are available in PC desktop environments or applications. #+end_quote Bound by default to ~C-~ and ~C-S-~, I have decided that these are sane defaults. Just install this and turn it on. #+begin_src emacs-lisp (use-package pc-bufsw) (pc-bufsw) #+end_src ** Writeable grep mode with ack Writable grep mode allows you to edit the results from running grep on a project and easily save changes back to all of the original files #+BEGIN_SRC emacs-lisp (use-package ack) (use-package ag) (use-package wgrep-ack) #+END_SRC ** Better help buffers #+begin_src emacs-lisp (use-package helpful) (global-set-key (kbd "C-h f") #'helpful-callable) (global-set-key (kbd "C-h v") #'helpful-variable) (global-set-key (kbd "C-h k") #'helpful-key) #+end_src ** Quickly jump around buffers #+begin_src emacs-lisp (use-package ace-jump-mode) #+end_src ** Dumb jump Dumb jump provides an interface to grep that does a pretty good job of finding definitions when a smarter backend like LSP is not available. This registers it as a backend for XREF. #+begin_src emacs-lisp (use-package dumb-jump) (add-hook 'xref-backend-functions #'dumb-jump-xref-activate) (setq xref-show-definitions-function #'xref-show-definitions-completing-read) #+end_src ** Kubernetes Mode Provides an interactive Kubernetes Mode inspired by ~magit~. Since ~magit~ is one of my favorite tools, I have to try out the Kubernetes mode as well. #+begin_src emacs-lisp (use-package kubernetes :ensure t :commands (kubernetes-overview)) ;; add this config if I experience issues with Emacs locking up ;;:config ;;(setq kubernetes-poll-frequency 3600 ;; kubernetes-redraw-frequency 3600)) #+end_src I need the ~evil~ compatiblity mode, too, because I run ~evil~. #+begin_src emacs-lisp (use-package kubernetes-evil :after kubernetes) #+end_src ** multiple cursors #+begin_src emacs-lisp (use-package evil-mc) #+end_src ** elfeed #+begin_src emacs-lisp (use-package elfeed) #+end_src * Font The FiraCode font is a programming-focused font with ligatures that looks nice and has a open license so I'm standardizing my editor configuration on that font ** FiraCode Font Installation Script :properties: :header-args: :tangle ~/.emacs.d/install-firacode-font.bash :shebang #!/usr/bin/env bash :end: Installing fonts is always a pain so I'm going to use a variation of the installation script that the FireCode devs provide under their manual installation guide. This should be Linux-distribution agnostic, even though the font can be installed as a system package with on all of my systems on 2022-02-19 Sat with just : sudo apt install fonts-firacode because I don't intend to use Ubuntu as my only system forever. I just happen to be on Ubuntu on 2022-02-19 Sat. But first, I want to be able to run this script every time Emacs starts, but only have the script actually do anything if the font is not already installed. This guard will check to see if there's any font with 'fira' in it (case insensitive) and if so, just exits the script. This will happen on most executions. #+begin_src bash set -eo pipefail [[ $(fc-list | grep -i fira) != "" ]] && exit 0 #+end_src Now here's the standard installation script #+begin_src bash fonts_dir="${HOME}/.local/share/fonts" if [ ! -d "${fonts_dir}" ]; then mkdir -p "${fonts_dir}" fi version=5.2 zip=Fira_Code_v${version}.zip curl --fail --location --show-error https://github.com/tonsky/FiraCode/releases/download/${version}/${zip} --output ${zip} unzip -o -q -d ${fonts_dir} ${zip} rm ${zip} # for now we need the Symbols font, too zip=FiraCode-Regular-Symbol.zip curl --fail --location --show-error https://github.com/tonsky/FiraCode/files/412440/${zip} --output ${zip} unzip -o -q -d ${fonts_dir} ${zip} rm ${zip} fc-cache -f #+end_src This installation script was sourced from [[https://github.com/tonsky/FiraCode/wiki/Linux-instructions#installing-with-a-package-manager]] ** Enable FiraCode Font Calling the script from above will install the font #+begin_src emacs-lisp (shell-command "chmod +x ~/.emacs.d/install-firacode-font.bash") (shell-command "~/.emacs.d/install-firacode-font.bash") #+end_src Enable it #+BEGIN_SRC emacs-lisp (add-to-list 'default-frame-alist '(font . "Fira Code-10")) (set-frame-font "Fira Code-10" nil t) #+end_src ** Configure FiraCode special features FiraCode offers ligatures for programming symbols, which is cool. #+begin_src emacs-lisp (use-package ligature :load-path "./vendor/" :config ;; Enable the "www" ligature in every possible major mode (ligature-set-ligatures 't '("www")) ;; Enable traditional ligature support in eww-mode, if the ;; `variable-pitch' face supports it (ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi")) ;; ;; Enable ligatures in programming modes (ligature-set-ligatures 'prog-mode '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\" "{-" ":::" ":=" "!!" "!=" "!==" "-}" "----" "-->" "->" "->>" "-<" "-<<" "-~" "#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_(" ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*" "/**" "/=" "/==" "/>" "//" "///" "&&" "||" "||=" "|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "==" "===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">=" ">=>" ">>" ">>-" ">>=" ">>>" "<*" "<*>" "<|" "<|>" "<$" "<$>" "