You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
earnest ma daf41916c1
6 months ago
publish ci: Automate HTML publishing 6 months ago
straight/versions straight: Update lockfile 6 months ago
.drone.yml ci: Add and load htmlize when exporting, update + document 6 months ago
.editorconfig format: Add .editorconfig file 8 months ago
.gitignore straight: Update lockfile 6 months ago
LICENSE license: Fix 9 months ago Notice 6 months ago

My personal Emacs configuration


Note that this will no longer be maintained, as I am shifting over to a non-literate configuration that will replace this repo soon

Tagged HTML Version - (latest)

Commit/ version .


  • Font: Fira Code

  • Icons: M-x all-the-icons-install-fonts


I use Emacs 27.1, although this should mostly work on Emacs 26.3+. If you would like to try this out, run the following:

# if you have an existing config, back it up and move it out of the way
#     you may need to substitute ~/.config/emacs with ~/.emacs.d or ~/.emacs
mv ~/.config/emacs ~/.config/emacs.bak
# clone
git clone ~/.config/emacs --recurse-submodules
# tangle and run!
emacs --batch --eval "(require 'org)" --eval '(org-babel-tangle-file "~/.config/emacs/")' && emacs


This project is licensed under the Apache 2.0 License. You may discuss/ send patches (mailing list address) or file issues/ pull requests here.



The following code blocks are tangled into early-init.el, which is loaded before the main init.el is.

Garbage collection

Defer garbage collection until later, speeding up initial startup.

  (setq gc-cons-threshold most-positive-fixnum
        gc-cons-percentage 0.6)

When done startup, set everything back to "normal"

  (add-hook 'emacs-startup-hook
            (lambda ()
              (setq gc-cons-threshold 16777216
                    gc-cons-percentage 0.1)))

Display settings

Turn off the menu and toolbar.

