How do I string together functions and actions in a key binding.

Say I want to make a binding that does comment-dwim and then appends "TODO: " to achieve /* TODO: */ in C.

(global-set-key (kbd "C-c T") 'comment-dwim "TODO: ")

This doesn't work because comment-dwim interprets the following as an argument.

Same for

(defun insert-todo () "TODO: ")

(global-set-key (kbd "C-c T") 'comment-dwim 'insert-todo)
  • 2
    You compose the two functions into one: (lambda () (interactive) (comment-dwim) (insert-todo)).
    – NickD
    Feb 9, 2021 at 2:27
  • @NickD: Please consider posting something like that as an answer. (Comments can be deleted at any time.)
    – Drew
    Feb 9, 2021 at 3:43
  • @Drew Even though my title might not have been accurate, you don't think it would be what someone misled as me would type in?
    – mcp
    Feb 10, 2021 at 16:05
  • @NickD Could you compare Arch Stanton's answer to yours?
    – mcp
    Feb 10, 2021 at 16:08
  • My comment is basically a bare-bones summary of the last paragraph of Arch Stanton's answer (starting with "So I'd say you want to string together ...").
    – NickD
    Feb 10, 2021 at 16:36

1 Answer 1


The problem with your first binding is not that comment-dwim interprets "TODO: " as an argument, it's that global-set-key tries to do so. You're giving "TODO: " as a third argument to global-set-key, but it only accepts two (the keys and the command).

To be clear, let me make another example using the command insert-char, which uses its argument in a more straightforward way than comment-dwim. (insert-char ?c) inserts a c in the current buffer. Now can you bind that call like this: (global-set-key (kbd "C-c T") (insert-char ?c))? No you can't. Key bindings only accept commands, not command calls (insert-char is a command, (insert-char ?c) is a call to that command). Note, command means interactive function, i.e. a function with an interactive form in its definition (see the manual). That's one of the problems with your second binding, the function is not interactive.

The other problem with your insert-todo is that the way it's defined, it returns "TODO: ", you didn't tell Emacs to insert it. Did you try to run insert-todo? Type M-:, enter (defun insert-todo () "TODO: "), hit RET, then type M-: again, (insert-todo), RET. You'll see "TODO: " in the echo area, as its return value. You need to give "TODO: " as argument to the function insert in order to insert it in the buffer when you call the function.

So I'd say you don't want to string together a function and an "action", you want to string together two function calls: (comment-dwim nil) (comment-dwim requires an argument and nil will do) and (insert "TODO: "). Now if you need to call one or more functions with their arguments using a key combination, you can define a new interactive function that includes the calls to the functions and bind this new function, or you can put the same definition as an unnamed interactive function directly in the key binding:

  • named function
(defun insert-todo-comment ()
  (interactive "*")
  (comment-dwim nil)
  (insert "TODO: "))
(global-set-key (kbd "C-c T") #'insert-todo-comment)
  • anonymous function
(global-set-key (kbd "C-c T") (lambda ()
                                (interactive "*")
                                (comment-dwim nil)
                                (insert "TODO: ")))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.