;;;;;
title: C-d to close eshell
tags: eshell, emacs, elisp, config
date: 2013-08-17 02:25
format: md
;;;;;

One of the "tricks" that I have learned to use when working with
terminals is using `C-d` to close them, or when working on a TTY
logout. It somehow grew to the extent that if I can't use it, I get
annoyed, like with `eshell`.

I have customized `ansi-term` to immediately close its buffer after the
shell quits. This makes it very easy to start an `ansi-term`, which I've
bound to `C-c t`, run a quick command (perhaps `make`, or similar), `C-d`,
and I'm out. I want that for my `eshell` too.

There are a few conditions that I want met before the buffer is
killed, though.

1. Since `eshell` is an Emacs mode like any other, `C-d` is usually used
   to forward-kill characters, I don't want to lose this.
2. I only want it to quit when the line of input is empty.

The following piece of code make sure these conditions are met.

1. It interactively calls `delete-char`, which keeps keybindings like
   `C-4 C-d` to delete 4 characters working.
2. It catches the error condition which is signaled whenever
   `delete-char` can't do it's job (like when there's nothing left to
   delete in the buffer).
3. It checks to make sure that the signaled error is the `end-of-buffer`
   error. I don't want to kill the buffer if I try to delete more
   characters than are in the buffer because I feel that could cause
   irritating surprises.
4. It checks of the cursor is at the `eshell` prompt. This, combined
   with only responding to the `end-of-buffer` error, makes sure we're
   on an empty line and not just at the end of the input. Sometimes
   keys are pressed at the wrong time and I don't want to have to
   re-type a command just because I was being an idiot.
5. If the right conditions aren't met, signal the error again so I can
   see what's going on.

``` emacs-lisp
(defun eshell-C-d ()
  "Either call `delete-char' interactively or quit."
  (interactive)
  (condition-case err
      (call-interactively #'delete-char)
    (error (if (and (eq (car err) 'end-of-buffer)
                    (looking-back eshell-prompt-regexp))
               (kill-buffer)
             (signal (car err) (cdr err))))))
```

I then bind this to `C-d` in eshell.

``` emacs-lisp
(add-hook 'eshell-mode-hook
          (lambda () (local-set-key (kbd "C-d") #'eshell-C-d)))
```
