Всем привет. Я достаточно давно пользуюсь редактором Vim для редактирования очень разных текстов: правки конфигов, написания кода, в качестве внешнего редактора в браузере и почтовом клиенте…

Редактор почти идеален, но было одно маленькое «но». В частности, бесило переключение раскладки. Недавно в личной беседе зашла речь о лежащих на пути использования Vim граблях, и решил я набросать эдакие «заметки на полях».


Казалось бы, ну что там такого: нажал сочетание, вводишь латиницу, нажал еще раз — кириллицу. Однако, в нормальном режиме, пребывание в котором занимает как минимум 80% времени, накладываются свои отпечатки. Ладно, про langmap, думаю, знают все.

Прописываем в ~/.vimrc следующее:

set langmap=ФИСВУАПРШОЛДЬТЩЗЙКЫЕГМЦЧНЯ;ABCDEFGHIJKLMNOPQRSTUVWXYZ,фисвуапршолдьтщзйкыегмцчня;abcdefghijklmnopqrstuvwxyz

И можем выполнять команды в нормальном режиме с кириллической раскладкой. Правда, для ввода команд через <:> по-прежнему требуется переключить раскладку, что бесило.

Тогда я вспомнил, что редактор имеет собственную переключалку.

set keymap=russian-jcukenwin

Более детально прием описывался, например, здесь.

Вроде бы, проблема решилась: раскладка переключается только в режиме вставки, а команды вводятся латиницей. Но вновь проблема: встроенная переключалка ну очень сильно конфликтует с иксовой. Да и дефолтное сочетание <C-^> не особо эргономично.

Отключать иксовую и полностью полагаться на Vim? В принципе, на это можно было пойти, но очень не хотелось. Вызывать редактор, для набора кириллических названий в терминале показалось мне некоторым перебором. Какое-то время пользовался, путался и матерился.

Так дело не пойдет, снова в гугл. Нашел интересную статью Храброва Дмитрия DeXPeriX о написанном им и Алексеем Радковым плагине vim-xkbswitch.

Вкратце, плагин делает следующее: при переходе в нормальный режим, переключает общесистемную раскладку (через xkb-switch, который должен быть установлен в системе) на латиницу, запомнив текущую раскладку, а при переходе в режим вставки, переключает на запомненную. Сказать, что я был рад — не сказать ничего. Огромная благодарность авторам за то, чего так не хватало все эти годы.

Однако, в процессе эксплуатации, столкнулся-таки с некоторыми трудностями. Зашел как-то на свой компьютер по ssh:

$vim file.txt
Vim: Caught deadly signal SEGV
Vim: Finished.
zsh: segmentation fault  vim file.txt

Сразу на плагин не подумал, были мысли о посыпавшемся жестком диске, сбойнувшей файловой системе и тому подобных «приятных» вещах… В общем, xkb-switch при неустановленной переменной DISPLAY выпадает в segfault, а следом за ним и плагин, унося за собой и vim.

В качестве решения, можно проверить задана ли переменная и, только в случае успеха, активировать плагин. Выглядит это примерно так:

if $DISPLAY == "" 
	let g:XkbSwitchEnabled = 0
else
	let g:XkbSwitchEnabled = 1
	let g:XkbSwitchLib = '~/.local/lib/libxkbswitch.so'
	let g:XkbSwitchIMappings = ['ru']
endif 

После этого вылеты прекратились. Если хочется иметь функционал плагина при редактировании по ssh, то нужно подключаться с поддержкой X-forwarding'a, с ключами -X или -Y.

Надеюсь, статья поможет сохранить немного нервных клеток.

UPD: Сделал pull request разработчику.
Поделиться с друзьями
-->

