Emacs: Friend or Foe?

Frustrated with Emacs? Here's how to wrestle it into submission.
Multiple key sequences

Now, what about bindings involving multiple keys? For example, I like to use the key sequence C-d C-d to delete a line (somewhat like its vi counterpart), and C-d g to delete text from point to the end of the buffer. The second argument to global-set-key can include multiple keys, but the problem is that C-d already has a meaning. We want to use C-d as a “prefix” key for the commands C-d C-d and C-d g, so first we have to unbind C-d. Here's what we do:

; Various keys for nuking text (global-unset-key "\C-d")
(global-set-key "\C-d g" 'my-nuke-to-end)
(global-set-key "\C-d\C-d" 'my-nuke-line)

We simply use global-unset-key to unbind C-d, and then bind new meanings to C-d g and C-d C-d. However, these functions are bound to the mysterious functions my-nuke-to-end, and my-nuke-line, which the astute among you will notice aren't standard Emacs functions. We have to define them.

Defining a new function

Defining an Emacs LISP function is rather simple. Of course, Emacs functions can be quite powerful and complex, but in this case we're going to use the function to call a short sequence of other functions, which isn't so frightening. In general, if you need a key binding to call several functions in sequence, you must define a new function to wrap the sequence in.

Here is the definition of my-nuke-to-end, which should be placed above the corresponding call to global-set-key, which uses it, in the .emacs file.

(defun my-nuke-to-end ()
  "Nuke text from here to end of buffer."
  (interactive "*")
  (kill-region (point) (point-max)))

The defun function takes as arguments the function name to define, a list of arguments (which, here, is empty), followed by the body of the function. Note that the first line of the body is a string constant, which is a short description of the function. This string is displayed when describe-key or describe-function is used to display information about my-nuke-to-end.

The second line of the body is a call to the interactive function. This is required for functions that are bound to keys. It simply tells Emacs how to execute the function interactively (that is, when called from a key sequence). The argument to interactive, “*”, indicates that the function should not be executed within a read-only buffer. Check out the documentation for interactive if you want the gritty details. (See the sidebar “Getting the Emacs LISP Manual” for information on obtaining this documentation.)

The last line of the body uses the Emacs internal function kill-region to delete a region of text. The two functions point and point-max return the current location of point, and the position of the end of the buffer, respectively. kill-region deletes the text between these two locations.

The definition for my-nuke-line is somewhat more involved, because there is no single Emacs function that this operation maps to. Here it is:

(defun my-nuke-line (arg)
   "Nuke a line."
   (interactive "*p")
   (beginning-of-line nil)
   (kill-line arg)
   (if (= (point) (point-max))
        (forward-line -1)
        (end-of-line nil)
        (kill-line arg)
        (beginning-of-line nil))))

First of all, we see that this function now takes an argument, which we have called arg. Many Emacs key functions take a single numeric argument, which you can specify by prefixing the key with C-u, followed by a number. (That is, unless you've rebound C-u, as we have.) This numeric argument changes the behavior of certain functions. Here, arg is passed along to kill-line, used on lines 4 and 9 of the function body.

my-nuke-line is essentially a wrapper for kill-line, but takes care of a special case for the last line in the buffer. In this case, we want to delete the newline before the last line, which causes the last line to be clipped out altogether (otherwise, Emacs deletes the line, but leaves a blank one in its place). After interactive is called (with the “*p” argument, which causes arg to be converted to a number), beginning-of-line moves point to (surprise!) the beginning of the line. kill-line is then invoked. Note that kill-line only kills text from point to the end of the line; not the entire line.

Next, we compare the cursor position (point) with the end-of-buffer position (point-max). If they are equal, then we are trying to delete the last line of the buffer, and want to kill the previous terminating newline. We move back a line (using forward-line with an argument of -1), move to the end of that line, kill the rest of the line (which consists only of the terminating newline), and move back to the beginning of the line. All of this results in the last line of the buffer being deleted.

I'm sure that there are Emacs gurus out there that can find much better ways to accomplish the same thing; please bear with me. I've been brainwashed by vi. One of Emacs' better features is that there are many ways to modify its behavior.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Thanks - nice article for an

eyolf's picture

Thanks - nice article for an new emacs convert (from vi...)
A couple of typos in the code for multiple key sequences:

; Various keys for nuking text (global-unset-key "\C-d")
(global-set-key "\C-d g" 'my-nuke-to-end)
(global-set-key "\C-d\C-d" 'my-nuke-line)

which I believe should be:

; Various keys for nuking text
(global-unset-key "\C-d")
(global-set-key "\C-dg" 'my-nuke-to-end)
(global-set-key "\C-d\C-d" 'my-nuke-line)

At least that worked for me.

Compare with this..

ILoveEmacs's picture

Ever tried viper mode?