Làm thế nào để ràng buộc Ci khác với TAB?


15

Tôi muốn Control-ithực hiện indent-region(về cơ bản vì Xcode đã xây dựng bộ nhớ cơ đó rồi).

Tôi nhận ra điều đó Control-itabkhông thể phân biệt được theo nghĩa của chữ Asii, nhưng chúng theo nghĩa mã khóa.

Tôi đã thử một điều hiển nhiên:

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

vô ích - nhấn Control-ivẫn chỉ làm bất cứ điều gì tabphím làm trong bối cảnh hiện tại. Có điều gì tôi có thể làm để giúp Emacs đối xử với nút tab khác nhau Control-ikhông?

Cập nhật: Tôi đoán một kết quả tương đương sẽ đạt được bằng cách có thể ánh xạ lại những gì a tab/ Control-inhấn làm khi một vùng được chọn.


1
Đây là từ khung GUI hay khung đầu cuối? Tôi không biết nếu bạn có thể ghi đè lên thiết bị đầu cuối.
dgtized

Thông thường, khung GUI tốt, nhưng tôi thường điều khiển từ xa vào máy chủ và đôi khi sử dụng emacs trong một thiết bị đầu cuối (dĩ nhiên là với emacs.d được chia sẻ git :)
Đánh dấu Aufflick

Câu trả lời:


14

Tôi không nghĩ rằng điều này có thể đạt được từ một thiết bị đầu cuối, nhưng trong chế độ GUI, bạn có thể thử điều này:

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

Tôi làm điều tương tự với C-mnó để có thể phân biệt vớiRET

BIÊN TẬP:

Các thao tác sau sẽ hoạt động cho dù bạn đang ở chế độ GUI hay 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))

Nó không đẹp, nhưng dường như để thực hiện công việc. Tôi hoan nghênh mọi tinh chỉnh hoặc chỉnh sửa mã này khi cần thiết.


1
Hoạt động hoàn hảo! ++ sẽ mua một emacs stackexchange một lần nữa :)
Đánh dấu Aufflick

Chỉ có một vấn đề nhỏ mà tôi nghĩ đến là bây giờ chúng ta có thể đưa ra một thiết bị đầu cuối chống lại một Emacs đã được khởi động với một hệ thống cửa sổ (đôi khi tôi làm như vậy). Nếu tôi không thấy bất kỳ sự chậm trễ nào, tôi sẽ chỉ sử dụng chức năng thay thế tab trong mọi trường hợp.
Đánh dấu Aufflick

1
Tôi chỉ muốn thêm rằng <C-i>[C-i]có thể là một định danh tùy ý, như <foobar>[foobar], và nó vẫn sẽ hoạt động; đừng gọi nó tabhoặcbackspace
xdavidliu

13

Khung GUI

