Bagaimana cara mengikat Ci berbeda dari TAB?

16

Saya ingin Control-iberkinerja indent-region(pada dasarnya karena Xcode sudah membangun memori otot itu).

Saya menyadari itu Control-idan tabtidak bisa dibedakan dalam pengertian Ascii, tetapi mereka berada dalam pengertian kode kunci.

Saya sudah mencoba yang jelas:

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

sia-sia - menekan Control-imasih hanya melakukan apa pun yang dilakukan tabkunci dalam konteks saat ini. Apakah ada yang bisa saya lakukan untuk membantu Emacs memperlakukan tombol tab secara berbeda Control-i?

Pembaruan: Saya kira hasil yang setara akan dicapai dengan mampu memetakan kembali apa yang dilakukan oleh tab/ Control-ipers ketika suatu wilayah dipilih.

Tandai Aufflick
sumber
1
Apakah ini dari frame GUI atau frame terminal? Saya tidak tahu apakah Anda bisa menimpanya untuk terminal.
dgtized
Baik Q, bingkai GUI biasanya, tapi saya melakukan remote ke server dan kadang-kadang menggunakan emacs di terminal (dengan emacs.d git-shared tentu saja :)
Mark Aufflick

Jawaban:

15

Saya tidak berpikir bahwa ini dapat dicapai dari terminal, tetapi dalam mode GUI Anda dapat mencoba ini:

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

Saya melakukan hal yang sama C-msehingga dapat dibedakanRET

EDIT:

Berikut ini akan berfungsi apakah Anda dalam mode GUI atau 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))

Itu tidak cantik, tetapi tampaknya melakukan pekerjaan itu. Saya menerima penyempurnaan atau pengeditan untuk kode ini sebagaimana diperlukan.

nispio
sumber
1
Bekerja dengan sempurna! ++ akan membeli emacs stackexchange lagi :)
Mark Aufflick
Hanya masalah kecil yang saya pikirkan adalah bahwa sekarang kita dapat memunculkan terminal emacsclient terhadap Emacs yang dimulai dengan sistem jendela (yang kadang saya lakukan). Jika saya tidak melihat penundaan, saya hanya akan menggunakan fungsi penggantian-tab di semua kasus.
Mark Aufflick
1
Saya hanya ingin menambahkan bahwa <C-i>dan [C-i]bisa menjadi pengidentifikasi yang sewenang-wenang, seperti <foobar>dan [foobar], dan itu masih akan berfungsi; hanya saja jangan menyebutnya tabataubackspace
xdavidliu
13

Bingkai GUI

Dalam frame GUI (apakah X11, Windows, OSX, ...), Emacs membaca Tabkunci sebagai tombol tabfungsi. Namun, karena Tabkunci pada terminal secara tradisional mengirimkan karakter ^I( Control + I), Emacs menerjemahkan tabkunci fungsi ke dalam karakter Control + I (karakter 9), yang ditampilkan sebagai TAB. Terjemahan ini dilakukan melalui function-key-map.

Terjemahan serupa terjadi dengan beberapa tombol fungsi lainnya. ( Backspacedan Deletemerupakan kasus pelik yang tidak akan saya bahas secara rinci di sini.)

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

Jika Anda ingin memisahkan Tabdari Ctrl+ Isama sekali, hapus ikatan dari function-key-map:

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

Namun hal ini tidak terlalu berguna, karena entri dalam function-key-mapditimpa oleh binding di keymap modus-tertentu atau dalam peta global. Jadi, jika Anda ingin mendefinisikan pengikatan yang berbeda untuk tab, lakukan saja (di Elisp, tidak secara interaktif, karena prompt membaca kunci menerapkan function-key-mapterjemahan sehingga Anda akhirnya akan memutuskan untuk membatalkan TABdan tidak tab):

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

Semua mode standar yang memodifikasi aksi Tabkunci melakukannya dengan memodifikasi TABkunci, yang merupakan nama panggilan untuk C-ikarakter yang dihasilkan oleh kombinasi tombol Ctrl+ I. Jika Anda ingin binding standar untuk diterapkan tabdaripada C-i, biarkan function-key-mapdan mode keymaps saja, dan alih-alih mengarahkan Ctrl+ Ike kunci yang berbeda.

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

Sekarang Emacs akan melaporkan Ctrl+ Isebagai “ <control-i>(diterjemahkan dari TAB)”. Ini tidak cantik, tetapi tidak dapat dihindari: pencetakan karakter 9 yang cantik TABdimasukkan ke dalam kode sumber Emacs.

Bingkai terminal

Dalam bingkai terminal, masalahnya lebih sulit dan seringkali tidak mungkin. Terminal tidak mengirimkan kunci, mereka mengirimkan karakter (lebih tepatnya, mereka mengirimkan byte). The Tabkey ditransmisikan sebagai karakter tab - yang merupakan Control + I, sama seperti apa kombinasi tombol Ctrl+ Imenghasilkan. Tombol fungsi yang tidak memiliki karakter yang sesuai (seperti tombol kursor) ditransmisikan sebagai urutan melarikan diri, yaitu urutan karakter dimulai dengan ESC= Control + [(itulah sebabnya Emacs mendefinisikan escapesebagai kunci awalan - ESCharus menjadi awalan). Lihat Bagaimana cara kerja input keyboard dan output teks? untuk latar belakang lebih lanjut.

Ada beberapa terminal yang dapat dikonfigurasi untuk mengirim urutan tombol yang berbeda untuk tombol fungsi, tetapi tidak banyak. Kedua LeoNerd ini libtermkey / libtickit dan xterm Thomas Dickey ini (sejak versi 216) mendukung ini. Di Xterm, fitur ini opsional dan diaktifkan melalui modifyOtherKeyssumber daya. Namun, saya tidak tahu adanya emulator terminal populer selain xterm yang mendukung ini, khususnya banyak emulator yang dibangun di atas libvte . Beberapa emulator terminal memungkinkan Anda melakukan ini secara manual melalui korespondensi yang ditentukan pengguna dari gantungan kunci untuk menghindari urutan.

Mekanisme ini memungkinkan banyak kombinasi tombol untuk dibedakan, bukan hanya tab / Ci, return / Cm dan escape / C- [. Lihat Masalah dengan ikatan kunci saat menggunakan terminal untuk deskripsi yang lebih rinci.

Fitur xterm dasar didukung sejak Emacs 24.4. Namun dasar-dasar (khususnya Tab, Return, Escape, Backspace) masih mengirim karakter kontrol tradisional, karena ini aplikasi apa harapkan. Ada mode di mana Ctrl+ lettermengirim urutan pelarian alih-alih karakter kontrol. Jadi untuk membedakan tombol fungsi dari Ctrlkombinasi pada Emacs 24.4, modifikasi dukungannya untuk modifyOtherKeysmenggunakan mode ini dengan mengatur sumber daya ke 2 alih-alih 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- berhenti menjadi jahat'
sumber
Ketika Anda mengatakan "Emacs 24.24", maksud Anda "Emacs 24.4"?
tarsius
1
@tarsius Komentar dalam kode yang disalin dari file init saya mengatakan "24,4", jadi saya pikir itu benar, dan "24,24" dalam teks yang saya tulis untuk jawaban ini adalah salah ketik untuk "24,4".
Gilles 'SANGAT berhenti menjadi jahat'