Vim в Windows и переключение раскладки клавиатуры

    UPD: Это «историческая» версия топика. Новое решение проблемы смотреть здесь.

    Проблема русской раскладки в Vim поднималась много раз. Одно из решений можно увидеть здесь, однако оно заставляет привыкать к новой горячей клавише для переключения раскладки. Также существует множество решений с вызовом системной утилиты для смены раскладки, но под Windows подобной утилиты не нашел, так что пришлось реализовать её самостоятельно.

    По сути получился консольный интерфейс для WinAPI-функций. Для установки новой раскладки для окна программа получает имя класса данного окна и двухбуквенный код языка. Если есть такое окно и найден соответствующий языковой код, то программа посылает сообщение WM_INPUTLANGCHANGEREQUEST данному окну.

    Для создания связи программы с Vim'ом опирался на запись из блога Тех-Детали. Чтобы переключение работало в Windows нужно в _vimrc добавить следующие строки:

    fun! <SID>xkb_switch(mode)
        let cur_layout = system('dxlsw.exe -get VIM')
        if a:mode == 0
            if cur_layout != 'en'
                call system('dxlsw.exe -set VIM en')
            endif
            let b:xkb_layout = cur_layout
        elseif a:mode == 1
            if exists('b:xkb_layout') && b:xkb_layout != cur_layout
                call system('dxlsw.exe -set VIM '.b:xkb_layout)
            endif
        endif
    endfun
    
    if executable('dxlsw.exe')
        autocmd InsertEnter * call <SID>xkb_switch(1)
        autocmd InsertLeave * call <SID>xkb_switch(0)
    endif
    


    Также не забудьте положить dxlsw.exe (3.5 KB) куда-нибудь в %PATH, например, в C:\Windows\System32. Если кому-нибудь нужно, то есть и 64х-битная версия (5 KB). Исходный код (6.8 KB) доступен под лицензией LGPL2.

    Преимущества: работает, переключает раскладку только в окне Vim'a.
    Недостатки: при вызове функции system из GVim на краткий промежуток времени открывается окно cmd.exe и окно GVim'a ненадолго теряет фокус.

    UPD:
    По совету хабраюзера ivnik собрал DLL-версию переключателя языка. Окно cmd не появляется, тормозов нет.

    _vimrc изменился до:
    fun! <SID>lib_kb_switch(mode)
        let cur_layout = libcallnr('libdxlsw', 'dxGetLayout', 0)
        if a:mode == 0
            if cur_layout != 1033
                call libcallnr('libdxlsw', 'dxSetLayout', 1033)
            endif
            let b:lib_kb_layout = cur_layout
        elseif a:mode == 1
            if exists('b:lib_kb_layout') && b:lib_kb_layout != cur_layout
                call libcallnr('libdxlsw', 'dxSetLayout', b:lib_kb_layout)
            endif
        endif
    endfun
    
    autocmd InsertEnter * call <SID>lib_kb_switch(1)
    autocmd InsertLeave * call <SID>lib_kb_switch(0)
    


    DLL-файл класть в директорию с ЕХЕ-файлом Gvim'a. Если сборка Vim'a 64х-битная, то использовать соответствующую библиотеку.
    Share post

    Similar posts

    Comments 9

      0
      Зачем делать .exe, если можно воспользоваться скриптом на python/ruby/whatever, который так же будет дергать функции WinAPI но при этом у него на порядок меньше шансы получить вирус?
        0
        Для python/ruby нужно прогружать интерпретатор. Да и скорость выполнения получится ниже.
        В windows обычно используется много ЕХЕ-файлов и вирус может получить любой из них.
        0
        Насчёт проблемы фокуса и мелькания cmd.exe, соберите dll и вызывайте из vim с помощью libcall:

        :help libcall
          0
          Спасибо, попробую!
          0
          Спасибо за отличное решение, пользовался до этого ctrl+^ — не удобно.
          Предлагаю несколько доработок:

          plugin.vim

          let s:dll_path = ''
          if has('win32')
          let s:dll_path = fnamemodify(expand(""), ":h"). "\\libdxlsw.dll"
          elseif has('win64')
          let s:dll_path = fnamemodify(expand(""), ":h"). "\\libdxlsw64.dll"
          endif

          fun! lib_kb_switch(mode)
          let cur_layout = libcallnr(s:dll_path, 'dxGetLayout', 0)
          if a:mode == 0
          if cur_layout != 1033
          call libcallnr(s:dll_path, 'dxSetLayout', 1033)
          endif
          let b:lib_kb_layout = cur_layout
          elseif a:mode == 1
          if exists('b:lib_kb_layout') && b:lib_kb_layout != cur_layout
          call libcallnr(s:dll_path, 'dxSetLayout', b:lib_kb_layout)
          endif
          endif
          endfun

          if s:dll_path != ''
          autocmd InsertEnter * call lib_kb_switch(1)
          autocmd InsertLeave * call lib_kb_switch(0)
          endif

          1. можно оформить в виде плагина, использовать с pathogen, vundle;
          2. корректно «не работает» для любой ОС кроме Win;
          3. dll берется из папки скрипта;
            0
            1. Думаю, даже нужно будет
            2,3. Вот именно, что для Linux и MacOS оно точно не работает. По хорошему, наверное, стоит собрать нормальный плагин, подключающийся через vundle одной строчкой и работающий хотя бы в самых распространённых системах. Найду время — займусь
              0
              Так собирать ничего не надо, у тебя уже все готово.
              Бери код с моими правками, выкладывай на гитхаб вместе с собраными dll и все готово.
              Пункты 2,3 в моих правках реализованы.
              vundle с гитхаба все подхватит сам.
                0
                Отписал в Тех-Детали — надеюсь запилим вместе крутой плагин. У меня с конфигами вимовскими не очень…
            0
            Плагин сделали. Описано тут

            Only users with full accounts can post comments. Log in, please.