如何使用 emacs 插入模式和 vi 命令模式设置混合 readline?

Tom*_*ale 5 shell readline

我想要一个混合模式,默认情况下我可以使用所有默认keymap emacs键绑定,并且仍然可以更改为vi-command模式。

我该如何设置?

dos*_*ter 8

在这里这里阅读了@Tom Hale的答案 。

我认为更好的方法是将绑定vi-insert移动到,而不是将绑定移动到。原因是因为有切换到的绑定,并且很难模拟功能以使其切换到模式。emacsemacsvi-insertvi-commandvi-insertemacs

例如,A命令 invi-command默认为vi-append-eol(附加在行尾并切换到vi-insert)。

您无法A切换到emacs模式,因为它绑定到函数而不是宏。

例如,这行不通

"A": vi-append-eol emacs-editing-mode
Run Code Online (Sandbox Code Playgroud)

也不是这个,使用@Tom Hale答案

"A": vi-append-eol "\ee"
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

"A": "$a\ee"
Run Code Online (Sandbox Code Playgroud)

但现在这取决于"a",vi-append-mode命令,它也需要反弹。"a"那么就可以继续前进了"i""i"可以切换到emacs. 有一个完整的命令链需要转换为宏,这是一件很痛苦的事情。

因此,您最好将emacs绑定移至vi-insert.


因此,我们想要设置vi-insert唯一的绑定emacs ,如果它们对相同的按键序列有不同的绑定,我们想要决定使用哪个绑定。如果它们具有完全相同的绑定,我们将忽略它们。

这可以用这个命令来完成

comm -3\
  <(INPUTRC=/dev/null bash -c 'bind -pm emacs' |
    LC_ALL='C' grep -vE '^#|: (do-lowercase-version|self-insert)$' |
    sort) \
  <(INPUTRC=/dev/null bash -c 'bind -pm vi-insert' |
    LC_ALL='C' grep -vE '^#|: (do-lowercase-version|self-insert)$' |
    sort) | cat
Run Code Online (Sandbox Code Playgroud)

这里| cat解释了为什么存在的原因

丢弃-3“完全相同的绑定”,因此您浏览此列表并在左列中查找绑定。对于左列上的每个绑定:

如果同一按键序列存在重复绑定,例如

"\C-d": delete-char
    "\C-d": vi-eof-maybe
Run Code Online (Sandbox Code Playgroud)

选择其中之一。如果您想要vi-insert(右侧),您可以删除这两行,因为我们将添加这些绑定到vi-insert已经具有vi-insert绑定的位置。如果您想要该emacs(左侧),请将其删除vi-insert

如果右列 ( ) 上存在唯一绑定vi-insert,例如

    "\e": vi-movement-mode
Run Code Online (Sandbox Code Playgroud)

