Bagaimana saya bisa membuat mode vi zsh berperilaku lebih seperti mode vi bash?

24

Saya benar-benar menyukai kecepatan umum zsh, tetapi ada dua hal yang mengganggu saya.

  1. Saya harus menekan tunggu beberapa saat antara memukul melarikan diri dan memukul garis miring untuk sampai ke pencarian riwayat (jika memukul garis miring terlalu cepat dikatakan zsh: do you wish to see all 514 possibilities (172 lines))
  2. Setelah masuk ke mode memasukkan karena memukul aatau A, saya tidak bisa mundur melewati titik di mana saya memasuki mode memasukkan.

Saya tahu bahwa 2 seperti klasik vi, tetapi saya lebih suka gaya vim.

Chas. Owens
sumber
Jika ada yang mengalami masalah pelarian ganda yang sangat menjengkelkan yang menyebabkan Anda harus menekan idua kali untuk kembali ke mode insert, saya akan sangat merekomendasikan perbaikan ini !
cchamberlain
Ada juga ringkasan yang bagus di sini: dougblack.io/words/zsh-vi-mode.html
jackcogdill

Jawaban:

22

(1) Untuk beberapa alasan, bindkey berperilaku aneh ketika datang ke "/": <esc>diikuti dengan cepat /ditafsirkan sebagai <esc-/>. (Saya mengamati perilaku ini beberapa hari yang lalu; tidak yakin apa yang menyebabkannya.) Saya tidak tahu apakah ini bug atau fitur, dan apakah ini fitur jika dapat dinonaktifkan, tetapi Anda dapat mengatasinya dengan cukup mudah. .

Kombo kunci ini mungkin terikat _history-complete-older, yang menghasilkan hasil yang tidak diinginkan - Anda dapat menggunakan bindkey -Luntuk melihat apakah ini masalahnya.

Bagaimanapun, jika Anda tidak keberatan mengorbankan sebenarnya <esc-/> (ditekan bersama-sama, sebagai akord) yang mengikat, Anda dapat kembali mengikat ke vi-mode perintah pencarian sejarah, sehingga mengetik <esc>diikuti oleh /melakukan hal yang sama di mengetik setiap kecepatan. =)

Karena ini akan diperlakukan sebagai akor, itu tidak akan memiliki efek memasuki mode perintah vi pertama, jadi kita harus memastikan itu terjadi terlebih dahulu. Pertama, Anda perlu mendefinisikan suatu fungsi; letakkan di suatu tempat di Anda fpathjika Anda menggunakannya, atau taruh di .zshrc Anda sebaliknya:

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

Sisanya masuk dalam .zshrc Anda:

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

Harus baik untuk pergi.

(2) Anda dapat memperbaiki kunci backspace sebagai berikut:

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

Juga, jika Anda menginginkan perilaku serupa untuk perintah gaya vi lainnya:

bindkey "^W" backward-kill-word 
bindkey "^H" backward-delete-char      # Control-h also deletes the previous char
bindkey "^U" backward-kill-line            
Marshall Eubanks
sumber
Itu di bawah ^[/tidak \e/, tetapi keduanya adalah cara yang sah untuk mengatakan melarikan diri. Perubahan itu bekerja dengan sempurna. Sekarang saya bermain dengan lebih lengkap, sepertinya mode vi zsh menyebalkan dibandingkan dengan bash (atau setidaknya tidak sepenuhnya dikonfigurasikan secara default). Salah satu contoh dari hal ini adalah fakta bahwa ia memasukkan Anda ke mode penyisipan setelah riwayat pencarian. Saya harus kembali ke mode perintah untuk menekan n untuk menemukan item pencarian berikutnya.
Chas. Owens
1
Yah, saya tidak tahu apakah Anda memiliki contoh lain, tetapi yang Anda sebutkan adalah kesalahan saya, bukan milik zsh. =) Apa yang terjadi adalah saya telah terikat perintah editor mode vi-cmd dalam mode vi insert - perintah mengharapkan shell sudah ada dalam mode cmd dan berperilaku sesuai. Kita perlu menulis perintah editor yang pertama kali memanggil perintah "enter cmd mode", dan kemudian jalankan .vi-history-search-backward. Saya akan menulis dan mengedit jawaban saya - periksa kembali hari ini.
Marshall Eubanks
OK, saya memperbarui jawaban saya. Cobalah.
Marshall Eubanks
Sehubungan dengan (2), ketika saya melakukannya bindkey | grep <searchterm>untuk salah satu syarat, mereka semua diawali oleh vi-. Apakah saya perlu mengatur bindkeyperintah yang tidak diawali oleh vi-?
adam_0
1
Terima kasih. Peretasan ini (dan juga dari wjv di bawah ini juga) membuat mode vi zsh berubah dari hampir tidak dapat digunakan menjadi sangat baik. Saya membuat akun pengguna super sehingga saya dapat memilih Anda. :-)
ctrueden
14

