4

I have this on my init.el

;; magit status
(define-key evil-motion-state-map "gs" 'magit-status)

But I would like to have another binding, for C-u M-x magit-status (C-u is the universal-argument)

Something like (which obviously does not work):

;; magit status
(define-key evil-motion-state-map "ga" 'C-u magit-status)

rationale: with C-u M-x magit-status, magit allows you to supply a directory where a git repo resides, so you do not have to have an opened file that resides inside the git repo where you want to perform git actions.

Dan
  • 32,584
  • 6
  • 98
  • 168
ninrod
  • 1,436
  • 2
  • 13
  • 28
  • 1
    This question has been asked numerous times. Finding all the duplicates might not be so easy, but is probably worth doing, if someone has the time. The dup I pointed to provides the answer as essentially (for this question) `(define-key evil-motion-state-map "ga" (lambda (interactive) (let ((current-prefix-arg 4)) (call-interactively 'magit-status))))` – Drew Oct 07 '16 at 14:59

3 Answers3

3

#'magit-status is fun, because its argument to interactive isn't just a string, so running it naively with call-interactively doesn't work.Instead, the function generates its own interactive arguments. Here's the interactive call inside (defun magit-status ...:

(interactive
 (list (and (or current-prefix-arg (not (magit-toplevel)))
            (magit-read-repository
             (>= (prefix-numeric-value current-prefix-arg) 16)))))

So we have to bind current-prefix-arg directly, to pretend it's been called with a prefix arg:

(define-key evil-motion-state-map "ga"
 (lambda () (interactive)
   (let ((current-prefix-arg 4))
     (call-interactively #'magit-status))))

Or, as I tested it (because I don't use evil-mode):

(global-set-key
 (kbd "C-c C-s")
 (lambda () (interactive)
   (let ((current-prefix-arg 4))
     (call-interactively #'magit-status))))

Thanks to @npostavs for the instruction on binding current-prefix-arg. The previous answer (for the purposes of transparency) was:

(global-set-key
 (kbd "C-c C-s")
 (lambda () (interactive)
   (magit-status (magit-read-repository
                  (>= (prefix-numeric-value current-prefix-arg) 16)))))
zck
  • 8,984
  • 2
  • 31
  • 65
  • tks. How would you do it using `call-interactively`? (just so I can test it). – ninrod Oct 06 '16 at 22:33
  • "running it with call-interactively doesn't work" - it works for me, did you try it? – npostavs Oct 06 '16 at 23:43
  • @ninrod what did you want to use `call-interactively` for that binding it doesn't test? – zck Oct 07 '16 at 03:46
  • @npostavs I did try it. I don't see a way to pass a universal argument to it. So it does not do the desired behavior for the question, but runs magit-status on the current directory (if it's a git repo). I can only find ways to `call-interactively` it so it does *not* get a universal argument. – zck Oct 07 '16 at 03:49
  • Oh, I see, in that case just let-bind `current-prefix-arg` (note that this is not really related to using a string or lisp form as the argument to `interactive`). – npostavs Oct 07 '16 at 14:34
  • @zck, I wanted to test the `naive` way you mentioned: "so running it naively with call-interactively doesn't work". I'm so limited in lisp that I did not know how do do something as simple as that. – ninrod Oct 07 '16 at 17:13
  • "because its argument to interactive isn't just a string" - you would have the same issue with a function using `(interactive "P")`. – npostavs Oct 08 '16 at 17:34
2

You do not need a separate keybinding to pass the universal argument to the command. Based on your current keybinding, you may simply type C-ugs.

Dan
  • 32,584
  • 6
  • 98
  • 168
  • I knew that, but really, I use this so often that I would like to bind it to another more comfortable sequence like `ga`. – ninrod Oct 06 '16 at 22:31
  • suppose I wan't to know, actually, how I can bind a key sequence, like `C-x g` so that this sequence actually performs another sequence, like `C-u g s`? But i think that is a diferent question. – ninrod Oct 06 '16 at 22:44
  • @ninrod: sorry, misunderstood your intent! – Dan Oct 06 '16 at 23:11
2

Even better, if you don't want to risk doing the wrong thing when/if magit-status is changed:

(define-key evil-motion-state-map "ga"
  (lambda () (interactive)
    (universal-argument)
    (call-interactively 'magit-status))

or to test without using evil:

(global-set-key
 (kbd "C-c C-s")
 (lambda () (interactive)
   (universal-argument)
   (call-interactively 'magit-status)))

Basically universal-argument sets up the state as if you had pressed C-u (because C-u is actually bound to universal-argument), then the next interactively-called function reads that state. If you wanted to call a function as if C-u had been pressed twice, then you would first call universal-argument and then universal-argument-more. Numeric arguments could be supplied with digit-argument and negative-argument; see simple.el for the actual code that implements all of this.

db48x
  • 15,741
  • 1
  • 19
  • 23
  • 1
    I just tested and zck's solution works but your's does not. +1 anyway because I did not know how to do the universal-argument stuff. rich info here. – ninrod Oct 06 '16 at 22:52