A beginner’s Emacs config

Zac Wood
8 min readJun 6, 2018

This article was originally posted on zacwood.me. For a better reading experience, check it out there!

DISCLAIMER: If you are brand new to Emacs, this post is probably not the best place to start. I would recommend first going through the offical Emacs tutorial (C-h t in Emacs) and watching some cool videos about Emacs on YouTube. Once you understand the basics and are ready to build your own Emacs config, this is the place to be.

Introduction

In the first entry to this series, I’ll be going over my config file. Hopefully, this will be useful to those of you just starting out with Emacs who would like something to base your configs off of. Getting started is by far the hardest part; all configuration is in Emacs Lisp which is really confusing if you’re not already familiar with Lisp like languages. I will try my best to explain what’s happening in my config, but I would very strongly recommend reading through the first few chapters of An Introduction to Programming in Emacs Lisp. It does a great job going over the basics of the language, and once you understand the language, your abilty to work fluently in Emacs will start rapidly accellerating!

Package

Starting off the config is the setup of Emacs package manager, which allows you to install and work with the endless packages that make Emacs great. The code itself is some good, old fashioned black magic; essentially, all it’s doing is configuring what URL Emacs should fetch packages from depending on your Emacs version and your system. Don’t let this discourage you; the rest will be much simpler to understand. This was taken directly from MELPA’s getting started guide, which is the unoffical Emacs package archive.

