bearguns / bear-emacs

My personal Emacs configuration. Use at your own peril.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bearguns’ Emacs Configuration

Configuring Emacs can be as much of a task as using Emacs to do all the things we use Emacs for - writing books, publishing scientific papers, programming computers, developing games, creating web pages, etc. The temptation to spend more time fiddling with productivity tools than actually being productive is certainly high for Emacs users. However, the beauty of Emacs is its infinite configurability. Over time, Emacs becomes the perfect text editing tool for the person using it, and this document represents for me 3 years of Emacs use and configuration.

Many of the code blocks in this file were lifted directly from this excellent configuration from Andre Yorst.

Why Emacs?

I could spend hours discussing Emacs, what I use it for, and the various twists and turns of fate that led to me abandoning Atom, Spacemacs, Vim, and Visual Studio Code (in that order) before committing to working full-time in “vanilla” Emacs. (I apply air-quotes to “vanilla” because after all, I’ve got plenty of external packages and customization in here to make Emacs behave the way I want it to).

I won’t spend a lot of this document recounting the many ways I’ve personally found Emacs to be the best software for my daily needs, but if you simply must hear me expound a bit more about it, let me refer you to this clip on Twitch.tv where I answer a viewer’s question about my use of Emacs. That said, here are a few bullet points on why I prefer Emacs over the other popular choices available for editing text:

  • Emacs is old. I see this as a tremendous benefit, as it means that Emacs itself comes with a very large community of users, many of whom have been using Emacs for ~30 years!
  • Emacs is itself mostly just a pile of Lisp code. Sure, Lisp isn’t the most popular language, but Emacs Lisp is easy to use and well-documented, which means that extending or modifying Emacs is as easy as learning and writing a few lines of Emacs Lisp.
  • Emacs isn’t built on Electron. Don’t get me wrong, there are in fact some apps built in the Electron framework that aren’t unstable, insecure, resource-hungry piles of garbage. The excellent Hyper terminal emulator is a great example. However, as a web developer I already spend enough of my day with numerous Chrome and Chrome-derivative browser tabs consuming my computers RAM and CPU cycles. Emacs is, comparitively, extremely lightweight, fast, and efficient on all kinds of computer hardware.
  • Emacs plugins (more commonly referred to in the Emacs community as “packages”) have a single, common convention, and they’re all written in Emacs lisp (with the exception of packages that also require additional system software, like an Emacs front-end for grep as an example). Contrast this against Vim (another excellent, fast, efficient text editor that isn’t built with Electron!), where plugins might be written in (and therefore require you to manage versions of) Python…or is it Python 3? Or, Lua…or VimL…hopefully you get the picture. Installing Vim plugins can require all sorts of other dependencies which may or may not be installed on your system - or worse, they may require versions of those dependencies that conflict with what you want to have installed on your system. Emacs packages are, for better or worse, simply bundles of Emacs Lisp code. This consistency and “dog-fooding” is to me, a big win.

Using This Configuration

If you want to use this configuration as a baseline for your own Emacs setup, follow the steps below:

  • First, fork and clone this repo onto your system.

$ git clone https://github.com/bearguns/bear-emacs.git

  • Second, create the directory .emacs.d/ in your home directory.

$ mkdir ~/.emacs.d

  • Third, open config.org inside of the cloned repo.

$ emacs ~/bear-emacs/config.org

  • Finally, run the command org-babel-tangle inside of config.el. The quickest way is to press the key combination C-c C-v t.

Configuring Emacs with Org Mode

Org mode is an incredible package for managing just about everything life throws at you in easy, simple, plain text. But of course, nothing’s ever as simple as it seems with Emacs, and so it is with Org mode. Besides the capability for things like pomodoro timers, GTD project management, and rich web publishing (to name a few), Org mode is capable of understanding and executing blocks of source code.

This has far-reaching implications on its own (imagine writing a book on Python programming in Org mode, with inline source code blocks that can run and print their output right there inline with the rest of your writing!), but it means that I can embrace a bit of Literate Programming as I configure Emacs for my use. This benefits you, the reader who may or may not be new to Emacs, as you can have me (a slightly more experienced user) explain in plain language what all these lines of Emacs Lisp are doing. It also benefits me, the author, as it forces me to express in plain language what these lines of code are doing - no more blind copy/pasting! It means I’m learning just as much in writing this configuration document as you are in reading it.

About this File

