earnest ma's site

Version 0.5.3 (2020-01-19)

You can also view this via Github or my Gitea instance.

View posts tagged with Emacs on the blog.

Table of Contents


Hello, and welcome to my personal Emacs configuration!

Some background: I discovered Emacs in August 2019 and have been using it on and off since, with different configurations (my own, Spacemacs, Doom Emacs, etc.). This is my attempt to stay in one config and use it consistently.

Hopefully, having a literate configuration will help keep things organized.

It is generally agreed upon (me included) that GNU Emacs runs slower on Windows; my main laptop runs Windows 10 (Secondary is Ubuntu 20.04 at the moment).

I am also doing some versioning (using Git tags) to keep myself on track: changes will be made on develop and merged to master before each tag. To see major changes, you may view the message of each tag.

This file may look better on Github, or view the HTML version from the latest 'tag' on my website.

Inspiration 💜

Compatibility/ Installation

From [README.org]{.spurious-link target=“README.org”}:


Install the Jetbrains Mono font (which is licensed under the SIL Open Font License 1.1), and run M-x all-the-icons-install-fonts.

# Back up your previous config
mv ~/.config/emacs ~/.config/emacs.bak  # or subtitute for ~/.emacs.d throughout

# Clone repo
git clone https://git.earne.link/earnestma/earnemacs ~/.config/emacs

# OPTIONAL - checkout develop branch
cd ~/.config/emacs ; git checkout develop

# Run Emacs!


I use Emacs 27.1 daily - Windows mainly and also GNU/Linux (Ubuntu 20.04). Although not tested, this config will probably work for Emacs 26+ and for Emacs HEAD.


Use lexical binding

;;; config.el --- -*- lexical-binding: t; -*-

Package Initialization and Management

I am using straight.el + use-package. Bootstrap and configure it.

