¿Cómo vincular Ci como diferente de TAB?

16

Quiero hacer Control-ifuncionar indent-region(básicamente porque Xcode ya ha construido esa memoria muscular).

Me doy cuenta de eso Control-iy no puedo tabdistinguirlos en el sentido de Ascii, pero están en el sentido del código clave.

He intentado lo obvio:

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

en vano: presionar Control-isolo hace lo que hace la tabtecla en el contexto actual. ¿Hay algo que pueda hacer para ayudar a Emacs a tratar el botón de pestaña de manera diferente Control-i?

Actualización: Creo que un resultado equivalente se lograría por ser capaz de volver a asignar lo que un tab/ Control-ihace prensa cuando se selecciona una región.

Mark Aufflick
fuente
1
¿Es esto de un marco GUI o un marco de terminal? No sé si puedes anularlo para una terminal.
dgtized
Buena Q, marco GUI generalmente, pero hago remotos en los servidores y uso emacs en una terminal a veces (con un emacs.d compartido por git, por supuesto :)
Mark Aufflick

Respuestas:

15

No creo que esto se pueda lograr desde un terminal, pero en modo GUI podría intentar esto:

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

Hago lo mismo C-mpara que pueda distinguirse deRET

EDITAR:

Lo siguiente debería funcionar si está en modo GUI o 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))

No es bonito, pero parece hacer el trabajo. Agradezco cualquier modificación o edición de este código según sea necesario.

nispio
fuente
1
¡Funciona perfectamente! ++ compraría un emacs stackexchange de nuevo :)
Mark Aufflick
El único problema menor en el que acabo de pensar es que ahora podemos abrir un terminal emacsclient contra un Emacs que se inició con un sistema de ventanas (lo cual hago a veces). Si no veo ningún retraso, solo usaré la función de reemplazo de pestañas en todos los casos.
Mark Aufflick
1
Solo quiero agregar que <C-i>y [C-i]podría haber sido un identificador arbitrario, como <foobar>y [foobar], y aún funcionaría; simplemente no lo llames tabobackspace
xdavidliu
He añadido pieza de código editado en su respuesta al .emacsarchivo pero ambos TABy C-ise reasigna :-( @nispio
Alper
@alper, lo más probable es que esto sea (window-system)devuelto nilen el momento en que .emacsse cargó. Esto podría deberse a que está ejecutando una instancia no gráfica de Emacs, o porque está ejecutando un demonio Emacs.
nispio hace
13

Marcos GUI

En los marcos de la GUI (ya sea X11, Windows, OSX, ...), Emacs lee la Tabtecla como la tabtecla de función. Sin embargo, debido a que la Tabtecla en los terminales envía tradicionalmente el carácter ^I( Control + I), Emacs traduce la tabtecla de función al carácter Control + I (carácter 9), que se muestra como TAB. Esta traducción se realiza a través de function-key-map.

Una traducción similar ocurre con algunas otras teclas de función. ( Backspacey Deletees un caso espinoso que no discutiré en detalle aquí).

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+[

Si desea separar Tabde Ctrl+ por Icompleto, elimine el enlace de function-key-map:

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

Sin embargo, esto no es muy útil, ya que las entradas function-key-mapse anulan mediante enlaces en mapas de teclas específicos del modo o en el mapa global. Entonces, si desea definir un enlace diferente para tab, simplemente hágalo (en Elisp, no de forma interactiva, porque el indicador de lectura clave aplica la function-key-maptraducción para que termine volviendo a vincular TABy no tab):

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

Todos los modos estándar que modifican la acción de la Tabtecla lo hacen modificando la TABtecla, que es un apodo para el C-icarácter generado por la combinación de teclas Ctrl+ I. Si desea que se apliquen enlaces estándar en tablugar de C-i, deje function-key-mapy modele mapas de teclas solos y, en su lugar, redirija Ctrl+ Ia una tecla diferente.

(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] '…)

Ahora Emacs informará Ctrl+ Icomo " <control-i>(traducido de TAB)". Esto no es bonito, pero es inevitable: la bonita impresión del carácter 9 tal como TABestá integrado en el código fuente de Emacs.

Marcos terminales

En marcos de terminales, el problema es más difícil y a menudo imposible. Los terminales no transmiten claves, transmiten caracteres (más precisamente, de hecho, transmiten bytes). La Tabclave se transmite como el carácter de tabulación, que es Control + I, igual que lo que genera la combinación de teclas Ctrl+ I. Las teclas de función que no tienen un carácter correspondiente (como las teclas del cursor) se transmiten como secuencias de escape, es decir, secuencias de caracteres que comienzan con ESC= Control + [(por lo que Emacs define escapecomo una tecla de prefijo, ESCtiene que ser un prefijo). Consulte ¿Cómo funcionan la entrada del teclado y la salida de texto? para más antecedentes

Hay algunos terminales que se pueden configurar para enviar diferentes secuencias de teclas para las teclas de función, pero no muchos. Tanto libtermkey / libtickit de LeoNerd como xterm de Thomas Dickey (desde la versión 216) lo respaldan. En Xterm, la función es opcional y se activa a través del modifyOtherKeysrecurso. Sin embargo, no conozco ningún emulador de terminal popular que no sea xterm que admita esto, en particular los muchos emuladores creados en libvte . Algunos emuladores de terminal le permiten hacer esto manualmente a través de una correspondencia definida por el usuario desde los teclados para escapar de las secuencias.

Este mecanismo permite distinguir muchas combinaciones de teclas, no solo tab / Ci, return / Cm y escape / C- [. Consulte Problemas con las combinaciones de teclas al usar la terminal para obtener una descripción más detallada.

La función xterm básica es compatible desde Emacs 24.4. Sin embargo, los fundamentos (en particular Tab, Return, Escape, Backspace) siguen enviando los caracteres de control tradicionales, porque eso es lo que esperan que las aplicaciones. Hay un modo donde Ctrl+ letterenvía una secuencia de escape en lugar del carácter de control. Entonces, para distinguir las teclas de función de las Ctrlcombinaciones en Emacs 24.4, modifique su soporte para modifyOtherKeysusar este modo estableciendo el recurso en 2 en lugar de 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))
Gilles 'SO- deja de ser malvado'
fuente
Cuando dices "Emacs 24.24", ¿te refieres a "Emacs 24.4"?
tarsius
1
@tarsius Un comentario en el código que se copió de mi archivo init dice "24.4", así que creo que es correcto, y "24.24" en el texto que escribí para esta respuesta fue un error tipográfico para "24.4".
Gilles 'SO- deja de ser malvado'