Werden wir Helden für einen Tag

Home | About | Archive

Advent of emacs #5: How I use shell in emacs

Posted on Dec 5, 2022 by Chung-hong Chan

Hongchao Liu thinks that emacs is the “2D command-line interface”. Sure enough, I could do many things with the 2D command-line interface. However, there are still many things I prefer to use the 1D command-line interface, i.e. the Linux shell. Hate to admit, but I prefer to manage files with the Linux command line than the sometimes-cumbersome dired. “But that can well be my prejudice.”™

For a long time, I needed to find a good terminal emulator and settled with Kitty. But I use the boring gnome-terminal now.

One can also run shell inside emacs. Just talking about built-in ones, there are shell, term, ansi-term, and eshell. Advanced emacs users tend to use eshell, probably because it can execute both shell and emacs lisp.


Mixing shell and emacs lisp in eshell

But I am not very excited by mixing shell and emacs lisp 1. Instead, I use vterm by Lukas Fürmetz et al. Because it is fast, no-BS, highly compatible, and exactly as advertised: “Using vterm is like using Gnome Terminal inside Emacs”. Unlike eshell, vterm respects my .zshrc configuration file. Some tricky interactive shell programs such as top and less work, even hipster favorites such as htop and bat


Maybe I am a hispter.

Installation was not straightforward. But with the currtent emacs 28, it is now super easy.

(use-package vterm)

I have two bullet points on how to use vterm more joyfully.

vterm-yank

Running vterm inside emacs, instead of a standalone terminal emulator, is usually for copying and pasteing (or using the emacs terminology: killing and yanking) text from and to other buffers. For pasting yanking text to vterm, I recommand making C-y (the usual yank key combination) map to vterm-yank, i.e.

(use-package vterm
  :bind 
  (:map 
  vterm-mode-map
		("C-y" . vterm-yank))
)

For copying killing text from vterm, it is a bit complicated. First, it is really 1D. And the cursor can really walk in one direction. The up and down arrow keys are used to look at command history in a shell. The orthodox emacs combinations, C-p (previous-line) and C-n (next-line) also functions as arrow keys.

In order to kill text from vterm, one needs to switch on copy mode by pressing M-x vterm-copy-mode (or C-c C-t) to enable “vterm-copy-mode”. Then the cursor can be moved in two dimensions and the usual way of killing text works. After that, just M-x vterm-copy-mode once again to go back to the default mode, i.e. “vterm-copy-mode” disabled.

vterm-send-next-key

Another issue is that some shell programs use the same key combinations of emacs. For whatever reasons, you might need to run nano under vterm sometimes 2. You might find that you can’t quit nano by pressing C-x. It is because emacs intercepts your C-x and thinks: “um… maybe you still want to press something, e.g. e.”

There are actually two solutions. First solution is to use the crazy array of vterm-send-* commands. In this example, you can press M-x vterm-send-C-x. However, my preferred solution is to use the command vterm-send-next-key, i.e. M-x vterm-send-next-key and then C-x.

But it can get tedious very quickly. I map C-q to the command vterm-send-next-key (I will talk about C-q later, I promise), i.e.

(use-package vterm
  :bind 
  (:map 
  vterm-mode-map
	  ("C-y" . vterm-yank)
	  ("C-q" . vterm-send-next-key))
)

Therefore, I can just press C-q C-x in this situation to quit. But, it can be more informative. I don’t like that vterm-send-next-key doesn’t give me any visual clue that it is activated. I write myself a little command in emacs lisp.

(use-package vterm
  :init
  (defun vterm-send-next-key-verbose ()
	(interactive)
	(progn
	  (message "vterm-send-next-key enabled.")
	  (vterm-send-next-key)
	))
  :bind
  (:map 
  vterm-mode-map
		("C-y" . vterm-yank)
		("C-q" . vterm-send-next-key-verbose))
)

After pressing C-q, I know the next key will not be captured by emacs but capturd by the program running inside vterm.

After talking about the terminal, I will talk about how / why I use the GUI version of emacs but not the terminal version. Also, how I fix some quirks.

Update

Deep-learnest’s remark on Mastodon: “I recommend M-x shell for sessions where you need to capture large outputs, or for long-lived interactive sessions in the shell. The ability to have an infinite log of easily accessible text that you can navigate in the regular emacs ways is very powerful and eventually grows on you. I only use vterm for applications that need a proper terminal (htop, btop, nvtop, …), but for everything else I much prefer the feeling of M-x shell.”

And yes, shell is better in this case because the cursor is not walking in 1D (as vterm) but in 2D. Yanking and killing work the same way as a regular buffer.

Thank you, Deep-learnest!


  1. Usual disclaimer: eshell is a shell, not a terminal emulator. 

  2. You might sometimes need to run emacs in tty mode under vterm, for example in an SSH session. But I try not to anymore. I will write about it later. 


Powered by Jekyll and profdr theme