我正在了解 zsh 更强大的制表符完成和扩展功能,但当我在 emacs 中使用 Mx shell 运行 zsh 时它们似乎不起作用:
cat $PATH<TAB>
在终端中扩展 tab 变量,但在 shell 模式下它只会发出哔哔声。
我研究了 emacs 环境并发现了以下内容:
TAB(翻译自)运行命令completion-at-point,它是“minibuffer.el”中的交互式编译的Lisp函数。
它与 TAB 绑定。
(完成点)
对 point 周围的文本执行补全。补全方法由 `completion-at-point-functions' 决定。
Completion-at-point-functions 是定义在 `minibuffer.el' 中的变量,其值为 (tags-completion-at-point-function)
因此我推测我需要向 finish-at-point-functions 添加一个函数,但是添加哪一个呢?
答案1
您不能在 中使用 shell 补全M-x shell
。当您按下 时,Emacs 会将输入一次一行地发送到 shell RET
。当您按下TAB
时,会触发 Emacs 的内置补全。在 shell 模式下,Emacs 会尝试跟踪您的当前目录并补全文件名,但仅此而已。
根据您正在与之交互的程序,shell 模式可能很好(因为您可以获得 Emacs 版本的所有功能,而不是 shell 或其他程序提供的任何有限功能)或者不那么好(因为您无法获得 shell 或其他程序提供的任何漂亮功能。当运行 zsh 时,您属于后一类。在 Emacs 内部,您可以运行M-x term
以运行 Emacs 内部更完整的终端仿真器,您可以在其中直接与底层程序交互。您获得了一些,但也失去了一些:Term 模式的回滚功能很差。
你可以在同一个缓冲区中切换 Term 模式和 Shell 模式。您必须以 Term 模式启动缓冲区,然后才能使用这些函数在两者之间切换。(注意:我最初没有编写这些函数,它是贡献给 Emacs Wiki 的,我还没有找到源代码。我对其进行了轻微的改编;警告:我没有测试过此代码。)
(defun term-switch-to-shell-mode ()
(interactive)
(shell-mode)
(set-process-filter
(get-buffer-process (current-buffer)) 'comint-output-filter )
(compilation-shell-minor-mode 1)
(comint-send-input))
(defun shell-switch-to-term-mode ()
(compilation-shell-minor-mode -1)
(font-lock-mode -1)
(set-process-filter
(get-buffer-process (current-buffer)) 'term-emulate-terminal)
(term-mode)
(term-char-mode)
(term-send-raw-string (kbd "C-l")))
答案2
尝试多术语。它是唯一一个似乎可以与 zsh 很好地配合的 Emacs 终端模式。
在“Mx shell”模式下,您无法使用 zle(很酷的 zsh 制表符补全功能)。
使用“Mx term”(和 ansi-term),终端可以捕获您想要路由到 emacs 的大多数 emacs C 和 M 命令。
MultiTerm 可让您轻松设置希望 emacs 捕获的命令以及希望路由到终端的命令。它预装了运行良好的默认设置。
还有一个提示:将以下内容添加到您的 .zshrc 中,以允许 emacs 在您使用 cd 时跟踪您的当前目录。
if [ -n "$INSIDE_EMACS" ]; then
chpwd() { print -P "\033AnSiTc %d" }
print -P "\033AnSiTu %n"
print -P "\033AnSiTc %d"
fi
答案3
这是一个复杂的问题。让我们看看这背后到底发生了什么。
根据 Emacs Doc,<Tab>
必定会起作用completion-at-point
。下面是它completion-at-point
的作用:
对点周围的文本进行完成。
完成方法由“完成点函数”决定。
检查 Emacs Doc,变量completion-at-point-functions
位于此处:
频次完成函数(completion-at-point-functions)是在‘minibuffer.el’中定义的变量。
其值为 (comint-completion-at-point t)
缓冲区中的本地壳;全局值是
(标签在函数点完成)
然后让我们看看文件comint-completion-at-point
icicle-comint-dynamic-complete' in
comint-completion-at-point 是ob-sh.el的别名。
(comint-完成点)
在点处动态执行完成。
调用“comint-dynamic-complete-functions”中的函数,但
冰柱函数替代,直到函数执行完成
返回非零。返回该值。
因此很明显,您需要将您最喜欢的完整函数添加到comint-dynamic-complete-functions
变量中。
(add-to-list 'comint-dynamic-complete-functions 'icicle-shell-dynamic-complete-filename)
(add-to-list 'comint-dynamic-complete-functions 'icicle-shell-dynamic-complete-command)
答案4
感谢 bengineerd 的精彩解释和片段 :)
我将 zsh 与 grml 配置一起使用 - chpwd 函数完全解决了这个问题,但有些东西坏了。以下是修改后的代码片段,可恢复箭头键自动完成功能:
if [ -n "$INSIDE_EMACS" ]; then
# keep the old chpwd command and apply it later
which zsh_chpwd >&/dev/null || \
eval "$(echo "zsh_chpwd() {"; declare -f chpwd | tail -n +2)"
chpwd() {
print -P "\033AnSiTc %d"
zsh_chpwd
}
print -P "\033AnSiTu %n"
print -P "\033AnSiTc %d"
fi
并将其附加到 .zshrc 的底部
更新
为了修复终端中的跳转和删除问题,我找到了几个有助于改善整体体验的来源:
- https://github.com/rlister/emacs.d/blob/master/lisp/multi-term-cfg.el
- https://stackoverflow.com/questions/14444265/kill-the-terminal-buffer-with-cd
并将它们组合成https://github.com/0x17de/emacs-config/blob/master/multi-term-settings.el
以下是关键部分的摘录,可启用 meta+箭头键(单词跳转)和向后删除单词:
(add-hook 'term-mode-hook
(lambda ()
(define-key term-raw-map (kbd "M-d") 'term-send-raw-meta)
(define-key term-raw-map (kbd "M-<left>") 'term-send-backward-word)
(define-key term-raw-map (kbd "M-<right>") 'term-send-forward-word)
(define-key term-raw-map (kbd "M-<backspace>") 'term-send-backward-kill-word))
(add-to-list 'term-bind-key-alist '("C-z e" . term-send-esc))))
(defun term-send-esc ()
"Send ESC in term mode."
(interactive)
(term-send-raw-string "\e"))
如果您使用 ssh 进入远程服务器,在某些 vim 上进入插入模式并卡住,因为尚无 ESC 映射,请参见 git repo 中的 term-send-esc。
也只是有点相关 - 使用 grml zshrc 如果您希望箭头键完成,如果您有少于五个选项,请搜索
zstyle ':completion:*' menu select=5
并将“5”(默认值)更改为“1”