【分享】vterm 才是Emacs中我最棒的终端模拟器

最近再次在macOS 上开始 vterm,发现性能很好,终于不用经常切换到 iterm2 使用终端了。

我用的是 zsh,只要在 ~/.zshrc 中加入以下配置,dired 就能和 term 同步目录了。

# vterm
if [[ "$INSIDE_EMACS" = 'vterm' ]] \
    && [[ -n ${EMACS_VTERM_PATH} ]] \
    && [[ -f ${EMACS_VTERM_PATH}/etc/emacs-vterm-zsh.sh ]]; then
    source ${EMACS_VTERM_PATH}/etc/emacs-vterm-zsh.sh
fi

如果希望有更好的体验,强烈推荐安装 vterm-toggle,方便随时调出 vterm。再订制一个 vterm-compile 函数,就可以不用 M-x compile 了。

不过官方README中推荐的 vterm-compile 设置有点问题,如果 vterm 窗口正在显示时就没法工作,我稍加修改一下,就可以在任意的非 vterm buffer 中通过 vterm-compile 命令进行编译,分享给需要的朋友。

在 vterm-compile 中加入了compilation-shell-minor-mode, 如果编译出错,就可以直接点击出错位置跳转到相应的行,非常方便。

 (defvar vterm-compile-buffer nil)
  (defun vterm-compile ()
    "Compile the program including the current buffer in `vterm'."
    (interactive)
    (let* ((command (eval compile-command))
           (w (vterm-toggle--get-window)))
      (setq compile-command (compilation-read-command command))
      (let ((vterm-toggle-use-dedicated-buffer t)
            (vterm-toggle--vterm-dedicated-buffer (if w (vterm-toggle-hide)
                                                    vterm-compile-buffer)))
        (with-current-buffer (vterm-toggle-cd)
          (setq vterm-compile-buffer (current-buffer))
          (rename-buffer "*vterm compilation*")
          (compilation-shell-minor-mode 1)
          (vterm-send-M-w)
          (vterm-send-string compile-command t)
          (vterm-send-return)))))

这是本人的 vterm 设置

12 个赞

看起来效果非常好,我周末试试

想问 vterm 与 alacritty 区别

vterm 是利用 libvterm 这个抽象终端实现,然后渲染进 Emacs buffer 里.

Alacritty 是用 OpenGL 渲染的, 终端实现是自己用 Rust 写的.

vterm 的优点是和 Emacs 结合紧密, 至于 OpenGL 渲染, 对于终端来说, 作用有限. 反正终端基本都是纯文本.

4 个赞

vterm 里面可以 Ctrl-x Ctrl-e 编辑大段内容吗?

刚刚试了下,C-x C-e 是可以用的,不知道你说的编辑大段内容是怎么样的应用?

并且在vterm中使用emacsclient很丝滑,宛如eshell 中使用 find-file

1 个赞

还有这样用啊,在emacs 里面怎么不直接 find-file? 我试过用在vterm 里跑 vim,也丝滑 :grinning_face_with_smiling_eyes:

刚刚试了 emacsclient 确实很快,:cow:

这是无限套娃的节奏!

1 个赞

就…我写python目录一般是这样的

.
├── poetry.lock
├── pyproject.toml
├── readme.org
├── src
│   ├── __init__.py
│   ├── __pycache__
│   ├── config.py
│   └── var_cov.py
└── tests
    ├── __init__.py
    ├── __pycache__
    └── test_src.py

在子目录下 python xxx.py运行完,符合预期写到 org 文档里面,不符合就emacsclient打开

感谢分享工作流。
我个人还是比较习惯 C-x C-f 打开文件,会跟随当前 vterm 所在的目录,可能因为我主要用原生按键习惯了😄。

用 emacsclient 的话在 eglot 中会提示有些特性不支持,不过其他基础编辑功能都很好。

就是从命令行进入多行编辑状态。在 bash 里的快捷键是 Ctrl-x Ctrl-e,在 fish 里是 Meta-e

   edit-and-execute-command (C-xC-e)
          Invoke  an  editor  on  the current command line, and execute the
          result as shell commands.  Bash attempts to invoke $VISUAL, $EDITOR,
          and emacs as the editor, in that order.

你打开 iTmer2 试一下,看看跟 vterm 的是不是一回事。

原来是这个功能。vterm 默认没有定义 C-x C-e,所以目前执行还是会执行 emacs 的 eval-last-sexp.

这个需求得请教一下 @jixiuf 大佬了。我目前没找到相关的命令。

看来是不支持了。

虽然我还没用上 vterm,但是正在考虑让 separedit 支持 vterm。不过麻烦的是 vterm--get-prompt-point 函数不能返回正确的位置。例如,按 C-p 召回上一条命令:

$ echo '1
2
3'

M-: (vterm--get-prompt-point) 只能返回 3 的位置,正确应该是返回 $ 的位置。这算不算是 bug?

如果手动 C-a 移动光标到 prompt 位置,那么目前已经可以通过 separedit 进行多行编辑了:

image

开 vterm copy mode,然后执行 vterm-previous-prompt 是可以移动到 $ 那里的。 你看看这个函数里面调用了什么

vterm-previous-prompt 调用的是 vterm--get-prompt-point,而且要指定参数 N,无法自动探测到期望的位置:

$ echo '1
2
3'

上边执行 (vterm-previous-prompt 1) 返回的是 2 的位置。

vterm--get-prompt-pointvterm-[previous,next]-prompt 都把是否携带 vterm-prompt 属性当作判断 prompt 的依据,可是默认配置下 prompt 根本不带这个属性。可能这就是 bug 的原因?

先前还试过不断 (vterm-send-left),直到光标位置不动为止,后来发现这些 send 函数都是异步的,执行之后立即获取到的 (point) 没有变化:

(let ((last-point (point)))
  (vterm-send-left)
  (list last-point (point)))
;; => (81 81)

有没有试试这个?

我前面截图展示的功能里做过很多尝试。

而且,不开 copy mode 无法用 overlay 锁定编辑内容:

image

我想到一个更好的方案,就是把 C-x C-e 绑定到一个函数,这个函数接收 vterm 的 buffer name 用于等会儿定位到要发回去的地方。
然后函数里触发打开一个 shell mode 的空 buffer,在这个 buffer 里编辑完之后,然后按 C-c C-c 把 buffer 中的内容发送给函数参数中传来的 vterm 所在的 buffer。
在这个临时 buffer 里可以绑定两个快捷键,一个是只发送 buffer 内容但不执行,一个是发送并执行(就多 send 一个回车的动作给 vterm buffer)。

如果是新的编辑,也许可以这么做。

但如果是召回上一条命令,进行修改,然后发给 vterm。就会遇到我说的问题。

首先你要定位 prompt,获得还未提交执行的内容。然后保护现场不被修改,把内容放到另外一个地方编辑。编辑好了发回 vterm,覆盖旧的内容,解除保护。

1 个赞

vterm和evil的协作该如何避免冲突呢?官方的issue里好像有关于这个问题的讨论,建议似乎是在vterm中默认使用emacs-state,然后需要的话再开启zsh的vim-mode,这感觉就有点多此一举了……