删除它,因为 `vi-insert 已经有它了。

其余的绑定将位于左列 ( emacs)。不要管这些,因为我们会将它们添加到vi-insert.


这是我选择添加到的.inputrc绑定。emacsvi-insert

我决定不在@Tom Hale答案中使用"kj"to switch to ,因为它可以通过which 带你从到然后另一个带你从to 来完成。实际上除了blackjack以外还有含有kj的单词和带有jk的单词(大多是地名)vi-command"\ee"emacsvi-insert"\e"vi-insertvi-command

我保留"\C-d": delete-char并扔掉了"\C-d": vi-eof-maybe。因为我可以只使用Enterforvi-eof-maybe并且我不想通过按意外退出 readline "\C-d"。这意味着删除"\C-d": vi-eof-maybe绑定,因为我们正在使用绑定覆盖modevi-eof-maybe中的绑定。vi-insertdelete-char

我保留"\C-n": menu-complete而不是"\C-n": next-history因为我可以使用向下箭头next-history。这意味着删除两个绑定,因为vi-insert已有menu-complete绑定。

我保留"\C-p": menu-complete-backward而不是"\C-p": previous-history因为我可以按向上箭头previous-history。这意味着删除两个绑定,因为vi-insert已有menu-complete-backward绑定。

我保留"\C-w": vi-unix-word-rubout而不是"\C-w": unix-word-rubout. 我不知道有什么区别。我只是坚持其中vi-insert一个。这意味着删除两个绑定,因为vi-insert已有vi-unix-word-rubout绑定。

我保留了"\e": vi-movement-mode。这意味着删除此绑定,因为vi-insert已有该vi-movement-mode绑定。

set editing-mode vi

set keymap emacs
"\ee": vi-editing-mode

set keymap vi-command
"\ee": emacs-editing-mode

# key bindings to get out of vi-editing-mode
set keymap vi-insert
"\ee": emacs-editing-mode

# emacs keybindings in vi-insert mode
"\C-@": set-mark
"\C-]": character-search
"\C-_": undo
"\C-a": beginning-of-line
"\C-b": backward-char
"\C-d": delete-char
"\C-e": end-of-line
"\C-f": forward-char
"\C-g": abort
"\C-k": kill-line
"\C-l": clear-screen
"\C-o": operate-and-get-next
"\C-q": quoted-insert
"\C-x!": possible-command-completions
"\C-x$": possible-variable-completions
"\C-x(": start-kbd-macro
"\C-x)": end-kbd-macro
"\C-x*": glob-expand-word
"\C-x/": possible-filename-completions
"\C-x@": possible-hostname-completions
"\C-x\C-?": backward-kill-line
"\C-x\C-e": edit-and-execute-command
"\C-x\C-g": abort
"\C-x\C-r": re-read-init-file
"\C-x\C-u": undo
"\C-x\C-v": display-shell-version
"\C-x\C-x": exchange-point-and-mark
"\C-xe": call-last-kbd-macro
"\C-xg": glob-list-expansions
"\C-x~": possible-username-completions
"\e ": set-mark
"\e!": complete-command
"\e#": insert-comment
"\e$": complete-variable
"\e&": tilde-expand
"\e*": insert-completions
"\e-": digit-argument
"\e.": insert-last-argument
"\e.": yank-last-arg
"\e/": complete-filename
"\e0": digit-argument
"\e1": digit-argument
"\e2": digit-argument
"\e3": digit-argument
"\e4": digit-argument
"\e5": digit-argument
"\e6": digit-argument
"\e7": digit-argument
"\e8": digit-argument
"\e9": digit-argument
"\e<": beginning-of-history
"\e=": possible-completions
"\e>": end-of-history
"\e?": possible-completions
"\e@": complete-hostname
"\e\C-?": backward-kill-word
"\e\C-]": character-search-backward
"\e\C-e": shell-expand-line
"\e\C-g": abort
"\e\C-h": backward-kill-word
"\e\C-i": dynamic-complete-history
"\e\C-r": revert-line
"\e\C-y": yank-nth-arg
"\e\\": delete-horizontal-space
"\e\e": complete
"\e^": history-expand-line
"\e_": insert-last-argument
"\e_": yank-last-arg
"\eb": backward-word
"\ec": capitalize-word
"\ed": kill-word
"\ef": forward-word
"\eg": glob-complete-word
"\el": downcase-word
"\en": non-incremental-forward-search-history
"\ep": non-incremental-reverse-search-history
"\er": revert-line
"\et": transpose-words
"\eu": upcase-word
"\ey": yank-pop
"\e{": complete-into-braces
"\e~": complete-username
Run Code Online (Sandbox Code Playgroud)

更新

我想我做得更好了一点。有一段时间,我重新打开了"jk"切换到的命令vi command,因为按下"\e"切换到vi-command有一个延迟,因为很多emacs命令转移到了作为领导者vi-insert使用。"\e"

这表明"jk"moshing 已被注释掉。我目前使用"\ee"循环模式。我没有解除"\e"vi-insert到 的绑定vi-command,因为我认为没有必要。因此,它的效果是,如果您按"\e"vi-insert并等待,您将转到vi-command

要从 到vi-commandvi-insert您只需按命令之一,例如"A""i"so ,允许在 3 种模式之间循环不会有什么坏处,因为您也可以在 2 种vi模式之间循环。

set keymap emacs
"\ee": vi-editing-mode

set keymap vi-command
"\ee": emacs-editing-mode

# key bindings to get out of vi-editing-mode
set keymap vi-insert
# Choose one of these editor switching modes
# 
# moosh jk to switch
#"\ee": emacs-editing-mode
#"\ejk": vi-movement-mode
#"\ekj": vi-movement-mode
#
# "\ee" to cycle
# can unmap "\e" to switch to vi-command but don't see a need
#"\e":
"\ee": vi-movement-mode
Run Code Online (Sandbox Code Playgroud)

更新

在 中vi-insert"\C-w"绑定到 `vi-unix-word-rubout,它在单词边界或其他地方停止。我不喜欢这个功能。

例如,尝试这个

$ cannot-delete'
# press left arrow to go back behind the single quote
# press \C-w in vi-insert to try to delete cannot-delete, it won't work
Run Code Online (Sandbox Code Playgroud)

尽管我对提供的示例没有问题,但此错误报告描述了该问题。

所以你可以绑定"\C-w"到 emacsunix-word-rubout来解决这个问题。

要重新绑定"\C-w",您可能需要取消绑定默认值

# In .bashrc
stty werase undef
Run Code Online (Sandbox Code Playgroud)

如果您想解除所有默认值的绑定:

# In .inputrc
set bind-tty-special-chars off
Run Code Online (Sandbox Code Playgroud)

不确定这是否重要,但我使用的是 macOS,因此我删除了其他默认绑定

然后在你的.inputrc

"\C-w": unix-word-rubout
Run Code Online (Sandbox Code Playgroud)