Emacs shell 模式中的 Bash 自动完成

Posted

技术标签:

【中文标题】Emacs shell 模式中的 Bash 自动完成【英文标题】:Bash autocompletion in Emacs shell-mode 【发布时间】:2010-09-14 21:12:02 【问题描述】:

在 GNOME 终端中,Bash 进行智能自动完成。例如

apt-get in<TAB>

变成

apt-get install

在 Emacs shell 模式下,即使在我明确来源 /etc/bash_completion 之后,这种自动完成功能也不起作用。上面的示例坚持为in 或使用当前目录中的文件名自动完成,而不是有效的apt-get 命令选项。据推测,这是因为 Emacs 正在拦截 Tab 按键。如何在shell-mode 中启用智能自动补全?

【问题讨论】:

对于 emacs 的新手... emacs 中还有其他 shell 模式。比如eshell-mode,它有tab补全。更多信息:masteringemacs.org/articles/2010/11/01/… eshell 的自动补全功能只对本地目录有效,如果你 ssh 到另一台机器,你会突然失去这个能力。 【参考方案1】:

我知道这个问题已经问了三年了,但我也一直有兴趣解决这个问题。网络搜索让我找到了一个 elisp,它使 Emacs 使用 bash 在 shell 模式下完成。无论如何,它对我有用。

查看https://github.com/szermatt/emacs-bash-completion。

【讨论】:

+1 表示有勇气回答一个三岁但仍然完全相关的问题。谢谢! 这个已经在melpa里了melpa.org/#/bash-completion,用M-x package-list-packages安装很简单,但是最后还是对我不起作用,不知道为什么,可能是我的emacs( 24.3.1) 或 bash (4.3.30) 版本。文档说“bash-completion.el 对操作系统和 BASH 版本非常敏感......”然后是我的版本不存在的列表。 不幸的是,待定 issue #6 这对于当前的 CLI 工具似乎不是那么有用。 第 6 期已关闭,因为(释义)。【参考方案2】:

在 emacs shell 中,实际上是 emacs 进行自动完成,而不是 bash。如果 shell 和 emacs 不同步(例如,通过使用 pushd、popd 或一些改变 shell 当前目录的 bash 用户函数),那么自动完成将停止工作。

要解决这个问题,只需在 shell 中输入“dirs”,一切就会恢复同步。

我的 .emacs 中还有以下内容:

(global-set-key "\M-\r" 'shell-resync-dirs)

然后只需按 Esc-return 即可重新同步自动完成。

【讨论】:

这是另一个问题的好答案! 谢谢。我使用自动跳转,这就是目录不同步的问题。 克里斯康威,是的,给这个:emacs.stackexchange.com/questions/30550/… :-)【参考方案3】:

我不知道这个问题的答案。但它不能按预期工作的原因可能是因为 emacs shell 中的完成是由 emacs 内部处理的(由 comint-dynamic-complete 函数),并且没有内置那些智能完成函数。

恐怕这不是一件容易解决的事情。

编辑:njsf 对使用 term-mode 的建议可能是最好的。从

开始
M-x 项
它包含在标准 emacs 发行版中(至少在 Ubuntu 和 Debian 上包含在 emacs21-common 或 emacs22-common 中)。

【讨论】:

使用M-x term 的制表符补全更差。【参考方案4】:

请考虑另一种模式M-x term,就像我在 2011 年遇到问题时所做的那样。当时我试图通过 Inet 集中所有的努力来使 shell 与 Bash 完成一起工作,包括这个问题。但自从面对term-mode 发现替代品后,我什至不想尝试eshell

它是完整的终端模拟器,所以你可以在里面运行交互式程序,比如午夜指挥官。或者切换到 zsh 完成,这样您就不会在 Emacs 配置上浪费时间。

您可以在 bash 中免费获得 TAB 补全。但更重要的是,您可以获得完整的 Readline 功能,例如增量或前缀命令搜索。为了让这个设置更方便,请查看我的.inputrc、.bashrc、.emacs。

.inputrc的基本部分:

# I like this!
set editing-mode emacs

# Don't strip characters to 7 bits when reading.
set input-meta on

# Allow iso-latin1 characters to be inserted rather than converted to
# prefix-meta sequences.
set convert-meta off

