Переключение раскладок в Emacs

  emacs

UPD: перепост старой заметки в новый блог, планирую обновлять и поддерживать в актуальном состоянии

Последнее обновление: Среда, 10. Март 2010

Описание проблемы: при переключении на не-латинскую раскладку клавиатуры в Emacs не работают горячие клавиши. Также не запоминается своя раскладка для каждого буфера Emacs.

Варианты решения проблемы:

  • Продублировать все горячие клавиши для нужной раскладки и отказаться от отдельной для каждого буфера раскладки (плохой выход из ситуации).
  • Использовать встроенную в Emacs переключалку (то, что нам нужно).

По умолчанию цикличное переключение раскладок в Emacs работает по комбинации C-\
Очевидно, вы пожелаете настроить в Emacs клавиатурное сочетание для переключения раскладок как в xorg, и тут появляется проблема номер 2:

Xorg не передает приложению нажатие клавиш, которыми он переключает раскладку. К тому же нет возможности настроить игнорирование иксорговской переключалкой конкретного приложения. Для решения этой проблемы ранее предлагали использовать патченный xxkb, который переключал раскладку в окне Emacs всегда на латиницу и передавал окну нужные клавиатурные сочетания. Как показала практика - работает это решение не всегда и глючит, поэтому появились следующие, более корректные решения этой проблемы:

  • Хороший человек с псевдонимом Akshaal написал программку, а другой хороший человек с псевдонимом Bzek доработал её. К сожалению, все ссылки на описание этого метода померли, поэтому я переношу эту информацию сюда (см. ниже). Этот метод имеет свои недостатки, но я пользуюсь именно им. Суть метода сводится к переключению раскладок сторонней программой, которая сама определяет окно Emacs и отправляет ему нужное сочетание клавиш вместо переключения раскладки.
  • Использовать для переключения языков альтернативный метод ввода ibus (scim). Достаточно отключить его в Emacs и клавиатурные сочетания, которые вы настроили в ibus (scim), будут обрабатываться в Emacs. Описание тоже ниже.

Настраиваем ibus (scim)

  1. Устанавливаем scim, scim-m17n и scim-tables (для ibus сами определяйтесь, не пользуюсь им). Последние два включают в себя приличное количество раскладок клавиатуры для различных языков.
  2. Прописываем в профиль переменные среды: Для scim:
    
    export XMODIFIERS=@im=SCIM
    export GTK_IM_MODULE=xim
    export QT_IM_MODULE=xim
                
    Для ibus видимо будет как-то так:
    
    export GTK_IM_MODULE=ibu
    export QT_IM_MODULE=ibu
                
  3. Отключаем ibus (scim) в Emas, для этого пишем в .Xdefaults:
    Emacs*useXIM:false
    (не знаю правда, как тут с ibus; если кто-то знает - пишите мне)
  4. Запускаем scim-setup и настраиваем всё что нужно. Останавливаться на этом не буду - если не разберетесь, то можете спросить в xmpp-конференции emacs@conference.jabber.ru.
  5. Настраиваем в Emacs обработку этих сочетаний клавиш. Например так:
    (global-set-key (kbd "C-1")
    		(lambda ()
    		  (inactivate-input-method)))
    (global-set-key (kbd "C-2")
    		(lambda ()
    		  (set-input-method 'russian-computer)))
                

Вы можете добавить сюда переключение словаря flyspell, например. Или настроить цикличное переключение, в этом вам поможет функция toggle-input-method.

Использование emxkb

Замечание к этому методу: с его помощью нельзя настроить раскладку на цикличное переключение (хотя, можно просто модифицировать программу). У меня, например, группа "us" включается по нажатию C-1, а "ru" - C-2.

Первое, что нам понадобится - программа emxkb. Её нужно скачать и скомпилировать вот так:


gcc -L/usr/X11R6/lib -lX11 -o emxkb emxkb.c
        

При выполнении emxkb 0 раскладка переключается на первую группу, emxkb 1 - вторую и т.д. Если же значение WM_CLASS текущего окна равно "emacs", то вместо переключения групп emxkb шлёт нажатие виртуальных клавиш F31, F32, F33 и т.д. Остается только обрабатывать эти нажатия в emacs.

Итак, вы уже настроили в своём wm (или xbindkeys) какие-либо сочетания клавиш на выполнение комманд emxkb 0 и emxkb 1. Но ещё не все готово для настройки emacs. Дело в том, что клавиш F31, F32 и т.д. не существует и нужно их виртуально "создать" с помощью xmodmap:


keycode 431=F31
keycode 432=F32
keycode 433=F33
      

431, 432, 433 можно заменить на любые другие свободные кейкоды.

Теперь можно настраивать emacs:

(defun reset-flyspell-with-new-dict (dict)
  "Set new dictionary and restart flyspell"

  (unless (equal dict ispell-local-dictionary)
    (setq ispell-local-dictionary dict)
    (when flyspell-mode
      (flyspell-mode)
      (flyspell-mode)))

  (when flyspell-mode
    (save-excursion
      (flyspell-region (window-start) (window-end))))

  (message nil))

(global-set-key [(f31)]
                (lambda ()
                  (interactive)
                  ;; (reset-flyspell-with-new-dict "american")
                  (inactivate-input-method)))

(global-set-key [(f32)]
                (lambda ()
                  (interactive)
                  ;; (reset-flyspell-with-new-dict "russian")
                  (set-input-method 'russian-computer)))

(global-set-key [(f33)]
                (lambda ()
                  (interactive)
                  ;; (reset-flyspell-with-new-dict "italian")
                  (set-input-method 'italian-keyboard)))


(defun toggle-specified-isearch-input-method (new-input-method)
  "Toggle specified input method in interactive search."
  (interactive)
  (let ((overriding-terminal-local-map nil)))

  (if (eq new-input-method 'default-method)
      (inactivate-input-method)
    (set-input-method new-input-method))

  (setq isearch-input-method-function input-method-function
	isearch-input-method-local-p t)
  (setq input-method-function nil)
  (isearch-update))


(add-hook 'isearch-mode-hook
          (lambda ()
            (define-key isearch-mode-map (kbd "<f31>")
              (lambda ()
                (interactive)
                (toggle-specified-isearch-input-method 'default-method)))

            (define-key isearch-mode-map (kbd "<f32>")
              (lambda ()
                (interactive)
                (toggle-specified-isearch-input-method 'russian-computer)))

            (define-key isearch-mode-map (kbd "<f33>")
              (lambda ()
                (interactive)
                (toggle-specified-isearch-input-method 'italian-keyboard)))))
      

Внимательно (или не очень) изучите приведенный кусок кода на elisp и настройте его под свои нужды.

P.S. Если у вас что-то не получилось, вы можете найти меня в конференции emacs@conference.jabber.ru. Приветствуются любые дополнения и правки к статье.
P.P.S. Для консольной версии Emacs проблема всё ещё актуальна.