My first Emacs Lisp

Or: How I can't let Vim go
An Emacs Lisp function called `learn-some-elisp`. The content of the function is just filler to make it look like it's doing something.

Not as hard as you might think.

In my previous post on reading the Emacs manual, I mentioned that there were a couple of things that I was missing from my editing workflow when using regular Emacs bindings. The most notable of which was the ability to kill up until a search hit. I knew that it'd be possible to write a function to do it, but I didn't really know where to start, so I figured I'd just do it later.

In the Reddit thread about the post, user e17i gave me a little snippet to get me started for writing such a function. Turns out that was all I needed to get started. I put aside my fear of Lisp and went to work.

I ended up with three functions for Vim-like search movement: just exiting a search at a result, killing up to a search result, and copying up to a search result. Not particularly complicated, but I was so proud of myself when I got it working. The functions are included below for your viewing pleasure1:

  (defun isearch-vim-style-exit ()
    "Move point to the start of the matched string, regardless
    of search direction."
    (interactive)
    (when (eq isearch-forward t)
      (goto-char isearch-other-end))
    (isearch-exit))

  (defun isearch-vim-style-kill ()
    "Kill up to the search match when searching forward. When
  searching backward, kill to the beginning of the match."
    (interactive)
    (isearch-vim-style-exit)
    (call-interactively 'kill-region))

  (defun isearch-vim-style-copy ()
    "Copy up to the search match when searching forward. When
    searching backward, copy to the start of the search match."
    (interactive)
    (isearch-vim-style-exit)
    (call-interactively 'kill-ring-save)
    (exchange-point-and-mark))

I've mapped the functions to three separate key bindings in isearch-mode-map to make them easily accessible while searching:

  (define-key isearch-mode-map
    (kbd "<C-return>") 'isearch-vim-style-exit)

  (define-key isearch-mode-map
    (kbd "<M-return>") 'isearch-vim-style-kill)

  (define-key isearch-mode-map
    (kbd "<C-M-return>") 'isearch-vim-style-copy)

Justification and motivation

In Emacs, when searching with isearch, when you 'accept' a match and move point there, Emacs will put you at the end of your matched text. Sometimes this is exactly what you want. Often, though, I find that I'd rather have point move to just before where the search string matches. This is how it works in Vim, and I have convinced myself that it's also the standard way of moving cursors to searches in other text editors. In addition to just moving to a search result, I want the same pattern to apply for killing and for copying the text between your original cursor position and the search match. In short, these functions act on everything between your original cursor position and the start of the selected match, regardless of whether you search forwards or backwards.

This functionality is the same as regular isearch when searching backward, but when searching forward it's the same as adding an extra C-r (isearch-backward) after picking a match.

The Reddit snippet

The tip I got on Reddit gave me a little code snippet to start me off. At first I thought it was just what I wanted, but I realized later that it wasn't quite what I was looking for. The original snippet is very symmetrical in that it goes to the end of a match when searching backward and to the start of a match when searching forward. However, I have found that I always want to move to the start of a match, no matter what side I come at it from. This may seem asymmetrical, but one nice thing about it is that it'll work the same on any match, regardless of whether you're searching forward or backward. This is especially useful if your search has wrapped.

The original snippet was:

  (define-key isearch-mode-map (kbd "<C-return>")
    (lambda () (interactive)
      (isearch-repeat(if (eq isearch-forward nil)
                        'forward
                      'backward))
      (isearch-exit)))

So even if it wasn't quite what I needed, it gave me the tools necessary to start working on my own implementation, namely the isearch-repeat and isearch-exit functions and the isearch-forward variable. With this I had all I needed to start playing around with the functionality myself.

Elisp crash course

If you've never encountered Lisp before, here's a short (and very incomplete) introduction to Emacs Lisp. Do bear in mind that this is the first Emacs Lisp code I've written myself, so I'm probably missing a lot of context and nuance. If you find any errors, please do tell me; I'll be very grateful. For a more complete introduction to the language, check out the Emacs Lisp manual.

~defun~ [name] (args)

The first line of the function contains the keyword defun, signifying that we're defining a function, the name of the function, and a list of parameters. In all the above functions, the parameter list is empty, so it's just a set of empty parentheses (()).

Comments

After the first line of each function, I've added a string describing what the function does. This works as documentation. When looking up the function in Emacs (C-h f <name of function>), this text gets displayed. It's not a requirement to put into a function, but it's nice to have when you need to look things up.

Furthermore, lines starting with ; are standard code comments, such as the one about setting current~prefix-arg.

~(interactive)~ and ~call-interactively~

In Emacs Lisp, (interactive) turns a Lisp function into a command. In short, this means that you can assign it to a key sequence and call it from anywhere in Emacs by using M-x. Similarly, call-interactively is used to call interactive commands that take arguments. These commands can either be given explicit arguments and called like normal functions, or we can use call-interactively. When doing the latter, certain parameters can be passed implicitly. For instance kill-region needs two arguments BEG and END to know what region to operate on. When called interactively, BEG and END get the values of point and mark, so we don't need to pass them explicitly. For more information about the commands and interactive, check out the Emacs Lisp manual, chapter 21.2.

What's with the quotes?

Again, the manual (chapter 10.3) has info on what the single quotes do on a deeper level, but in our case, what we want is simply to pass the quoted function as an argument, and not the result of evaluating that function. In short, it's passing a function pointer, rather than the result of a function.

~when~

when is the Lisp way to only evaluate some code when a condition holds true. It's similar to an if expression, but an if expression needs an else-clause to run if the provided condition doesn't hold true. In languages like Python, Rust, JavaScript, etc., it's the equivalent of just using an if expression/statement without an else-clause.

~let~ and ~current-prefix-arg~

There's not a whole lot of variable binding going on in these functions. The one place it's done is in the isearch-vim-style-copy function. As you might expect, the let keyword assigns a value to a variable (in this case (4) to current-prefix-arg). In Emacs Lisp, variables can have global scope, so the let binding ensures that the variable is only bound to this value within this scope. The variable current-prefix-arg is used to augment set-mark-command. We do this to move point back to where the search started after copying the region.


And that's the story of how I got started writing Lisp. It's dead simple, but I've got a taste for it now, and I think I like it. ... yeah. I think I like it.

Footnotes

The code above has been modified from its original published state after feedback from Reddit and working with it some more. The original snippet looked like this:

  (defun isearch-vim-style-exit ()
    "Move point to the start of the matched string, regardless
      of search direction."
    (interactive)
    (when (eq isearch-forward t)
      (isearch-repeat 'backward))
    (isearch-exit))

  (defun isearch-vim-style-kill ()
    "Kill up to the search match when searching forward. When
      searching backward, kill to the beginning of the match."
    (interactive)
    (isearch-vim-style-exit)
    (call-interactively 'kill-region))

  (defun isearch-vim-style-copy ()
    "Copy up to the search match when searching forward. When
      searching backward, copy to the start of the search match."
    (interactive)
    (isearch-vim-style-exit)
    (call-interactively 'kill-ring-save)
    ;; set prefix arg to move point back to where search started
    (let ((current-prefix-arg '(4)))
      (call-interactively 'set-mark-command)))

Tips and tricks for the fledgling Emacs user

Notes from my journey through the manual
The iconic Superman emblem, but instead of an 'S' in the center, it's the Emacs 'E'.

Yup. I did it. I finally read the f*cking manual.

Whenever I have gone looking for the Emacs equivalent of Drew Neil's amazing Practical Vim1, I have always found people recommending reading the Emacs manual, saying how good it is. For the longest time I put it off, and I was on the verge of ordering Mickey Petersen's Mastering Emacs, which I also often see mentioned, when I decided that I should just bite the bullet and at least start with the manual. It can't be that much of an undertaking, right? ... right?

Wrong.

Coming into 2020, I decided that I'd get through the manual by the end of January. I had no idea how much content was in there. Throughout the first 30 days of 2020 I sunk hour upon hour upon hour into this collection of text documents, assembled through many decades, until I finally found my way out, clothes tattered and torn, beard long and graying2, but my mind opened and enlightened.

What I want to share today is not the entire voyage, but rather just the high points that come to mind when I think back on my journey. Undoubtedly there will be things I've forgotten that should be here and things that are here that I should have forgotten, but such is the nature of human experiences. The best we can do is to just sit back and enjoy what we have.

I will be using the same notation for key bindings as Emacs does in the manual. Most characters are just represented as themselves, but some are special. Chords (or modified keys) are represented as modifiers connected to letters by a hyphen. A list of the modifiers and special keys used in this document is presented below. More information on characters and notation can be found in chapter 2 of the Emacs manual.
  - ~C~ and ~M~ :: These upper case letters represent ~Control~ (your trusty control key) and ~Meta~ (usually alt), respectively. When used with other characters, they're connected by a hyphen: ~C-a~ and ~C-M-a~, for instance.
  - <RET> :: The return (or enter) key
  - <ESC> :: The escape key

In addition, note that ~M-x~ opens the ~execute-extended-command~ menu, wherein you can type in the name of a command. Thus, when you see something such as ~M-x other-window~, that means that the text after ~M-x~ is the name of the command. To execute the command, press return.

The Emacs key bindings

Among the reasons to go through the manual, learning the 'proper' Emacs key bindings was actually pretty high up. It might seem trivial, but there's been a number of times where I've ended up in a state that I just can't navigate and don't know how to get out of.

As someone who fell in love with the Vim way of editing, I came to Emacs through Spacemacs, which uses Evil mode to provide a very comfortable editing experience for Vim users. As such, I've already spent a considerable amount of time learning one very specific set of key bindings (and I love it every day). Why would I bother learning another?

For some time, I've been using 'hybrid-mode', giving me Vim key bindings in every mode except insert mode, where I switch to using Emacs key bindings. What I have discovered is that they both have their uses, and they shine in different ways. When navigating text, I feel much safer and much more agile in Vim's normal mode, able to take leaps and bounds through the buffer, working on text as objects, confident that I won't accidentally insert some text by a stray key press. However, when typing out text, having to exit insert mode and then type a command before entering insert mode again can be pretty tedious, and being able to make certain movements from that state just makes a lot of sense. A good example would be needing to go back to an earlier part of a line, delete some text, and then keep inserting at the end. In Vim, assuming we're already in insert-mode:

 ?   d a w A

And in Emacs

C-r   M-d C-e

If my counting is correct, the Vim version has three more key presses than the Emacs version. Unless you're into golfing, this probably doesn't mean much to you (especially if d a w feels like a single movement to you), but to me, it feels (subjective, I know) like more work.

A surprising key binding that I've never really considered, but which Emacs has bound to C-M-v by default: scroll-other-window. If you have multiple windows open in a single frame, it'll scroll one of the other windows, but lets you keep your focus (and your cursor) in your current window. There may be a way to target a specific window, but I found it most useful when having only two windows open. If you have more than two windows open, it'll scroll the next window as defined by Emacs buffer ordering. From what I can tell, this is left to right, top to bottom. It's the same order as when going through the windows by C-x o (other-window).

Navigating text (chapter 25)

Naturally, coming from Vim, I think that the Vim way is the way for navigating text on a screen. Or at least I did. In fact, I was surprised to learn that Emacs has all the same facilities for navigating a buffer as Vim does, and it turns out that all the same movement patterns that I rely on in Vim are available in Emacs; they're just bound to different keys.

There are a couple of editing patterns that I haven't found the equivalent of yet, though (send me tips if you've got them!):

The equivalent of ~vim-surround~
Yup, Evil has a function for it, and I'm sure you can write a function that will contain text, change surrounding delimiters to something else, or delete surrounding delimiters, but it's not immediately available. That said, even in Vim, it's a plugin, so maybe this shouldn't count.
Delete to next search hit
In Vim (normal mode), a common way for me to operate on some text is to d / <search string> <RET> to delete up until the first occurrence of the search string. I haven't found a way to do that in barebones Emacs. Emacs comes with the default binding of M-z as zap-to-char, which is almost what I want. However, rather than being able to input just a single character, I want to be able to input a search string and kill up to that point. It'd be similar to C-<SPC> C-s <search string> <RET> <DEL>, but without killing the actual search string (kill up to, not including), and it should be a single key chord. If I were to override the M-z binding, it'd be M-z <search string> <RET>.

Overall, though, I am surprisingly happy with Emacs' movement keys, and while I don't think I'll be switching over completely just yet (or ever?), being able to navigate comfortably in either mode can only make things better.

The kill ring (chapter 12.2.1)

Of all the things I've learned, this is probably the one that's given me the most bang for my buck. Whatever your preferred way of getting text into your kill ring (also known as the clipboard), once you've got it there, Emacs doesn't just store the last entry, but rather your last 60 (by default) entries. The reason it's called a ring is that you can cycle through your entries, and once you reach the end, you loop back around.

So when you paste (or yank) something into a buffer with C-y, you can then follow that up by using M-y to cycle through the kill ring. You can view the entire contents of the kill ring with C-h v kill-ring, though be warned: the kill ring is displayed as lisp code, so it may not read as plainly as you'd expect.

Side note: I wish Vim and Emacs could at least agree on the meaning of the word 'yank'. Whether it means to copy something from the buffer (Vim) or to paste something into the buffer (Emacs), I don't care. Just pick one.

The undo-stack (chapter 16.1)

Spacemacs uses the undo-tree-mode (see the Emacs wiki entry) to store buffer states in a change tree. This is really intuitive and lets you visualize your changes in a graphical model that maps pretty well onto my view of a set of changes. What I wasn't aware of was that plain, ol', vanilla Emacs comes with a pretty uncommon, but super-powerful undo history out of the box: instead of taking the view that history branchces after undoing a change, the default undo is more like a strictly linear timeline. When you undo a change, Emacs simply pushes this onto the history 'stack' as a new change. Thus, Emacs doesn't have a traditional redo as such: it's just another undo.

It's a bit tricky to wrap your head around, but is really worth checking out. For context, here is an excerpt from the relevant chapter in the manual (link to web version):

To begin to undo, type ~‘C-/’~ (or its aliases, ~‘C-_’~ or ~‘C-x u’~). This undoes the most recent change in the buffer, and moves point back to where it was before that change. Consecutive repetitions of ~‘C-/’~ (or its aliases) undo earlier and earlier changes in the current buffer. If all the recorded changes have already been undone, the undo command signals an error. Any command other than an undo command breaks the sequence of undo commands. Starting from that moment, the entire sequence of undo commands that you have just performed are themselves placed into the undo record. Therefore, to re-apply changes you have undone, type ~‘C-f’~ or any other command that harmlessly breaks the sequence of undoing; then type ~‘C-/’~ one or more times to undo some of the undo commands. Alternatively, if you want to resume undoing, without redoing previous undo commands, use ~‘M-x undo-only’~. This is like ‘undo’, but will not redo changes you have just undone.

Bookmarks (chapter 13.8)

When reading through a document in several sittings, it's useful to be able to jot down how far you'd gotten so that you can easily resume your reading. To this end, Emacs has a bookmarks system which was very useful when reading through the documentation. Similarly to how one might use bookmarks in a web browser, I've also bookmarked a number of documents to read later, so that I don't forget. In addition to being useful for reading, I can also imagine it being useful if you often need to refer back to a specific file when programming, though registers (chapter 13), specifically File Registers (chapter 13.6), may be better suited to this.

Amusements (chapter 47)

The Amusements chapter covers a couple of really neat, mostly useless, tricks that you can do with Emacs. It lists a couple of built-in games, including classics such as Snake, Pong, and Tetris (turns out I'm horrible at that, by the way); text-based adventure games (dunnet) and Conway's Game of Life simulation (life); ways to convert a region into morse code or NATO phonetic alphabet (morse-region and nato-region to encode, unmorse-region and denato-region to decode); ways to play with the display when idling (zone); and references to XKCD.

Glasses mode (chapter 26.9) and flipping tables

In addition to being able to encode a region as morse or the NATO phonetic alphabet, Emacs also comes with something known as glasses mode. This isn't actually an amusement and is originally supposed to help make camelCased words easier to read by turning them into snake_cased words instead (or using any other separator that you want). This is all just presentational, so even though Emacs might display the identifier as my_Favorite_Identifier, it'll get saved as myFavoriteIdentifier, and when you edit the text, the underlines aren't actually there for you to edit.

Because you can change the separator, you can also use glasses mode to separate your identifiers with an arbitrary text string. My coworker suggested the 'table flip', and sure enough, Emacs is happy to comply:

    myFavoriteIdentifier = someOtherIdentifier

becomes

    my(╯°□°)╯︵ ┻━┻Favorite(╯°□°)╯︵ ┻━┻Identifier = some(╯°□°)╯︵ ┻━┻Other(╯°□°)╯︵ ┻━┻Identifier

and for fun, here it is in morse

    --/-.--/..-./.-/...-/---/.-./../-/./../-.././-./-/../..-./.././.-. -...- .../---/--/./---/-/...././.-./../-.././-./-/../..-./.././.-.

Debugging (chapter 27.6)

This is something I want to get into at a later stage, and the manual makes it seem rather impressive. I've not used a debugger at all since switching to Emacs, and while I usually manage just fine, I find myself longing for one every so often.

Compilation buffers and jumping to the next error (chapter 27.2)

Somewhat related to debugging is compiling your programs in your editor. While a lot of text editors have built-in terminals that you can run things in, I haven't often seen them also have the ability to jump to any errors that occurred during compilation, though I haven't actually gone looking. I am told, however, that this is fairly commonplace in applications known traditionally as IDEs. Emacs, eager not to disappoint, comes with support for this built in.

When using Emacs for compiling a program, it will usually open a compilation buffer. If you have compilation errors, you can then use one of a number of keyboard shortcuts (my favorite is `M-g n`) to jump to the next compilation error. The best part is: this works from any buffer, even one not in compilation mode. In other words, if you have errors, you can jump through them sequentially without ever going back to the compilation buffer.

Indentation and programming indentation (chapter 24 and chapter 26.3)

There's a number of cool indentation tricks listed in the indentation and the program indentation chapters, but I think the one I found that the most useful was for indenting languages derived from C. For a detailed explanation, check out this page on customizing indentation in C, C++, and Java, but the short version of it is that you can analyze the various syntactic parts of your program directly in your buffer, and then tell Emacs how it should indent a specific construct.

Org mode

What about org mode? You'd think that that was one of the best parts of Emacs, and that I'd have a ton of tips to share, right? Well, you're probably right, but org mode comes with its own manual (another pretty massive text document), and I haven't made my way through that one yet. Trust me, though: it's on my list of must-reads this year. I'll get to it later.


So that's the first item on my SMART goal list completed. I'm not quite ready to start configuring Emacs from scratch, but it doesn't feel that daunting anymore. Now: do I go to work on Kubernetes, or do I spend some extra time with org mode to get really comfy with Emacs?

Footnotes

Seriously, if you're into Vim and haven't read it: I highly recommend you take the time to go through it. For a beginner it's an absolute gem, and even for experienced vimmers, I'm sure there's a good few tricks in there that you didn't know about.

I have actually found a grey hair in my beard this month (sshhh!) and I attribute that wholly to Emacs.


Use git to restore parts of a file

The restoration movement
The git logo with the 'git restore --patch' superimposed next to it.

One of my favorite, little-known git tricks is using the --patch option (-p for short) to affect only parts of a file when you're adding, committing, or---as I recently found out---/restoring/.

Now some of you may be thinking 'hold up! Restoring a file? What's that?' If you haven't been following git development for a while (while a solid project, it's not exactly the JS framework du jour), you'd be excused for not knowing about the two new git commands switch and restore. Introduced in git 2.23.0 (released in August 2019), these two commands were introduced to offload the checkout command for switching branches and restoring files. As you may expect, switch allows you to switch branches (and create new ones in the same way as checkout -b by using switch -c), and restore takes over for restoring files from a previous commit.

git restore -p

While there is lots to be said about the new commands and all the options they accept, I advise you to go check out the official docs for ~switch~ and ~restore~ for that. This post is about using restore with the -p option, specifically.

Sometimes (read: quite often) when making changes to a file, you have some changes that you want to keep and some that you want to discard. In situations like this, there's a number of ways to go about it. If the changes are ready to be staged or committed, you can use the -p option to pick the parts you want and then just restore the file afterwards. However, in the event that the changes you want aren't quite ready just yet, and you don't want to stage, restore, and unstage, you might want to consider using restore -p.

By default, restore -p brings up an interactive prompt that lists the differences between your working tree and the last commit, asking you in turn whether you want to discard each hunk. This way, if you have some changes in one part of the file that you don't want to keep, you can discard those, but keep the rest. As per usual with the --patch option, you can also split hunks or edit them manually if you need to.

When not given any paths to act on, git restore -p will ask you about all your unstaged changes in all your files. To limit it to a specific file, supply a pathspec: git restore -p myfile.txt. The documentation says that '--patch can accept no pathspec and will prompt to restore all modified paths.'1, but when testing it out using git version 2.23.1, using pathspecs was no problem at all.

In addition to the default patch restoration functionality, some of the other notable options are:

~-s <tree>~ / ~--source=<tree>~
Use this option if you want to restore a file to a different commit than HEAD. What you pick as your source can be another branch, a commit hash, or something like HEAD~2. Without the --patch flag, this will change the entire file to reflect the state at that source.
~-S~ / ~--staged~
Not to be confused with the lowercase -s above, the -S option allows you to restore changes from the index (your staging area or 'added files'). By default, restore only works on files in your work tree. Using this option will make it work only on files in your staging area. To act on both at once, supply both the -S and the -W (--worktree) options: git restore -SW.

Footnotes

1https://git-scm.com/docs/git-restore#Documentation/git-restore.txt---patch