Jak powiązać Ci jako inny niż TAB?

16

Chcę sprawić, by Control-igrała indent-region(w zasadzie odkąd Xcode już zbudował tę pamięć mięśniową).

Zdaję sobie z tego sprawę Control-ii tabsą nierozróżnialne w sensie Ascii, ale są w sensie klucza.

Próbowałem oczywistego:

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

bezskutecznie - naciśnięcie Control-inadal robi tylko to, co tabrobi klawisz w bieżącym kontekście. Czy jest coś, co mogę zrobić, aby pomóc Emacsowi potraktować przycisk Tab inaczej niż Control-i?

Aktualizacja: Wydaje mi się, że równoważny wynik zostałby osiągnięty dzięki możliwości zmiany mapowania działania prasy tab/ Control-ipo wybraniu regionu.

Mark Aufflick
źródło
1
Czy pochodzi to z ramki GUI czy ramki terminala? Nie wiem, czy możesz to zmienić na terminal.
zdiagnozowano
Dobra ramka Q, GUI zwykle, ale zdalnie łączę się z serwerami i czasami używam emacsa w terminalu (oczywiście z emacs.d-share :)
Mark Aufflick

Odpowiedzi:

15

Nie sądzę, że można to osiągnąć z terminala, ale w trybie GUI można spróbować:

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

Robię to samo, C-maby można było je odróżnićRET

EDYTOWAĆ:

Poniższe czynności powinny działać niezależnie od tego, czy jesteś w trybie GUI czy 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))

To nie jest ładne, ale wydaje się, że wykonuje swoją pracę. Z zadowoleniem przyjmuję wszelkie poprawki lub zmiany w tym kodzie, jeśli to konieczne.

nispio
źródło
1
Działa świetnie! ++ znów
kupiłby wymianę stosu
Jedynym niewielkim problemem, o którym właśnie pomyślałem, jest to, że teraz możemy wywołać terminal emacsclient przeciwko Emacsowi, który został uruchomiony z systemem okien (co czasami robię). Jeśli nie widzę żadnego opóźnienia, po prostu użyję funkcji zamiany tabulatorów we wszystkich przypadkach.
Mark Aufflick,
1
Chcę tylko dodać, że <C-i>i [C-i]mógł być dowolnym identyfikatorem, takim jak <foobar>i [foobar], i nadal działałoby; po prostu nie nazywaj tego tablubbackspace
xdavidliu,
Dodałem edytowanego kawałek kodu na odpowiedź do .emacspliku, ale zarówno TABi C-ijest remapped :-( @nispio
alper
@alper, najprawdopodobniej oznacza to, że (window-system)wrócił nilw momencie .emacszaładowania. Może to być spowodowane tym, że korzystasz z graficznej instancji Emacsa lub z powodu demona Emacsa.
nispio
13

Ramki GUI

W ramkach GUI (X11, Windows, OSX,…) Emacs odczytuje Tabklawisz jako tabklawisz funkcyjny. Ponieważ jednak Tabklawisz terminali tradycyjnie wysyła znak ^I( Control + I), Emacs tłumaczy tabklawisz funkcyjny na znak Control + I (znak 9), który jest wyświetlany jako TAB. Tłumaczenie odbywa się za pośrednictwem function-key-map.

Podobne tłumaczenie dzieje się z niektórymi innymi klawiszami funkcyjnymi. ( Backspacei Deletedrażliwą sprawą, której nie będę tutaj szczegółowo omawiać).

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

Jeśli chcesz całkowicie oddzielić Tabod Ctrl+ I, usuń powiązanie z function-key-map:

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

Nie jest to jednak bardzo przydatne, ponieważ wpisy w function-key-mapsą zastępowane przez powiązania w mapach klawiszy specyficznych dla trybu lub na mapie globalnej. Więc jeśli chcesz zdefiniować inne wiązanie tab, po prostu zrób to (w Elisp, nie interaktywnie, ponieważ monit o odczytanie klucza stosuje function-key-maptłumaczenie, aby skończyć wiązanie, TABa nie tab):

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

Wszystkie standardowe tryby, które modyfikują działanie Tabklawisza, robią to poprzez modyfikację TABklawisza, który jest pseudonimem dla C-iznaku generowanego przez kombinację klawiszy Ctrl+ I. Jeśli chcesz zastosować standardowe powiązania tabzamiast C-i, pozostaw klawisze function-key-mapklawiszy trybu i pozostaw w spokoju, a zamiast tego przekieruj Ctrl+ Ido innego klucza.

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

Teraz Emacs zgłosi Ctrl+ Ijako „ <control-i>(przetłumaczone z TAB)”. To nie jest ładne, ale jest nieuniknione: ładne drukowanie znaku 9, takiego jak TABwbudowany w kod źródłowy Emacsa.

Ramy terminali

W ramkach terminali problem jest trudniejszy i często niemożliwy. Terminale nie przesyłają kluczy, przesyłają znaki (a dokładniej, w rzeczywistości bajty). TabKlucz jest przekazywana jako znak - co jest zakładka Control + I, a tym samym, co kombinacja klawiszy Ctrl+ Igeneruje. Klawisze funkcyjne, które nie mają odpowiedniego znaku (takie jak klawisze kursora) są przesyłane jako sekwencje specjalne, tj. Sekwencje znaków rozpoczynające się od ESC= Control + [(dlatego Emacs definiuje escapejako klawisz prefiksu - ESCmusi być prefiksem). Zobacz Jak działa wprowadzanie za pomocą klawiatury i tekst? po więcej tła.

Istnieje kilka terminali, które można skonfigurować do wysyłania różnych sekwencji klawiszy dla klawiszy funkcyjnych, ale nie wiele. Zarówno libtermkey / libtickit LeoNerda, jak i xterm Thomasa Dickeya (od wersji 216) obsługują to. W Xterm funkcja jest opcjonalna i aktywowana przez modifyOtherKeyszasób. Jednak nie znam żadnego popularnego emulatora terminali innego niż xterm, który to obsługuje, w szczególności wielu emulatorów zbudowanych na libvte . Niektóre emulatory terminali pozwalają to zrobić ręcznie poprzez zdefiniowaną przez użytkownika korespondencję od słów kluczowych do sekwencji ucieczkowych.

Ten mechanizm pozwala na rozróżnienie wielu kombinacji klawiszy, nie tylko tab / Ci, return / Cm i escape / C- [. Aby uzyskać bardziej szczegółowy opis, zobacz Problemy ze skrótami klawiszowymi podczas korzystania z terminala .

Podstawowa funkcja xterm jest obsługiwana od Emacsa 24.4. Jednak podstawy (w szczególności Tab, Return, Escape, Backspace) nadal wysyłać tradycyjne znaki sterujące, bo to właśnie aplikacje spodziewać. Istnieje tryb, w którym Ctrl+ letterwysyła sekwencję zmiany znaczenia zamiast znaku sterującego. Aby odróżnić klawisze funkcyjne od Ctrlkombinacji w Emacsie 24.4, zmodyfikuj jego obsługę dla modifyOtherKeysużywania tego trybu, ustawiając zasób na 2 zamiast 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- przestań być zły”
źródło
Kiedy mówisz „Emacs 24.24”, masz na myśli „Emacs 24.4”?
tarsjusz
1
@tarsius Komentarz w kodzie skopiowany z mojego pliku init mówi „24.4”, więc myślę, że jest poprawny, a „24.24” w tekście, który napisałem dla tej odpowiedzi, było literówką do „24.4”.
Gilles „SO- przestań być zły”