Làm thế nào để ghi đè các ràng buộc chế độ chính


38

Đôi khi các phím bấm toàn cầu của tôi bị ghi đè bởi một chế độ chính. Một ví dụ dễ hiểu là cài đặt sau trong tệp init của tôi

(global-set-key (kbd "C-j") 'newline-and-indent)

Nhưng thật khó chịu khi thao tác bấm phím này bị ẩn bởi chế độ chính "Tương tác Lisp" là chế độ mặc định của bộ đệm đầu.

Khi tôi thấy mình trong một tình huống mà một chế độ chính (hoặc chế độ phụ) đang ẩn khóa liên kết toàn cầu của tôi, làm thế nào tôi có thể lấy lại được?

Lưu ý: Câu hỏi của tôi không phải là "Làm thế nào tôi có thể ràng buộc C-jđể newline-and-indent'? Chế độ' trong" Lisp Interaction Tôi quan tâm đến một câu trả lời tổng quát hơn nhiều về cách xử lý các keymap mà xung đột hoặc các keybindings bị ẩn bởi một số chế độ chính / phụ.


Câu trả lời:


39

Có một cách tiếp cận "phím tắt" cho cùng một giải pháp nếu bạn không muốn xác định chế độ nhỏ của riêng mình (mà tôi nói về câu trả lời đầu tiên của tôi).

Bạn có thể cài đặt use-packagegói có sẵn từ Melpa và sử dụng bind-key*hoặc bind-keys*macro đó là một phần của bind-keygói đi kèm use-package.

Từ các tài liệu của bind-key.el:

;; If you want the keybinding to override all minor modes that may also bind
;; the same key, use the `bind-key*' form:
;;
;;   (bind-key* "<C-return>" 'other-window)

;; To bind multiple keys in a `bind-key*' way (to be sure that your bindings
;; will not be overridden by other modes), you may use `bind-keys*' macro:
;;
;;    (bind-keys*
;;     ("C-o" . other-window)
;;     ("C-M-n" . forward-page)
;;     ("C-M-p" . backward-page))

Trong trường hợp bất kỳ ai cũng tò mò, lưu ý rằng điều này chỉ sử dụng một chế độ nhỏ phía sau hậu trường. Nó thực hiện một cách tiếp cận khác nhau để xung đột với các chế độ nhỏ khác, bằng cách sử dụng emulation-mode-map-alistsđể thực thi quyền ưu tiên).
phils

Tôi đã đánh dấu đây là câu trả lời vì tính đơn giản của nó. Nó chủ yếu làm điều tương tự như câu trả lời này , nhưng nó thuận tiện thực hiện hầu hết các công việc cho bạn đằng sau hậu trường.
nispio

@nispio Bạn nói đúng. Theo kinh nghiệm của tôi, sự khác biệt duy nhất là phương pháp được sử dụng để tạo các ràng buộc chế độ nhỏ ghi đè lên các chế độ khác trên toàn cầu. Nhưng lợi thế của cách tiếp cận khác (có chế độ nhỏ của riêng bạn) là bạn có thể tắt nó ngay lập tức để thử các ràng buộc emacs ban đầu nếu bạn cần (tôi cần phải làm điều đó một vài lần).
Kaushal Modi

1
Thêm (bind-key* "C-M-&" 'override-global-mode), vào init của bạn và bạn thường có thể nhanh chóng nhận được các ràng buộc theo cách của bạn nếu bạn cần. Vì override-global-modekhông phải là chế độ nhỏ "toàn cầu", bạn vẫn sẽ cần hủy kích hoạt nó trên cơ sở mỗi bộ đệm. Vì vậy, nếu bạn thấy mình thường xuyên hủy kích hoạt các phím ghi đè toàn cầu thì giải pháp này không thuận tiện.
nispio

25

Bạn có thể xác định chế độ phụ của riêng mình và bản đồ chính của nó và có chế độ ghi đè tất cả các chế độ khác (phụ + chính). Đó chính xác là lý do tại sao tôi chọn viết chế độ nhỏ của riêng mình.

Các bước để có các ràng buộc chính của bạn ghi đè tất cả các ràng buộc:

  • Xác định chế độ nhỏ của riêng bạn và bản đồ chính như dưới đây.
  • Kích hoạt chế độ nhỏ của bạn trên toàn cầu
  • (define-key my-mode-map (kbd "C-j") #'newline-and-indent)

Tương tự, các ràng buộc khóa khác của bạn được đặt trong chế độ nhỏ của bạn sẽ ghi đè lên các ràng buộc trong các chế độ khác.

Tôi đặc biệt khuyên bạn nên đọc bài đăng trên blog của Christopher Wellons về cách viết một chế độ nhỏ. Blog đó cộng với sự khó chịu khi phải đặt nhiều ràng buộc chính nilở nhiều chế độ chính và phụ đã thôi thúc tôi viết chế độ nhỏ của riêng mình.

Phần tốt nhất của việc sử dụng phương pháp này là khi bạn muốn kiểm tra các ràng buộc chính làm gì trong cấu hình mặc định của emacs, bạn chỉ cần tắt chế độ nhỏ của mình; sau đó bạn bật lại và bạn lấy lại các ràng buộc khóa tùy chỉnh của mình.

;; Main use is to have my key bindings have the highest priority
;; https://github.com/kaushalmodi/.emacs.d/blob/master/elisp/modi-mode.el

(defvar my-mode-map (make-sparse-keymap)
  "Keymap for `my-mode'.")

