13 декабря 2012 в 15:56

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

VIM*
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х-битная, то использовать соответствующую библиотеку.
Храбров Дмитрий @DeXPeriX
карма
48,5
рейтинг 0,0
Пользователь
Самое читаемое Разработка

Комментарии (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
    Плагин сделали. Описано тут

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