Overview
Using Emacs in daily life, I rely on vterm terminal emulator instead of eshell. However, I've
noticed that there are certain limitations in terms of integration between vterm and Emacs. While
the package supports some user-accessible functions, they are not sufficient. I've always wanted the
integration level as VSCode, and at least it should be able to open files from the terminal
interface. Well, this is one of essential features of the terminal emulator running on editors, so I
thought that having this kind of issue was ridiculous. So I tried to find solutions by googling
about it, but none of them had a one-shot method to achieve this. So, I made up my mind to write
functions by myself.
In this article, I am going to describe the following things:
- A callback function to open files from vterm
- Functions to manage vterm session
Note that since I am using Doomemacs right now, the keymap setting could differ from yours. If you do not want to read any details about functions that I wrote, just use the following settings.
(require 'filenotify)
(defvar my:get-vterm--backup nil)
(defun my:vterm-new ()
(interactive)
(if (not (string-match-p "vterm" (buffer-name (current-buffer))))
(setq my:get-vterm--backup (current-buffer)))
(call-interactively #'+vterm/here))
(defun my:vterm-toggle ()
(interactive)
(let ((cnt (cl-remove-if #'null
(mapcar (lambda (x)
(and (string-match-p "vterm" (buffer-name x)) (buffer-name x)))
(buffer-list)))))
(if (null cnt)
(progn
(setq my:get-vterm--backup (current-buffer))
(call-interactively #'+vterm/here))
(if (and (string-match-p "vterm" (buffer-name (current-buffer)))
my:get-vterm--backup)
(my:vterm--restore)
(call-interactively #'my:vterm--select)))))
(defun my:vterm--restore ()
(switch-to-buffer my:get-vterm--backup)
(setq my:get-vterm--backup nil))
(defun my:vterm--select (choice)
"Argument CHOICE user's selection."
(interactive
(list (completing-read "Choose: "
(cl-remove-if #'null
(mapcar (lambda (x) (and (string-match-p "vterm" (buffer-name x)) (buffer-name x)))
(buffer-list)))
nil t)))
(car (split-string choice " "))
(setq my:get-vterm--backup (current-buffer))
(switch-to-buffer choice))
(after! vterm
;; Following must be used with bash alias:
;; => alias eo='echo $1 > ~/.config/emacs/.local/cache/vterm-pipe'
(let* ((pipe-file (expand-file-name "vterm-pipe" user-emacs-directory))
(func-open-file (lambda (event)
(find-file
(with-temp-buffer
(insert-file-contents (expand-file-name "vterm-pipe" user-emacs-directory))
(string-trim (buffer-string)))))))
(file-notify-add-watch pipe-file '(change) func-open-file))
(add-hook 'vterm-mode-hook (lambda ()
(evil-emacs-state))))
File open from vterm - filenotify
Since Emacs-28.1, Emacs supports the filenotify package, which makes it possible to watch any change
from the file. It means that whenever I write any to the file, Emacs can get the triggered event
from the write. Let's register a callback function for the vterm-pipe in user-emacs-directory.
(file-notify-add-watch pipe-file '(change) callback-func)
Add the following code to $HOME/.bashrc to use the alias eo command. Now, using the eo alias will
trigger the event and invoke the callback function. It's done.
alias eo='realpath $1 > ~/.config/emacs/.local/cache/vterm-pipe'Vterm session management
Unfortunately, vterm does not support any functions to manage its session. And a function to toggle
it is not perfect. Let's improve it by using an interactive menu. You can toggle the vterm session
with my:vterm-toggle. In the code, there are many to refactor but it is sufficient to resolve the
lack of session management and inefficient UI toggle.
Wrap up
Since I started to learn how to write code in elisp, I have been able to use Emacs efficiently.
Beyond the simple editor, now I can see why Emacs has been loved by lots of developers. I know, this
should be the same for VI/M users :P.