8

Many Emacs commands alter their behavior when called with one or more C-u prefixes. In some cases the default behavior of a command is less useful to me than the behavior I get when prefixing it with C-u. At the same time, I do not want to get rid of the default behavior completely.

To give a concrete example, the quit-window command (bound to q in help-mode) quits the current window and buries the buffer displayed in it by default. When called with C-u it kills the buffer instead. I would like the command to kill the buffer by default and bury it when called with a C-u prefix.

Q: How can I tell Emacs to make a command behave as if it was called with a C-u prefix by default while moving the default behavior to the C-u prefix?

I know that I could address this by redefining the original command or wrapping it in a custom command that passes prefix args according to my preferences. But I'd rather do something like this:

(swap-args 'quit-window)
itsjeyd
  • 14,586
  • 3
  • 58
  • 87

3 Answers3

5

Something like this:

(defun swap-args (fun)
  (if (not (equal (interactive-form fun)
                  '(interactive "P")))
      (error "Unexpected")
    (advice-add
     fun
     :around
     (lambda (x &rest args)
       "Swap the meaning the universal prefix argument"
       (if (called-interactively-p 'any)
           (apply x (cons (not (car args)) (cdr args)))
         (apply x args))))))

Obviously, only works for (interactive "P"). For other types of interactive it may not make sense to negate the first argument.

olejorgenb
  • 125
  • 4
abo-abo
  • 13,943
  • 1
  • 29
  • 43
  • 1
    Could be a bit simpler using `:filter-args` instead of `:around`, I think. – npostavs May 26 '15 at 15:32
  • Thanks, `swap-args` works in that it makes `C-u` behavior the default when applying it to a command. However, when I call the command with `C-u`, I don't get the original behavior. Maybe `called-interactively-p` is to blame? My understanding is that with an arg of `'any`, this function will only return `nil` if the containing command is called from Lisp, so when invoking the advised command via a key binding, the else branch can't be reached. – itsjeyd May 27 '15 at 07:58
  • `'any` was fine, I used `funcall` instead of `apply` by mistake. – abo-abo May 27 '15 at 10:54
  • 2
    You should add something to the docstring as well, otherwise this is bound to be confusing. – Gilles 'SO- stop being evil' May 27 '15 at 12:02
  • 1
    Annoyingly some functions (eg. `org-insert-subheading`) check literally for the raw universal argument (apparently `(4)`) instead of a truth coercing check. – olejorgenb Jan 15 '17 at 12:43
2

Another method based on the fact that an interactive-form property in the function's plist overrides the form specified in code: (ref)

(put 'org-insert-subheading
     'interactive-form
     '(interactive (progn (if current-prefix-arg
                              '(nil)
                            '((4))))))

Using (interactive-form 'function) to get the current spec, it's possible to handle more complex specs too.

I think the best solution (if possible) is to wrap the function with (let ((prefix-arg (not current-prefix-arg))) though

olejorgenb
  • 125
  • 4
0

Not that easy as thought. However, as it's trivial to have the swapped command:

(defun my-quit-window-args-swapped (&optional bury window)
"Adapted docstring blah, blah "
  (interactive "P")
  (quit-restore-window window (if bury 'bury 'kill)))
Andreas Röhler
  • 1,894
  • 10
  • 10
  • Thanks, but as I mentioned in my question, I am looking for a generic solution that I can apply to different commands, not a wrapper that is specific to the `quit-window` command. – itsjeyd May 27 '15 at 12:02
  • @itsjeyd There is no finite restriction WRT to result and employment of an argument. It's not about "(swap-args ..." but about use and meaning of args - which is free finally. – Andreas Röhler May 27 '15 at 12:55
  • 1
    I should have chosen my words more carefully. I didn't mean to suggest that your solution can't be adapted to work for other commands. It's a perfectly good solution for swapping default and `C-u` behavior, and I am sure others who come across your post will be helped by it! Personally, I am not going to use it because I have multiple commands whose behavior I want to change, and would like to avoid having to define multiple wrappers. – itsjeyd May 27 '15 at 15:30
  • @itsjeyd AFAIU the only thing defined here is the way the body receives C-u. There is no standard at which point in body the arg is used, also there are many ways to make use of it - in a boolean sense here. Sure, you could employ some kind of AI to cover and deal with all these possibilities. :) – Andreas Röhler May 28 '15 at 06:00