0

I would like to bind CTRL+A ( ( CTRL+A followed by a ( without having to hold the CTRL key) to a sequence of evil commands, lets say yi( (yank in parenthesis).

1. Attempts

1.1. C-a and C-) with global-set-key

I succeed in binding CTRL+A with:

(global-set-key (kbd "C-a") (kbd "yi(")) 

This also works with CTRL+(:

(global-set-key (kbd "C-(") (kbd "yi(")) 

1.2. C-a-(

1.2.1. C-a-( with global-set-key

But when it comes to do CTRL+A and then ( I fail...

With the following code I got the error global-set-key: Key sequence C-a ( starts with non-prefix key C-a when reloading the config file:

(global-set-key (kbd "C-a (") (kbd "yi(")) 

1.2.2. C-a-( with general package

So I tried the following:

(use-package general :config (general-evil-setup) (general-create-definer testkey :states '(normal insert visual emacs) :keymaps 'override :prefix "C" ;; set leader :global-prefix "M-C") ;; access leader in insert mode (testkey "a" '((kbd "yi(") :wk "Yank in ()")) ) 

The above code gives no error at config file reload but the following error when pressing CTRL+A : command-execute: Wrong type argument: commandp, (kbd "yi(")

2. Questions

  1. How to bind CTRL+A followed by a ( (without having to hold the CTRL key) to this sequence of evil commands: yi(?

  2. Why my attempt 1.2.1. fails?

  3. Why my attempt 1.2.2. fails?

5
  • This question may be a duplicate. Maybe search for tag [prefix-keys]. See [Prefix Keys]()gnu.org/software/emacs/manual/html_node/elisp/Prefix-Keys.html. Commented Jul 5 at 16:33
  • Tanks. Found useful ones indeed: (1) (same "starts with non-prefix key" error), (2) (which shows how to unset a prefix) and (3) Commented Jul 7 at 15:00
  • If you think this is a duplicate question then please consider deleting this one. Commented Jul 7 at 16:38
  • I found it, it is a duplicate of this one: emacs.stackexchange.com/q/6037/44108 Commented Jul 7 at 19:55
  • Thx............ Commented Jul 8 at 1:24

1 Answer 1

3

I'm going to do this using the newer functions from keymap.el, as I feel they are more intuitive. They also don't require use of kbd to normalize key sequences.

First, let's look at this outside the context of Evil-mode.

You're trying to create a prefix-key in global-map. In other words, the key will be bound to another keymap.

Emacs lets you bind keys to commands, other keymaps, or keyboard macros.

When you open Emacs, C-a is bound to the command move-beginning-of-line. But you are then free to bind it to something else, a keymap, command, or a keyboard macro. That's what you did when you evaluated (global-set-key (kbd "C-a") (kbd "yi(")). You simply replaced the binding of C-a with a keyboard macro that acts as if you pressed yi(.

  1. How to bind CTRL+A followed by a ( (without having to hold the CTRL key) to this sequence of evil commands: yi(?
(keymap-global-unset "C-a") (keymap-global-set "C-a (" "y i (") 

Or, using the older keybinding functions...

(global-unset-key (kbd "C-a")) (global-set-key (kbd "C-a (") (kbd "yi(")) 
  1. Why my attempt 1.2.1. fails?

The reason that (global-set-key (kbd "C-a (") (kbd "yi(")) doesn't work without first unsetting C-a is explained in the error message you received:

Key sequence C-a ( starts with non-prefix key C-a

When you bind to a sequence of keys (like C-a (), you're really telling Emacs to create a keybinding for ( in whatever keymap is bound to the C-a key. But as Emacs advises you, that key is already bound to something else, which is not a keymap.

If there were no binding at all for C-a, Emacs would happily bind an empty keymap to it and then bind ( in that keymap. So we solve this problem by first un-binding C-a and then binding ( underneath C-a.

If you wanted to be more explicit about what you were doing, you could actually create the keymap first, then bind it to C-a, and then you'd also be able to bind the same keymap to other keys.

(defvar-keymap my/macro-keys "(" "y i (") (keymap-global-set "C-a" my/macro-keys) ;; same keymap on another prefix (keymap-global-set "C-c m" my/macro-keys) 
  1. Why my attempt 1.2.2. fails?

I don't have any way to test this, because I don't have General installed. So the following only discusses the error you're getting. If there are other problems with how you're using General, I wouldn't know.

Again, let's look at the error message.

CTRL+A : command-execute: Wrong type argument: commandp, (kbd "yi(")

This is saying that Emacs called a function, command-execute. In doing so, Emacs checked whether one of the arguments satisfied the predicate commandp, which checks that the argument is a command. But the argument failed that check, because it is not a command. The argument was (kbd "yi(").

Emacs is assuming that the object bound to C-a should be a command and attempting to execute it. So why is Emacs assuming that? Well, there are two other things it could be, a keymap, or a key sequence (keyboard macro).

If the object were a keymap, it would be a list that begins with the symbol keymap. In fact, the object (kbd "yi(") is a list. But it does not begin with the keymap symbol, so it is definitely not a keymap.

You intended for the object to be a key sequence, but instead it is only a list that begins with kbd. That's because when you passed the argument, it was inside of a quoted list.

(testkey "a" '((kbd "yi)") :wk "Yank in ()")) 

Normally, when you use kbd, you simply pass the form to a function. But since the form (kbd "yi)") is inside of a quoted list, every item in that list gets passed as-is, without being evaluated. That's why the error message tells you the argument is (kbd "yi(") and not just "yi(" (which is what that form evaluates to).

There are a few different ways you could write this to get around the error.

One is you could switch to using a backquoted list, which allows you to select which items get evaluated by prefixing them with commas.

(testkey "a" `(,(kbd "yi)") :wk "Yank in ()")) 

This should cause the kbd form to be evaluated while still preventing the other forms from being evaluated.

Since the other two elements are a keyword :wk and a string, both of which will always evaluate to themselves anyway, you could just return a list without quoting it.

(testkey "a" (list (kbd "yi)") :wk "Yank in ()")) 

Or you could just pass the thing that (kbd "yi)") evaluates to, which is the string "yi)".

(testkey "a" '("yi)" :wk "Yank in ()")) 

Any of these will result in the same thing being passed to your testkey definer. But as I wrote above, I have no way to test that the other parts of it are written correctly.

6
  • 1
    Thank you very much for taking the time to write such a comprehensive response! Your first three suggestions work very well for me and therefore answer my question. However, for the sake of completeness, I would like to point out that the last three proposals using General package do not work for me (nothing happens except that my cursor moves to the opening parenthesis after pressing CTRL+A and then seems to jump to the beginning of the paragraph by pressing (). Commented Jul 5 at 18:25
  • Hmm. But it is actually doing something and not erroring out? I notice that in the first example you used a left-parenthesis, but in the 1.2.2 example, you used a right-parenthesis. Maybe you just need to press the right-parenthesis instead (or change the code to use a left-parenthesis?). Sorry, I can't give much help related to General. Commented Jul 5 at 18:35
  • No error when reloading the config file or press CTRL+A (no message at all). Erratum: after further testing, when pressing CTRL+A the cursor jumps to the beginning of the line (not to the opening parenthesis as stated in my previous comment). Thanks for pointing me out the typo about the parenthesis in my question (typo corrected) but after some tests it doesn't seems to be the problem. Thanks for your support and as a reminder: your first answer is enough for me. Commented Jul 6 at 7:03
  • Consider moving your answer to the original question, of which this one is a duplicate: emacs.stackexchange.com/q/6037/105 Commented Jul 8 at 1:25
  • 1
    @mmarshall540: You might be right, but OP has closed this question, so your useful answer will effectively be lost if you don't post it to some unclosed question. Commented Jul 11 at 16:49

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.