Комментарии (31)


  1. acmnu
    15.02.2017 17:48

    Спасибо. Действительно полезная вещь.


  1. MooNDeaR
    15.02.2017 18:00
    -14

    Серьезно, когда уже наконец хоть ЧТО-НИБУДЬ начнет работать в линуксах без эпичного ё#линга.

    P.S.
    Сам линуксовод уже даже со стажем, но уже задолбался. Чувствую вернусь скоро на винду или разорюсь и куплю мак.


    1. acmnu
      15.02.2017 18:52
      +2

      Под маком и виндой на виме будет ровно эта же проблема. Только решать её надо будет как-то по-другому.


    1. mtex
      15.02.2017 19:57
      +3

      Ну, увы, большой кнопки с надписью «сделать пи^Wхорошо всем и сразу» нет. В линуксе очень многое работает сразу и хорошо. А когда есть возможность подпилить что-то под личные предпочтения или по-быстрому пофиксить — это очень хорошо. Линукс я люблю, в том числе, за это.


    1. immaculate
      15.02.2017 20:30
      +2

      При чем конкретно в данном случае Linux? Вы статью-то прочитали?


    1. p00h
      15.02.2017 21:04

      Держите нас в курсе


    1. rPman
      16.02.2017 02:28
      +2

      Потелепатствую — вы про опенсорс.
      Вы не поняли, в случае с проприетарной идеологией, у вас даже в мыслях не появятся желание (и тем более возможности) отойти от заранее проторенной дорожки, а значит и 'эпичного ё#линга' у вас будет поменьше, но он будет, чтобы это хотя бы просто заработало, особенно когда устареет!

      с opensource же у вас появляется возможность сделать что то интересное, и возможно, иногда, даже не тратить на это море сил!


  1. pmcode
    15.02.2017 19:31

    А какое у вас DE? xkb-switch с какого-то момента перестал работать в Unity, а когда я перешел на Gnome 3, то оказалось что и в нем тоже. Некоторое время жил с такой штукой.

    function SetUSLayoutGnome()
    silent !qdbus org.gnome.SettingsDaemon.Keyboard /org/gnome/SettingsDaemon/Keyboard org.gnome.SettingsDaemon.Keyboard.SetInputSource 0
    endfunction


    Но сейчас по большей части ушел на Атом, потому что vim, если его использовать как IDE, обрастает каким-то жутким количеством костылей и теряет ту простоту, из-за которой им и приятно пользоваться.


    1. mtex
      15.02.2017 19:59

      DE у меня нет, в качестве wm использую i3. Atom пробовал пользоваться, но концепция редактора, сляпанного из браузера меня как-то ужаснула. Как и то, что этот самый голый atom запускается гораздо медленнее моего обвешенного по самое «не могу» vim'a.


    1. immaculate
      15.02.2017 20:33
      +1

      Да, я так понял, что сейчас используется уже не xkb, а другие решения (какой-то im метод, детали уже не помню). Не знаю, с чем связано такое решение, оно гораздо хуже xkb: переключение случается не всегда (у меня CapsLock переключает раскладку, и иногда вместо переключения включается CapsLock), переключаются раскладки с задержкой, при переключении теряется фокус ввода (это самая раздражающая особенность).


  1. bravosierrasierra
    15.02.2017 19:57
    +1

    Именно по этой причине я ушёл на Emacs + evil-mode, которое и есть самый лучший vim на настоящий момент. Я привык во всех ОС переключаться по alt-space и мне важно было сохранять работоспособность на любой раскладке. Под windows при помощи punto-switcher было настроено игнорирование Emacs и переключение по alt-space. В самом emacs сделаны настройки:

    (global-unset-key (kbd «M-SPC»))
    (global-set-key (kbd «M-SPC») 'toggle-input-method)
    (define-key helm-map (kbd «M-SPC») 'toggle-input-method)
    (define-key isearch-mode-map (kbd «M-SPC») 'toggle-input-method)
    (setq mac-command-modifier 'meta)
    (setq mac-option-modifier 'super)
    (setq mac-pass-command-to-system nil)
    (setq mac-pass-control-to-system nil)

    В результате под windows и mac всё ок и во всех приложениях язык переключается Alt/Cmd+Space. Под Linux Mint после недолгих плясок с штатными настройками DE заработало само.


    1. mtex
      15.02.2017 20:01

      Evil-mode я пробовал, в свое время. Сильно не порадовало, что нужно держать в голове (или, скорей, намоторивать рефлексы) на два типа хоткеев: для vim и, собственно, родные emacs'овские. Потому, что evil-mode распространяется, увы, не на все: в том же dired, например, не работает.


      1. bravosierrasierra
        15.02.2017 20:22
        +1

        да, пришлось evil-ифицировать всё нижеприведёнными маппингами и emacs заиграл очень яркими красками.

        Возни было много, но результат меня радует, уж больно сильно комбайн emacs превосходит текстовый редактор vim. Один только org-mode для меня, как менеджера, уже окупил emacs. Ещё очень радует связка tramp+eshell+dired+compile, когда я отлаживаю код на удалённых серверах как на локальной машине и при этом по M-x eshell попадаю в шелл на том сервере, файл которого у меня в буфере.

        Начинающим, чтобы не заморачиваться, стоит использовать spacemacs, там как раз всё приведено к единой vim-системе.

        Мои наборы для примера:
        (define-key dired-mode-map (kbd "<M-return>") 'shell-instead-dired)
        (define-key dired-mode-map (kbd «K») 'dired-do-kill-lines)
        (define-key dired-mode-map (kbd «k») 'dired-previous-line)
        (define-key dired-mode-map (kbd «J») 'dired-goto-file)
        (define-key dired-mode-map (kbd «j») 'dired-next-line)
        (define-key dired-mode-map (kbd «M-p») 'ace-window)
        (define-key dired-mode-map (kbd «M-q») 'ace-window)
        (define-key dired-mode-map (kbd "/") 'helm-occur)

        ;; в любом режиме Alt+hjkl ведут себя как обычный vim
        (global-set-key (kbd «M-h») 'backward-char)
        (global-set-key (kbd «M-j») 'evil-next-line)
        (global-set-key (kbd «M-k») 'evil-previous-line)
        (global-set-key (kbd «M-l») 'forward-char)
        (define-key evil-insert-state-map (kbd «M-h») 'backward-char)
        (define-key evil-insert-state-map (kbd «M-j») 'evil-next-line)
        (define-key evil-insert-state-map (kbd «M-k») 'evil-previous-line)
        (define-key evil-insert-state-map (kbd «M-l») 'forward-char)

        (loop for (mode. state) in '((inferior-emacs-lisp-mode. emacs)
        (nrepl-mode. insert)
        (pylookup-mode. emacs)
        (chronos-mode. emacs)
        (comint-mode. normal)
        (shell-mode. insert)
        (eshell-mode. emacs)
        (git-commit-mode. insert)
        (git-rebase-mode. emacs)
        (term-mode. emacs)
        (help-mode. motion)
        (helm-grep-mode. emacs)
        (grep-mode. emacs)
        (bc-menu-mode. emacs)
        (elfeed-show-mode. emacs)
        (elfeed-search-mode. emacs)
        (rdictcc-buffer-mode. emacs)
        (dired-mode. emacs)
        (image-dired-thumbnail-mode. emacs)
        (ztree-mode. emacs)
        (diff-mode. emacs)
        (vc-svn-log-view-mode. emacs)
        (wdired-mode. normal))
        do (evil-set-initial-state mode state))

        (push '("\\*CAPTURE-\\*". insert) evil-buffer-regexps)
        (push '("\\*help\\*". motion) evil-buffer-regexps)
        (push '("\\*chronos\\*". emacs) evil-buffer-regexps)
        (push '("\\*eshell\\*". emacs) evil-buffer-regexps)
        (push '("\\*backtrace\\*". emacs) evil-buffer-regexps)
        (push '("\\*vc-\\*". insert) evil-buffer-regexps)

        (evil-define-key 'normal magit-mode-map
        «j» 'magit-goto-next-section
        «k» 'magit-goto-previous-section)
        (evil-define-key 'normal magit-log-mode-map
        «j» 'magit-goto-next-section
        «k» 'magit-goto-previous-section)
        (evil-define-key 'normal magit-diff-mode-map
        «j» 'magit-goto-next-section
        «k» 'magit-goto-previous-section)
        ;; Make HJKL keys work in special buffers
        (evil-add-hjkl-bindings magit-branch-manager-mode-map 'emacs
        «K» 'magit-discard
        «L» 'magit-key-mode-popup-logging)
        (evil-add-hjkl-bindings magit-status-mode-map 'emacs
        (kbd «DEL») 'magit-discard
        «K» 'magit-discard
        ;; «l» 'magit-key-mode-popup-logging
        «l» 'magit-log-popup
        «h» 'magit-toggle-diff-refine-hunk)
        (evil-add-hjkl-bindings ztree-mode-map 'emacs)
        (evil-add-hjkl-bindings magit-log-mode-map 'emacs)
        (evil-add-hjkl-bindings magit-commit-mode-map 'emacs)
        (evil-add-hjkl-bindings occur-mode 'emacs)
        (evil-define-key 'motion compilation-mode-map
        «h» 'evil-backward-char
        )
        (define-key compilation-mode-map
        (kbd «C-u») 'evil-scroll-up
        )

        ;;;; Evil-leader
        (require 'evil-leader) ;; https://github.com/cofi/evil-leader
        (evil-leader/set-leader «SPC»)
        (global-evil-leader-mode)

        (evil-leader/set-key
        «0» 'select-window-0
        «1» 'select-window-1
        «2» 'select-window-2
        «3» 'select-window-3
        «4» 'select-window-4
        «5» 'select-window-5
        «6» 'select-window-6
        «7» 'select-window-7
        «8» 'select-window-8
        «9» 'select-window-9
        "" 'evil-buffer
        «b» 'ido-switch-buffer
        ;; «t» 'helm-chronos-add-timer
        «t» 'chronos-add-timers-from-string
        «T» '(lambda() (interactive)(switch-to-buffer "*chronos*"))
        «c» 'org-ctrl-c-ctrl-c
        «e» 'eshell
        «f» 'helm-find-files
        «w» 'avy-goto-word-or-subword-1
        «W» 'evil-ace-jump-char-mode
        «l» 'evil-ace-jump-line-mode
        ;; «l» 'avy-goto-line
        «SPC» 'evil-ace-jump-word-mode
        «d» 'kill-this-buffer
        «D» 'other-window-kill-buffer
        «o» 'helm-occur
        ;; «i» 'helm-imenu
        «i» 'helm-semantic-or-imenu
        «p» 'projectile-find-file
        «g» 'magit-status
        ;; «j» 'ranger
        «j» 'dired-jump
        «k» 'kill-buffer
        «u» 'undo-tree-visualize
        «y» 'helm-show-kill-ring
        «s» 'save-buffer
        "|" '(lambda () (interactive) (split-window-horizontally) (other-window 1))
        «m» 'folding-toggle-show-hide
        «nh» 'org-habit-toggle-habits
        «nb» 'bss/org-toggle-budgets-and-habits
        «nn» 'bss/org-todo
        «nd» 'org-deadline
        «na» 'org-archive-subtree-default
        «nr» 'bss/org-refile
        «nm» 'bss/org-refile
        «nf» 'org-narrow-to-subtree ;; focus
        «ns» 'bss/org-schedule
        «nw» 'widen ;;unfocus
        «ny» 'org-cut-special
        «nc» 'org-capture
        «nt» 'org-ctrl-c-ctrl-c
        «v» 'org-capture
        «x» 'nlinum-mode
        "-" (lambda () (interactive) (split-window-vertically) (other-window 1))
        "/" 'helm-occur
        ;; «gg» '(lambda () (interactive) (w3m-search «g» (thing-at-point 'symbol)))
        )


        1. mtex
          15.02.2017 20:34

          Спасибо, сохранил набор биндингов на всякий случай. Хотя, к Vim'у я прирос слишком сильно и вряд ли сменяю его на Emacs.


        1. olegkrasnov
          15.02.2017 21:18
          +1

          Такой лонгрид под спойлер бы =)


          1. bravosierrasierra
            15.02.2017 21:30

            Простите, не знал про спойлеры в комментариях. Править собственный комментарий не могу, нету контролов для правки. У меня конфиг 7841 строчка и 313килобайт, вот это лонгрид, да. :) Чего я только туда за полтора года битвы за перфекционизм не запихал: и punto switcher и 100500 тюнингов всего подряд. И хочется ещё пилить и пилить :) Сейчас пробую программировать в intellij idea на питоне over ssh и понимаю что в общем-то на уровне моих задач вся эта магия у меня уже есть в emacs спустя полтора года настроек :)


            1. immaculate
              15.02.2017 21:43
              +1

              Справедливости ради, допилить Emacs до того уровня, чтобы он понимал Python как PyCharm — невозможно. Когда работаешь с большими проектами, возможности PyCharm понимания кода и рефакторинга — неоценимы.


              Например, когда открываешь простыню спагетти-кода, с функциями по 300-400 строк и переменными с названиями в один символ. PyCharm сразу подсветит, где в этих 300-400 строчных монстрах неиспользуемые переменные и ветки, потом потихоньку можно в нем безопасно дать переменным человеческие имена, а потом уже становится понятно, как работает этот код, и его можно переписать или разбить на более короткие и осмысленные функции/методы.


              Да и с проектами меньшего размера тоже помогает. PyCharm как наркотик, слезть с него невозможно. :)


              Использую в нем с первого дня плагин IdeaVIM, работает идеально.


              1. bravosierrasierra
                15.02.2017 22:10
                +1

                Если говорить о рафинированной эффективности программиста, то да, Intellij Idea/Pycharm это стандартный и чрезвычайно эффективный инструмент программиста. Но нужно понимать, что «достаточно хорошего» инструмента зачастую достаточно.

                Для меня emacs это универсальный тамагочи, в котором можно эффективно и без вреда для других провести большую часть рабочего дня со всеми GTD/толканиями проектов/pomodoro/скриптописаниями/magit/tramp/dired. Причём особый шарм в том, что один и тот же конфиг ездит в онлайн-режиме вместе с одними и теми же заметками при помощи dropbox по куче машин с разными ОС, причём ездит в виде git-репозитория. Я отдаю себе отчёт, что Idea/Pycharm «just works», но тамагочи они не являются и фана мне не дают. И да, я их изучаю и мне ничего не мешает по мере необходимости запустить их и сделать что-то, что мне лень или сложно делать в emacs+unix. Я отдаю себе отчёт что если говорить о рациональности, то сейчас emacs и vim среднему айтишнику нужны на стартовом уровне, если вообще нужны. Но фана они могут подарить море. Мне дарят.


                1. am-amotion-city
                  16.02.2017 11:35
                  -1

                  при помощи dropbox [...], причём [...] в виде git-репозитория

                  Вот это я понимаю, это система будущего. Я бы еще туда куда-нибудь вовнутрь почту бы прикрутил, чтобы оно рассылалось аттачами на те машины, где нет клиента дропбокса.


                  С такой архитектурой можно главный приз на каком-нибудь конкурсе великих интеграционных решений взять, отвечаю.


              1. olegkrasnov
                16.02.2017 03:51

                Что vim, что emacs редакторы не дружелюбные и поглощают много времени на обучение.


              1. Crandel
                16.02.2017 11:11

                У меня в емакс есть flycheck-mode, который так само подсвечивает все pep8 ошибки и варнинги и зачастую приходится еще и исправлять после программистов с пайчармом, потому что они забивают на ворнинги. Для рефакторинга переменных и класов есть rope-mode так что разница не слишком большая.


                1. immaculate
                  16.02.2017 14:09

                  Сомневаюсь, что дело в PyCharm. PyCharm тоже подсвечивает все ошибки и ворнинги, даже эффективнее, чем любая сторонняя утилита, так как он знает о структуре проекта и умеет выводить типы. Если обладатель PyCharm их не фиксит — то это уже его личный выбор.


                  Когда в последний раз прикручивал rope-mode к emacs, это были неудобные и ненадежные костыли. Рад, если это изменилось — я в последний раз пользовался Emacs около 8-9 лет назад, уверен, что с тех пор произошли большие сдвиги.


                  Еще одна причина, по которой ушел с Emacs — туннельный синдром запястья. Поэтому сначала ушел на vim, затем на PyCharm с IdeaVim. Симптомы туннельного синдрома ушли.


                  1. olegkrasnov
                    16.02.2017 18:51

                    Если вдруг туннельный синдром вернётся, попробуйте заняться штангой (это не шутка).


    1. mpetrunin
      15.02.2017 20:16

      А есть какая-то крутая статья про Emacs + evil-mode для vim-оводов? А то много упоминаний слышу, но чтобы кто-то зажёг и убедил меня, что так гораздо круче, ещё не видел.


      Было бы здорово, если бы бы был текст (видео?) со всеми преимуществами.


      1. bravosierrasierra
        15.02.2017 20:34
        +1

        Вот о причинах, почему стоит попробовать evil-mode: https://www.youtube.com/watch?v=JWD1Fpdd4Pc

        Вот quickstart-режим для начинающих с использованием spacemacs: https://www.youtube.com/watch?v=ZFV5EqpZ6_s&list=PLrJ2YN5y27KLhd3yNs2dR8_inqtEiEweE

        Вот вводный курс по emacs от Бушенко, который мне помог втянуться: https://www.youtube.com/playlist?list=PLECBtie1W1tGlrbDDBvcxnttRR4IA5qZn

        Вот краткое введение в elisp для начинающих: http://steve-yegge.blogspot.ru/2008/01/emergency-elisp.html

        Вот «золотая» схема работы с org-mode для менеджеров: http://doc.norang.ca/org-mode.html

        Кстати, как по мне, для использования emacs caps-lock обязательно должен быть замаплен как ctrl, иначе пальцы отвалятся, т.к. он был спроектирован под клавиатуру, у которой ctrl рядом с пробелом.


  1. mpetrunin
    15.02.2017 20:18

    Кстати, разумно было бы кинуть баг в список задач проекта: чтобы плагин сам определял, установлен ли $DISPLAY и матерился бы, что не установлен, и надо ssh -Y


    1. mtex
      15.02.2017 20:22
      +1

      Кстати да проект вроде есть на git, попробую пулл-реквест сделать. Там, в принципе, эту проверку в начало плагина добавить, и дело в шляпе.


      1. mpetrunin
        15.02.2017 20:26

        Только я бы посоветовал, что всё-таки плагин пыхтел что-нибудь типа забыли "-Y" в ssh!!! или там что-то в таком стиле (не всегда же отсутствие $DISPLAY означает ssh-сессию, иногда это просто безиксовый терминал). :)


        1. mtex
          15.02.2017 20:36

          Ну, xkb-switch валится не только при логине через ssh, то же самое поведение и в текстовой консоли. Вообще, по-хорошему, тут надо не плагин лечить, а именно xkb-switch, так как проблема в нем.


  1. Wilk
    16.02.2017 22:24

    Здравствуйте.

    Сходная проблема есть и в Emacs: при переключении системной раскладки на русский язык какие-либо команды выполнить нельзя. Для себя я это и на Windows, и на Kubuntu решил очень просто: в настройках переключения раскладки включил использование отдельной раскладки для каждого отдельного приложения (могу ошибаться, но быть может даже окна на Kubuntu) и Emacs использовал только встроенную систему интернационализации и переключения раскладок. В результате волки сыты (всё равботает, я доволен), и овцы целы (не пришлось что-либо делать).


  1. olegkrasnov
    17.02.2017 00:09

    На маке xkbswitch не хочет работать за пределами /usr/local/
    Променял его на ISS:
    let g:XkbSwitchLib = $HOME.'/.vim/bin/libInputSourceSwitcher.dylib'