0

I would like to implement a sensible strategy for handling optional arguments. In this instance, I want the function to be as for a function like abbrev-mode (although not in the context of a minor mode):

If called interactively, toggle the ‘Abbrev mode’ mode. If the prefix argument is positive, enable the mode, and if it is zero or negative, disable the mode.

If called from Lisp, toggle the mode if ARG is ‘toggle’. Enable the mode if ARG is nil, omitted, or is a positive number. Disable the mode if ARG is a negative number.

Have started with this simple implementation.

(defun roto-abbrev (&optional n)
  "Enables words to be expanded as one types."
  (interactive)
  (unless n (setq n 1))
  (abbrev-mode n))

How do I make it behave similar to abbrev-mode function with respect to its argument handling?

8
  • 1
    The question is unclear: How do you want the function to behave? What would the user see and do? IOW, what is its specification?
    – NickD
    Aug 6 at 0:01
  • 1
    IOW, you want to know how exactly abbrev-mode (or really any minor mode function`, since they all implement the behavior you describe) implements its argument handling. Is that correct?
    – NickD
    Aug 6 at 2:15
  • Yes, want to replicate that for my function.
    – Ephram
    Aug 6 at 2:18
  • 1
    OK - then you still need to explain what the function's action should be when the corresponding action of a minor mode is "enable the mode" or "disable the mode". What should happen if I do M-x roto-abbrev or I do (roto-abbrev 'toggle) from lisp? If roto-abbrev were a minor-mode function then the minor-mode would be toggled: what's the behavior you expect in this case? Similarly for all the other cases.
    – NickD
    Aug 6 at 3:06
  • 1
    "How do I make it behave similar to abbrev-mode function with respect to its argument handling?" -- you would begin by looking at how abbrev-mode handles its argument. I trust that there is an unwritten portion of this question in which you did that, found it wasn't satisfactory, but decided to omitted that code and explanation from your text. I suggest you improve your question by restoring those things.
    – phils
    Aug 6 at 3:38

2 Answers 2

0

At some point I thought I had understood what the OP wanted and edited the question to reflect that understanding, but the OP demurred and changed it back a bit, so I don't know if this will answer his current question. It is really an answer to the previous form of the question after my edit, but I still think there is a chance that this is really what the OP is after.

The basic idea is that we want a function that handles its argument exactly the way a minor mode does, so we figure out what a "real" minor mode function does and strip way all the minor mode paraphernalia, leaving only the argument handling. But the argument is supposed to enable or disable something, so we emulate this enabling/disabling by defining a variable and set it to t to "enable" or to nil to "disable" whatever it is we are enabling or disabling. For a "real" minor mode, the variable is a buffer-local variable with the same name as the mode function (e.g. abbrev-mode is the name of the mode function and also the name of the mode variable), so we follow that convention.

With that as explanation, here's a minimal function that handles its argument the way the quoted paragraph in the question describes and "enables"/"disables" whatever it is we are trying to enable/disable by setting the corresponding variable. Here's the minimal code:

(defvar-local foo-bar nil)

(defun foo-bar (&optional arg)
    (interactive
     (list
      (if current-prefix-arg
          (prefix-numeric-value current-prefix-arg)
        'toggle)))

    (setq foo-bar
            (cond
             ((eq arg 'toggle) (not foo-bar))
             ((and (numberp arg) (< arg 1)) nil)
             (t t)))
    foo-bar)

I assume that the variable should be buffer-local here, but it could be made global: just change the defvar-local to defvar. Initially, foo-bar is nil, so it is "disabled".

The tests that @antonio describes in his answer can be used to verify that everything is as it should be: after every test, you can evaluate the foo-bar variable in the appropriate buffer to make sure its value is as expected.

BTW, I did this by macroexpanding a fictitious (define-minor-mode foo-bar "foo-bar docstring") and ruthlessly eliminating everything but the argument handling and the state variable.

Although I hope this will answer the OP's "real" question, I am not holding my breath. Depending on how it goes, I might keep it, amend it or delete it.

9
  • Correct, like minor-mode but with minor mode paraphernalia stripped off.
    – Ephram
    Aug 6 at 19:13
  • How do you make (defun foo-bar but also (setq foo-bar?
    – Ephram
    Aug 8 at 2:44
  • A symbol in Emacs Lisp has separate value and function components.
    – NickD
    Aug 8 at 2:50
  • 1
    Ok, so they are separate, with setq changing the value component.
    – Ephram
    Aug 8 at 2:59
  • What would the last foo-bar do?
    – Ephram
    Aug 8 at 3:25
1

It is unclear what is your final goal, but this replicates the behaviour described in the quoted part of your answer. Indeed, it entails the large part of the call-type/prefix combinations, so it could be useful in different contexts.

(defvar my-minor-mode-active nil)

(defun  my-minor-mode-set (activate)
  "Activate my-minor-mode if ACTIVATE is non-nil, else deactivate it."
  (cond
   (activate
    (setq my-minor-mode-active t)
    ;; More initialising code here
    )
   ((not activate)
    (setq my-minor-mode-active nil)
    ;; More deinitialising code here
    )))
    

(defun f (&optional prefix)
  (interactive "P")

  ;; Manage user interactive calls
  (when (called-interactively-p 'interactive)
    (cond
     ((null prefix)
      (my-minor-mode-set (not my-minor-mode-active))
      (message (concat "Inter. call with nil prefix, hence the mode is toggled to "
               (if my-minor-mode-active "active." "inactive."))))

     ((> (prefix-numeric-value prefix) 0)
      (my-minor-mode-set t)
      (message "Inter. call with positive prefix, hence the mode is activated."))
     ((<= (prefix-numeric-value prefix) 0)
      (my-minor-mode-set nil)
      (message "Inter. call with non-positive prefix, hence the mode is deactivated."))))

  ;; Manage Lisp calls (including keyboard macros)
  (when (not (called-interactively-p 'interactive))
    (cond
     ((equal prefix 'toggle)
      (my-minor-mode-set (not my-minor-mode-active))
      (message (concat "Lisp call with 'toggle prefix, hence the mode is toggled to "
               (if (not my-minor-mode-active) "active." "inactive."))))
     ((or (null prefix) (> (prefix-numeric-value prefix) 0))
      (my-minor-mode-set t)
      (message "Lisp call with nil/positive prefix, hence the mode is activated."))
     ((<= (prefix-numeric-value prefix) 0)
      (my-minor-mode-set nil)
      (message "Lisp. call with non-positive prefix, hence the mode is deactivated.")))))

Test is with the lisp calls:

(f 'toggle)
(f)
(f 0)
(f 1)
(f -1)

And with the interactive calls:

M-f
C-u M-f
C-u 0 M-f
C-u 1 M-f
C-u -1 M-f

The requested behaviour of (f 0) is not immediately clear. I am assuming that non-positive prefixes trigger deactivation.

3
  • Please heed the warnings in the doc-string of call-interactively-p: it would be better to emulate what define-minor-mode does as exactly as possible, where it only uses call-interactively-p for notifying the user: The only known proper use of ‘interactive’ for KIND is in deciding whether to display a helpful message, or how to display it. If you’re thinking of using it for any other purpose, it is quite likely that you’re making a mistake. Think: what do you want to do when the command is called from a keyboard macro?
    – NickD
    Aug 6 at 15:41
  • @NickD: Perhaps I misunderstood the OP, but he wrote "I want the function ... not in the context of a minor mode". As for called-interactively-p, do you mean replacing it with something like: (defun foo (&optional print-message) (interactive "p") (when print-message (message "foo")))? How to distinguish here C-u 1 M-x foo from (foo 1)? They appear to be two distinct required cases in the question.
    – antonio
    Aug 6 at 22:27
  • It is not clear what the OP wants, so we are all trying to figure that out. As for called-interactively-p (sorry I screwed up the name in my first comment), I have never used it (never needed it really), so I have no idea if there is or not a way to distinguish the two cases you mention. But the warning in the doc seemed important enough to flag.
    – NickD
    Aug 7 at 0:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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