Henrik Lissner edited this page Jan 5, 2019 · 12 revisions

Table of Contents

Common Questions

Does Doom work on Windows?

As a Linux/MacOS user, I have limited access to the Windows platform. Doom hasn’t been tested nor designed for it, so your mileage will vary. Some have reported success, however.

If you’ve got Doom working on Windows, let me know about it! I’d be happy to include Windows support.

How does Doom compare to Spacemacs?

To paraphrase a reddit answer to this question by gilbertw1:

  • Doom is lighter than Spacemacs. Expect faster startup times and a snappier experience.
  • Doom is much more opinionated than Spacemacs. It is the work of one developer, and it is catered to his eccentric taste. You can expect a more curated experience from Doom at the cost of flexibility.
  • Doom is thinner than Spacemacs. There’s less between you and vanilla Emacs, which means less complexity to wrap your head around if you want to modify and customize Doom.
  • Doom lacks the sheer manpower available to Spacemacs. Bugs stick around longer, documentation stays light and development is at the mercy of it’s maintainer’s schedule.
  • Doom is not beginner friendly. Spacemacs works out of the box. Your mileage may vary with Doom, where bugs may creep in undetected from upstream (or plugins), my workflows may conflict with yours and things I haven’t anticipated may go wrong. To get the most out of Doom, you should be comfortable with Emacs Lisp, git and the command line.

Why such a complicated package management system?

  1. Scriptability: I live in the command line. I want an externally scriptable interface for updating and installing Emacs packages.
  2. Flexibility: I want packages from sources other than ELPA (like github). Some plugins are out-of-date through official channels, have changed hands, have a superior fork, or simply aren’t in any ELPA repo.
  3. Stability: I used Cask before this. It would error out with cyrptic errors depending on the version of Emacs I used and the alignment of the planets. No more.
  4. Performance: this system is lazy-loaded (more so if you byte-compile). Not having to initialize package.el or quelpa (and check that your packages are installed) every time you start up or load a package speeds things up.
  5. Seperation of concerns: I want package management and configuration to be separate. It makes it easier to grok modules and their dependencies.
  6. Simplicity: no external dependencies (unless you count make).

How do I start customizing Doom?

The Customization page will help you set up your private module.

How is Doom’s startup so fast?

