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.