0

I have this code:


(defmacro with-ignore-mouse-events (&rest body)
  "Macro to ignore mouse events before evaluating BODY."
  `(progn
     (when (input-pending-p)
       (let ((event (read-event)))
         (if (mouse-event-p event)
             (let ((button (event-basic-type event)))
               (if (eq button 'mouse-1)
                   (ignore)
                 (setq unread-command-events (list event))))
           (setq unread-command-events (list event)))))
     ,@body))

(defun mwe-function-1 ()
  "MWE function 1"
  (interactive)
  (unwind-protect
      (query-replace "foo" "bar" nil (point-min) (point-max))
    ;; UNWINDFORMS
    (read-string "function 1 executed")))

(defun mwe-function-2 ()
  "MWE function 3"
  (interactive)
  (unwind-protect
      (query-replace "foo" "bar" nil (point-min) (point-max))
    ;; UNWINDFORMS
    (read-string "function 2 executed")))

(defun mwe-function-3 ()
  "MWE function 3"
  (interactive)
  (unwind-protect
      (query-replace "foo" "bar" nil (point-min) (point-max))
    ;; UNWINDFORMS
    (read-string "function 3 executed")))

(defvar mwe-function-names '(mwe-function-1 mwe-function-2 mwe-function-3))

I want to generate modified version of the functions like in the following example:

(defun mwe-function-1* ()
  (interactive)
  (with-ignore-mouse-events
   (mwe-function-1)))

I tried:

(defmacro generate-mwe-star-functions (&rest function-names)
  "Macro to generate mwe-function-* functions."
  `(dolist (func-name ,@function-names)
    (message "%s*" func-name) ;; The string built here is correct...
    (defun ,(make-symbol (format "%s*" func-name)) ()
      (interactive)
      (with-ignore-mouse-events
       (intern func-name)))
    ))

(generate-mwe-star-functions mwe-function-names)

But I'm getting lost in the mechanism of back quoting (at least).

Where am I wrong?

Edit. The question suggested as a duplicated of mine does not explain how to loop a list of functions to define other functions. I thing this make the task a bit more complicated.

This is evidenced by the fact that the solution proposed by the user who (probably) flagged my question as a duplicate does not work.

1

2 Answers 2

2

Finally I found a solution:

(defun generate-mwe-star-functions (functions)
  "Generate starred versions of the given functions, where original
functions are wrapped with the `with-ignore-mouse-events' macro"
  (dolist (func functions)
    (let* ((func-name (symbol-name func))
       (starred-func-name (intern (format "%s*" func-name))))
      (defalias starred-func-name
        `(lambda ()
           (interactive)
           (with-ignore-mouse-events
             (funcall ',func)))
    (format "Starred version of the function `%s', where the original function is wrapped with the `with-ignore-mouse-events' macro" func-name)))))

I followed db48x's suggestion, using defun instead of defmacro, and Phil's suggestion to use defalias instead of fset in order to add a docstring to the functions.

4
  • 1
    I suggest quoting %s in the docstring: `%s' (so that the final version has a quoted function name).
    – phils
    Commented Feb 19 at 22:50
  • Good idea. Thanks!
    – Gabriele
    Commented Feb 19 at 23:07
  • Also note that IFF you're using lexical-binding for your library then you could skip the backquote/unquote, as func would already be a lexical variable for the lambda, and so (funcall func) would be sufficient. That ought to have byte-compilation benefits (the macro would need to be known at compile time).
    – phils
    Commented Feb 19 at 23:25
  • I think I'll need to delve deeper into this topic. Thank you.
    – Gabriele
    Commented Feb 19 at 23:32
1

There’s no reason at all for this to be a macro. Just make it an ordinary function. If you really want to fix the quoting, search for duplicate questions about quoting in macros; it is a very common question.

Edit:

The answer you mentioned in the comments has the perfect solution for you. Instead of using defun inside of a macro, use fset inside of a function. Like this:

(defun generate-mwe-star-functions (function-names)
  "Generate mwe-function-* functions.  FUNCTION-NAMES is a list of symbols."
  (dolist (func-name function-names)
    (fset (make-symbol (format "%s*" (symbol-name func-name)))
          (lambda ()
            (interactive)
            (with-ignore-mouse-events
              (funcall func-name))))))

If you really want to use a macro for some reason, the main problem is that you compute func-name inside the code generated by the macro, but try to substitute it into the same code with a comma. Everything that is substituted in with a comma is evaluated when the macro is compiled, and the code inside the backquote is only called later when the code is run. Thus you either need to compute func-name outside the backquote so that it exists to be substituted in with a comma, or you need to remove the comma. Which you prefer is up to you.

You can no doubt find a dozen other people asking the same macro question where the answer is to move something out of the backquoted code.

9
  • 1 - Your response seems more like a comment than an answer to me. 2 - Of course, I did some research before writing this question, and one of the things I found in these searches is that many recommend using macros for this kind of operations. Take a look here: emacs.stackexchange.com/q/70483/15606 3 - If I were able to solve my problem on my own, I wouldn't have written this question. I've spent several hours trying different approaches.
    – Gabriele
    Commented Feb 18 at 23:01
  • Thanks. I tried your function this way (generate-mwe-star-functions mwe-function-names) but it doesn't work. No new function is defined. At least not with the expected names.
    – Gabriele
    Commented Feb 18 at 23:48
  • Did you see the part where I changed the argument to a list of symbols, instead of a list of strings?
    – db48x
    Commented Feb 18 at 23:55
  • In my question I use (defvar mwe-function-names '(mwe-function-1 mwe-function-2 mwe-function-3)) that I think is a list of symbols. Where did you see a list of string in my question?
    – Gabriele
    Commented Feb 19 at 0:04
  • 1
    As we're talking about interactive functions, I'd suggest using defalias rather than fset for ease of generating and adding a docstring for each of the new functions. (Each docstring can trivially say that it's a version of `<func-name>' which is wrapped by `with-ignore-mouse-events'.)
    – phils
    Commented Feb 19 at 1:19

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.