Rusmode — решение проблемы с русской раскладкой

    Проблема


    Думаю многие из русскоговорящих пользователей Vim часто сталкивались с проблемой, что при переключении на Vim с почты/скайпа/браузера забывали переключить раскладку. Далее события развиваются следующим образом:
    1. У вас нет никакого map руских символов — Vim пищит, вы материтесь и переключаете раскладку;
    2. У вас есть map русских символов — скорее всего он кривой, команды не выполняются, Vim пищит, вы материтесь и меняете раскладку.

    Очевидно, что map русских символов — не выход из положения.

    Решение


    Правильное решение — по нажатию русского символа в normal mode переключать раскладку на английский и ввести уже нажатую клавишу.

    Реализация


    Именно такое поведение я и захотел реализовать. Скрипт(github) получился очень простым и понятным.
    Сначала мы проверяем, не загружен ли уже наш скрпит
    if exists('g:loaded_rusmode') || &cp || version < 700
    	finish
    endif
    let g:loaded_rusmode = 1
    

    Затем создадим функцию для переключения раскладки и ввода нажатого символа:
    function ChangeLayout(key)
        call system('osascript -e "tell application \"System Events\" to key code 49 using command down"')
        if a:key ==? ':'
            execute("normal! \<esc>:")
        else
            execute("normal!" . a:key)
        endif
    endfunction
    

    И затем следует долгий и нудный nmap:
    nmap <silent> <unique> й :call ChangeLayout('q')<CR>
    nmap <silent> <unique> ц :call ChangeLayout('w')<CR>
    nmap <silent> <unique> у :call ChangeLayout('e')<CR>
    ...
    nmap <silent> <unique> Ь :call ChangeLayout('M')<CR>
    nmap <silent> <unique> Б :call ChangeLayout('<')<CR>
    nmap <silent> <unique> Ю :call ChangeLayout('>')<CR>
    

    Я использую MacOS, поэтому решение было написано только для этой системы. Если уважаемым юзерам Linux нравится такой подход — очень жду Pull request'ов на гитхабе, самому написать
    setxkbmap -layout us
    проблем нет, но протестировать, к сожалению, возможности нет.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 20

      +1
      Простите мою неосведомленность, но чем nmap первого символа и переключения раскладки лучше nmap всех символов без переключения? И как я понял назад раскладка не переключается? И еще, у Вас буква «Ж» выделяется из ряда маппингов:
      nmap <unique> Ж :call ChangeLayout(':')<CR>:
      Опечатка или скрытый смысл?
      А вообще проблема весьма насущная, поддерживаю попытки ее решить.
        +3
        Тем, что если nmap'ить все символы, то при попытке сделать Жцй(:wq) вы получите кукиш. Если же по нажатию Ж переключить раскладку, то вы получите как раз :wq и успешно сохранитесь и закроете окно. Этим же объясняется выделяющийся маппинг «Ж» — сайлент отключен, чтобы фокус остался в коммандной строке и вы успешно продолжили ввод команды.
        Возможно, есть другой вариант вернуть фокус в командную строку, но я Vim script изучил за день до написания этого плагина и более умного способа мне в голову не пришло.
        +2
        Не проще ли настроить, чтобы раскладка была не глобальной, а привязывалась к текущему окну? В Минте (и наверное в Гноме вообще) это в настройках Keyboard Layout -> Layouts. Наверняка же в MacOS такое тоже есть.
          +1
          И в макоси есть такая настройка. Наверное это проще, но не для всех. Лично для меня это создает большие неудобства и на порядок выросшее количество переключений раскладки.
            +1
            О, я совсем о другом думал. Когда в Vim редактируешь русский текст, то работа с «нормальным» режимом — просто мука. Приходится постоянно переключать раскладку туда-сюда. Вот думал ваш скрипт и решает это. Тогда ему не достает возвращения старой раскладки при выходе из нормального режима.
              0
              Не проблема! Скоро обновлю код для подобного переключения при установке соответствующей настройки!
                +1
                А правильно настроенные keymap или langmap не помогают?
                  0
                  Я вообще обычно для кириллицы использую «set keymap=russian-jcukenwin», не помню где-то когда-то встретил такое. Язык тогда по Ctrl+^ переключается только внутри вима. И своих заморочек там тоже хватает — если в системе выбрана русская раскладка, то хоткеи в виме соответственно не работают.
                  А с langmap я перестал дружить еще как с koi8 перешел на utf, помнится она с юникодом не особо нормально себя ведет. Но это давно было, может я чего-то и пропустил, надо поглядеть.
                  +1
                  Пофиксил баги, теперь работают последовательности, например: 10w, gg…
                  Так же сделал ваш фичреквест.

                  Для переключения раскладки по выходу из insert mode нужно в .vimrc добавить строку:
                  let g:rusmode_autotoggle_insertleave=1
              0
              Есть же langmap. см. тут
                +1
                Langmap == проблемы с привязками с левой частью вида <Plug>ISurround:
                :inoremap <Plug>Isurround <Nop>
                :imap <C-g>S <Plug>Isurround
                :set langmap=YI,IY
                :execute "normal i\<C-g>S"
                :1print
                <Plug>ISurround
                  0
                  + несистемное переключение раскладки и необходимость помнить в каком окне какая раскладка (даже хуже: в каком Vim’е какого эмулятора терминала, у меня обычно первых больше чем последних, но и последних тоже много). Так что расслабьтесь, langmap — это просто ещё одна возможность, которая есть, но которой нет.
                    0
                    Я и не напрягался. Вы привели пример, в котором заменяются 2 буквы английского алфавита. Если у вас langmap вроде этого то никаких проблем не будет.
                      0
                      С такой может быть и так (хотя эту ошибку я видел в списке рассылки, но что на что меняли реально не помню. Точно меняли не латиницу на латиницу). Вторая и третья проблемы от того никуда не делись.
                +2
                Идея интересная, но
                1. Тем, что если nmap'ить все символы, то при попытке сделать Жцй(:wq) вы получите кукиш.
                  решается таким же набором привязок для командного режима, это не является принципиально неустранимой проблемой старого способа;
                2. nmap в вашем случае не надо использовать (Learn vimscript the hard way, глава 5: Strict mapping). Для такой задачи вообще‐то nore обычно вреден, но в вашем коде без nore хуже.
                3. чтобы сохранить поведение нельзя использовать normal! (с восклицательным знаком).
                4. чтобы сохранить поведение многосимвольных команд нельзя использовать normal вообще, хоть с, хоть без восклицательного знака;
                5. чтобы не менять символы в двух удалённых друг от друга местах при копировании проще сделать функцию‐помощник и менять их в одном месте.


                В вашем коде нужно либо заменить nmap на nnoremap и execute("normal…") на call feedkeys(a:key, "t"), либо использовать <expr>:
                function s:ChangeLayout(key)
                    call system('osascript -e "tell application \"System Events\" to key code 49 using command down"')
                    return a:key
                endfunction
                nmap <expr> <unique> й <SID>ChangeLayout('q')
                
                  0
                  Большое спасибо за столь объемный и полезный комментарий, в самое ближайшее время разберусь и внесу в код соответствующие изменения. Не могли бы вы мне, как новчику в Vim script, объяснить подробнее пункты 3 и 4, здесь или в личку?
                    0
                    3. Определите привязку, к примеру nnoremap Y :echom "I am HERE"<CR>. И попробуйте её использовать с вашим кодом.
                    4. Попробуйте ввести с вашим кодом составные команды (вроде gg) или операторы (вроде yy). Поместить все символы после <CR> с или без <silent> является куда как лучшей идеей, чем использовать :normal в функции: эффект от :normal на этих командах слабо отличается от того, из‐за чего вы использовали исключительный случай для :. Разве что убирать <silent> не обязательно. Замечу что исключительный случай для : в функции не выполняет ровным счётом никакой полезной работы.
                      0
                      Ага, вижу баги. Спасибо, пойду почитаю хэлпы и пофикшу! :)
                        +2
                        Там наверху у меня ссылка на учебник. Основная страница: learnvimscriptthehardway.stevelosh.com/. Ещё можно посмотреть на мою статью с описанием некоторых особенностей vimL. Правда по последней давно (со дня написания, но тогда, разумеется, мне казалось иначе) плачет refactoring.
                          0
                          Учебник я читал пару дней назад, немного «по диагонали», но читал. Пример с плагином синтаксиса у автора не очень удачный, хорошо бы его дополнить каким-нибудь utility скриптом. Вашу статью зачитаю обязательно.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое