AKA: how to use Doom Emacs as my main text editor for note-taking and software development.

But what is Doom Emacs ?

An Emacs framework for the stubborn martian hacker

The TL;DR is that Doom Emacs offers sane defaults for most stuff, and uses SPACE as your leader key. The killer feature for me is using the beautifully lisp-y Emacs ecosystem of packages together with the insanely practical vim keybindings (if you like being evil ).

NOTE: Everything I write is tested against my own config, which you can find in my dotfiles repo: https://github.com/vvzen/dotfiles

For more info on Doom Emacs itself, have a look at https://github.com/doomemacs/doomemacs

Buffer and File Navigation

  • SPACE + . -> Find a file to open in a new buffer

This^ uses dired, so you will be able to fuzzy match, etc.

  • SPACE + SPACE -> Find a file in the current project (also uses fuzzy find, but leverages projectile)

  • CTRL + x + d -> Edit the dired directory for the current buffer (and more)

  • SPACE + b + i -> Open a buffer listing all buffers (via ibuffer)

  • SPACE + b + p -> Go to the previous buffer
  • SPACE + b + n -> Go to the next buffer
  • SPACE + b + k -> Kill the current buffer

Git

  • SPACE + g + g -> Open the Magit buffer

I haven't moved to use magit fully, I still do some stuff just using git in a terminal, since that feels more natural.

What I have been using so far is:

  • SPACE + g + g + c + c -> Create a commit
  • SPACE + g + g + c + w -> Reword a commit
  • SPACE + g + g + c + a -> Amend a commit

Then, for navigation purposes:

  • [ + d -> Go to previous diff hunk
  • ] + d -> Go to next diff hunk
  • / -> Search in the current buffer (evil)
  • SPACE + / -> Search in current project (via projectile mode)
  • SPACE + s + s -> Conduct a text search in the current buffer (uses fuzzy match)

Windows

  • SPACE + w + v -> Create a vertical split of the current buffer
  • SPACE + w + s -> Create a horizontal split of the current buffer
  • SPACE + w + h -> Focus on the window on the left
  • SPACE + w + l -> Focus on the window on the right
  • SPACE + w + k -> Focus on the window on the top
  • SPACE + w + j -> Focus on the window on the bottom
  • SPACE + w + q -> Close the current window split

Note: this^ will not close the buffer itself.

Text manipulation

Search and replace

Thanks to evil mode, when in normal mode, you can do similar things to what you'd do in vim, for example

  • s/search/replace -> Search and replace current line
  • :%s/search/replace -> Global search/replace inside current buffer

Another way is also:

  • SPACE + replace-string -> To replace a string within your visual selection.

To perform search and replace across multiple files, you can do the following:

  • SPACE + find-name-dired to fuzzy match the files you want to work on
  • t to select everything from the fuzzy match
  • Q to enter regex query replace mode
  • SPACE to confirm each replace
  • Finally, SPACE + save-some-buffers to save the modified buffers

Characters pairs

The perform a transformation like thing -> 'thing', you can:

  • Go in visual mode
  • Press S (evil-surround-region)
  • Press '

The same workflow can be applied for a few other 'pair' characters like (), {}, [], <>, etc.

Multiple cursors

It feels a bit clunky compared to what helix offers natively, but you can use evil for some simple multiple cursor operations. For example, go to visual mode, select some lines, then type g + z + I to enter insert mode over those lines.

Indentation

  • SHIFT + . -> Indent
  • SHIFT + , -> Dedent

LSP

  • SPACE + c + j -> Consult symbols
  • g + d -> Go to definition
  • g + D -> List all references
  • SPACE + s + S -> Search buffer for symbol at point
  • [ + g + f -> Go to previous function declaration
  • ] + g + f -> Go to next function declaration
  • [ + g + F -> Go to previous function call
  • ] + g + F -> Go to next function call

I also have SPACE + r mapped to lsp-rename, which lets me quickly rename the symbol under the cursor:

(map! :leader
      "r" #'lsp-rename)

Workspaces

A workspace is just series of buffers shown together at one point in time. They way I use workspaces is to logically group together buffers and vterm buffers that are related to the same project.

  • SPACE + TAB + n -> Create a new workspace
  • SPACE + TAB + r -> Rename workspace
  • SPACE + TAB + 1 -> Switch to workspace 1 (and so on)
  • SPACE + q + s -> Save all workspaces
  • SPACE + q + l -> Load last saved workspace

Terminal

I just use vterm (see https://docs.doomemacs.org/v21.12/modules/term/vterm).

  • SPACE + vterm -> Create a new terminal emulator buffer
  • SPACE + o + T -> Open Terminal in current buffer, using current dired location

The integration with evil is a bit funky sometimes, which is why I have a d + n + e (Do No Evil) shortcut to temporarily disable evil mode. That's particularly useful if you somehow enter vim from vterm and wonder why you can't exit..

(map! :leader
      "d n e" #'turn-off-evil-mode)

Despite its rough edges, being able to use vim-like movements in my terminal (and to use visual mode too!) is a life changing experience. Selecting and copying text is faster, and I can still use escape sequences generated by CTRL+a and CTRL+e (and so on) once my shell prompt is configured for it, which basically means I get the best of both worlds!

Org mode

I was already a note-taking freak before learning about ORG mode in Emacs. I take daily notes of everything that I do at work, and I started just with plain text notes written in the TextEdit app in macOS, then I moved to just write markdown notes with my editor (helix, at the time) . Now I am a ORG-mode freak.

There's several tiny things that I like about ORG mode, so far:

  • Literate programming (executable code blocks within my notes using #+begin_src and #+end_src). For example:
#+begin_src
(defun something ()
  (interactive)
  (message "Hello world"))

(something)
#+end_src

#+RESULTS:
: Hello world
  • Ability to maximize/minimize a bullet point via Tab
    • This is incredibly useful if, like me, you tend to add a lot of tiny snippets into a single item
  • Check-boxes with syntax highlighting (so that list entries that start with [X] get grayed out)
  • TODOs with syntax highlighting
  • Tables that auto-align by pressing tab

Evil mode

Evil mode is an emulation of the vim keybindings. Here's a few things that I use daily..

Moving around

  • k,j -> Move up, down a line
  • h,l -> Left, Right a character
  • w -> Move forward to next word
  • b -> Move backward to next word
  • gg -> Move cursor at the beginning of the file
  • G -> Move cursor at the end of the file
  • H -> Move cursor to the top of the screen
  • M -> Move cursor to the middle of the screen
  • L -> Move cursor to the bottom of the screen
  • f + char -> Move to next occurrence of char (from there, type ; to keep moving across occurrences)

Custom ones:

  • gh -> Move to beginning of visual line
  • gl -> Move to end of visual line
;;; Evil start of line
(map! :n
      "g h" #'evil-beginning-of-visual-line)

;;; Evil end of line
;;; NOTE: 'g l' is already mapped natively to something else, so this overrides it
(map! :after evil
      :nv
      "g l" #'evil-end-of-visual-line)

Editing (in normal mode)

  • x -> Delete character under the cursor
  • yy -> Yank current line to clipboard
  • dd -> Delete whole line and yank it
  • dw -> Delete across Word boundary
  • d$ -> Delete from cursor until end of line