1

I'm trying to write a function/macro to wrap arbitrary interactive function with some pre- and post- processing. Those processing does not involve the arguments of the interactive function.

For example, give f = delete-region, I'd like to write a function/macro that return a new function g = delete-region-wrapper with the definition like

(defun delete-region-wrapper (begin end) # or maybe (&rest args)...
    (interactive "r")
    (read-only-mode -1) # 'pre-processing'
    (delete-region begin end) # or maybe (apply #'delete-region ...) or maybe (funcall #'delete-region ... ) 
    (read-only-mode 1)) # 'post-processing'

Or the Python equivalent looks like

def wrap(f):
    def wrapper(*args, **kwargs):
        read_only_mode(-1)
        res = f(*args, **kwargs)
        read_only_mode(1)
        return res
    
    return wrapper

 delete_region_wrapper = wrap(delete_region)

How can I write this wrap macro/function?

(defmacro/defun wrap(f)
    ???
)

(setq delete-region-wrapper (wrap #'delete-region))
2
  • 1
    You should let-bind inhibit-read-only instead of switching read-only-mode off and on. Aside from anything else, switching modes like that would require you to also do error-handling in order to ensure that you ended up in the correct state even if something went wrong in the code you were wrapping (notwithstanding that your example wasn't actually checking the pre-existing state at the beginning).
    – phils
    Aug 13, 2022 at 2:18
  • @phils You are right! Thanks for the insight! Aug 13, 2022 at 5:09

1 Answer 1

1

You just need to return a lambda, since functions are first class members of the language.

(defun wrapper (f)
  (lambda (beg end)
    (interactive "r")
    (read-only-mode -1)
    (apply f (list beg end))
    (read-only-mode 1)))

Above will create a function object with a closure (f is fixed in this closure).
Then assign the returned function object into the function-slot of a symbol (e.g. w-d-r):

(setf (symbol-function 'w-d-r) (wrapper #'delete-region))

or

(fset 'w-d-r-2 (wrapper #'delete-region))

Above example is fixed to wrapping functions with two arguments, whose rely on region borders. It is possible to abstract more, by using &rest args as parameters, then your call to interactive needs to become more complex and instead of (list beg end) you would supply args to apply.

(defun wrapper (f)
  (lambda (&rest args)
    (interactive (SOMETHING-FANCY-HERE))
    (read-only-mode -1)
    (apply f args)
    (read-only-mode 1)))

Edit: instead of using apply, a call of call-interactively might suffice:

(defun wrapper-2 (f)
  (lambda () 
    (interactive)
    (read-only-mode -1)
    (call-interactively f)
    (read-only-mode 1)))
3
  • Thanks for the answer, that works! I have one more question, what if I also want to pass the pre- post-processing body (maybe multiple sexp for each) to wrapper-2? Do I need a macro then? Aug 13, 2022 at 5:14
  • you mean you want to replace (read-only-mode ..) with some other function? It still works with a function. Wrapper-2 would take more parameters like f1 and you would call those functions via apply or call-interactively. Of course it would be possible to write a macro, but one should prefer a function if a function would do.
    – jue
    Aug 13, 2022 at 19:05
  • Thanks for the hint! Aug 14, 2022 at 2:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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