;;; Package config -- see https://melpa.org/#/getting-started
(require 'package)
(let* ((no-ssl (and (memq system-type '(windows-nt ms-dos))
(not (gnutls-available-p))))
(proto (if no-ssl "http" "https")))
;; Comment/uncomment these two lines to enable/disable MELPA and MELPA Stable as desired
(add-to-list 'package-archives (cons "melpa" (concat proto "://melpa.org/packages/")) t)
;;(add-to-list 'package-archives (cons "melpa-stable" (concat proto "://stable.melpa.org/packages/")) t)
(when (< emacs-major-version 24)
;; For important compatibility libraries like cl-lib
(add-to-list 'package-archives '("gnu" . (concat proto "://elpa.gnu.org/packages/")))))
(package-initialize)

use-package

use-package is a fantastic package that allows you to configure your packages in an easy, readable, and consistent way.

(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))

This code checks if use-package is already installed. If it isn't, it tells Emacs to refresh its list of packages and then to install use-package.

Manual load path

In the rare case a package I want to use isn’t on MELPA or GNU, the following code tells Emacs that it can find manually installed packages in the ~/.emacs.d/lisp directory. This is just convention – you can set this to anything.

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

Packages

exec-path-from-shell

Emacs on Mac doesn’t configure its PATH correctly. This is a simple package that configures it to be whatever your shell's PATH is set to, which is usually what you want.

(use-package exec-path-from-shell
:if (memq window-system '(mac ns x))
:ensure t
:config
(exec-path-from-shell-initialize))

This is the first time we’ve used use-package to configure a package. First, we tell use-package to load the package named exec-path-from-shell. We also specify that this package should only be loaded if the system is a Mac. :ensure t tells use-package to install the package if it isn't installed already, and the commands that follow :config are executed when the package is loaded.

Auto completion with company

company is a fantastic package that provides auto-completion in Emacs. Most programming languages provide what's called a "backend" for company, which provides the actual auto-completion data for company.

(use-package company
:ensure t
:init
(add-hook 'after-init-hook 'global-company-mode)
:config
(setq company-dabbrev-downcase 0)
(setq company-idle-delay 0.1)
(setq company-minimum-prefix-length 1)
(setq company-tooltip-align-annotations t))

Here is the first time we use a hook. Hooks are a fundamental part of Emacs; if you’re writing your own config, you will see them and use them everywhere. Hooks are how to tell Emacs to attach actions to eachother. For example, 'before-save-hook is called before everytime a save is requested. Here, we attach the global-company-mode function to after-init-hook, which ensures that company-mode will always be enabled.

In the :config, I set some variables to tweak company to behave to my liking. You can see what each of them do in your own Emacs by using the keys C-h v and typing the name of the variable. Learning how to get information in Emacs is a vital part of using the software; nearly every part of Emacs is documented in the editor itself!

Helm

Helm provides a great way to search through menus in Emacs such as the M-x screen, help screens, and more. In its own words, it is an "an Emacs framework for incremental completions and narrowing selections." It's a little hard to explain – the best way to understand it is to use it.

(use-package helm
:ensure t
:init
(require 'helm-config)
:config
(global-set-key (kbd "M-x") #'helm-M-x)
(global-set-key (kbd "C-x r b") #'helm-filtered-bookmarks)
(global-set-key (kbd "C-x C-f") #'helm-find-files)
(helm-mode 1))

My config sets some important key bindings to use Helm instead of the Emacs default.

Projectile

Projectile is one of my favorite Emacs pacakges, and was the reason I’ve kept coming back to Emacs. It, along with Helm, provide a great way to work with multiple projects alongside each other. Projectile automatically recognizes any directory containing a .git folder as a project, which will then show up in your projects menu. You can also search through all the files in the project, run tests, and so much more.

(use-package projectile
:ensure t
:config
(projectile-mode))

To use projectile with helm, the helm-projectile package has to be installed and enabled.

(use-package helm-projectile
:ensure t
:config
(helm-projectile-on))

Magit

magit is a fully featured git client in Emacs. I've always hated git GUIs; they've always slowed me down and allowed me to do less than the git CLI. magit is different. Once you start to use it and discover its power, it will make it really hard to go back to the command line. It's that good.

(use-package magit
:ensure t)

which-key

which-key is a simple package that helps you find different keybindings easily. Whenever you press a leader key, like C-x, a list of all the different keybindings pops up. This is especially useful if you're new to Emacs!

(use-package which-key
:ensure t
:config
(which-key-mode))

prettier-js

If you’re a Javascript developer, I’m sure you’re familiar with prettier. It's a great tool to format your Javascript code, and it also works with Typescript and JSX. There's a really popular VSCode extension, but there's also one for Emacs!

(use-package prettier-js
:ensure t
:config
(setq prettier-js-args '(
"--trailing-comma" "es5"
"--single-quote" "true"
"--print-width" "120"
"--tab-width" "4"
"--use-tabs" "false"
"--jsx-bracket-same-line" "false"
"--stylelint-integration" "true"
)))

smartparens

smartparens provides intelligent editing of anything that you normally have to type twice: quotes, parenthesees, curly braces, etc. I like to use its functionality everywhere.

(use-package smartparens
:ensure t
:init
(smartparens-global-mode))

Javascript developement

The following packages provide a good, modern Javascript development environment. There are a number of modes and packages that make it up: js2, rjsx, and tide. js2 is a Javascript editing mode that provides a lot more functionality than the built in javascript-mode. rjsx is a package that extends js2 to include support for JSX and React files. Since it's a superset of js2, I have all .js files set to just use rjsx mode.

Next is tide, which provides great auto-completion for Javascript and Typescript. All of these modes are set to use prettier-js-mode, which formats files on save.

(use-package js2-mode
:ensure t)

(use-package rjsx-mode
:ensure t
:mode(("\\.js\\'" . rjsx-mode)
("\\.jsx\\'" . rjsx-mode))
:init
(add-hook 'rjsx-mode-hook 'prettier-js-mode)
(add-hook 'rjsx-mode-hook 'tide-mode))

(use-package tide
:ensure t
:mode(("\\.ts\\'" . typescript-mode))
:init
(add-hook 'typescript-mode-hook 'tide-mode)
(add-hook 'typescript-mode-hook 'prettier-js-mode)
:config
(tide-setup)
(flycheck-mode +1)
(setq flycheck-check-syntax-automatically '(save-mode-enabled))
(eldoc-mode +1)
(tide-hl-identifier-mode +1)
(company-mode +1))

My config isn’t quitecomplete for Javascript development; there are still a few packages I’d like to add. It’s definitely workable however, and I’ve been using it daily for the past few weeks for developing in both Javascript and Typescript at work.

JSON

json-mode provides a good mode for viewing and editing .json files.

(use-package json-mode
:ensure t)

Theme

I’m a big fan of pretty much all of the themes from doom-emacs, a popular Emacs starter pack. All of it's themes are luckily available with the doom-themes package!

(use-package doom-themes
:ensure t
:preface (defvar region-fg nil) ; this prevents a weird bug with doom themes
:init (load-theme 'doom-one t))

General configuration

The following is a bunch of personal preference general configuration. The most important part is definitely the following line:

(setq mac-command-modifier 'meta) ; set cmd to meta

which sets the cmd key to act as meta on the mac. This is a much more comfortable binding than the default option key. The rest has comments explaining what each line does.

(global-set-key (kbd "C-\\") 'comment-or-uncomment-region) ; easy binding for commenting

(scroll-bar-mode 0) ; no scroll bar
(tool-bar-mode 0) ; no tool bar
(menu-bar-mode 0) ; no menu bar
(show-paren-mode 1) ; visualize matching parenthesees
(global-hl-line-mode 1) ; highlight current line
(eldoc-mode 1) ; enable docs in minibuffer
(setq inhibit-startup-screen 1) ; no start screen

;; store all backups in a single directory
(setq backup-directory-alist
`(("." . ,(concat user-emacs-directory "backups"))))

;; y or n instead of yes-or no
(fset 'yes-or-no-p 'y-or-n-p)

;; no annoying bell!
(setq ring-bell-function 'ignore)

;; set font Hack 15 pt
(set-face-attribute 'default nil
:family "Hack"
:height 150)

;; when on mac
(when (eq system-type 'darwin)
(setq mac-command-modifier 'meta) ; set cmd to meta
(setq mac-option-modifier nil)
(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)) ; configure title bar
(add-to-list 'default-frame-alist '(ns-appearance . 'nil)))

;; set my init filt to be this file
(setq user-init-file "~/.emacs.d/init.el")

Conclusion

I hope this rundown of my Emacs config has been helpful. As you can see, getting a very functional Emacs up and running really doesn’t take that much work. As always, there’s much more that can be done, but that’s really the beauty of Emacs :)

If you have any questions about Emacs or my config, please don’t hesitate to leave a comment below or reach out to me @\zacwood on Twitter!

--

--