Wie binde ich Ci anders als TAB?

15

Ich möchte etwas Control-iperformen indent-region(im Grunde genommen, da Xcode dieses Muskelgedächtnis bereits aufgebaut hat).

Ich erkenne das Control-iund bin tabim Ascii-Sinne nicht zu unterscheiden, aber im Keycode-Sinne.

Ich habe das Offensichtliche versucht:

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

erfolglos - drücken Control-imacht immer noch, was auch immer die tabTaste im aktuellen Kontext tut. Kann ich Emacs helfen, die Tabulatortaste anders zu behandeln als Control-i?

Update: Ich schätze, ein gleichwertiges Ergebnis wird erzielt, wenn Sie neu zuordnen können, was eine tab/ Control-iPresse tut, wenn eine Region ausgewählt wird.

Mark Aufflick
quelle
1
Handelt es sich um einen GUI-Frame oder einen Terminal-Frame? Ich weiß nicht, ob Sie es für ein Terminal überschreiben können.
25.
Gute Q, GUI-Rahmen in der Regel, aber ich mache Remote in Server und Emacs in einem Terminal manchmal (mit einem git-shared emacs.d natürlich :)
Mark Aufflick

Antworten:

14

Ich glaube nicht, dass dies von einem Terminal aus erreicht werden kann, aber im GUI-Modus können Sie Folgendes versuchen:

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

Ich mache dasselbe C-mdamit, damit man es unterscheiden kannRET

BEARBEITEN:

Folgendes sollte funktionieren, egal ob Sie sich im GUI- oder TTY-Modus befinden:

;; 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))

Es ist nicht schön, aber es scheint den Job zu machen. Verfeinerungen oder Änderungen an diesem Code sind mir bei Bedarf willkommen.

Nispio
quelle
1
Funktioniert perfekt! ++ würde wieder einen emacs stackexchange kaufen :)
Mark Aufflick
Das einzige kleine Problem, an das ich gerade gedacht habe, ist, dass wir jetzt einen Terminal-Emacsclient gegen einen Emacs eröffnen können, der mit einem Fenstersystem gestartet wurde (was ich manchmal tue). Wenn ich keine Verzögerung sehe, benutze ich in jedem Fall die Tab-Replacement-Funktion.
Mark Aufflick
1
Ich möchte nur hinzufügen, dass das <C-i>und [C-i]ein beliebiger Bezeichner sein könnte, wie <foobar>und [foobar], und es würde immer noch funktionieren; Nennen Sie es einfach nicht taboderbackspace
Xdavidliu
13

GUI-Frames

In GUI-Frames (ob X11, Windows, OSX, ...) liest Emacs die TabTaste als tabFunktionstaste. Da die TabTaste auf den Terminals traditionell das Zeichen ^I( Strg + I) sendet , übersetzt Emacs die tabFunktionstaste in das Zeichen Strg + I (Zeichen 9), das als angezeigt wird TAB. Diese Übersetzung erfolgt über function-key-map.

Eine ähnliche Übersetzung erfolgt mit einigen anderen Funktionstasten. ( BackspaceUnd Deletees handelt sich um einen heiklen Fall, auf den ich hier nicht näher eingehen werde.)

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

Wenn Sie sich Tabvon Ctrl+ trennen möchten I, entfernen Sie die Bindung von function-key-map:

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

Dies ist jedoch nicht sehr nützlich, da Einträge in function-key-mapdurch Bindungen in modusspezifischen Keymaps oder in der globalen Zuordnung überschrieben werden. Wenn Sie also eine andere Bindung definieren möchten tab, tun Sie dies einfach (in Elisp, nicht interaktiv, da die Eingabeaufforderung zum Lesen des Schlüssels die function-key-mapÜbersetzung anwendet, sodass Sie erneut binden TABund nicht tab):

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

Alle Standardmodi, die die Aktion der TabTaste ändern, ändern dazu die Taste. Dies TABist ein Kurzname für das C-idurch die Tastenkombination Ctrl+ erzeugte Zeichen I. Wenn Sie Standard - Bindings anwenden möchten tabals vielmehr C-i, verlassen function-key-mapund Modus keymaps allein und stattdessen Umleitung Ctrl+ Izu einem anderen Schlüssel.

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

Jetzt wird Emacs Ctrl+ Ials " <control-i>(übersetzt von TAB)" melden . Das ist nicht schön, aber es ist unvermeidlich: das schöne Drucken von Zeichen 9, wie TABes im Emacs-Quellcode enthalten ist.

Terminalrahmen

In Terminal-Frames ist das Problem schwerer und oft unmöglich. Terminals übertragen keine Schlüssel, sie übertragen Zeichen (genauer gesagt, sie übertragen Bytes). Die TabTaste wird als Tabulatorzeichen übertragen - das ist Strg + I, genau wie das, was die Tastenkombination Ctrl+ Ierzeugt. Funktionstasten, die kein entsprechendes Zeichen enthalten (z. B. Cursortasten), werden als Escape-Sequenzen übertragen, dh Sequenzen von Zeichen, die mit ESC= Strg + [beginnen (weshalb Emacs escapeals Präfix-Taste definiert - ESCmuss ein Präfix sein). Siehe Wie funktionieren Tastatureingaben und Textausgaben? für mehr hintergrund.

Es gibt einige Terminals, die so konfiguriert werden können, dass sie unterschiedliche Tastenfolgen für Funktionstasten senden, aber nicht viele. Dies wird sowohl von LeoNerds libtermkey / libtickit als auch von Thomas Dickeys xterm (seit Version 216) unterstützt. In Xterm ist die Funktion optional und wird über die modifyOtherKeysRessource aktiviert . Ich kenne jedoch keinen anderen beliebten Terminal-Emulator als xterm, der dies unterstützt, insbesondere die vielen Emulatoren, die auf libvte basieren . Bei einigen Terminal-Emulatoren können Sie dies manuell über eine benutzerdefinierte Entsprechung von Tastenkombinationen zu Escape-Sequenzen ausführen.

Mit diesem Mechanismus können viele Tastenkombinationen unterschieden werden, nicht nur tab / Ci, return / Cm und escape / C- [. Eine ausführlichere Beschreibung finden Sie unter Probleme mit der Tastaturbelegung bei Verwendung des Terminals .

Die grundlegende xterm-Funktion wird seit Emacs 24.4 unterstützt. Allerdings sind die Grundlagen (insbesondere Tab, Return, Escape, Backspace) nach wie vor die traditionellen Steuerzeichen senden, weil das, was Anwendungen erwarten. Es gibt einen Modus, in dem Ctrl+ letteranstelle des Steuerzeichens eine Escape-Sequenz sendet. CtrlUm die Funktionstasten von den Kombinationen in Emacs 24.4 zu unterscheiden, ändern Sie die Unterstützung für modifyOtherKeysdiesen Modus, indem Sie die Ressource auf 2 statt auf 1 setzen.

;; 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 - hör auf böse zu sein'
quelle
Wenn Sie "Emacs 24.24" sagen, meinen Sie "Emacs 24.4"?
Tarsius
1
@tarsius Ein Kommentar in dem Code, der aus meiner Init-Datei kopiert wurde, lautet "24.4", daher denke ich, dass dies korrekt ist, und "24.24" in dem Text, den ich für diese Antwort geschrieben habe, war ein Tippfehler für "24.4".
Gilles 'SO - hör auf böse zu sein'