# Display characters with the eighth bit set directly rather than as
# meta-prefixed characters.
set output-meta on

# Ignore hidden files.
set match-hidden-files off

# Ignore case (on/off).
set completion-ignore-case on

set completion-query-items 100

# First tab suggests ambiguous variants.
set show-all-if-ambiguous on

# Replace common prefix with ...
set completion-prefix-display-length 1

set skip-completed-text off

# If set to 'on', completed directory names have a slash appended. The default is 'on'.
set mark-directories on
set mark-symlinked-directories on

# If set to 'on', a character denoting a file's type is appended to the
# filename when listing possible completions. The default is 'off'.
set visible-stats on

set horizontal-scroll-mode off

$if Bash
"\C-x\C-e": edit-and-execute-command
$endif

# Define my favorite Emacs key bindings.
"\C-@": set-mark
"\C-w": kill-region
"\M-w": copy-region-as-kill

# Ctrl+Left/Right to move by whole words.
"\e[1;5C": forward-word
"\e[1;5D": backward-word
# Same with Shift pressed.
"\e[1;6C": forward-word
"\e[1;6D": backward-word

# Ctrl+Backspace/Delete to delete whole words.
"\e[3;5~": kill-word
"\C-_": backward-kill-word

# UP/DOWN filter history by typed string as prefix.
"\e[A": history-search-backward
"\C-p": history-search-backward
"\eOA": history-search-backward
"\e[B": history-search-forward
"\C-n": history-search-forward
"\eOB": history-search-forward

# Bind 'Shift+TAB' to complete as in Python TAB was need for another purpose.
"\e[Z": complete
# Cycling possible completion forward and backward in place.
"\e[1;3C": menu-complete                    # M-Right
"\e[1;3D": menu-complete-backward           # M-Left
"\e[1;5I": menu-complete                    # C-TAB

.bashrc(是的!在 Bash 中有来自 ~/.bash_history 中的任何单词的 dabbrev):

set -o emacs

if [[ $- == *i* ]]; then
  bind '"\e/": dabbrev-expand'
  bind '"\ee": edit-and-execute-command'
fi

.emacs 使导航在术语缓冲区中舒适:

(setq term-buffer-maximum-size (lsh 1 14))