;;;###autoload
(define-minor-mode my-mode
  "A minor mode so that my key settings override annoying major modes."
  ;; If init-value is not set to t, this mode does not get enabled in
  ;; `fundamental-mode' buffers even after doing \"(global-my-mode 1)\".
  ;; More info: http://emacs.stackexchange.com/q/16693/115
  :init-value t
  :lighter " my-mode"
  :keymap my-mode-map)

;;;###autoload
(define-globalized-minor-mode global-my-mode my-mode my-mode)

;; https://github.com/jwiegley/use-package/blob/master/bind-key.el
;; The keymaps in `emulation-mode-map-alists' take precedence over
;; `minor-mode-map-alist'
(add-to-list 'emulation-mode-map-alists `((my-mode . ,my-mode-map)))

;; Turn off the minor mode in the minibuffer
(defun turn-off-my-mode ()
  "Turn off my-mode."
  (my-mode -1))
(add-hook 'minibuffer-setup-hook #'turn-off-my-mode)

(provide 'my-mode)

;; Minor mode tutorial: http://nullprogram.com/blog/2013/02/06/

5

Để có một ràng buộc toàn cầu ghi đè lên một ràng buộc chế độ chính, chỉ cần đặt liên kết thành niltrong chế độ chính:

(define-key my-major-mode-map (kbd "C-j") nil)

Không thể có sự ràng buộc toàn cầu được ưu tiên hơn tất cả các chế độ nói chung (nếu không sẽ không có điểm nào trong việc có các chế độ chính), nhưng bạn có thể hack xung quanh nó bằng cách tạo chế độ nhỏ của riêng bạn với các ràng buộc quan trọng nhất của bạn. Sau đó, bạn ít nhất sẽ có quyền ưu tiên hơn hầu hết các chế độ (mặc dù không nhất thiết là tất cả).


Điều này sẽ làm việc cho bất kỳ chế độ chính tùy ý? Nói cách khác, nếu tôi cài đặt một chế độ chính gọi là "foo-mode", tôi có thể đặt (define-key foo-mode (kbd "C-j") nil)tệp .emacs của mình và mong đợi nó hoạt động không?
nispio

Trên thực tế, bạn sẽ muốn nó foo-mode-map(ví dụ của tôi trong câu trả lời là xấu), nhưng đúng vậy, điều đó sẽ vô hiệu hóa tính năng khóa trong chế độ chính để thay thế phím bấm toàn cầu sẽ được sử dụng (trừ khi có một chế độ nhỏ khác sử dụng nó).
shosti

Có phải nó khá phổ biến rằng sơ đồ cho foo-modesẽ được gọi foo-mode-map?
nispio

@nispio vâng, điều đó đúng với đại đa số các chế độ (mặc dù có một vài điều bất hảo ngoài kia).
shosti

0

Bạn có thể sử dụng các macro này:

(defmacro expose-global-keybinding (binding map)
  `(define-key ,map ,binding (lookup-key (current-global-map) ,binding)))

(defmacro expose-bindings (map bindings)
  `(dolist (bnd ,bindings)
     (expose-global-keybinding (kbd bnd) ,map)))

CHỈNH SỬA :

Kiểm tra ví dụ dưới đây:

Nếu sơ đồ bàn phím X ghi đè lên ràng buộc toàn cầu Y của bạn, bạn viết:

(expose-bindings X '("Y"))

Và sau đó, phần ghi đè sẽ được 'hoàn tác'.


macro của bạn, bạn được hưởng lợi từ việc sử dụng backquote: `(khóa xác định, bản đồ, ràng buộc (khóa tra cứu (bản đồ hiện tại toàn cầu), ràng buộc))
Sigma

Bạn có thể nhận xét về những gì macro đang làm, và làm thế nào để sử dụng nó? Nó không rõ ràng với tôi từ mã.
nispio

Macro đầu tiên chỉ tìm kiếm khóa trong bản đồ toàn cầu và gán kết quả cho other map, do đó, phơi bày ràng buộc bản đồ toàn cầu thông qua other map. Cái thứ hai chỉ cho phép bạn áp dụng cái trước cho một danh sách các ràng buộc.
Renan Ranelli

Xin lỗi nếu tôi dày đặc, nhưng tôi vẫn không hoàn toàn hiểu cách sử dụng. Tôi có cần xác định sơ đồ toàn cầu đặc biệt của riêng mình không? Có cần phải thuộc về một chế độ nhỏ? Tôi có thực hiện expose-bindingstrước không, và sau đó trên toàn cầu liên kết các khóa đó với các lệnh mà tôi muốn? Có lẽ bạn có thể đưa ra một ví dụ về những gì tôi có thể đưa vào tệp init của mình để làm cho công việc này.
nispio

2
Lưu ý rằng các tên macro là tên sai. Họ không "vạch trần" ràng buộc toàn cầu, mà là họ nhân đôi ràng buộc toàn cầu. Nếu ràng buộc toàn cầu thực sự thay đổi, những bản sao này sẽ không.
phils
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.