Как сделать так, чтобы режим vi в zsh вел себя как режим vi в bash?

Мне действительно нравится общая скорость zsh, но две вещи меня раздражают.

  1. Мне нужно подождать мгновение между нажатием клавиши "escape" и "косой чертой", чтобы перейти к поиску истории zsh: do you wish to see all 514 possibilities (172 lines))
  2. После входа в режим вставки из-за удара a или же AЯ не могу вернуться за точку, в которой я вошел в режим вставки.

Я знаю, что 2 похожа на классический vi, но мне больше нравится стиль vim.

3 ответа

Решение

(1). По какой-то причине bindkey ведет себя странно, когда дело доходит до "/": <esc> с последующим быстрым / интерпретируется как <esc-/>, (Я наблюдал это поведение на днях; не совсем уверен, что его вызывает.) Я не знаю, является ли это ошибкой или функцией, и если это функция, если ее можно отключить, но вы можете обойти ее довольно легко,

Эта ключевая комбинация, вероятно, связана с _history-complete-older, который генерирует нежелательный результат - вы можете использовать bindkey -L чтобы увидеть, если это так.

Во всяком случае, если вы не против пожертвовать фактическим <esc-/> (прижатый вместе, как аккорд), вы можете повторно привязать его к команде поиска истории vi-mode, чтобы набрать <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). Вы можете исправить клавишу Backspace следующим образом:

`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            

Я собираюсь ответить только на вопрос (1).

Ваша проблема - KEYTIMEOUT. Я цитирую из zshzle(1):

Когда ZLE читает команду из терминала, он может прочитать последовательность, которая привязана к какой-либо команде и также является префиксом строки с более длинной привязкой. В этом случае ZLE будет ждать определенное время, чтобы увидеть, набрано ли больше символов, и если нет (или они не соответствуют более длинной строке), то выполнит связывание. Это время ожидания определяется параметром KEYTIMEOUT; его значение по умолчанию составляет 0,4 сек. Тайм-аут не существует, если строка префикса сама не привязана к команде.

Эти 0,4 секунды - это задержка, которую вы испытываете после нажатия ESC. Исправление состоит в том, чтобы установить KEYTIMEOUT вплоть до 0,01 с в одном из файлов запуска оболочки:

export KEYTIMEOUT=1

К сожалению, это имеет эффект: другие вещи начинают работать неправильно...

Во-первых, теперь есть проблема в командном режиме vi: ввод ESC приводит к зависанию курсора, а затем любой символ, который вы вводите следующим, проглатывается. Это связано с тем, что ESC по умолчанию не связан ни с чем в командном режиме vi, но есть многосимвольные виджеты, которые начинаются с ESC (клавиши курсора!). Поэтому, когда вы нажимаете ESC, ZLE ждет следующего символа... и затем использует его.

Исправление состоит в том, чтобы привязать ESC к чему-либо в командном режиме, таким образом гарантируя, что что- то будет передано в ZLE через $KEYTIMEOUT сантисекунд. Теперь мы можем поддерживать привязки, начиная с ESC, в командном режиме без этих вредных последствий. Я связываю ESC с персонажем колокольчика, который я считаю еще менее навязчивым, чем само вставка (и моя оболочка заставлена ​​замолчать):

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

Обновление 2017:

С тех пор я нашел еще лучшее решение для связывания ESC - undefined-key виджет. Я не уверен, был ли этот виджет доступен в zsh, когда я изначально писал этот ответ.

bindkey -M vicmd '^[' undefined-key

Следующая проблема: по умолчанию есть некоторые виджеты с двумя ключами, начинающиеся с ^X в режиме вставки vi; они становятся непригодными для использования, если $KEYTIMEOUT установлен полностью вниз. Что я делаю, так это отсоединяю ^X в режиме вставки vi (по умолчанию это самостоятельная вставка); это позволяет этим двухключевым виджетам продолжать работать.

bindkey -rM viins '^X'

Вы теряете привязку для самостоятельной вставки, но, конечно, можете привязать ее к чему-то другому. (Не знаю, так как мне это не нужно.)

Последняя проблема (которую я нашел до сих пор): Есть некоторые оставшиеся по умолчанию комбинации клавиш, которые мы "теряем" из-за установки $KEYTIMEOUT прямо вниз, то есть: те, которые начинаются с ESC в режиме вставки vi, которые не являются клавишами курсора. Я лично связываю их, чтобы начать с ^X вместо этого:

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

Обновление 2018:

Оказывается, весь приведенный выше раздел (после "Обновления 2017") не обязательно требуется. Можно установить ключ META эквивалентным ESC в отображениях клавиатуры, используя:

bindkey -mv

Следовательно, можно не отменять привязку ^X и получить доступ к сочетаниям клавиш, начинающимся в ESC, нажав вместо этого META в качестве лидера (ALT или OPT на современных клавиатурах).

Если у вас есть доступ к книге " От Bash до Z Shell " Киддла и др., Эквивалентность ESC и META в привязках клавиш обсуждается в боковой панели главы 4 на стр. 78–79.

Я решил отменить завершение истории, чтобы устранить конфликт. Запустите следующее послеcompinitсвязывает аккорд.

      bindkey -M viins -r "^[/"
bindkey -M viins "^[." _history-complete-newer
Другие вопросы по тегам