Trong các khung GUI (cho dù X11, Windows, OSX, '), Emacs đọc Tabkhóa dưới dạng tabphím chức năng. Tuy nhiên, vì Tabkhóa trên các thiết bị đầu cuối truyền thống gửi ký tự ^I( Control + I), Emacs dịch tabphím chức năng thành ký tự Control + I (ký tự 9), được hiển thị dưới dạng TAB. Bản dịch này được thực hiện thông qua function-key-map.

Một bản dịch tương tự xảy ra với một số phím chức năng khác. ( BackspaceDeletelà một trường hợp gai góc mà tôi sẽ không thảo luận chi tiết ở đây.)

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

Nếu bạn muốn tách Tabkhỏi Ctrl+ Ihoàn toàn, hãy xóa liên kết khỏi function-key-map:

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

Tuy nhiên, điều này không hữu ích lắm, bởi vì các mục trong function-key-mapbị ghi đè bởi các ràng buộc trong các keymap cụ thể theo chế độ hoặc trong bản đồ toàn cầu. Vì vậy, nếu bạn muốn xác định một ràng buộc khác cho tab, chỉ cần thực hiện (trong Elisp, không tương tác, bởi vì dấu nhắc đọc chính áp dụng function-key-mapbản dịch để cuối cùng bạn không bị đảo ngược TABvà không tab):

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

Tất cả các chế độ tiêu chuẩn sửa đổi hành động của Tabkhóa đều thực hiện bằng cách sửa đổi TABkhóa, đó là biệt danh cho C-iký tự được tạo bởi tổ hợp phím Ctrl+ I. Nếu bạn muốn các ràng buộc tiêu chuẩn được áp dụng tabthay vì C-i, hãy để lại function-key-mapvà chế độ keymap một mình, và thay vào đó chuyển hướng Ctrl+ Isang một khóa khác.

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

Bây giờ Emacs sẽ báo cáo Ctrl+ Inhư là <control-i>(dịch từ TAB). Điều này không đẹp, nhưng không thể tránh khỏi: việc in ấn nhân vật 9 như TABđược tích hợp vào mã nguồn Emacs.

Khung thiết bị đầu cuối

Trong các khung thiết bị đầu cuối, vấn đề khó hơn và thường là không thể. Thiết bị đầu cuối không truyền khóa, chúng truyền ký tự (chính xác hơn là trên thực tế, chúng truyền byte). Các Tabphím được truyền như các ký tự tab - đó là Control + I, giống như những gì tổ hợp phím Ctrl+ Itạo ra. Các phím chức năng không có ký tự tương ứng (như phím con trỏ) được truyền dưới dạng các chuỗi thoát, tức là các chuỗi ký tự bắt đầu bằng ESC= Control + [(đó là lý do tại sao Emacs định nghĩa escapelà khóa tiền tố - ESCphải là tiền tố). Xem Làm thế nào để đầu vào bàn phím và đầu ra văn bản hoạt động? để có thêm nền tảng.

Có một vài thiết bị đầu cuối có thể được cấu hình để gửi các chuỗi khóa khác nhau cho các phím chức năng, nhưng không nhiều. Cả libtermkey / libtickit của LeoNerdxterm của Thomas Dickey (kể từ phiên bản 216) đều hỗ trợ điều này. Trong Xterm, tính năng này là tùy chọn và được kích hoạt thông qua modifyOtherKeystài nguyên. Tuy nhiên, tôi không biết bất kỳ trình giả lập thiết bị đầu cuối phổ biến nào ngoài xterm hỗ trợ điều này, đặc biệt là nhiều trình giả lập được xây dựng trên libvte . Một số trình giả lập thiết bị đầu cuối cho phép bạn thực hiện việc này một cách thủ công thông qua sự tương ứng do người dùng xác định từ các tổ hợp phím để thoát khỏi chuỗi.

Cơ chế này cho phép phân biệt nhiều tổ hợp phím, không chỉ tab / Ci, return / Cm và esc / C- [. Xem các vấn đề với keybindings khi sử dụng terminal để có mô tả chi tiết hơn.

Tính năng xterm cơ bản được hỗ trợ kể từ Emacs 24.4. Tuy nhiên, nguyên tắc cơ bản (đặc biệt Tab, Return, Escape, Backspace) vẫn gửi ký tự điều khiển truyền thống, bởi vì đó là những gì các ứng dụng mong đợi. Có một chế độ trong đó Ctrl+ lettergửi một chuỗi thoát thay vì ký tự điều khiển. Vì vậy, để phân biệt các phím chức năng với các Ctrlkết hợp trên Emacs 24.4, hãy sửa đổi sự hỗ trợ của nó modifyOtherKeysđể sử dụng chế độ này bằng cách đặt tài nguyên thành 2 thay vì 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))

Khi bạn nói "Emacs 24.24", bạn có nghĩa là "Emacs 24.4"?
tarsius

1
@tarsius Một nhận xét trong đoạn mã được sao chép từ tập tin init của tôi nói là 24.4.
Gilles 'SO- ngừng trở nên xấu xa'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.