I’ve alluded to it above, but hard as it may be to believe, the whole of my Emacs configuration is contained with in this .org document. Throughout this document, you will see blocks of Emacs Lisp source code. Through a function, org-babel-tangle, these blocks are extracted to the file init.el, which is the file Emacs uses on startup to configure itself. This means that the source code blocks you see in this file aren’t merely examples - they’re the real deal, and they’ll really configure my Emacs installation for me.

Configuration Init

Because of a custom function (defined further on in this document), every time I save this file, the source code contained here will be compiled into my init.el file. Feel free to jump to the parts of this configuration that interest you the most!

To begin, we will set the front matter of our init.el per the recommended conventions of Emacs Lisp:

;;; init.el --- Emacs configuration file -*- lexical-binding: t; -*-
;;;
;;; Commentary:
;;; Emacs configuration by Sean Brage.
;;; This file was auto-generated by `org-babel-tangle`.
;;; Don't modify this file directly - all changes should be applied in .emacs.d/config.org
;;;
;;; Code:

From here on, I’ll be breaking out the configuration into different sections for easy reading and skimming.

Startup

There a number of changes we can make early on in our config to make sure Emacs starts up quickly. Slow startup is a common issue for Emacs users, but it’s not too difficult to keep our startups snappy.

Custom packages in load-path

Sometimes, a package or function isn’t available on ELPA, MELPA, etc. These files can be loaded in by adding them to Emacs’ load-path, which in my case is ~/.emacs.d/lisp.