I frequently restart Emacs and I don’t use the daemon, so startup times are important to me. Doom employs a number of techniques to make Emacs startup as zippy as can be, a lot of it is premature optimization. Here, I will describe the more effective techniques:

  • Avoid garbage collection during startup. The GC eats up quite a bit of time, easily doubling startup time. The trick is to turn up the memory threshold to prevent it from running:
    (setq gc-cons-threshold 402653184
          gc-cons-percentage 0.6)
    
    ;; ... your whole emacs config here ...
    
    ;; Then reset it as late as possible; these are the reasonable defaults I use.
    (add-hook! 'emacs-startup-hook
      (setq gc-cons-threshold 16777216
            gc-cons-percentage 0.1))
        

    Don’t forget to reset these variables to some reasonable default after startup! A large gc-cons-threshold will cause freezing and stuttering during long-term interactive use.

  • Like we did with the garbage collector, unset file-name-handler-alist too (temporarily). Every file opened and loaded by Emacs will run through this list to check for a proper handler for the file, but during startup, it won’t need any of them. Doom doesn’t, at least.
    (defvar doom--file-name-handler-alist file-name-handler-alist)
    (setq file-name-handler-alist nil)
    
    ;; ... your whole emacs config here ...
    
    (add-hook! 'emacs-startup-hook
      (setq file-name-handler-alist doom--file-name-handler-alist))
        
  • Cut down on load-path lookups. Each load and require (without an absolute path) costs an O(n) lookup on load-path. My Doom config has over 260 entries in its load-path, so that’s a worst case of n=260 on every lookup.

    There is little you can do about this, at large, but Doom tries.

    • Where possible, load-path is let-bound to a smaller subset of itself. For example, in the doom-initialize function.
    • Module files are loaded with Doom’s load! macro, which does simple string concatenation (and file checking) to build an absolute path. If byte-compiled, you also avoid that too. Altogether avoiding the load-path lookup.
  • Package.el initialization is expensive, so disable it! package.el is sneaky though, it will initialize itself if you’re not careful. Not on my watch, criminal scum!
    (setq package-enable-at-startup nil ; don't auto-initialize!
          ;; don't add that `custom-set-variables' block to my initl!
          package--init-file-ensured t)
        

    Be warned! Without package.el initialization, you mainly lose two things:

    • Emacs won’t load your packages’ autoloads files. This may be distastrous to some especially silly geese, but not to Doom. Doom defines its own with use-package.
    • Your load-path will be empty, so you have to populate it manually. Doom does this in ~doom-initialize-load-path~.
  • Lazy load everything. This is a well known technique. use-package helps you defer packages, and Doom defers as many as possible. It also goes the extra mile to lazy-load packages that are very difficult to lazy-load, like Company or Helm.
    (def-package! smart-forward
      :commands (smart-up smart-down smart-backward smart-forward))
        

    Note: def-package! is simply a thin wrapper around use-package.

  • Exploit byte-compilation! Many expensive things are cached (or cut out altogether) at compile time with the help of Emacs’ eval-when-compile macro, which replaces a bunch of forms with its result in the byte-compiled result.

    Some good candidates for caching (or omitting) are the load-path, package checks (Doom does this for core packages), exec-path (exec-path-from-shell is expensive, but a necessity for MacOS users), and so on.

    (setq load-path (eval-when-compile (doom-initialize t)
                                       (doom-initialize-load-path t))
        

    Running make compile-core buys me a \~40% improvement in startup times. You can also byte-compile the entire config with make compile, but this takes much longer and yields significantly diminished returns.

  • Use lexical-binding everywhere. Add ;; -*- lexical-binding: t; -*- to the top of your elisp files. This can break code if you’ve written it to depend on dynamic variables, but I’ve written Doom not to.
  • Avoid lambdas, if you can. Prefer cl-loop and dolist over mapc or mapcar. Lambdas add a little overhead each time they’re executed in loops (and the byte-compiler has trouble inlining them; especially when lexical-binding is on!). This is definitely premature optimization :P

That’s not everything, but those are the most important. Here are two articles that may shed some light on these decisions.

Troubleshooting

What’s wrong with my icons?

Many of Doom’s UI modules use the all-the-icons plugin, which uses special fonts to display icons. These fonts must be installed for them to work properly, otherwise you’ll get a bunch of squares and mismatched icons.

Run M-x all-the-icons-install-fonts to install them.

I’m getting “certificate validation failed” or “peculiar error” messages

If you’re a MacOS user, you need to install emacs with the --with-gnutls option.

Otherwise, you can try setting (setq gnutls-verify-error nil), but this disables TLS verification. Whether this is an acceptable compromise or not is up to you.

If you are receiving these issues while trying to run a make command, you can set the INSECURE environment variable. e.g. INSECURE=1 make install.

I’ve been unable to track down this issue, so these workarounds are your only option for now.

How do I…?

Remove Vim/Evil for a more vanilla Emacs experience?

You must do three things:

  1. Remove :feature evil from init.el,
  2. Run make to clean up lingering dependencies and refresh yuor autoloads file.
  3. Assign a new doom-leader-key and doom-localleader-key. By default, these are bound to SPC and SPC m, which will break the space key in vanilla Emacs.

Note that evil-specific configuration and keybindings (defined with map!) will be ignored without feature/evil present (and stripped out when byte-compiling).

Unfortunately, since Doom was designed by a vimmer, for vimmers, I’ve put little consideration into a keybinding scheme for vanilla Emacs users.

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.