This is a series of tutorials geared around building up your own customized environment, using emacs, from scratch.

You can find part 2 here You can find part 3 here

This tutorial is geared toward those who are starting with extending Emacs, and want to learn the pieces you need to know to really extend and build your custom environment. If you want to just get started with a rocking environment and don't care about understanding the nitty-gritty, I'd suggest looking at emacs-prelude

We're going to go in-depth on discussing the following topics:

  • extending your base emacs with init-files, and some good practices there
  • managing and installing packages, and doing so automatically
  • binding command and keys to installed packages
  • writing some custom code, and integrating it into your emacs environment

If you follow all these tutorials word for word, you'll end up with the building blocks to set up your own personalized emacs environment. Here's mine:

My Emacs Setup

However, I would recommend picking and choosing the parts that best suit your purposes. Please leave comments if I'm not going in-depth enough to provide the tools to do so.

So let's begin!

The Tutorial

Emacs Lisp

To follow these tutorials, you need to have a very rudimentary understanding of emacs lisp. Basically just remeber this:

(method arg1 arg2 ...)

Is how methods are called: the first element in a parentheses is the method being called, and the subsequent elements are it's arguments.

You can also nest calls into calls, like this:

(foo (bar bar-argument) foo-argument)

This nesting of statements is what starts the chain of parentheses that lisp and lisp dialects are famous for.

Init-Files

init-files are the files that emacs loads on startup. This is similar to the rc files that you often see with shells/terminals, vim, or a variety of other tools.

Emacs has a few standard places to add an init-file, but those are already well documented in the manual. If you want to know more, feel free to read there.

For the most part, however, there are two main locations where init-files lie: the .emacs file and the .emacs.d directory in the user root.

Unlike some other init files, Emacs's initialization is basically evaluating the init-files with it's built-in elisp (Emacs Lisp) interpreter. This is what provides Emacs users with real power: since the great majority of Emacs is written directly in elisp, it is possible to extend any of that code with your init-file, or even evaluating code on the fly after the system starts up. This is a stark contrast to other extensibile architectures, which only allow a discrete set of configs or apis from which to modify application behaviour. When you hear someone say Emacs is "infinitely extensible", this is what they mean: you can practically modify whatever you want in Emacs!

But before we get to this awesome power, it's first best to learn some ways to organize your init-files. Emacs init-files can get huge, and having good practices now will help you manage all the pieces in the long run.

The .emacs.d directory is the de-facto place to store configuration files beyond the .emacs file. Package managers add their packages there, packages add their configuration there, so it's also a good place to add our custom configuration.

For our example, let's disable the menu, scroll and tool bars. This removes all of the dropdown gui menus, so all we have are the buffers left. You might think this is crippling at first, but learning your keybindings will make you way more efficient in the long run.

You can name your files whatever you want, but I've found it's easy to find files if you prefix them. I prefix all of my init-files with "my-". This also has the advantage of signifying it's a personal configuration.

So let's make a file now called "my-noexternals". This signifies to me that these are configs for components that are native to Emacs (don't depend on third-party packages, or "externals"). Let's disable the menu, tool, and scroll bar now:

; ~/.emacs.d/my-noexternals.el

;; Remove scrollbars, menu bars, and toolbars
; when is a special form of "if", with no else clause, it reads:
; (when <condition> <code-to-execute-1> <code-to-execute2> ...)
(when (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(when (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(when (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))

Now that we have that, we need to load our my-noexternals.el file in our .emacs. Add the following line to ~/.emacs:

; ~/.emacs
(load "~/.emacs.d/my-noexternals.el")

And now when you start emacs, you'll have the bars disabled! Of course, you're welcome to enable whatever you like, I was just using this as an example.

Rebinding Keys

Now let's say we want to re-bind keys. elisp has a command for that as well, and it's called global-set-key. It works like this:

(global-set-key <keychord> <function-name>)

An easy way to declare the key chord you want to use is by using the "kbd" command, which evaluates a keychord formatted in typical emacs key-chord fashion, and evaluates it to something global-set-key can understand. As an example:

(global-set-key (kbd "C-c C-j") 'foo)

Would make C-c C-j (<Ctrl + c> followed by <Ctrl + j>) run the "foo" function.

As a personal preference, I like navigating through my open windows with vim-like movement (hjkl). As a compromise, I bind the following commands:

; ~/my-noexternals.el

;; Wind-move

(global-set-key (kbd "C-c C-j") 'windmove-left)
(global-set-key (kbd "C-c C-k") 'windmove-down)
(global-set-key (kbd "C-c C-l") 'windmove-up)
(global-set-key (kbd "C-c C-;") 'windmove-right)

windmove-<direction> is a command that moves your window focus to the first window in the direction specified. I bind them to the chords C-c C-(jkl;), because C-c C-h is a help command.

Using Hooks

However, the problem with some keybindings is that they get overridden depending on the order global-set-key gets run. This is especially a problem when using external packages, which can sometimes override keys with their own configuration. This is not a common practice now, but can still happend.

To help ensure your commands run in a particular order, Emacs provides hooks into it's startup (along with several other places, but we'll get into that later). So let's modify our .emacs so my-noexternals.el gets loaded at the very end, after everything else has run:

; ~/.emacs

(add-hook 'after-init-hook '(lambda ()
  (load "~/.emacs.d/my-noexternals.el")
))

The "add-hook" command allows you to hook methods to run at a particular time, and the "'after-init-hook" tells emacs to run the method after everything else in the init-file loaded.

Note that in this example, I used a lambda/anonymous method versus an explicit function. It's typically the standard to do lambdas in hooks over, say, defining a function and passing it.

Summary

So to recap, here's the useful things we learned:

  • ~/.emacs and ~/.emacs.d/ are the standard locations to add init-files
  • splitting out ~/.emacs into several other files and loading those is a lot easier to manage
  • (load <filename>) will evaluate a file
  • (global-set-key <keychord> <function-name>) will set <keychord> to run <function-name>
  • (add-hook <hook> <lambda>) to run lambda at a particular event
  • the "'after-init-hook" event will run functions after the rest of the init-file has finished loading.

Final Code

.emacs:

(add-hook 'after-init-hook '(lambda ()
  (load "~/.emacs.d/my-noexternals.el")
))

.emacs.d/my-noexternals.el:

; ~/.emacs.d/my-noexternals.el

;; Remove scrollbars, menu bars, and toolbars
(when (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(when (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(when (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))

;; Wind-move
(global-set-key (kbd "C-c C-j") 'windmove-left)
(global-set-key (kbd "C-c C-k") 'windmove-down)
(global-set-key (kbd "C-c C-l") 'windmove-up)
(global-set-key (kbd "C-c C-;") 'windmove-right)

What's Next

Next tutorial, we'll talk about package management. You can find part 2 here

Further Reading / References


Comments

comments powered by Disqus

About Yusuke Tsutsumi
I work at Zillow. I focus on tools and services for developer productivity, including build and testing.

My other interests include programming language design, game development, and learning languages (the non-programming ones).