Как переопределить привязки основного режима

38

Иногда мои глобальные сочетания клавиш переопределяются основным режимом. Простой пример - следующая настройка в моем файле инициализации

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

Но досадно, что эта связка клавиш скрыта основным режимом «Лисп взаимодействия», который является режимом по умолчанию для рабочего буфера.

Когда я нахожусь в ситуации, когда основной режим (или второстепенный режим) скрывает мою глобальную привязку клавиш, как я могу вернуть ее обратно?

Примечание: Мой вопрос не «Как я могу связать C-jс newline-and-indent„? Режим“в» Lisp Interaction Меня интересует гораздо более общий ответ о том, как обращаться с сопоставлениями клавиш, которые конфликтуют, или с сочетаниями клавиш пользователя, которые скрываются в каком-то основном / второстепенном режиме.

nispio
источник

Ответы:

39

Для того же решения также существует «быстрый» подход, если вы не хотите определять свой собственный второстепенный режим (о котором я говорю в моем первом ответе).

Вы можете установить use-packageпакет , доступный от Melpa и использование сделать из bind-key*или bind-keys*макрос , который является частью из bind-keyпакета , который поставляется с use-package.

Из документации 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))
Каушал Моди
источник
В случае, если кому-то интересно, обратите внимание, что это просто использует второстепенный режим за кулисами. Он использует другой подход к конфликтам с другими второстепенными режимами, используя emulation-mode-map-alistsдля обеспечения приоритета).
Фил
Я отметил это как ответ из-за его простоты. По сути, он делает то же самое, что и этот ответ , но он удобно выполняет большую часть работы за кулисами.
Ниспио
@nispio Ты прав. По моему опыту, единственное отличие состоит в том, что метод, который используется для привязки второстепенных режимов, перекрывает другие режимы во всем мире. Но преимущество другого подхода (наличия вашего собственного второстепенного режима) состоит в том, что вы можете на мгновение отключить его, чтобы попробовать исходные привязки emacs, если вам когда-либо понадобится (мне нужно было сделать это несколько раз).
Каушал Моди
1
Добавьте (bind-key* "C-M-&" 'override-global-mode)к своей инициации, и вы можете быстро избавиться от привязок, если это необходимо. Поскольку override-global-modeэто не «глобальный» второстепенный режим, вам все равно придется деактивировать его для каждого буфера. Так что если вы часто деактивируете глобальные ключи переопределения, тогда это решение не удобно.
Ниспио
25

Вы можете определить свой собственный второстепенный режим и его карту ключей, и он будет перекрывать все другие режимы (минорный + мажорный). Именно поэтому я решил написать свой собственный второстепенный режим.

Шаги, чтобы ваши привязки клавиш перекрывали все привязки:

  • Определение вашего собственного второстепенного режима и карты ключей, как показано ниже.
  • Активируйте ваш второстепенный режим глобально
  • (define-key my-mode-map (kbd "C-j") #'newline-and-indent)

Точно так же ваши другие привязки клавиш, установленные в вашем второстепенном режиме, будут переопределять их в других режимах.

Я настоятельно рекомендую прочитать сообщение в блоге Кристофера Уэллонса о том, как писать второстепенный режим. Этот блог плюс досадная необходимость установки нескольких привязок клавиш nilв нескольких основных и второстепенных режимах вдохновили меня на написание своего собственного второстепенного режима.

Лучшая часть использования этого подхода заключается в том, что когда вы хотите проверить, что делают привязки клавиш в конфигурации по умолчанию в emacs, вы просто отключаете свой второстепенный режим; затем вы снова включаете его и возвращаете свои пользовательские привязки клавиш.

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

Чтобы глобальная привязка переопределяла привязку основного режима, просто установите привязку nilв основном режиме:

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

Глобальное связывание не может иметь приоритет над всеми режимами в целом (иначе было бы бессмысленно иметь основные режимы), но вы можете взломать его, создав собственный второстепенный режим с вашими наиболее важными привязками. Тогда вы, по крайней мере, будете иметь приоритет над большинством (хотя и не обязательно всеми) режимами.

shosti
источник
Будет ли это работать для любого произвольного основного режима? Другими словами, если я установлю основной режим, называемый «foo-mode», я смогу вставить (define-key foo-mode (kbd "C-j") nil)в свой файл .emacs и ожидать, что это сработает?
Ниспио
На самом деле вы хотите, чтобы это было foo-mode-map(мой пример в ответе был плохим), но да, это отключит привязку клавиш в основном режиме, так что вместо этого будет использоваться глобальная привязка клавиш (если только не используется другой второстепенный режим).
шости
Является ли достаточно универсальным, что foo-modeбудет называться раскладка клавиатуры foo-mode-map?
Ниспио
@nispio Да, это верно для подавляющего большинства режимов (хотя есть несколько жуликов).
шости
0

Вы можете использовать эти макросы:

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

РЕДАКТИРОВАТЬ :

Проверьте пример ниже:

Если раскладка X переопределяет вашу глобальную привязку Y, вы пишете:

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

И тогда переопределение будет отменено.

Ренан Ранелли
источник
ваш макрос, для которого вы получаете выгоду от использования обратной цитаты: `(define-key, map, binding (ключ поиска (current-global-map), привязка))
Sigma
Можете ли вы прокомментировать, что делает макрос, и как его использовать? Мне не понятно из кода.
Ниспио
Первый макрос просто ищет ключ в глобальной карте и присваивает результат other map, таким образом, выставляя привязку глобальной карты через other map. Второй просто позволяет применить первый для списка привязок.
Ренан Ранелли
Извините, если я плотный, но я все еще не полностью понимаю использование. Нужно ли мне определять свою собственную специальную глобальную таблицу ключей? Это должно принадлежать второстепенному режиму? Должен ли я выполнить expose-bindingsсначала, а затем глобально связать эти клавиши с командами, которые я хочу? Может быть, вы могли бы показать пример того, что я мог бы поместить в мой файл инициализации, чтобы сделать эту работу.
Ниспио
2
Обратите внимание, что эти имена макросов являются неправильными. Они не «выставляют» глобальное связывание, а скорее дублируют глобальное связывание. Если реальная глобальная привязка изменится, этих дубликатов не будет.
Фил