4

Say I have two functions

(defun tmp:a ()
  (interactive)
  (tmp:b))

(defun tmp:b ()
  (message "called from %S" "?"))

If I call M-x tmp:a, how can I determine this from tmp:b?

4 Answers 4

4

The current interactive command is:

C-hv this-command RET

That doesn't use the stack (I don't have a good answer for that), but it's the normal mechanism for doing this kind of thing.

1
  • 1
    For completion, there is also real-this-command. Jan 15, 2015 at 13:25
7

You can look up the stack using the backtrace frames, here is an implementation getting the name of the current function.

Can functions access their name?

The code in that question looks up the stack until it finds the frame representing a function call which is the current function, simply continue to move up the backtrace frames until you find the second function call and you'll have your answer.

Here is the modified code to do just that.

(defun get-caller-func-name ()
  "Get the current function' caller function name"
  (let* ((index 5)
         (frame (backtrace-frame index))
         (found 0))
    (while (not (equal found 2))
      (setq frame (backtrace-frame (incf index)))
      (when (equal t (first frame)) (incf found)))
    (second frame)))


;; demo
(defun function-A ()
  (print (format "A called from %s" (get-caller-func-name)))
  (function-B))

(defun function-B ()
  (print (format "B called from %s" (get-caller-func-name)))
  (function-C))

(defun function-C ()
  (print (format "C called from %s" (get-caller-func-name))))

(function-A) 
;; output

"A called from eval"

"B called from function-A"

"C called from function-B"

Additionally you can use backtrace-frame to get a full list of the current frames. Here is another implementation of that where we can see the call stack in function-C. We can see that it was called from function-B and that was called from function-A all the way up to the interactive command the started the evaluation.

(defun call-stack ()
  "Return the current call stack frames."
  (let ((frames)
        (frame)
        (index 5))
    (while (setq frame (backtrace-frame index))
      (push frame frames)
      (incf index))
    (remove-if-not 'car frames)))

(defun function-stack ()
  "Like call-stack but is a list of only the function names"
  (butlast (mapcar 'cl-second (call-stack))))

Demonstration

(defun function-A ()
  (function-B))

(defun function-B ()
  (function-C))

(defun function-C ()
  (print (call-stack))
  (print (function-stack)))


;; now we call function A
(function-A)

;; printed output running with `eval-print-last-sexp` in scratch buffer

((t command-execute eval-print-last-sexp) (t call-interactively eval-print-last-sexp nil nil) (t funcall-interactively eval-print-last-sexp nil) (t eval-print-last-sexp nil) (t eval-last-sexp t) (t eval-last-sexp-1 t) (t eval (function-A) nil) (t function-A) (t function-B) (t function-C))

(command-execute call-interactively funcall-interactively eval-print-last-sexp eval-last-sexp eval-last-sexp-1 eval function-A function-B function-C)
0
2

depending on your precise use-case, (called-interactively-p 'interactive) can be cleaner--returns t if the current function was called directly, nil if the current function was called by another function, even if that function was called interactively

https://www.gnu.org/software/emacs/manual/html_node/elisp/Distinguish-Interactive.html

1

Here an implementation of phils answer:

defun tmp:a ()
  (interactive)
  (tmp:b this-command))


(defun tmp:b (&optional from-a)
  (interactive)
  (if from-a
      (message "called from %S" from-a)
    (message "called from %S" this-command)))

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.