web-dev-qa-db-ja.com

zshのviモードをbashのviモードのように動作させるにはどうすればよいですか?

私はzshの一般的な速度が本当に好きですが、2つのことが私を悩ませています。

  1. ヒストリ検索に到達するには、エスケープとスラッシュの間に少し待ってヒットする必要があります(スラッシュのヒットが速すぎる場合はzsh: do you wish to see all 514 possibilities (172 lines)
  2. aまたはAを押したため挿入モードに入った後、挿入モードに入ったポイントを超えてバックスペースできません。

2はクラシックviに似ていますが、vimスタイルの方が好きです。

24
Chas. Owens

(1)。何らかの理由により、bindkeyは「/」に関して奇妙な動作をします。<esc>の直後に/が続くと、<esc-/>として解釈されます。 (私は先日この動作を観察しました。原因がよくわかりません。)これがバグなのか機能なのか、またそれが無効化できる機能なのかわかりませんが、かなり簡単に回避できます。 。

このキーコンボはおそらく_history-complete-olderにバインドされているため、望ましくない結果が生成されます。bindkey -Lを使用して、これが当てはまるかどうかを確認できます。

とにかく、actual<esc-/>(和音として一緒に押された)バインディングを犠牲にすることを気にしない場合は、それをviモード履歴検索コマンドに再バインドできます。 <esc>の後に/を入力すると、どの入力速度でも同じことができます。 =)

これはコードとして扱われるため、最初にviコマンドモードに入る効果はありません。そのため、最初にそれが発生することを確認する必要があります。まず、関数を定義する必要があります。使用する場合はfpathのどこかに配置し、それ以外の場合は.zshrcに配置します。

vi-search-fix() {
zle vi-cmd-mode
zle .vi-history-search-backward
}

残りはどちらかの方法で.zshrcに入ります:

autoload vi-search-fix
zle -N vi-search-fix
bindkey -M viins '\e/' vi-search-fix

行くのが良いでしょう。

(2)。バックスペースキーは次のように修正できます。

`bindkey "^?" backward-delete-char`

また、他のviスタイルのコマンドでも同様の動作が必要な場合:

bindkey "^W" backward-kill-Word 
bindkey "^H" backward-delete-char      # Control-h also deletes the previous char
bindkey "^U" backward-kill-line            
23

質問(1)についてのみ説明します。

あなたの問題はKEYTIMEOUTです。私はzshzle(1)から引用します:

ZLEが端末からコマンドを読み取っているとき、ZLEはいくつかのコマンドにバインドされていて、長いバインド文字列のプレフィックスでもあるシーケンスを読み取る場合があります。この場合、ZLEはさらに文字が入力されたかどうかを確認するために一定の時間待機し、入力されなかった場合(または文字列が一致しない場合)はバインドを実行します。このタイムアウトは、KEYTIMEOUTパラメータによって定義されます。デフォルトは0.4秒です。プレフィックス文字列自体がコマンドにバインドされていない場合、タイムアウトはありません。

その0.4秒は、ESCを押した後の遅延です。修正は、シェルのスタートアップファイルの1つでKEYTIMEOUTを0.01秒に設定することです。

export KEYTIMEOUT=1

残念ながら、これにはノックオン効果があります。他のことがうまくいかない…

まず、viコマンドモードに問題があります。ESCを入力するとカーソルがハングし、次に入力する文字が飲み込まれます。これは、viコマンドモードではESCがデフォルトで何にもバインドされていないためですが、ESC(カーソルキー!)で始まる複数文字のウィジェットがあります。したがって、ESCを押すと、ZLEは次の文字を待ち、それを消費します。

修正は、コマンドモードでESCをsomethingにバインドすることで、something$ KEYTIMEOUTセンチ秒後にZLEに渡されます。これで、これらの悪影響なしに、コマンドモードでESCで始まるバインディングを維持できます。 ESCをベルキャラクターにバインドします。これは、自己挿入よりも邪魔にならないことがわかります(そして、私のシェルは沈黙しています)。

bindkey -sM vicmd '^[' '^G'

2017年更新:

私はそれ以来、ESCをバインドするためのより良い解決策を見つけました— undefined-keyウィジェット。最初にこの回答を書いたとき、このウィジェットがzshで使用可能であったかどうかはわかりません。

bindkey -M vicmd '^[' undefined-key

次の問題:デフォルトでは、vi挿入モードの^ Xで始まる2つのキーのウィジェットがあります。 $ KEYTIMEOUTがずっと下に設定されている場合、これらは使用できなくなります。私が行うことは、vi挿入モードで^ Xをバインド解除することです(デフォルトでは自己挿入です)。これにより、これらの2つのキーウィジェットが引き続き機能します。

bindkey -rM viins '^X'

自己挿入のバインディングは失われますが、もちろん他のものにバインドすることができます。 (私はそれを使用していないので、しません。)

最後の問題(私はこれまでに見つけました):$ KEYTIMEOUTを右に設定したために "失われた"既定のキーバインドがいくつか残っています。つまり、vi挿入モードでESCで始まるnotカーソルキー。代わりに、^ Xで始まるようにそれらを再バインドします。

bindkey -M viins '^X,' _history-complete-newer \
                 '^X/' _history-complete-older \
                 '^X`' _bash_complete-Word

2018年の更新:

上記のセクション全体(「Update 2017」以降)は必ずしも必要ではないことがわかりました。以下を使用して、キーボードマッピングでMETAキーをESCと同等に設定できます。

bindkey -mv

したがって、^ Xのバインドを解除できないことができます。代わりに、METAをリーダーとして押すことで、ESCで始まるキーバインドにアクセスできます(モダンではALTまたはOPT)キーボード)。

Kiddleらの本From Bash to Z Shellにアクセスできる場合、キーバインドにおけるESCとMETAの同等性については、第4章で説明しています。 78〜79ページのサイドバー。

14
wjv