10

suppose I want to bind a key to a partial key sequence (i.e. a key sequence that is a prefix of some other bound keybindings).

I've tried the obvious

(global-set-key (kbd "C-x C-x") (kbd "C-c"))

But that doesn't work for some reason.

PythonNut
  • 10,243
  • 2
  • 29
  • 75
  • Control+X has some nice built-in bindings to choose from: (1) ctl-x-map; (2) ctl-x-4-map; (3) ctl-x-5-map; and (4) ctl-x-r-map. Will any of those meet your needs? If not, you could define your own by grepping for those maps and copying the same method used to create them to create you own custom Control-X map. For example, grepping for `ctl-x-4-map` leads us to `subr.el` where there is `(defvar ctl-x-4-map (make-sparse-keymap) . . .` – lawlist Jan 05 '15 at 03:27
  • Grepping the Emacs source code for `ctl-x-` will give you every example in Emacs of how to define keys using all of the maps mentioned in the previous comment. And by doing that, we see that I missed one: ctl-x-ctl-p-map – lawlist Jan 05 '15 at 03:34
  • @lawlist, this is an example. I really want to bind arbitrary prefixes. – PythonNut Jan 05 '15 at 05:06
  • @lawlist Just out of curiosity, which command did you run to grep the Emacs source? – Håkon Hægland Jan 05 '15 at 12:34
  • 1
    @Håkon Hægland: I use the folowing grep arguments in a custom function that I put together: `i` case insensitive;`n` print line number; `I` ignore binary files; `E` extended regular expressions; and, `r` recursive. I primarily use Emacs on OSX with all the `*.el` and `src` directory packaged into an `*.app` directory. After building Emacs, I *temporarily* change the time on my computer to an hour or so before the build. Then I use another tool to find and extract all of the `*.el` files from the `*.gz` compressed files into the same directories they are presently in. – lawlist Jan 05 '15 at 17:19

3 Answers3

8

If you want to no longer have the prefix key be a prefix key, and you want to instead bind it to a single command, you can do that - no problem: just bind it.

If you want to have the prefix key act as both a single command and as a prefix key then that is of course impossible - a key is bound to either a command or a keymap (or to a command that is a keymap).

But if you want to bind a different prefix key to the same keymap that is bound to some other prefix key, that is not a problem. What you tried to do suggests that this is maybe what you really want: to make the key sequence C-x C-x act as a prefix key the same way that C-c does.

To do that, you bind C-x C-x not to C-c but to the binding of C-c. (lookup-key global-map (kbd "C-c")) tells you that C-c is bound to command mode-specific-command-prefix (which is in fact a keymap). So if you bind C-x C-x to that then it will behave like C-c:

(global-set-key (kbd "C-x C-x") (lookup-key global-map (kbd "C-c")))

or just:

(global-set-key (kbd "C-x C-x") 'mode-specific-command-prefix)
Drew
  • 75,699
  • 9
  • 109
  • 225
  • I believe there may be an exception to the rule regarding a prefix key acting as both a single command and as a prefix key: http://stackoverflow.com/questions/20026083/how-to-use-escape-conditionally-as-a-modifier-key I've been using the example in the previous link for about a year now and am very happy with it. – lawlist Jan 05 '15 at 07:12
  • No, that is not an exception at all. That is simply binding a key to a command (menu item, if you like) that *dispatches the behavior conditionally*. – Drew Jan 05 '15 at 16:04
  • Thank you, that fixes my problem perfectly and elegantly. – PythonNut Jan 05 '15 at 17:37
  • You can bind a key to both a keymap and an action, but only through hacks. Both cua mode and ergoemacs-mode do this for C-c and C-x. – Matthew Fidler Feb 28 '16 at 04:32
  • @MatthewFidler: No, you *cannot*. Look at how CUA mode etc. obtain the effect that they do. See, for example, `(define-key cua--prefix-override-keymap [(control x)] 'cua--prefix-override-handler)`. See the definition of `cua--prefix-override-replay` - it is essentially a dispatch function. – Drew Feb 28 '16 at 15:36
  • I admited it is a hack, but it does have the effect of binding both a keymap and an action – Matthew Fidler Feb 29 '16 at 23:39
  • 1
    Nice. Just looking at Windows-ifying my keybindings and this allowed me to move help to F1 in one statement `(global-set-key [f1] (lookup-key global-map (kbd "C-h")))` – Philip Daniels Feb 12 '17 at 12:22
4

For completeness, here's an alternative to Drew's answer. It may or may not be completely equivalent.

(defun simulate-key-press (key)
  "Pretend that KEY was pressed.
KEY must be given in `kbd' notation."
  `(lambda () (interactive)
     (setq prefix-arg current-prefix-arg)
     (setq unread-command-events (listify-key-sequence (read-kbd-macro ,key)))))

(global-set-key (kbd "C-x C-x") (simulate-key-press "C-c"))
Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • For what it's worth, and it took me a while to figure this out when I was getting started, but if you just do `,key` instead of `(read-kbd-macro ,key)`, then `simulate-key-press` is just consistent with all the `global-set-key`/`define-key`/etc functions, and you can do any of `(simulate-key-press (kbd "C-c"))`, `(simulate-key-press "\C-c")`, or `(simulate-key-press [?\C-c])`. – mtraceur Apr 29 '23 at 03:01
2

OPTION # 1:

(global-unset-key "\C-x\C-x")

(global-set-key (kbd "C-x C-x C-c") 'help-for-help)

OPTION # 2:

The following example is taken from: .../lisp/textmodes/page-ext.el and modified slightly:

(global-unset-key "\C-x\C-x")

(defvar ctl-x-ctl-x-map (make-sparse-keymap)
  "Keymap for subcommands of C-x C-x, which are for PythonNut.")

(define-key ctl-x-map "\C-x" 'ctl-x-ctl-x-prefix)
(fset 'ctl-x-ctl-x-prefix ctl-x-ctl-x-map)

(define-key ctl-x-ctl-x-map "\C-c" 'help-for-help)

Example creating C-z as a new global prefix key:

(global-unset-key "\C-z")

(defalias 'ctl-z-keymap (make-sparse-keymap))
(defvar ctl-z-map (symbol-function 'ctl-z-keymap)
  "Global keymap for characters following C-z.")
(define-key global-map "\C-z" 'ctl-z-keymap)

(define-key ctl-z-map "\C-c" 'help-for-help)
lawlist
  • 18,826
  • 5
  • 37
  • 118