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х-битная, то использовать соответствующую библиотеку.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

        :help libcall
      • 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
          Плагин сделали. Описано тут

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