; (if (not (eq system-type 'darwin)) (menu-bar-mode -1))
(tool-bar-mode -1)

Taken from Doom Emacs: resize frame/ fonts to halve startup times.

(setq frame-inhibit-implied-resize t)

Do not use/ retain custom.el file (from)

  (setq custom-file (expand-file-name
                     (format "custom-%d-%d.el" (emacs-pid) (random))

Define some variables

  (setq calendar-latitude 43.7682)
  (setq calendar-longitude -79.4126)

Package management

Do not enable the default package.el (why in the next block)

  (setq package-enable-at-startup nil)

I am currently using straight.el for package management (lol). Here is the code provided to bootstrap and load it:

  (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))

Shallow clone to save space

  (setq straight-vc-git-default-clone-depth 15)

Use the use-package integration by default and make sure it's installed.

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

Load the helper package for additional contributed commands.

  (require 'straight-x)


Always defer unless :demand is specified.

  (setq user-package-always-defer t)

Keep ~/.config/emacs organized

  (use-package no-littering

Display Customization


  (use-package dashboard
    :if (< (length command-line-args) 2)
    (setq dashboard-banner-logo-title (concat "Welcome to earnemacs!"))
    (setq dashboard-startup-banner 'logo)
    (setq dashboard-set-footer nil)
    (setq dashboard-items
          '((recents . 5)
            (bookmarks . 5)
            (agenda . 5)
            (projects . 5))))

Prevent the usual GNU Emacs startup screen from appearing

  (setq inhibit-startup-screen t)


  (add-to-list 'default-frame-alist '(font . "Fira Code 11.5"))

Frame Title

Daemon identification

  (if (equal (daemonp) nil)
    (setq tychoish-emacs-identifier "indiv")
  (setq tychoish-emacs-identifier (daemonp)))
  (setq frame-title-format (list "%b %* " tychoish-emacs-identifier "@" system-name))


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



  (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)


I've always loved the collection of doom-themes.

  (use-package doom-themes

Automatically switch themes based on time of day

  (use-package theme-changer
    (change-theme 'doom-dracula 'doom-Iosvkem))

General Configuration

Completion: company and ivy

  (use-package company
    :init (global-company-mode)
    (use-package company-quickhelp
    (use-package company-statistics
    (setq company-tooltip-limit 20)
    (setq company-idle-delay 0)
    (setq company-echo-delay 0))
  (use-package ivy
    (use-package counsel
    (ivy-mode 1))

Improve performance for large files

  (global-so-long-mode 1)

Useful functions

  (use-package crux)

Misc Functions

Interactive paste
  (defun +doom/yank-pop ()
    "Interactively select what text to insert from the kill ring."
     (cond ((fboundp 'counsel-yank-pop) #'counsel-yank-pop)
           ((error "No kill-ring search backend available.")))))


y/n instead of typing yes/no.

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

Inhibit startup echo area message

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


from (2 lines, hold shift to scroll 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)

UTF-8 and line endings

Needed for Windows

  (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)

Windows 10-specific adjustments

Note: an MSYS2-compiled Emacs should show up as windows-nt. This requires Git Bash (available with Git for Windows). Detection:

  (defconst sys-windows (memq system-type '(windows-nt cygwin ms-dos)))

Set HOME if it hasn't been set manually

  (when (and sys-windows (null (getenv-internal "HOME")))
    (setenv "HOME" (getenv "USERPROFILE"))
    (setq abbreviated-home-dir nil))

Set Git Bash as the default shell

  (defun earne/sys-windows-git-bash-shell ()
      "Set Git Bash as shell"
    (setq explicit-shell-file-name
          "c:/Program Files/Git/bin/bash.exe")
    (setq shell-file-name explicit-shell-file-name)
    (add-to-list 'exec-path "c:/Program Files/Git/bin"))

  (if sys-windows

Use a different font size on Windows

  (if sys-windows
      (add-to-list 'default-frame-alist '(font . "Fira Code 11")))

Open the current working directory in Explorer.exe

  (if sys-windows
      (defun earne/open-dir-in-explorer ()
        "Open the current working directory in Windows Explorer"
        (shell-command "explorer.exe .")))

WSL2-specific adjustments

  (defun earne/wsl2-adjustments ()
    "Adjustments only for Emacs running under WSL2"
    ;; Adjust font
    (add-to-list 'default-frame-alist '(font . "Fira Code 11"))
    ;; xdg-open
    (defun wsl/browse-url-xdg-open (url &optional ignored)
      (interactive (browse-url-interactive-arg "URL: "))
      (shell-command-to-string (concat "explorer.exe " url)))
    (advice-add #'browse-url-xdg-open :override #'wsl/browse-url-xdg-open)
    ;; start server-mode if not already
    (if (equal (daemonp) nil)

The environment variable WSLEMACS is set in both WSL2's .bashrc and in the WSL2 script used to launch Emacs from a runemacs.vbs script.

  (if (getenv "WSLEMACS")

Native compilation

feature/native-comp branch

  (defun earne/native-comp-adjustments ()
    "Extra options or adjustments when using Emacs build with native compilation"
    (setq comp-async-report-warnings-errors nil))
  (if (and (fboundp 'native-comp-available-p)

Wait until idle to GC

  (use-package gcmh



Make escape always quit prompts.

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


  (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)
    (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 'magit-status-mode 'emacs)
    (evil-set-initial-state 'zpresent-mode 'emacs))


I'm now experimenting with using a few SPC keybindings (from my time with Doom Emacs).

  (use-package general
     :states '(normal emacs insert visual)
     :prefix "SPC"
     :non-normal-prefix "C-SPC"
     "SPC" 'projectile-find-file
     "," 'projectile-switch-to-buffer
     "." 'find-file
     "/" 'ivy-switch-buffer

     ";" 'eval-expression
     ":" 'shell-command

     "b" '(:wk "Buffer")
     "bb" 'ivy-switch-buffer
     "be" 'eval-buffer
     "bk" 'kill-buffer
     "bK" 'kill-current-buffer
     "br" 'revert-buffer
     "bs" 'save-buffer
     "bS" 'save-some-buffers
     "bx" '(lambda () (interactive) (switch-to-buffer "*scratch*"))
     "bX" 'crux-create-scratch-buffer
     "bz" 'bury-buffer
     "bZ" 'unbury-buffer
     "b <left>" 'switch-to-next-buffer
     "b <right>" 'switch-to-prev-buffer

     "c" '(:wk "Code")

     "f" '(:wk "File")
     "ff" 'find-file
     "fr" 'crux-sudo-edit
     "fR" '(lambda () (interactive) (crux-sudo-edit ""))
     "fP" '(lambda () (interactive) (find-file (concat user-emacs-directory "")))
     "fs" 'save-buffer
     "fS" '(lambda () (interactive) (save-buffer ""))
     "fy" '(lambda () (interactive) (kill-new buffer-file-name))
     "fY" '(lambda () (interactive) (kill-new default-directory))

     "g" '(:wk "Git")
     "g." 'magit-dispatch
     "gb" 'magit-branch
     "gc" 'ghq
     "gf" 'magit-fetch
     "gF" 'magit-pull
     "gg" 'magit-status
     "gs" 'magit-stage-file
     "gS" 'magit-unstage-file
     "gm" 'magit-merge
     "gM" 'magit-remote
     "gP" 'magit-push
     "gr" 'magit-rebase
     "gz" 'magit-stash

     "h" '(:keymap help-map :wk "Help")

     "i" '(:wk "Insert")
     "ip" '+doom/yank-pop
     "is" 'yas-insert-snippet

     "m" (general-key "C-c")
     "p" '(:keymap projectile-command-map :package projectile :wk "Projectile")

     "o" '(:wk "Open")
     "oA" 'org-agenda
     "oC" 'org-capture
     "oe" 'eshell
     "of" 'make-frame-command
     "or" 'bjm/elfeed-load-db-and-open
     "op" 'treemacs
     "oP" 'zpresent
     "oS" 'shell

     "q" '(:wk "Quit/Reload")
     "qf" 'delete-frame
     "qK" 'save-buffers-kill-emacs
     "qq" 'save-buffers-kill-terminal
     "qQ" 'evil-quit-all-with-error-code
     "qr" '(lambda () (interactive) (org-babel-tangle-file "~/.config/emacs/") (load-file "~/.config/emacs/init.el"))

     "t" '(:wk "Toggle")
     "tf" 'toggle-frame-fullscreen
     "to" 'olivetti-mode
     "tr" 'toggle-read-only
     "tw" 'visual-line-mode

     "w" '(:wk "Window")
     "w=" 'balance-windows
     "w-" 'evil-window-decrease-height
     "w+" 'evil-window-increase-height
     "w<" 'evil-window-decrease-width
     "w>" 'evil-window-increase-width
     "w <left>" 'winner-undo
     "w <right>" 'winner-redo
     "wh" 'evil-window-left
     "wj" 'evil-window-down
     "wk" 'evil-window-top
     "wl" 'evil-window-right

     "wd" 'evil-window-delete
     "wn" 'evil-window-new
     "wo" 'ace-window
     "wO" 'ace-delete-window
     "wt" 'tear-off-window
     "wu" 'winner-undo
     "wU" 'winner-redo
     "ww" 'evil-window-next
     "wv" 'evil-window-vsplit
     "wX" 'ace-delete-other-windows)

    (general-def :keymaps 'projectile-command-map
    "A" 'projectile-add-known-project))


  (use-package which-key

Window management

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



  (use-package super-save
    (super-save-mode +1)
    (setq super-save-auto-save-when-idle t)
    (setq super-save-remote-files nil)
    (setq super-save-exclude '(".gpg")))

Modify some default autosave settings.

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

Revert files automatically when changed on disk.

  (global-auto-revert-mode 1)


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


  (use-package smartparens
    (smartparens-global-mode 1)
    (show-smartparens-global-mode 1))
  (use-package rainbow-delimiters
    :hook (prog-mode . rainbow-delimiters-mode))


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

Add snippets for yasnippet (none are included by default)

  (use-package yasnippet-snippets)

Org Mode

Main configuration

This still needs to be looked at a bit.

  (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)
    (setq-default org-indent-mode t)
    (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))


This still needs to be looked at a bit.

  (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/"))
    (org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)
    (org-agenda-skip-deadline-if-done nil)
    (org-agenda-skip-scheduled-if-done nil))


This still needs to be looked at a bit.

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

Table of contents for files

  (use-package toc-org
    :hook (org-mode . toc-org-mode))



  (use-package zpresent)


Source Code


  (use-package ghq)


Show added/ removed/ changed lines from git in the fringe.

  (use-package git-gutter
    (setq git-gutter:update-interval 2)


Extremely useful, but also slow on Windows (no way around it)

  (use-package magit


  (setq vc-follow-symlinks t)

Project Management

Projectile: <>

  (use-package projectile
    (projectile-mode +1)
    (when (file-directory-p "~/ghq")
      (setq projectile-project-search-path '("~/ghq"))))


  (use-package treemacs
    (use-package treemacs-evil :demand)
    (use-package treemacs-projectile :demand)
    (treemacs-git-mode 'deferred)
    ;(define-key treemacs-mode-map [mouse-1] #'treemacs-single-click-expand-action)
    (setq treemacs-follow-mode t)
    (setq treemacs-width 26)
    (setq treemacs-silent-refresh t)
    (setq treemacs-show-cursor t)
    (setq treemacs-fringe-indicator-mode 'always))



  (use-package git-modes)


  (use-package json-mode)


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


  (use-package elpy

Code formatting: blacken depends on black, pip3 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)))



  (use-package flycheck)


Device Management

Export an org-mode file to an SSH configuration file.

  (use-package ox-ssh)


Olivetti-mode: focused environment

  (use-package olivetti)



RSS reader. You need to have curl installed.

  (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))
    (setq elfeed-use-curl t)
    (setq elfeed-curl-max-connections 10)
    (setq-default elfeed-search-filter "@3-months-ago +unread "))

Some additional packages

  (use-package elfeed-protocol)
  (use-package elfeed-goodies
    (setq elfeed-goodies/feed-source-column-width 25))


    (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"

    (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))



  (use-package elpher)


  (use-package circe
    (setq circe-reduce-lurker-spam t))


  (use-package speed-type)

Final Notes


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.

Packages to keep an eye on


Should a file private.el exist, load it.

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