Saya hanya akan menjawab pertanyaan (1).

Masalah Anda adalah KEYTIMEOUT. Saya mengutip dari zshzle (1):

Ketika ZLE membaca perintah dari terminal, ia dapat membaca urutan yang terikat pada beberapa perintah dan juga merupakan awalan dari string terikat yang lebih panjang. Dalam hal ini ZLE akan menunggu waktu tertentu untuk melihat apakah lebih banyak karakter yang diketik, dan jika tidak (atau mereka tidak cocok dengan string yang lebih panjang) akan dilakukan pengikatan. Batas waktu ini ditentukan oleh parameter KEYTIMEOUT; standarnya adalah 0,4 detik. Tidak ada batas waktu jika string awalan tidak terikat dengan perintah.

0.4s adalah keterlambatan yang Anda alami setelah memukul ESC. Cara mengatasinya adalah menetapkan KEYTIMEOUT hingga 0,01 dalam salah satu file startup shell:

export KEYTIMEOUT=1

Sayangnya ini memiliki efek knock-on: Hal-hal lain mulai salah ...

Pertama, sekarang ada masalah dalam mode perintah vi: Mengetik ESC menyebabkan kursor hang, dan kemudian karakter apa pun yang Anda ketik selanjutnya akan tertelan. Ini karena ESC tidak terikat dengan apa pun secara default dalam mode perintah vi, namun ada widget multi-karakter yang dimulai dengan ESC (tombol kursor!). Jadi ketika Anda menekan ESC, ZLE menunggu karakter berikutnya ... dan kemudian mengkonsumsinya.

Cara mengatasinya adalah untuk mengikat ESC ke sesuatu dalam mode perintah, sehingga memastikan bahwa sesuatu akan diteruskan ke ZLE setelah $ KEYTIMEOUT centiseconds. Sekarang kita dapat tetap mengikat mulai dengan ESC dalam mode perintah tanpa efek buruk ini. Saya mengikat ESC ke karakter bel, yang menurut saya lebih tidak mengganggu daripada memasukkan sendiri (dan shell saya dibungkam):

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

Pembaruan 2017:

Saya telah menemukan solusi yang lebih baik untuk mengikat ESC - undefined-keywidget. Saya tidak yakin apakah widget ini tersedia di zsh ketika saya awalnya menulis jawaban ini.

bindkey -M vicmd '^[' undefined-key

Masalah berikutnya: Secara default ada beberapa widget dua-kunci yang dimulai dengan ^ X dalam mode insert vi; ini menjadi tidak dapat digunakan jika $ KEYTIMEOUT disetel sepenuhnya. Apa yang saya lakukan adalah unbind ^ X dalam mode insert vi (self-insert secara default); ini memungkinkan widget dua tombol untuk terus bekerja.

bindkey -rM viins '^X'

Anda kehilangan ikatan untuk disisipkan sendiri, tetapi Anda dapat mengikatnya dengan sesuatu yang lain tentu saja. (Aku tidak, karena aku tidak menggunakannya.)

Masalah terakhir (saya telah menemukan sejauh ini): Ada beberapa keybindings default yang tersisa yang kita "kehilangan" karena pengaturan $ KEYTIMEOUT segera, untuk menjelaskan: yang dimulai dengan ESC dalam mode insert vi yang bukan merupakan kunci kursor. Saya pribadi membukanya kembali dengan ^ ^ sebagai gantinya:

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

Pembaruan 2018:

Ternyata seluruh bagian di atas (setelah "Pembaruan 2017") tidak selalu diperlukan. Dimungkinkan untuk mengatur kunci META agar setara dengan ESC di pemetaan keyboard menggunakan:

bindkey -mv

Karena itu dimungkinkan untuk tidak melepas ^ X, dan untuk mengakses ikatan kunci yang dimulai pada ESC dengan menekan META sebagai pemimpin sebagai gantinya (ALT atau OPT pada keyboard modern).

Jika Anda memiliki akses ke buku From Bash to Z Shell oleh Kiddle et al., Kesetaraan ESC dan META dalam ikatan kunci dibahas dalam bilah sisi Bab 4 pada hal. 78-79.

wjv
sumber
Terima kasih. Peretasan ini (dan juga dari marshaul di atas) membuat mode vi zsh berubah dari hampir tidak dapat digunakan menjadi sangat baik. Saya membuat akun pengguna super sehingga saya dapat memilih Anda. :-)
ctrueden
1
Terima kasih! Saya merasa agak khawatir bahwa, setelah sekian lama, kita masih membutuhkan apa yang pada dasarnya merupakan peretasan dan solusi untuk membuat sedikit inti fungsionalitas zsh dapat digunakan!
wjv