Emacs, how to associate a command with all keys, freeing up a few keys?

I want to make a secondary mode (foo-mode), which has its own map (foo-mode-map), but when the user presses any key not in (foo-mode-map), the secondary mode should quit. How to bind turn-off-foo-mode all other keys?

EDIT: here is the solution I came up with based on the answer selected. It also accepts numerical input.

 (defalias 'foo-electric-delete 'backward-kill-word) (defun foo-mode-quit (&optional arg) (interactive) (let ((global-binding (lookup-key (current-global-map) (single-key-description last-input-event)))) (unless (eq last-input-event ?\Cg) (push last-input-event unread-command-events)) (unless (memq global-binding '(negative-argument digit-argument)) (foo-mode -1)))) (defvar foo-mode-map (let ((map (make-keymap))) (set-char-table-range (nth 1 map) t 'foo-mode-quit) (define-key map "-" 'negative-argument) (dolist (k (number-sequence ?0 ?9)) (define-key map (char-to-string k) 'digit-argument)) (define-key map [backspace] 'foo-electric-delete) map)) (define-minor-mode foo-mode "Toggle Foo mode. With no argument, this command toggles the mode. Non-null prefix argument turns on the mode. Null prefix argument turns off the mode. When Foo mode is enabled, the control delete key gobbles all preceding whitespace except the last. See the command \\[foo-electric-delete]." ;; The initial value. :init-value nil ;; The indicator for the mode line. :lighter " Foo" ;; The minor mode bindings. :keymap foo-mode-map :group 'foo) 
+4
source share
4 answers

I included a complete working example to create a secondary mode with the desired behavior; the key should use set-char-table-range on the layout created by make-keymap , which creates a dense layout with a full char-table ; using this on a sparse layout created with make-sparse-keymap will not work.

 (defalias 'foo-electric-delete 'backward-kill-word) (defun foo-mode-quit (&optional arg) (interactive) (foo-mode -1)) (defvar foo-mode-map (let (map (make-keymap)) (set-char-table-range (nth 1 map) t 'foo-mode-quit) (define-key map [backspace] 'foo-electric-delete) map)) (define-minor-mode foo-mode "Toggle Foo mode. With no argument, this command toggles the mode. Non-null prefix argument turns on the mode. Null prefix argument turns off the mode. When Foo mode is enabled, the control delete key gobbles all preceding whitespace except the last. See the command \\[foo-electric-delete]." ;; The initial value. :init-value nil ;; The indicator for the mode line. :lighter " Foo" ;; The minor mode bindings. :keymap foo-mode-map :group 'foo) (defvar major-baz-mode-map '(keymap (t . major-baz-mode-default-function))) 

Setting the default binding for the main mode map is simpler, and here I include this example, but, as I noted above, this kind of spare keyboard will not work for the secondary mode:

 (defvar major-baz-mode-map '(keymap (t . major-baz-mode-default-function))) 

This is discussed in the keyboard layout format documentation, which states:

 (t . binding) This specifies a default key binding; any event not bound by other elements of the keymap is given binding as its binding. Default bindings allow a keymap to bind all possible event types without having to enumerate all of them. A keymap that has a default binding completely masks any lower-precedence keymap, except for events explicitly bound to nil (see below). 
+4
source

One thing you have to decide is if the user presses another key, you should just exit your mode if you have to run the command associated with the corresponding key (e.g. in incremental-search ).

One way is to use pre-command-hook to check if this-command one of your commands, and turn off your mode if it is not.

+4
source

Solution 2. You can use set-char-table-range to set all the characters on the map. For instance:

 (defvar foo-mode-map (let ((map (make-keymap))) (set-char-table-range (nth 1 map) t 'foo-turn-off-foo-mode) ... map)) 
+4
source

Answer 3:

I would say that the chosen solution is too complicated. Getting started with an event queue is not something you usually would like to do.

Instead, I would suggest a radically different solution. If you leave the mode turned on, you can bind keys, such as backspace , to a function that could define by itself if it should work both character-wise and word-wise.

Below is a simple proof of concept. It still has an explicit function for entering text mode, but this can be generalized to functions that activate the word mode and perform some action.

 (defun foo-electric-enabled nil "Non-nil when foo-mode operate in word mode.") (defun foo-electric-delete (&optional arg) (interactive "p") (if (and foo-electric-enabled (memq last-command '(kill-region foo-electric-delete foo-enable-word-operations))) (call-interactively 'backward-kill-word) (setq foo-electric-enabled nil) (call-interactively 'backward-delete-char-untabify))) (defvar foo-mode-map (let ((map (make-sparse-keymap))) (define-key map [backspace] 'foo-electric-delete) map)) (defun foo-enable-word-operations () (interactive) (setq foo-electric-enabled t)) (define-minor-mode foo-mode "Toggle Foo mode. With no argument, this command toggles the mode. Non-null prefix argument turns on the mode. Null prefix argument turns off the mode. When Foo mode is enabled, the backspace key gobbles all preceding whitespace except the last. See the command \\[foo-electric-delete]." ;; The initial value. :init-value nil ;; The indicator for the mode line. :lighter " Foo" ;; The minor mode bindings. :keymap foo-mode-map :group 'foo) 

The above code can be extended, but I leave this as an exercise for the reader:

  • Instead of checking if a function is a member of a literal list, there may be a general list of modules, as an alternative you can use properties for functions that should be treated as if you would like to stay in words mode.

  • Things, such as a mode indicator, are always on. When implementing this package using other minor modes, you can use the second to own the mode indicator and indicate that the package is in word mode, replacing foo-electric-enabled and foo-enable-word-operations .

  • The foo-electric-delete function explicitly calls either backward-kill-word or backword-delete-char-untabify . There are several methods for invoking what was associated with meta-inverse space and inverse space, respectively.

+1
source

Source: https://habr.com/ru/post/1386938/


All Articles