(eval-after-load 'term
  '(progn
    (defun my-term-send-delete-word-forward () (interactive) (term-send-raw-string "\ed"))
    (defun my-term-send-delete-word-backward () (interactive) (term-send-raw-string "\e\C-h"))
    (define-key term-raw-map [C-delete] 'my-term-send-delete-word-forward)
    (define-key term-raw-map [C-backspace] 'my-term-send-delete-word-backward)
    (defun my-term-send-forward-word () (interactive) (term-send-raw-string "\ef"))
    (defun my-term-send-backward-word () (interactive) (term-send-raw-string "\eb"))
    (define-key term-raw-map [C-left] 'my-term-send-backward-word)
    (define-key term-raw-map [C-right] 'my-term-send-forward-word)
    (defun my-term-send-m-right () (interactive) (term-send-raw-string "\e[1;3C"))
    (defun my-term-send-m-left () (interactive) (term-send-raw-string "\e[1;3D"))
    (define-key term-raw-map [M-right] 'my-term-send-m-right)
    (define-key term-raw-map [M-left] 'my-term-send-m-left)
    ))

(defun my-term-mode-hook ()
  (goto-address-mode 1))
(add-hook 'term-mode-hook #'my-term-mode-hook)

由于C-x o 等任何常用命令在终端仿真模式下都不起作用,因此我扩展了键盘映射:

(unless
    (ignore-errors
      (require 'ido)
      (ido-mode 1)
      (global-set-key [?\s-d] #'ido-dired)
      (global-set-key [?\s-f] #'ido-find-file)
      t)
  (global-set-key [?\s-d] #'dired)
  (global-set-key [?\s-f] #'find-file))

(defun my--kill-this-buffer-maybe-switch-to-next ()
  "Kill current buffer. Switch to next buffer if previous command
was switching to next buffer or this command itself allowing
sequential closing of uninteresting buffers."
  (interactive)
  (let ( (cmd last-command) )
    (kill-buffer (current-buffer))
    (when (memq cmd (list 'next-buffer this-command))
      (next-buffer))))
(global-set-key [s-delete] 'my--kill-this-buffer-maybe-switch-to-next)
(defun my--backward-other-window ()
  (interactive)
  (other-window -1))
(global-set-key [s-up] #'my--backward-other-window)
(global-set-key [s-down] #'other-window)
(global-set-key [s-tab] 'other-window)

请注意,我使用super 键,因此term-raw-map 和可能的任何其他键映射都不会与我的键绑定冲突。要从左侧制作superWin 键,我使用.xmodmaprc

! To load this config run:
!   $ xmodmap .xmodmaprc

! Win key.
clear mod3
clear mod4

keycode 133 = Super_L
keycode 134 = Hyper_R
add mod3 = Super_L
add mod4 = Hyper_R

您只需要记住 2 个命令:C-c C-j - 进入正常 Emacs 编辑模式(用于在缓冲区文本中复制或 grepping),C-c C-k - 返回终端仿真模式。

鼠标选择和Shift-Insertxterm 一样工作。

【讨论】:

谢谢,这是金子!尤其是.inputrc 部分!【参考方案5】:

正如 Matli 所说,这不是一件容易的事,因为 bash 以 --noediting 开头,而 TAB 绑定到 comint-dynamic-complete。

可以使用 local-set-key 将 TAB 重新绑定到 shell-comand-hook 中的 self-insert-command 并通过 M-x customize-variable RET explicit-bash-args 使 shell-mode 不以 --noediting 开头,但我怀疑它不适用于所有其他编辑。

您可能想尝试 term-mode,但它还有另一组问题,因为一些其他常规键绑定已被 term-mode 取代。

编辑:通过术语模式取代其他常规键标,我的意思是除了 C-c 之外的所有键,它成为能够切换缓冲区的转义。因此,您必须 C-c C-x k,而不是 C-x k 来杀死缓冲区。或者切换到另一个缓冲区 'C-c C-x o' 或 'C-c C-x 2'

【讨论】:

self-insert-command 不是这样:它插入 TAB 字符而不是将 TAB 按键传递给 Bash。 我没有 term-mode 命令,也不知道从哪里得到它。【参考方案6】:

我知道这篇文章已经超过 11 年了。但是我创建了一个函数来在 Emacs 中完成本机 shell。它只是向底层进程发送一个 tab 键并截取输出,因此它与您在 shell 本身中得到的完全相同。

https://github.com/CeleritasCelery/emacs-native-shell-complete

【讨论】:

哇!惊人的 ! (基于预览的初始展示。) 我喜欢它。太感谢了。如果因为有多个候选项而无法完成单个命令的 TAB 时,这些候选项会像在 Bash 中一样出现在 Emacs 中,这会更好。 你的意思是喜欢在终端的大列表中而不是使用 Emacs 补全显示?【参考方案7】:

我使用 Prelude,当我点击 Meta+Tab 时,它为我完成。

另外,Ctrl+i 似乎做同样的事情。

【讨论】:

【参考方案8】:

我使用掌舵模式。它具有此功能(按“TAB”后):

【讨论】:

【参考方案9】:

我没有声称自己是 emacs 专家,但这应该可以解决您的问题:

创建:~/.emacs

添加到它:

(需要'shell命令) (shell-command-completion-mode)

Emacs 接管了 shell,因此 BASH 设置不会执行。这将为 EMACS 本身设置自动完成。

【讨论】:

不,这不能解决问题。它仅适用于 shell 命令,不适用于 shell 模式。此外,它不会启用问题中请求的智能完成(它只会完成命令和文件名)。 shell-command-completion-mode 执行文件名完成并默认启用。我想要 Bash 的补全功能(可扩展为包括,例如 apt-get 和 svn 的子命令)。

以上是关于Emacs shell 模式中的 Bash 自动完成的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Emacs 中运行 Cygwin Bash Shell?

emacs 去除 shell 中的所有 ansi 颜色代码

理解 Emacs 完成模式选择

Mac OS X:如何在emacs客户端的shell中永久更改提示符(PS1的值)?

BASH命令行快捷键指南

Emacs Shell模式:如何让TAB字符通过?