zsh 补全在 emacs shell 中不起作用

zsh 补全在 emacs shell 中不起作用

我正在了解 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' incomint-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/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”

相关内容