(setq straight-repository-branch "master")
;;(setq straight-vc-git-default-clone-depth 5)

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
         'silent 'inhibit-cookies)
      (goto-char (point-max))
  (load bootstrap-file nil 'nomessage))

(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
(setq use-package-always-defer t)

General Configuration

Running as a daemon

Or more specifically, running multiple daemons (I used to use "personal" and "work").


Identify what type/ is a daemon or not (Used in setting frame title).

(if (equal (daemonp) nil)
  (setq tychoish-emacs-identifier "indiv")
(setq tychoish-emacs-identifier (daemonp)))


Frame Title

(setq frame-title-format (list "%b %* " tychoish-emacs-identifier "@" system-name))


emacs-doom-themes are great. I am currently using doom-dracula.

(use-package doom-themes
  (load-theme 'doom-dracula t))


(add-to-list 'default-frame-alist '(font . "Jetbrains Mono 11" ))



(use-package dashboard
  :if (< (length command-line-args) 2)
  (setq dashboard-banner-logo-title (concat "Welcome to earnemacs v" earnemacs-version "!")))
  (setq dashboard-startup-banner 'logo)

(setq inhibit-startup-screen t)

Line numbers

Thanks - System Crafters: https://www.youtube.com/watch?v=IspAZtNTslY (5:17)

(global-display-line-numbers-mode t)

Disable line numbers for some modes.

(dolist (mode '(elfeed-search-mode
  (add-hook mode (lambda () (display-line-numbers-mode 0))))


I love the look of doom-modeline, and it doesn't need much configuration (if at all).

(use-package doom-modeline
  (doom-modeline-mode 1))

Nyan-cat in modeline :)

(use-package nyan-mode


(use-package git-gutter
  (setq git-gutter:disabled-modes '(asm-mode))
  (setq git-gutter:update-interval 2)


Typing in yes or no now becomes just y or n.

(fset 'yes-or-no-p 'y-or-n-p)

Inhibit message every startup from echo area.

(defun display-startup-echo-area-message ()
  (message () ))

Enable messages when garbage collection occurs

(setq garbage-collection-messages t)


Improves scrolling


Normally 2 lines, hold Shift for scrolling 1 line

(setq mouse-wheel-scroll-amount '(2 ((shift) . 1)))
(setq mouse-wheel-progressive-speed nil)
(setq mouse-wheel-follow-mouse 't)
(setq scroll-step 1)


Emacs package that displays available keybindings in popup


Super useful as I cannot remember all the keybindings.

(use-package which-key


Autosaves, Backups, Reverting


(use-package super-save
  (super-save-mode +1)
  (setq super-save-auto-save-when-idle t)
  (setq super-save-remote-files nil)           ; do not save remote files
  (setq super-save-exclude '(".gpg" "config.org")))   ; excluded files

Stores the default Emacs auto-save files ".autosave" under your emacs config directory. While we are using super-save, I want to keep this active just in case.

(setq backup-directory-alist
      `(("." . ,(concat user-emacs-directory ".autosave"))))

(setq backup-by-copying t)
(setq delete-old-versions t
      kept-new-versions 6
      kept-old-versions 2
      version-control t)

Automatically revert buffer if it has been modified on disk.

(global-auto-revert-mode 1)


EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs. EditorConfig files are easily readable and they work nicely with version control systems.


(use-package editorconfig
  (editorconfig-mode 1))


vi-like keybindings! 😈

(use-package evil
  (setq evil-want-integration t)
  (setq evil-want-keybinding nil)
  (evil-mode 1)
  (setq evil-split-window-below t
        evil-vsplit-window-right t)
  (setq evil-undo-system 'undo-tree)
  (evil-ex-define-cmd "W" 'evil-write-all)
  (evil-ex-define-cmd "Q" 'save-buffers-kill-emacs)
  (evil-set-initial-state 'elfeed-search-mode 'emacs)
  (evil-set-initial-state 'elfeed-show-mode 'emacs)
  (evil-set-initial-state 'dashboard-mode 'emacs)
  (evil-set-initial-state 'magit-status-mode 'emacs))

Shift-u for redo

(with-eval-after-load 'evil-maps
  (define-key evil-normal-state-map (kbd "U") 'undo-tree-redo))


Required for elpy (I think). I will configure this more later if needed.

(use-package flycheck



A great completion frontend.

(use-package ivy
  (ivy-mode 1))


Make ESC always quit prompts.

(global-set-key (kbd "<escape>") 'keyboard-escape-quit)

Large files

Improve performance for large files

(global-so-long-mode 1)



(use-package smartparens
  (smartparens-global-mode 1)
  (show-smartparens-global-mode 1))


undo-tree: kind of iffy, doesn't really work well with evil either. Once Emacs 28 rolls out, replace it with the native undo-redo?

(use-package undo-tree

UTF-8 and Line Endings

(setq-default buffer-file-coding-system 'utf-8-unix)
(setq-default default-buffer-file-coding-system 'utf-8-unix)
(set-default-coding-systems 'utf-8-unix)
(prefer-coding-system 'utf-8-unix)


(use-package yasnippet
  (yas-global-mode 1))

These are the yasnippet-snippets.

(use-package yasnippet-snippets
  :after yasnippet)

Buffer/ window management

Confirm before quitting

(setq confirm-kill-emacs 'yes-or-no-p)


(use-package ace-window
  :bind (("M-o" . 'ace-window)
         ("M-O" . 'ace-delete-window)))

tab bar mode


Add some additional keybindings.

(define-key tab-prefix-map (kbd "n") #'tab-next)
(define-key tab-prefix-map (kbd "p") #'tab-previous)
(define-key tab-prefix-map (kbd "t") #'tab-new)
(define-key tab-prefix-map (kbd "x") #'tab-close)
(define-key tab-prefix-map (kbd "X") #'tab-close-other)
(define-key tab-prefix-map (kbd "C-x") #'tab-bar-undo-close-tab)

winner mode

(winner-mode 1)

Language Configuration


(use-package json-mode)



(use-package markdown-mode
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode))
  (setq markdown-command "multimarkdown"))


Org mode is for keeping notes, maintaining to-do lists, planning projects, authoring documents, computational notebooks, literate programming and more — in a fast and effective plain text system.


Org-mode is the reason I found and use Emacs! While I don't GTD or do that much task management with it anymore, it is still super useful.

(use-package org
  :straight nil
  (setq org-directory "~/org/")
  (add-to-list 'org-modules 'org-habit)
  (add-hook 'org-mode-hook 'org-indent-mode)
  (add-hook 'org-mode-hook 'visual-line-mode)
  (add-hook 'org-mode-hook 'toc-org-mode)
  (calendar-latitude 43.7682)
  (calendar-longitude -79.4126)
  (org-cycle-separator-lines 1)
  (org-columns-set-default-format "80ITM(Task) %10Effort(Effort){:} %10CLOCKSUM(Timed)")
  (org-enforce-todo-dependencies t)
  (org-global-properties (quote (("Effort_ALL" . "0:05 0:10 0:15 0:20
  0:30 0:45 1:00 1:30 2:00 4:00 6:00 8:00"))))
  (org-log-into-drawer "LOGBOOK")
  (org-clock-into-drawer "CLOCKING")
  (org-treat-S-cursor-todo-selection-as-state-change nil)
  (org-log-done 'time)
  (org-log-reschedule 'note)
  (org-log-redeadline 'note)
  (org-habit-show-habits-only-for-today t))

What was all of that?


Caution: I don't use org-agenda that much anymore.

(use-package org-agenda
  :after org
  :straight nil
  :bind (("C-c a" . org-agenda))
  (org-agenda-window-setup 'other-window)
  (org-agenda-restore-windows-after-quit 't)
  (org-agenda-dim-blocked-tasks t)
  (org-agenda-span 4)
  (org-agenda-start-on-weekday nil)
  (org-agenda-start-day "-1d")
  ;(org-agenda-files (list
  ;                   "~/org/privaat.org"))
  (org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)
  (org-agenda-skip-deadline-if-done nil)
  (org-agenda-skip-scheduled-if-done nil))


(use-package org-capture
  :after org
  :straight nil
  :bind (("C-c c" . org-capture))
  (org-default-notes-file "~/org/refile.org")
   `(("t" "Task" entry (file, "~/org/refile.org")
      "* TODO %^{Task}\n:PROPERTIES:\n- Added: %U\n:END:"
      :empty-lines 1 :immediate-finish t :clock-resume :kill-buffer))))


(use-package toc-org
  (add-hook 'org-mode-hook 'toc-org-mode))


Super simple Python configuration - Emacs Python Development Environment

(use-package elpy

Code formatting: blacken depends on black, pip install black in most cases.

(use-package blacken
  :hook (python-mode . blacken-mode)
  (setq blacken-line-length '79))


(use-package yaml-mode
  :mode (("\\.yml\\'" . yaml-mode)
         ("\\.yaml\\'" . yaml-mode)))

(add-hook 'yaml-mode-hook
      '(lambda ()
        (define-key yaml-mode-map "\C-m" 'newline-and-indent)))

Package Configuration

Elfeed (RSS Reader)

Previously, I used Thunderbird as an RSS reader. This requires cURL, which comes pre-installed/ easy to install on most GNU/Linux distributions. Use scoop on Windows, scoop install curl.

(use-package elfeed
  :bind (:map elfeed-search-mode-map
              ("q" . bjm/elfeed-save-db-and-bury)
              ("w" . (lambda () (interactive) (elfeed-db-save)))
              ("m" . elfeed-toggle-star)
              ("b" . mz/elfeed-browse-url)
              ("B" . elfeed-search-browse-url))
  :bind (("C-x x" . bjm/elfeed-load-db-and-open))
  (setq elfeed-use-curl t)
  (setq elfeed-curl-max-connections 10)
  (setq elfeed-db-directory "~/Nextcloud/shared/elfeed-db/") ; customize this ofc
  (setq-default elfeed-search-filter "@3-months-ago +unread "))

Use elfeed-protocol to sync with Nextcloud news.

(use-package elfeed-protocol)


(use-package elfeed-goodies)

Some extra functions for even better usability

  (defun elfeed-mark-all-as-read ()
    "Mark the whole buffer as read."

  (defun bjm/elfeed-load-db-and-open ()
    "Wrapper to load the elfeed db from disk before opening"

  (defun bjm/elfeed-save-db-and-bury ()
    "Wrapper to save the elfeed db to disk before burying buffer"

;; https://github.com/zamansky/dot-emacs/blob/master/README.org
  (defun mz/elfeed-browse-url (&optional use-generic-p)
      "Visit the current entry in your browser using `browse-url'.
    If there is a prefix argument, visit the current entry in the
    browser defined by `browse-url-generic-program'."
      (interactive "P")
      (let ((entries (elfeed-search-selected)))
        (cl-loop for entry in entries
                 do (if use-generic-p
                        (browse-url-generic (elfeed-entry-link entry))
                      (browse-url (elfeed-entry-link entry))))
        (mapc #'elfeed-search-update-entry entries)
        (unless (or elfeed-search-remain-on-entry (use-region-p))


elpher is a gopher and gemini client for Emacs.

(use-package elpher)

Project Management


I am still figuring out how to use it.

(use-package projectile
  ("C-x p" . projectile-command-map)
  (projectile-mode +1))


Sidebar file navigation. Fairly useful, but leave off by default.

(use-package treemacs
  (("<f8> <f8>" . treemacs)
   ("<f8> a" . treemacs-add-and-display-current-project)
   ("<f8> x" . treemacs-remove-project-from-workspace))
  (use-package treemacs-evil
  (use-package treemacs-projectile
  (treemacs-git-mode 'deferred)
  (setq treemacs-width 26)
  (setq treemacs-silent-refresh t)
  (setq treemacs-show-cursor t)
  (setq treemacs-fringe-indicator-mode 'always)


(use-package speed-type)


Focused writing environment.

(use-package olivetti
  :bind (("<f8> o" . olivetti-mode)))


Atomic Chrome

Use Emacs to input text from browser :)

(use-package atomic-chrome


Version Control

Follow symlinks!

(setq vc-follow-symlinks t)


Magit is seriously very useful, why didn't I start using it sooner!? Only thing is that operations take significantly longer on Windows (esp during interactive rebases), but that's to be expected and afaik there's no way around this :(


(use-package magit
  :bind (("C-x g" . magit-status)))


Major modes to easily edit git files (attributes, config, ignore).

(use-package git-modes)

User configuration

(let ((priv (concat user-emacs-directory "private.el")))
  (if (file-exists-p priv)
      (load-file priv)))


Copyright (C) 2020-2021 earnest ma

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at


Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Easily re-tangle and load again

;; Local Variables: ;; eval: (add-hook 'after-save-hook (lambda ()(if (y-or-n-p "Reload?")(load-file user-init-file))) nil t) ;; eval: (add-hook 'after-save-hook (lambda ()(if (y-or-n-p "Tangle?")(org-babel-tangle))) nil t) ;; End:

(defun earne/reload ()
  "Tangle config.org and reload configuration."
  (org-babel-load-file (concat user-emacs-directory "config.org"))
  (load-file user-init-file))


TODO: set the gc back to a reasonable level


(provide 'config)

Thanks for reading! I hope this helped you in some way :)