4

Is there a way to program some Emacs command in elisp that could tell whether it is called interactively through some key binding or by typing its full name after M-x? I thought this could be done using called-interactively-p but it seems that it isn't.

My motivation: if called via M-x command-name, the command would gently remind me of the key binding. Would be great for learning again key bindings that I have forgotten after some lapse in usage.

phs
  • 1,227
  • 7
  • 13
  • 3
    emacs -Q version 25.3.1 outputs the keysequence for commands input per M-x by default in the echo area. – Tobias Feb 01 '18 at 09:03
  • 1
    @Tobias: But how is it done? Can it be easily reproduced in elisp? – phs Feb 01 '18 at 09:34
  • 3
    You can study it easily. It is part of execute-extended-command. Note that execute-extended-command is bound to M-x. You can also determine that by C-h k M-x. Emacs is open source and self-documenting. Since the info about the binding is part of the command bound to M-x they don't need to identify whether the command is called via M-x or via another key sequence. – Tobias Feb 01 '18 at 09:37
  • @Tobias: thanks for the explanation on how it is done. The way I understand it, execute-extended-command knows as an axiom how the command was invoked so it does not have to determine if a key binding was used. Too bad for me. It would be great if one could use (if (called-interactively-p 'extended-command) ... or something similar. BTW, I know emacs is open source (it includes elisp code I contributed 30 years ago). – phs Feb 01 '18 at 09:55

2 Answers2

2

You can define a predicate function indicating that execute-extended-command is just running by advising execute-extended-command. The around advice wraps execute-extended-command with some active flag that can be tested in the predicate.

The following lisp code shows how it is done and presents also a test example. Call foobar once with M-x and once with key sequence C-c c to see the difference.

(defvar execute-extended-command--p) ;; Just declare to silence the byte compiler.

(eval ; for the sake of lexical binding (LEXICAL is t)
 (function ; quote but let the byte compiler do its job...
  (let (execute-extended-command--p)
    (defun execute-extended-command-p ()
      "Non-nil when `execute-extended-command' is just running."
      execute-extended-command--p)

     (defun execute-extended-command-wrapper (execute-extended-command &rest args)
       "Run EXECUTE-EXTENDED-COMMAND with ARGS.
Enable the predicate `execute-extended-command-p'
indicating that `execute-extended-command' is running."
       (unwind-protect
           (progn
             (setq execute-extended-command--p t)
             (apply execute-extended-command args))
         (setq execute-extended-command--p nil)))
     nil)) ;< Let eval just return nil.
 t) ;< LEXICAL arg of eval

(advice-add 'execute-extended-command :around #'execute-extended-command-wrapper)

(defun foobar (some-arg)
  "Test function."
  (interactive "sInput some string:")
  (message "Called `foobar' via %s with arg %S."
           (if (execute-extended-command-p)
               "via execute-extended-command"
             "via key sequence or menu item")
           some-arg))

(global-set-key (kbd "C-c c") #'foobar)
Tobias
  • 33,167
  • 1
  • 37
  • 77
  • Great !! (Just curious: is there any reason why you use a function (execute-extended-command-p) instead of testing the variable directly?) – phs Feb 01 '18 at 11:29
  • 1
    @phs Your question about execute-extended-command-p has some justification. One should replace the variable execute-extended-command-p by execute-extended-command--p (two slashes) to mark that variable as internal. The reason for the predicate (execute-extended-command-p) is to give (at least semantically) some kind of read-only access. The user should never fiddle around with the variable execute-extended-command-p itself -- (s)he should only use the predicate. This should also be clearly documented... Sorry for the lapsus. Pityingly one does not have private variables in elisp. – Tobias Feb 01 '18 at 11:39
  • @phs Fixed problems discussed in my previous comment. – Tobias Feb 01 '18 at 12:14
  • 1
    In thise case you could simulate a private variable with lexical binding, i.e., put a let-binding around both of your functions instead of using defvar. – npostavs Feb 01 '18 at 12:54
  • @npostavs Yes you are right. I included your suggestion. – Tobias Feb 01 '18 at 13:34
  • 1
    The eval thingy is not only ugly but ineffective: the arg to eval will be evaluated first (in lexical-binding or not depending on context) and eval will simply return the function it received as arg. – Stefan Feb 01 '18 at 14:00
  • @Stefan I opted for the eval variant because we only have an elisp snippet and I don't know where this snippet will be embedded (maybe in the configuration file). This way the code is independent on the kind of binding in the containing file. Nevertheless I addressed your objection and changed the return value of the eval form to nil. – Tobias Feb 01 '18 at 14:12
  • @Stefan I was about to write the same thing, but it seems the function quoting actually makes it work, although it's 100% equivalent to quote in that context (i.e., the compiler doesn't compile the contents). – npostavs Feb 01 '18 at 14:14
  • @npostavs You wrote: "although it's 100% equivalent to quote in that context (i.e., the compiler doesn't compile the contents)." You are right (symbol-function 'execute-extended-command-wrapper) returns the non-compiled function. Why is that the case? – Tobias Feb 01 '18 at 14:29
  • @npostavs Is there a better variant for a local form with lexical binding? – Tobias Feb 01 '18 at 14:34
  • 2
    @Tobias: Why would you want to restrict it to a "local" form? Just add ;; -*- lexical-binding:t -*- on the first line of all your Elisp files. As for why it's like quote: pure accident since a function form like above is basically invalid. – Stefan Feb 01 '18 at 14:56
  • 1
    @Stefan Because I don't want to potentially break the OP's init files. I know that the probability is low. E.g., all variables defined with defvar are automatically marked special. But, maybe one uses setq and wonders why the let-binding does not work in one of the called functions not covered by the let. Such errors can be difficult to be tracked down for not so experienced users. Be aware that many emacs users configure just by copying and pasting code from others! (I don't.) – Tobias Feb 01 '18 at 15:01
  • @Stefan About function: The doc string says: "Like ‘quote’, but preferred for objects which are functions. In byte compilation, ‘function’ causes its argument to be compiled. ‘quote’ cannot do that." Okay let's see. I applied it like quote but wanted the stuff inside byte-compiled. So judging from the doc string it fits like tailored for my case. But, maybe I misinterpret the doc string. – Tobias Feb 01 '18 at 15:21
  • @Tobias: Except that what you have within function is not "an object which is a function" (these can basically only be symbols or things of the form (lambda ...). – Stefan Feb 01 '18 at 17:47
  • @Stefan Should we delete the word preferred then? – Tobias Feb 01 '18 at 17:51
  • @Tobias: Why? For those cases where it's valid, it's very much preferred (actually for the (lambda ...) case, using quote may result in something that's simply wrong, so function is not just "preferred"). – Stefan Feb 01 '18 at 19:55
  • @Stefan Thanks for the explanation. The cases where function is valid or invalid should be clearly defined in the doc for function. The info node for function is a bit better but also not so clear. There stands that the argument to function is intended to be used as a function. In the text below that part stands Assuming the argument is a valid lambda expression that has two effects: (1) byte compilation, (2) conversion to a closure under lexical binding. What happens when the argument is not a valid lambda is unspecified. (Should it work like normal quote in that cases?) – Tobias Feb 01 '18 at 21:01
1

I like @Tobias's solution. I will add that I use third-party package counsel, which overrides M-x with its own counsel-M-x function, so advising execute-extended-command would not work for me.

Here's a barely-tested alternative that tests whether the keys reported by this-command-keys are bound to a given command, where "a given command" is typically the caller itself (i.e. some-example-command in the example below).

(defun called-via-key-binding (cmd)
  "Returns non-nil if `this-command-keys' is bound to CMD."
  (eq (key-binding (this-command-keys)) cmd))

(defun some-example-command ()
  (interactive)
  (cond
    ((not (called-interactively-p 'any))
     (message "Not called interactively"))
    ((called-via-key-binding #'some-example-command)
     (message "Called via key binding and not M-x"))
    (t
     (message "Not called via key binding, so maybe called via M-x"))))

(The call to called-interactively-p should arguably be moved into called-via-key-binding.)

Dale
  • 146
  • 5