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

Overview

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

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!
emacs

Compatibility

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.

Startup

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)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/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)
(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").

https://tychoish.com/post/running-emacs/

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

Display

Frame Title

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

Theme

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

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

Font

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

Dashboard

emacs-dashboard!

(use-package dashboard
  :demand
  :if (< (length command-line-args) 2)
  :config
  (dashboard-setup-startup-hook)
  (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)

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

Disable line numbers for some modes.

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

Modeline

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

(use-package doom-modeline
  :demand
  :init
  (doom-modeline-mode 1))

Nyan-cat in modeline :)

(use-package nyan-mode
  :demand
  :config
  (nyan-mode))

git-gutter

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

Prompts

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)

Scrolling

Improves scrolling

https://github.com/daviwil/dotfiles/blob/de3cba59f22da4426d9a2792352ee5224841a476/.emacs.d/init.el#L194-L198

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)

which-key

Emacs package that displays available keybindings in popup

https://github.com/justbur/emacs-which-key

Super useful as I cannot remember all the keybindings.

(use-package which-key
  :demand
  :config
  (which-key-mode))

Editing

Autosaves, Backups, Reverting

super-save

(use-package super-save
  :demand
  :config
  (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

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.

https://editorconfig.org/

(use-package editorconfig
  :demand
  :config
  (editorconfig-mode 1))

Evil

vi-like keybindings! 😈

(use-package evil
  :demand
  :init
  (setq evil-want-integration t)
  (setq evil-want-keybinding nil)
  :config
  (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))

Flycheck

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

(use-package flycheck
  :init
  (global-flycheck-mode))

emacs-lisp

Ivy

A great completion frontend.

(use-package ivy
  :demand
  :config
  (ivy-mode 1))

Keyboard

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)

smartparens

smartparens

(use-package smartparens
  :demand
  :config
  (smartparens-global-mode 1)
  (show-smartparens-global-mode 1))

Undo-Tree

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
  :demand
  :config
  (global-undo-tree-mode))

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)

Yasnippet

(use-package yasnippet
  :demand
  :config
  (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)

ace-window

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

tab bar mode

(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

JSON

(use-package json-mode)

markdown-mode

markdown-mode

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

org-mode

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.

https://orgmode.org

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
  :demand
  :config
  (doom-themes-org-config)
  (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)
  :custom
  (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?

org-agenda

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

(use-package org-agenda
  :after org
  :straight nil
  :bind (("C-c a" . org-agenda))
  :custom
  (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))

org-capture

(use-package org-capture
  :after org
  :straight nil
  :bind (("C-c c" . org-capture))
  :custom
  (org-default-notes-file "~/org/refile.org")
  (org-capture-templates
   `(("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))))

toc-org

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

Python

Super simple Python configuration - Emacs Python Development Environment

(use-package elpy
  :init
  (elpy-enable))

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

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

YAML

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

Goodies

(use-package elfeed-goodies)

Some extra functions for even better usability

  (defun elfeed-mark-all-as-read ()
    "Mark the whole buffer as read."
    (interactive)
    (mark-whole-buffer)
    (elfeed-search-untag-all-unread))

  (defun bjm/elfeed-load-db-and-open ()
    "Wrapper to load the elfeed db from disk before opening"
    (interactive)
    (elfeed-protocol-enable)
    (elfeed-db-load)
    (elfeed)
    (elfeed-goodies/setup)
    (elfeed-search-update--force))

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

;; 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))
        ;;(forward-line)
        )))

Gemini

elpher is a gopher and gemini client for Emacs.

(use-package elpher)

Project Management

Projectile

I am still figuring out how to use it.

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

Treemacs

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

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

Typing

(use-package speed-type)

Writing

Focused writing environment.

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

Useful

Atomic Chrome

Use Emacs to input text from browser :)

(use-package atomic-chrome
  :demand
  :config
  (atomic-chrome-start-server))

Games

Version Control

Follow symlinks!

(setq vc-follow-symlinks t)

Magit

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

https://magit.vc/manual/magit/Microsoft-Windows-Performance.html

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

git-modes

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

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

http://www.apache.org/licenses/LICENSE-2.0

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."
  (interactive)
  (org-babel-load-file (concat user-emacs-directory "config.org"))
  (load-file user-init-file))

Post-init

TODO: set the gc back to a reasonable level

end!

(provide 'config)

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