(add-to-list 'load-path "~/.emacs.d/lisp/")

package.el initialization

We can wait to initialize package.el until we actually need it. That’s a big win for startup times!

(setq package-enable-at-startup nil)
(require 'package)

Melpa is, from what I can gather, the most popular package repository for Emacs packages. Almost every package I use in my Emacs configuration is available on Melpa, so this block tells the Emacs package manager to point to Melpa for installing packages.

I also want to make sure that I’m installing from the stable branch of MELPA, and that Emacs will prioritize MELPA over the other options I list below.

Emacs 26.2 ELPA Bug

There seems to be a bug in v26.2 that makes elpa requests fail. If this block below resolves the issue, then I know I have the bug and I need to wait for 26.3 for resolution.

(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/") t)
(add-to-list 'package-archives '("marmalade" . "https://marmalade-repo.org/packages/") t)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)

Note for Windows users: change the above https to http (Thanks for the tip, Andre!).

Now we’re setup and ready to install packages later on.

Use Package

Use Package is an excellent way to make installing and configuring packages in Emacs easy and declarative. I use it liberally, so it needs to be configured first.

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(require 'use-package)
(require 'use-package-ensure)
(setq use-package-always-ensure t)

Auto Update Packages

This block will check for package updates every 14 days, and remove old versions to avoid bloating my .elpa directory.

(use-package auto-package-update
  :config
  (setq auto-package-update-delete-old-versions t)
  (setq auto-package-update-hide-results t)
  (setq auto-package-update-interval 14)
  (auto-package-update-maybe))

Emacs Defaults

While there are packages like better-defaults that can “magically” make your Emacs more better, I prefer to have explicit magic in my code, rather than behind-the-scenes magic. This isn’t a slight on the better-defaults package, it’s excellent especially for new users, and I used it for a long, long time.

Alright, let’s get configuring. First, don’t ring my computer’s bell.

(setq ring-bell-function 'ignore)

Next, let’s not create backup files and lockfiles all over the place. Personally, I don’t use them and I don’t like when they accidentally get into version control.

(setq backup-by-copying t
      create-lockfiles nil
      backup-directory-alist '(("." . "~/.cache/emacs-backups"))
      auto-save-file-name-transforms '((".*" "~/.cache/emacs-backups" t)))

By default, Emacs will ask you to enter “yes” or “no” instead of “y” or “n”, but we can fix that.

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

Emacs will by default add “custom” items to your init.el, but I don’t really care for that.

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file :noerror)

Additionally, there are disabled commands in Emacs that can also modify init.el. Again, I want to be the one modifying my init file!

(defadvice en/disable-command (around put-in-custom-file activate)
    "Put declarations in `custom-file'."
    (let ((user-init-file custom-file))
    ad-do-it))

Emacs has a nice history feature, but it isn’t preserved between sessions by default.

(savehist-mode 1)

It’s helpful to see the current line and column number, especially for tracking down errors reported by the console, etc. These built-in modes are perfect for just that, and will put a non-intrusive display in the modeline.

(line-number-mode)
(column-number-mode)

Emacs version 26 and above provides built-in (and quite excellent) modes for automatically pairing delimiters and auto-indenting code. This reduces the number of external packages we need to do work!

(electric-pair-mode 1)
(electric-indent-mode 1)
(show-paren-mode 1)

Tab characters are the devil.

(setq-default indent-tabs-mode nil)

Emacs also has a great feature for quickly grepping through your open buffers, but it’s not the default behavior you get when pressing C-x C-b. Let’s change that.

(defalias 'list-buffers 'ibuffer)

The Emacs UI

There are things about the way Emacs looks by default that I’m not crazy about. I also don’t really like any of the built-in themes. They’re all a little hard for me to read, but YMMV and you may find a built-in theme that you enjoy, in which case more power to you! I’m all about having fewer things to fiddle with.

UI Defaults

Emacs launches with a splash screen by default. I never use anything on this screen, so we will supress it. This means Emacs will open in the *scratch* buffer by default, which is great! *scratch* is awesome for writing the day’s todo list, or some notes about what you’re working on. But it would be even better if we started in an Org mode buffer! We’ll take care of all this here:

(setq initial-major-mode 'org-mode
      initial-scratch-message "")

Emacs also has a toolbar, menu bar (in GUI mode), and tooltips. Again, these add to the visual noise, and I want my Emacs to look as clean as possible so I can focus more. Newer users may want to keep the menu bar and toolbar around, since they contain a set of icons and menus you might be more familiar with.

(tooltip-mode -1)
(menu-bar-mode -1)
(fset 'menu-bar-open nil)

(when window-system
    (scroll-bar-mode -1)
    (tool-bar-mode -1))

I want Emacs to start maximized

(add-to-list 'default-frame-alist '(fullscreen . maximized))

I like to configure the cursor (the visual display of “point”) a little bit so it’s easier to find. I’ll also have the cursor expand when over a tab character so I can easily see them and replace them with spaces.

;; super-highlight tab characters so they can be murdered
(setq-default x-stretch-cursor t
      cursor-in-non-selected-windows nil)

Fonts

;; on most systems with a ~1080p display, 140 height is big and readable.
(cond ((string-equal system-type "gnu/linux") (set-face-attribute 'default nil :font "Inconsolata" :height 130))
        ((string-equal system-type "darwin") (set-face-attribute 'default nil :font "Inconsolata" :height 140)))

Color Themes

I change themes a lot. I’m ok with myself.

(use-package chocolate-theme
  :ensure t
  :config (load-theme 'chocolate t))

The Modeline

The Emacs modeline is a helpful feature that appears along the bottom of every window. It gives you information about the current buffer, enabled modes, etc. It can be extended with packages like doom-modeline to provide better Git status information, icons, and the like.

(use-package doom-modeline
      :ensure t
      :config
      (setq doom-modeline-project-detection 'project)
      (setq doom-modeline-buffer-file-name-style 'relative-from-project)
      (setq doom-modeline-height 15)
      (setq doom-modeline-width 3)
      (setq doom-modeline-icon (display-graphic-p))
      :hook (after-init . doom-modeline-mode))

Rainbow Delimiters

Rainbow delimiters mode is helpful in all kinds of programming modes. It colors parentheses, braces, brackets, etc. so that you can visually grok where blocks of code end, and where you are scope-wise.

(use-package rainbow-delimiters
  :config (add-hook 'prog-mode-hook #'rainbow-delimiters-mode))

Nyan Mode

Despite all my talk about visual noise and focus, I just can’t resist putting Nyan Cat in my modeline.

(use-package nyan-mode
  :config
  (setq-default nyan-animate-nyancat t)
  (setq-default nyan-wavy-trail t)
  (nyan-start-animation)
  :init
  (nyan-mode))

Ace Window

Splitting the Emacs frame into multiple windows is easy, fast, and a great productivity booster. The built-in command M-x o is great for quickly moving between two windows, but once you have more than that, it’s a pain. Getting around more than two windows requires multiple invocations of the same command, and is a frustrating time-waster.

Ace Window remedies this by providing a simple interface for jumping directly to the window you want. Here I’ll install the package and bind the ace-window command to C-x o, which is the default command to move between windows. Since ace-window will by default call the built-in other-window command when there are only two windows, I’m totally fine with overriding the default key binding for this command.

(use-package ace-window
    :config
    (global-set-key (kbd "C-x o") 'ace-window))

YA Snippet

Snippets are a way to provide instant text-expansion to Emacs. For example, when writing the source code blocks in this config document, I can simply type scbel and then press TAB to get an empty Org mode source code block, properly formatted, with the cursor set at the proper location to start writing code. This also has applications outside of programming, so I’m keeping YASnippet configuration in its own heading.

Snippets are located in ~~/.emacs.d/snippets/~. I recommend making your own snippets directory and symlinking it into ~~/.emacs.d/~.

(use-package yasnippet
  :ensure t
  :defer t
  :init (yas-global-mode 1))

Programming

Company Mode

Company seems to be the far-and-away favorite for auto-completion in Emacs buffers. I’m loading the package and configuring it a bit here, but for major modes that use Company, I’ll leave extra Company configuration to those modes.

(use-package company
  :config
  ;; get those suggestions up quickly
  (setq company-idle-delay 0.1)
  (setq company-minimum-prefix-length 3)
  :init
  ;; use company in all programming modes
  (add-hook 'prog-mode-hook 'company-mode))

Projectile

Projectile makes navigation between and within code repositories (any directory being tracked with VCS) fairly painless, and cuts down on some common tasks.

(use-package projectile
  :ensure t
  :config
  (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
  (setq projectile-enable-caching t)
  (setq projectile-completion-system 'ivy)
  (setq projectile-switch-project-action #'projectile-dired)
  (projectile-mode +1))

Counsel

Counsel is a suite of packages that provide completion to various Emacs functions. This gives me a nice menu of options when searching for files, commands, etc.

(use-package counsel
  :ensure t
  :init
  (ivy-mode 1)
  (setq ivy-use-virtual-buffers t)
  (setq ivy-count-format "(%d/%d) ")
  (global-set-key (kbd "C-c s") 'swiper)
  (global-set-key (kbd "C-c i") 'ivy-resume)
  (global-set-key (kbd "C-c k") 'counsel-rg)
  (global-set-key (kbd "C-c g") 'counsel-git)
  (global-set-key (kbd "C-x l") 'counsel-locate)
  (global-set-key (kbd "C-S-o") 'counsel-rhythmbox)
  (global-set-key (kbd "M-x") 'counsel-M-x)
  (global-set-key (kbd "C-x C-f") 'counsel-find-file))

Magit

Magit is the greatest software package ever created. It makes working in Git repositories painless, fast, and easy. I don’t want to do my job without it!

(use-package magit
  :config
  (setq magit-refresh-status-buffer nil)
  :init
  (global-set-key (kbd "C-x g") 'magit-status))

Editorconfig

Editorconfig is a great way to make sure that everyone working on a project has the same basic text editor settings for things like indentation, new lines at end-of-file, and other nitpicky things. This also helps me make sure that in my own projects, I get consistent behavior on my laptop and desktop.

(use-package editorconfig
  :ensure t
  :config
  (editorconfig-mode 1))

Add Node Modules to Path

This gem of a package allows Emacs to use things like ESLint without requiring you to install it globally. If you’re working in a project with a node_modules/ directory, Emacs will then use the project-specific version of those binaries.

(use-package add-node-modules-path
  :config
  (add-hook 'web-mode-hook 'add-node-modules-path)
  (add-hook 'js2-mode-hook 'add-node-modules-path))

Flycheck

This provides excellent error-checking in different major modes (for example, listing and jumping to ESLint errors when working in a JavaScript project). Like Company, I’ll configure different flycheck-checkers in the respective major mode configuration

(use-package flycheck
  :ensure t
  :config
  (setq flycheck-check-syntax-automatically '(mode-enabled idle-change))
  (setq flycheck-idle-change-delay 2)
  (setq flycheck-python-flake8-executable "python3")
  (setq-default flycheck-disabled-checkers
  (append flycheck-disabled-checkers
    '(javascript-jshint)))
  (add-to-list 'display-buffer-alist
    `(,(rx bos "*Flycheck errors*" eos)
    (display-buffer-reuse-window)
    (display-buffer-in-side-window)
    (side          . bottom)
    (window-height . 0.25))))
  :init
  (add-hook 'prog-mode-hook 'flycheck-mode)

Emmet

As a web developer, Emmet is invaluable for quickly expanding HTML elements inline. It takes input[name="my-input" v-model="username" class="form__input"] and expands to proper HTML markup.

(use-package emmet-mode)

Web Mode

I use web mode when editing HTML files, including Jinja templates (for Flask apps), Django templates, and the like. I do a little bit of customization to keep the syntax highlighting to a minimum, and to use Emacs’ built-in electric-pairs-mode.

(use-package web-mode
  :ensure t
  :config
  (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
  ;; enable web-mode for Svelte files
  (add-to-list 'auto-mode-alist '("\\.svelte?\\'" . web-mode))
  ;; enable web-mode for VueJS files
  (add-to-list 'auto-mode-alist '("\\.vue?\\'" . web-mode))
  ;; enable JS features in web-mode when working in Vue files
  (defun vue-in-web-mode ()
    (when (and (stringp buffer-file-name)
               (string-match "\\.vue\\'" buffer-file-name))
          (flycheck-mode)))
          
  (setq web-mode-enable-current-element-highlight t)
  (setq web-mode-enable-auto-pairing nil)
  (setq web-mode-enable-auto-closing t)
  (setq web-mode-code-indent-offset 4)
  (setq-default web-mode-builtin-face nil)
  (setq-default web-mode-keyword-face nil)
  (setq web-mode-ac-sources-alist
      '(("css" . (ac-source-css-property))
        ("html" . (ac-source-words-in-buffer ac-source-abbrev))))
  :init
  (add-hook 'web-mode-hook 'emmet-mode)
  (add-hook 'web-mode-hook 'electric-pair-mode)
  (defvar web-mode-electric-pairs '((?\' . ?\')))
  (defun web-mode-add-electric-pairs ()
    (setq-local electric-pair-pairs (append electric-pair-pairs web-mode-electric-pairs))
    (setq-local electric-pair-text-pairs electric-pair-pairs))
  (add-hook 'web-mode-hook 'web-mode-add-electric-pairs))
  (add-hook 'web-mode-hook 'vue-in-web-mode)

Prettier

For better or worse, a large population of the JavaScript community has embraced Prettier to…well, supposedly make code “pretty”. I personally hate most of the decisions prettier makes, but we use it at my company so I’m just embracing it with gritted teeth.

(use-package prettier-js
  :init
  (add-hook 'js2-mode-hook 'prettier-js-mode))

JavaScript Mode

(use-package js2-mode
  :init
  (flycheck-add-mode 'javascript-eslint 'js2-mode)
  (add-to-list 'auto-mode-alist '("\\.js\\'\\|\\.json\\'" . js2-mode)))

Python

So far, the most helpful package I’ve found for working in Python is Elpy. Pipenv also adds support for virtualenvs, which is really useful.

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

(use-package pipenv
  :hook (python-mode . pipenv-mode)
  :init
  (setq
   pipenv-projectile-after-switch-function
   #'pipenv-projectile-after-switch-extended))

Restclient

Restclient.el is an amazing package for making HTTP requests directly from Emacs. With a plain-text .http file, you can start hitting an API or webservice directly and seeing the output in a new buffer. If you’ve ever used Postman to query a RESTful API, then you should try out restclient.el.

(use-package restclient)

Exec Path from Shell

This package causes Emacs to read my system’s $PATH, so packages that rely on things like ripgrep will “just work”.

(use-package exec-path-from-shell
  :ensure t
  :config
  (exec-path-from-shell-initialize))

Expand Region

Expand Region is a package for intelligently selecting text in regions. For example, if I have a long statement between parentheses, subsequent presses of my expand-region command will select the word under point, and then everything within the parentheses, and finally everything, including the parentheses.

(use-package expand-region
    :ensure t
    :config (global-set-key (kbd "C-;") 'er/expand-region))

Org Mode

Org mode is a big enough part of my daily life to warrant its own heading here. I use Org for task management with GTD. I use it to write songs and stories. I use it to write tutorials, and I even use it to write blogs! It’s also the engine driving my entire Emacs configuration.

Org Defaults

I like to have some global keymaps available for capturing items into Org documents.

(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-cb" 'org-switchb)

Because I don’t install Org with use-package (it’s already installed in modern versions of Emacs), configuring Org mode looks a little different.

(add-hook 'org-mode-hook
          (lambda()
            (flyspell-mode)
            (setq org-startup-with-inline-images t
                  org-hide-leading-stars nil
                  revert-without-query '(".*\.pdf"))
            (auto-fill-mode)))

I want fontified source code blocks.

(setq org-src-fontify-natively t)

I want to use Flycheck when writing inline source code, but I don’t want certain warnings when writing Emacs Lisp blocks.

(add-hook 'emacs-lisp-mode-hook 'flycheck-mode)
(defvar flycheck-disabled-checkers)

(defun my/disable-flycheck-in-org-src-block ()
  "Disable checkdoc in emacs-lisp buffers."
  (setq-local flycheck-disabled-checkers '(emacs-lisp-checkdoc)))

(add-hook 'org-src-mode-hook 'my/disable-flycheck-in-org-src-block)

Concluding

Hopefully you like what you’ve seen, and you feel more empowered to get the most out of using Emacs. As one final step, let’s make our init file as proper as can be:

(provide 'init)
;;; init.el ends here

Happy hacking!

About

My personal Emacs configuration. Use at your own peril.