3

I have this advice in place that executes the called function over the whole buffer if a region is not selected. But I would also like to print a message that tells the user that the function they just called acted on the whole buffer.

(defun modi/whole-buffer-if-not-region (orig-fn &rest args)
  (save-excursion
    (if (region-active-p) ; do the default thing if a region is selected
        (apply orig-fn args)
      (let (fn-name) ; indent the whole buffer otherwise
        (apply orig-fn (list (point-min) (point-max)))
        (setq fn-name (cl-case (type-of orig-fn)
                        ('subr              (subr-name orig-fn))
                        ;; ('compiled-function (symbol-name orig-fn)) ; doesn't work
                        (t     orig-fn)))
        (message "Executed %s on the whole buffer" fn-name)))))

(advice-add #'indent-region :around #'modi/whole-buffer-if-not-region)
(advice-add #'eval-region   :around #'modi/whole-buffer-if-not-region)

Using the above code,

  • If I do M-x eval-region without selecting a region, I see the message Executed eval-region on the whole buffer.
  • I would like to get a similar message when I do M-x indent-region without selecting a region. symbol-name does not work here as I learned that the indent-region function is of the type 'compiled-function

With the above code I get the below when I do M-x indent-region without region selection:

Executed #[770 \211\203A \303!\262\212b\210\304 \262b\210n\204 \305y\210`W\2038 `\306\307w\210`|\210l\2042 \310\311"\210\305y\210\202 \307\211\223\210)\202\324 \203u \212b\210\304 \262b\210\312!`W\203p \313!\204j n\203g l\204j c\210\305y\210\202S \210)\202\324    \203\201    "\210\202\324 \212\314!\262b\210\315 ?\205\225 \316\317`#`W\203\305 n\203\243 l\204\246 \320 \210\305y\210\211\203\225 \211`\211\247\203\273 \211@Y\203\300 \321"\210\266\202\225 \211\203\315 \322!\210\307\211\223\266)\323\211\207 [fill-prefix indent-region-function deactivate-mark prefix-numeric-value point-marker 1    nil indent-to 0 regexp-quote looking-at copy-marker minibufferp make-progress-reporter Indenting region... indent-according-to-mode progress-reporter-do-update progress-reporter-done t] 9 2037903 r
P] on the whole buffer

How do I extract the compiled-function name (indent-region in this case)?

Solution

Thanks to @abo-abo's solution below, I got this working as I wanted using the below:

(defmacro modi/add-whole-buffer-if-not-region-advice (symbol)
  "Advice SYMBOL function (that originally acts on a region) to act on the
whole buffer if a region is not selected."
  `(advice-add ,symbol :around (lambda (orig-fn &rest args)
                                 (save-excursion
                                   ;; Execute the original SYMBOL function if
                                   ;; it is called indirectly. Example: we do
                                   ;; not want to trigger this advice if
                                   ;; `eval-region' is called via `eval-defun'.
                                   (if (or (not (eq ,symbol this-command))
                                           (use-region-p))
                                       (apply orig-fn args)
                                     (apply orig-fn (list (point-min) (point-max)))
                                     (message "Executed %s on the whole buffer"
                                              (propertize
                                               (symbol-name ,symbol)
                                               'face
                                               'font-lock-function-name-face)))))))

(modi/add-whole-buffer-if-not-region-advice #'indent-region)
(modi/add-whole-buffer-if-not-region-advice #'eval-region)
  • Function object isn't something that has a name... it can be pointed to by many symbols, so your question can't have a good answer. However, since you are adding these advices, you could maintain a list of functions you advised and look for the name in that list. – wvxvw Jun 10 '15 at 15:51
  • @wvxvw Ah, I wished it were possible to get the name of the symbol used to trigger that sub/compiled-function/.. – Kaushal Modi Jun 10 '15 at 17:01
1

You can't get the name from orig-fn, since it's just a byte-compiled lambda, and you can't get a name of lambda.

You can use something (simpler) instead:

;; -*- lexical-binding: t -*-
(defun modi-add-region-advice (symbol)
  (advice-add
   symbol
   :around (lambda (orig-fn &rest args)
             (save-excursion
               (if (region-active-p)
                   (apply orig-fn args)
                 (message "Executed %s on the whole buffer" symbol))))))
(modi-add-region-advice 'indent-region)
  • The symbol is not passed onto the lambda; I am getting Symbol's value as variable is void: symbol. Hmm.. I thought that simply adding symbol to the list of lambda args would work.. but it doesn't. It now prints Executed nil on the whole buffer. – Kaushal Modi Jun 10 '15 at 16:58
  • I finally got it working by making that defun a defmacro and evaluating the value of symbol in the macro using ,symbol. Thanks! I don't think it's possible to use defun here.. is it? – Kaushal Modi Jun 10 '15 at 17:08
  • Note the comment that sets lexical-binding. It's relevant. – abo-abo Jun 10 '15 at 17:43
0

Small comment : IMO, the body of the question (in particular the part entitled "Solution") is not really related to the title of the question. I suggested an edit of the title, but it was rejected. I think abo-abo's answer is the best to solve the problem described in the body, so now what follows is a solution to the problem stated in the title.


As others have said, the problem has no general solution : a function could very well have no name at all. So let us assume that it does have a name, i.e. there is a symbol (interned in obarray) which holds that function in its function cell.

The way to find out the name of a function is to loop through obarray and check every symbol for one that matches. This is pretty simple to write with mapatoms, and left as an exercise to the reader.

But there is one added difficulty : advices. If you advised indent-region, the function slot of that symbol no longer is the original indent-region. So the above method will not do what you want, and you have to dissect the function cell to check if it was advised, and what was the original function.

Now that the job description is done, to the code !

(defun yf/find-function-in-symbols (function &optional advicep)
  "Find FUNCTION in obarray and return a corresponding symbol, or nil.
When ADVICEP is non-nil, also return symbol if it is an advised
version of FUNCTION."
  (catch 'found
    (mapatoms (lambda (sym)
        (when (fboundp sym)
          (let ((symfun (symbol-function sym)))
            (when (or (eq symfun function)
                  (and advicep
                   (eq (advice--cd*r symfun)
                       function)))
              (throw 'found sym))))))
    nil))

(defun modi/whole-buffer-if-not-region (orig-fn &rest args)
  (save-excursion
    (if (region-active-p)
        (progn
          ;; do the default thing if a region is selected
          (message "Executed %s as usual" (yf/find-function-in-symbols orig-fn t))
          (apply orig-fn args))
      ;; indent the whole buffer otherwise
      (apply orig-fn (list (point-min) (point-max)))
      (message "Executed %s on the whole buffer" (yf/find-function-in-symbols orig-fn t)))))

(advice-add #'indent-region :around #'modi/whole-buffer-if-not-region)
(advice-add #'eval-region   :around #'modi/whole-buffer-if-not-region)

Last comment : when yf/find-function-in-symbols is not given its optional ADVICEP argument, it gives an answer to the easier problem where no advising is involved.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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