5

I have made the following function to count how many C-u prefixes a command was given (assuming it is called interactively), but I feel this is a common-enough problem that there should be a built-in function (or a package that solves this problem more robustly). Is there a better way to do this?

(defun tmp:how-many (arg)
  ;; Return zero if `arg' is 1 or nil
  (if (or (null arg) (equal arg 1)) 0
    ;; If `arg' is a list (as it is when called interactively), extract
    ;; the number.
    (when (listp arg)
      (setq arg (car arg)))
    ;; Recurse
    (if arg
        (1+ (tmp:how-many (/ arg 4)))
      0)))

(defun tmp:test (arg)
  (interactive "P")
  (message "%S" (tmp:how-many arg)))
Drew
  • 75,699
  • 9
  • 109
  • 225
Sean Allred
  • 6,861
  • 16
  • 85
  • 1
    I simply [use a cl-case statement](https://github.com/kaushalmodi/.emacs.d/blob/master/setup-files/setup-windows-buffers.el#L173-195) of 4,16,64,.., t. In absence of prefix, the 't' case will be evaluated. – Kaushal Modi Dec 06 '14 at 18:59
  • If you are asking about practical uses then I think it makes little sense to look for handling an arbitrary number of `C-u`s. If you are expecting users to use more than, say, five `C-u`s then you are probably really stretching it. In practice, from zero to three is plenty. And in any case you need to distinguish the cases, whether you test 1, 2, 3 or 4, 16, 64. – Drew Dec 09 '14 at 04:57
  • @Drew Absolutely agree on all counts. As with a lot of my questions (here and elsewhere), this one arises from sheer curiosity – not necessarily any overtly practical application. Still, I think the answers below are generally useful :) – Sean Allred Dec 09 '14 at 05:12

2 Answers2

6

Assuming you only want to understand arguments given using only C-u, you can use a base-four logarithm:

(defun tmp:how-many (arg)
  (when (consp arg)
    (truncate
     (log (car arg) 4))))

Note that such a function is doomed to fail if a sequence like C-3 M-x tmp:test is used; the only appropriate response to such usage is to signal an error or return nil, which was what I do here. (Returning arg would imply ambiguous results between, e.g., C-3 M-x tmp:test and C-u C-u C-u M-x tmp:test.)

Sean Allred
  • 6,861
  • 16
  • 85
  • 2
    +1 for a logarithm with base 4! I wish I could give a +2 for extreme cleverness. Only minor suggestion would be to wrap the `log` in a `truncate`, since `log` returns a float and you probably want an integer. – Dan Dec 06 '14 at 22:14
  • @Dan ah, yes! Good idea :) – Sean Allred Dec 06 '14 at 22:15
5

I will assume that you want to distinguish only uses of a plain prefix arg, e.g., C-u, C-u C-u, C-u C-u C-u, etc., and not uses of a numeric prefix arg, e.g., C-u 23, C-9, C-- 5.

Test both, in order:

  1. Whether the value of current-prefix-arg is a cons, using consp. If not, then a plain prefix arg was not used.

  2. The prefix-numeric-value of current-prefix-arg, to see which power of 4 it is.

For example:

(if (consp current-prefix-arg)
    (case (prefix-numeric-value current-prefix-arg)
      (4   (do-C-u-stuff))
      (16  (do-C-u-C-u-stuff))
      (64  (do-C-u-C-u-C-u-stuff))
      ...)
  ...) ; Do something for absent prefix arg or non-plain prefix arg

case is a macro from the CL libraries. Use (eval-when-compile '(require 'cl)). (Or you can use cl-case, if your Emacs version has it.)

Drew
  • 75,699
  • 9
  • 109
  • 225
  • Is there an alternative for `case` from `cl-lib`? – Sean Allred Dec 07 '14 at 15:09
  • Yes, `cl-case` is the same thing. – Drew Dec 07 '14 at 15:56
  • @Sean The hyperlink in my comment to your question takes you to an example of `cl-case` use. – Kaushal Modi Dec 08 '14 at 00:29
  • I'm going to accept this answer because it more fully addresses how one would handle prefix args, but I think it should be used in conjunction with my logarithm trick for clarity of code :) thanks! – Sean Allred Dec 09 '14 at 04:01
  • 1
    Note that an absent prefix argument (`nil`) is returned as `1` by `prefix-argument-numeric-value` – Sean Allred Dec 09 '14 at 04:03
  • Yes, of course `prefix-argument-numeric-value` returns 1 for `nil` `current-prefix-arg`. (But the only place that function is used here is for `consp` raw prefix args.) – Drew Dec 09 '14 at 04:50
  • @Drew your comment would imply otherwise ("Do something for absent prefix arg or non-plain prefix arg") but perhaps that isn't what you meant :) While `nil` doesn't satisfy `consp`, it is still a valid prefix argument (in the sense that it is the sheer lack of one). I'm not saying your code would break in any case :) – Sean Allred Dec 09 '14 at 05:13
  • Huh? One reason `(consp ...)` can return `nil` is that no prefix arg was provided. In that case, the second `if` branch is taken, just as it is taken if a numeric (non-cons) prefix arg is provided. – Drew Dec 09 '14 at 05:21
  • I guess I had an extra paren after the `case`. I've removed that now. That was probably what was confusing you. – Drew Dec 09 '14 at 05:23