无痛使用 Emacs 中的交互式 shell
在 前一篇文章 中,我们讨论了如何创建显式函数来调用单个 shell 命令。现在,让我们进入交互式 shell 中更有趣的内容。
Emacs 交互式 shell 的 API
派生 shell 的主要命令是: shell (可以接一个 buffer 参数)
,与单个 shell 命令类似,我们可以通过操作一些隐藏的变量来更改其行为。重要的变量如包括:
default-directorylocation | 指定从哪个地方运行 shell |
explicit-shell-file-name / shell-file-name | 执行的 shell 解释器 (例如 bash ...) |
explicit--args | 交互式 shell 的启动参数 |
注意, shell-command-switch
对对交互式 shell 没有用处,因此没有列在此处。相反,我们有了新的变量 explicit-<INTEPRETER>-args
, 它允许为交互式 shell 提供一系列参数。您可能已经在前文中见过它了,函数 eval-with-shell-interpreter
中用到了这个变量。
重用包装器
因此,我们可以重用前面定义的包装器 with-shell-interpreter
,这里有一个小麻烦: 默认的 shell
命令会提示用户指定解释器路径。每次都要提醒太麻烦了,我们可能更系统让它的默认值在启动本地 shell 时使用 shell-file-name
的值,而在启动远程 shell 时使用 default-remote-shell-interpreter
的值。
如果我们想要更改 shell 的位置,那么创建一个显式定义 :interpreter
的命令会更实用。要禁止 shell
提示输入解释器路径,我们必须使用默认的前缀参数来调用它(例如 C-u M-x shell
)。要以编程方式重现此行为,我们必须使用 let 将 current-prefix-arg
设置为 '(4)
。
例如:
(defun my/zsh-local ()
(interactive)
(with-shell-interpreter
:path "~"
:interpreter "zsh"
:form
(let (current-prefix-arg '(4))
(shell))))
(defun my/bash-on-raspi ()
(interractive)
(with-shell-interpreter
:path "/ssh:pi@raspi:/~"
:interpreter "bash"
:form
(let (current-prefix-arg '(4))
(shell))))
另一个包装器
这仍然很麻烦。因此,让我们创建另一个派生辅助函数来防止重复。
;; ------------------------------------------------------------------------
;; MAIN
(cl-defun prf-shell (&key path interpreter interpreter-args command-switch)
"Create a shell at given PATH, using given INTERPRETER binary."
(interactive)
(with-shell-interpreter
:form
(let* ((path (or path default-directory))
(is-remote (file-remote-p path))
(interpreter (or interpreter
(if is-remote
with-shell-interpreter-default-remote
shell-file-name)))
(interpreter (prf/tramp/path/normalize interpreter))
(shell-buffer-basename (prf-shell--generate-buffer-name is-remote interpreter path))
(shell-buffer-name (generate-new-buffer-name shell-buffer-name))
(current-prefix-arg '(4))
(comint-process-echoes t))
(shell shell-buffer-name))
:path path
:interpreter interpreter
:interpreter-args interpreter-args))
;; ------------------------------------------------------------------------
;; HELPERS: BUFFER NAME
(defun prf-shell--generate-buffer-name (is-remote interpreter path)
(if is-remote
(prf-shell--generate-buffer-name-remote interpreter path)
(prf-shell--generate-buffer-name-local interpreter path)))
(defun prf-shell--generate-buffer-name-local (&optional interpreter _path)
(if interpreter
(prf-with-interpreter--get-interpreter-name interpreter)
"shell"))
(defun prf-shell--generate-buffer-name-remote (intepreter path)
(let ((vec (tramp-dissect-file-name path)))
(prf-shell--generate-buffer-name-remote-from-vec vec)))
(defun prf-shell--generate-buffer-name-remote-from-vec (vec)
(let (user host)
(concat
(tramp-file-name-user vec) "@" (tramp-file-name-host vec))))
请注意,我们设置 comint-process-echos
为 t
来确保能正确地追踪目录变化。目录跟踪(简称 ditrack) 是 Emacs 的一项功能,能在执行 cd
时跟踪当前目录。此外,我们还提供了一些函数来让 shell 缓冲区的名称更加明确。
我们重写的命令变成了:
(defun my/zsh-local ()
(interractive)
(prf-shell :path "~" :interpreter "zsh"))
(defun my/bash-on-raspi ()
(interractive)
(prf-shell :path "/ssh:pi@raspi:/~" :interpreter "bash"))
prf-shell
的代码可以在包 prf-shell 中找到。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论