Как связать Ci в отличие от TAB?

16

Я хочу сделать Control-iвыступление indent-region(в основном, поскольку XCode уже построил эту мышечную память).

Я понимаю это Control-iи tabнеразличим в смысле Ascii, но они в смысле кода ключа.

Я попробовал очевидное:

(global-unset-key (kbd "C-i"))
(global-set-key (kbd "C-i") 'indent-region)

безрезультатно - нажатие Control-iвсе равно делает то, что tabделает клавиша в текущем контексте. Что я могу сделать, чтобы помочь Emacs по-другому относиться к кнопке вкладки Control-i?

Обновление: я полагаю, что эквивалентный результат был бы достигнут, если бы можно было переназначить то, что tab/ Control-iнажимает, когда выбран регион.

Марк Ауффлик
источник
1
Это из фрейма GUI или фрейма терминала? Я не знаю, можете ли вы переопределить это для терминала.
dgtized
Хороший Q, обычно GUI-фрейм, но я делаю удаленный доступ к серверам и иногда использую emacs в терминале (конечно, с git-shared emacs.d :)
Марк Ауффлик

Ответы:

15

Я не думаю, что это может быть достигнуто с терминала, но в режиме графического интерфейса вы можете попробовать это:

(define-key input-decode-map [?\C-i] [C-i])
(global-set-key (kbd "<C-i>") 'indent-region)

Я делаю то же самое с C-mтем, чтобы его можно было отличить отRET

РЕДАКТИРОВАТЬ:

Следующее должно работать, если вы находитесь в режиме GUI или TTY:

;; Unbind <C-i> from the TAB key and bind it to indent-region.
;; Since TAB and <C-i> cannot be differentiated in TTY emacs,
;; the workaround is to conditionally bind TAB to indent-region
;; when there is an active region selected.
(if (window-system)
  ; IF we are not in a TTY, unbind C-i from TAB
    (progn
      (define-key input-decode-map [?\C-i] [C-i])
      ; ... and remap it to indent-region
      (global-set-key (kbd "<C-i>") 'indent-region))
  ; ELSE IF we are in a TTY, create a replacement for TAB
  (defun my/tab-replacement (&optional START END)
    (interactive "r")
    (if (use-region-p)
      ; IF active region, use indent-region
        (indent-region START END)
      ; ELSE IF no active region, use default tab command
      (indent-for-tab-command)))
  ; Bind our quick-and-dirty TAB replacement to the TAB key
  (global-set-key (kbd "TAB") 'my/tab-replacement))

Это не красиво, но, кажется, делает работу. Я приветствую любые уточнения или изменения этого кода по мере необходимости.

nispio
источник
1
Работает отлично! ++ снова купил бы стек-обмен emacs :)
Марк Ауффлик
Единственная небольшая проблема, о которой я только что подумал, заключается в том, что теперь мы можем использовать терминальное средство Emacsclient для Emacs, которое было запущено с оконной системой (что я иногда делаю). Если я не вижу задержки, я просто использую функцию замены табуляции во всех случаях.
Марк Ауффлик
1
Я просто хочу добавить, что <C-i>и [C-i]может быть произвольным идентификатором, как <foobar>и [foobar], и он все равно будет работать; просто не называйте это tabилиbackspace
xdavidliu
Я добавил отредактированный кусок кода на ваш ответ в .emacsфайл , но как TABи C-iперераспределяется :-( @nispio
Alper
@alper, Скорее всего это означает, что (window-system)вернулся nilв момент .emacsзагрузки. Это может быть связано с тем, что вы запускаете неграфический экземпляр Emacs, или потому, что вы запускаете демон Emacs.
Ниспио
13

Рамки GUI

В фреймах GUI (X11, Windows, OSX,…) Emacs считывает Tabключ как tabфункциональную клавишу. Однако, поскольку Tabклавиша на терминалах традиционно отправляет символ ^I( Control + I), Emacs переводит tabфункциональную клавишу в символ Control + I (символ 9), который отображается как TAB. Этот перевод сделан через function-key-map.

Аналогичный перевод происходит с некоторыми другими функциональными клавишами. ( Backspaceи Deleteэто сложный случай, который я не буду подробно обсуждать здесь.)

Function key    Translated to character         Notes
                Number  Name  Decomposition
backspace       127     DEL   Ctrl+?            May be translated to C-h instead
tab               9     TAB   Ctrl+I
linefeed         10     LFD   Ctrl+J            Few keyboards have this key
return           13     RET   Ctrl+M
escape           27     ESC   Ctrl+[

Если вы хотите полностью отделить Tabот Ctrl+ I, удалите привязку из function-key-map:

(define-key function-key-map [tab] nil)

Однако это не очень полезно, потому что записи в function-key-mapпереопределяются привязками в зависимых от режима раскладках клавиш или в глобальной карте. Поэтому, если вы хотите определить другую привязку для tab, просто сделайте это (в Elisp, а не в интерактивном режиме, потому что приглашение чтения ключа применяет function-key-mapперевод, так что вы в конечном итоге перепривязываете, TABа не tab):

(global-set-key [tab] '…)
(define-key some-mode-map [tab] '…)

Все стандартные режимы, которые изменяют действие Tabклавиши, делают это, изменяя TABключ, который является псевдонимом для C-iсимвола, сгенерированного комбинацией клавиш Ctrl+ I. Если вы хотите, чтобы стандартные привязки применялись tabвместо C-i, вместо этого , оставляйте function-key-mapи изменяйте сопоставления клавиш в одиночку, а вместо этого перенаправляйте Ctrl+ Iна другую клавишу.

(define-key input-decode-map [(control ?i)] [control-i])
(define-key input-decode-map [(control ?I)] [(shift control-i)])
(define-key some-mode-map [control-i] '…)

Теперь Emacs сообщит Ctrl+ Iкак « <control-i>(перевод с TAB)». Это не красиво, но неизбежно: красивая печать символа 9 TABвстроена в исходный код Emacs.

Клеммные рамы

В терминальных кадрах проблема сложнее и часто невозможна. Терминалы не передают ключи, они передают символы (точнее, фактически, они передают байты). TabКлюч передается как символ табуляции - который Control + I, такой же , как то , что комбинация клавиш Ctrl+ Iгенерирует. Функциональные клавиши, которые не имеют соответствующего символа (например, клавиши курсора), передаются как escape-последовательности, то есть последовательности символов, начинающиеся с ESC= Control + [(именно поэтому Emacs определяет escapeкак префиксную клавишу - ESCдолжен быть префиксом). См. Как работают ввод с клавиатуры и вывод текста? для получения дополнительной информации.

Существует несколько терминалов, которые можно настроить для отправки различных последовательностей клавиш для функциональных клавиш, но не так много. Это поддерживают как libtermkey / libtickit от LeoNerd, так и xterm Томаса Дики (начиная с версии 216). В Xterm эта функция является необязательной и активируется через modifyOtherKeysресурс. Однако я не знаю ни одного популярного эмулятора терминала, кроме xterm, который бы это поддерживал, в частности многих эмуляторов, построенных на libvte . Некоторые терминальные эмуляторы позволяют вам делать это вручную через пользовательскую переписку из цепочек ключей для экранирования последовательностей.

Этот механизм позволяет различать многие комбинации клавиш, а не только tab / Ci, return / Cm и escape / C- [. См. Проблемы с сочетаниями клавиш при использовании терминала для более подробного описания.

Базовая функция xterm поддерживается начиная с Emacs 24.4. Однако основы (в частности Tab, Return, Escape, Backspace) до сих пор отправить традиционные управляющие символы, потому что это то , что ожидают приложение. Существует режим, в котором Ctrl+ letterотправляет escape-последовательность вместо управляющего символа. Поэтому, чтобы отличить функциональные клавиши от Ctrlкомбинаций в Emacs 24.4, измените его поддержку для modifyOtherKeysиспользования этого режима, установив для ресурса значение 2 вместо 1.

;; xterm with the resource ?.VT100.modifyOtherKeys: 2
;; GNU Emacs >=24.4 sets xterm in this mode and define
;; some of the escape sequences but not all of them.
(defun character-apply-modifiers (c &rest modifiers)
  "Apply modifiers to the character C.
MODIFIERS must be a list of symbols amongst (meta control shift).
Return an event vector."
  (if (memq 'control modifiers) (setq c (if (or (and (<= ?@ c) (<= c ?_))
                                                (and (<= ?a c) (<= c ?z)))
                                            (logand c ?\x1f)
                                          (logior (lsh 1 26) c))))
  (if (memq 'meta modifiers) (setq c (logior (lsh 1 27) c)))
  (if (memq 'shift modifiers) (setq c (logior (lsh 1 25) c)))
  (vector c))
(defun my-eval-after-load-xterm ()
  (when (and (boundp 'xterm-extra-capabilities) (boundp 'xterm-function-map))
    ;; Override the standard definition to set modifyOtherKeys to 2 instead of 1
    (defun xterm-turn-on-modify-other-keys ()
      "Turn the modifyOtherKeys feature of xterm back on."
      (let ((terminal (frame-terminal)))
        (when (and (terminal-live-p terminal)
                   (memq terminal xterm-modify-other-keys-terminal-list))
          (send-string-to-terminal "\e[>4;2m" terminal))))
    (let ((c 32))
      (while (<= c 126)
        (mapc (lambda (x)
                (define-key xterm-function-map (format (car x) c)
                  (apply 'character-apply-modifiers c (cdr x))))
              '(;; with ?.VT100.formatOtherKeys: 0
                ("\e\[27;3;%d~" meta)
                ("\e\[27;5;%d~" control)
                ("\e\[27;6;%d~" control shift)
                ("\e\[27;7;%d~" control meta)
                ("\e\[27;8;%d~" control meta shift)
                ;; with ?.VT100.formatOtherKeys: 1
                ("\e\[%d;3~" meta)
                ("\e\[%d;5~" control)
                ("\e\[%d;6~" control shift)
                ("\e\[%d;7~" control meta)
                ("\e\[%d;8~" control meta shift)))
        (setq c (1+ c)))))
  (define-key xterm-function-map "")
  t)
(eval-after-load "xterm" '(my-eval-after-load-xterm))
Жиль "ТАК - перестань быть злым"
источник
Когда вы говорите «Emacs 24.24», вы имеете в виду «Emacs 24.4»?
Tarsius
1
@tarsius Комментарий в коде, который скопирован из моего файла инициализации, говорит «24.4», так что я думаю, что это правильно, а «24.24» в тексте, который я написал для этого ответа, был опечаткой для «24.4».
Жиль "ТАК - перестань быть злым"