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?

Sean Allred
  • 6,861
  • 16
  • 85

4 Answers4

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)
Jordon Biondo
  • 12,332
  • 2
  • 41
  • 62
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.

phils
  • 48,657
  • 3
  • 76
  • 115
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

Braham Snyder
  • 360
  • 3
  • 8
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)))
Andreas Röhler
  • 1,894
  • 10
  • 10