9

It would be nice to have a quick way to toggle a variable. Here's something that I have in mind.

  • Call a "toggle var" function.
  • The completing-read should list only variables that have single values (not lists or alists). I don't know if that can be done. It would be even better if it lists only the vars that have values as either t or nil. Pretty sure this cannot be done.
  • The function then sets the var value to t if its current value is nil, and vice-versa.

The question is if we already have an elisp function that does this. If not, and if people have dealt with this problem with their custom solution, would like to see how they do it.

Update:

A bonus feature would be to make the completing-read suggest the symbol under point (if present) for toggling.

So if the "toggle var" fn is bound to C-c ~, the usage will be as short as C-c ~ RET.


My current procedure to toggle a var is as follows:

  • C-h v VAR, get the current var value.
  • M-: (setq VAR toggled-value).

or

  • M-: (setq VAR (not VAR)).

I am looking for a way to speed up this process (less typing).

Drew
  • 75,699
  • 9
  • 109
  • 225
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179

3 Answers3

4

Here's a quick answer --

(defun my-toggle-var (var)
  "..."
  (interactive
   (let* ((def  (variable-at-point))
          (def  (and def 
                     (not (numberp def))
                     (memq (symbol-value def) '(nil t))
                     (symbol-name def))))
     (list
      (completing-read
       "Toggle value of variable: "
       obarray (lambda (c)
                 (unless (symbolp c) (setq c  (intern c)))
                 (and (boundp c)  (memq (symbol-value c) '(nil t))))
       'must-confirm nil 'variable-name-history def))))
  (let ((sym  (intern var)))
    (set sym (not (symbol-value sym)))
    (message "`%s' is now `%s'" var (symbol-value sym))))

But if you use Icicles then you can just use command icicle-toggle-option (with a negative prefix arg, if you want any variable, and not just a user option):

icicle-toggle-option is an interactive compiled Lisp function in
`icicles-cmd1.el'.

It is bound to menu-bar options icicles icicle-toggle-option.

(icicle-toggle-option)

Toggle option’s value.  This makes sense for binary (toggle) options.
By default, completion candidates are limited to user options that
have ‘boolean’ custom types.  However, there are many "binary" options
that allow other non-nil values than t.

You can use a prefix argument to change the set of completion
candidates, as follows:

 - With a non-negative prefix arg, all user options are candidates.
 - With a negative prefix arg, all variables are candidates.

Read input, then act on it.

Input-candidate completion and cycling are available.  While cycling,
these keys with prefix ‘C-’ are active:

‘C-mouse-2’, ‘C-return’ - Act on current completion candidate only
‘C-down’, ‘C-wheel-down’ - Move to next completion candidate and act
‘C-up’, ‘C-wheel-up’ - Move to previous completion candidate and act
‘C-next’  - Move to next apropos-completion candidate and act
‘C-prior’ - Move to previous apropos-completion candidate and act
‘C-end’   - Move to next prefix-completion candidate and act
‘C-home’  - Move to previous prefix-completion candidate and act
‘C-!’     - Act on *all* candidates, successively (careful!)

When candidate action and cycling are combined (e.g. ‘C-next’), user
option ‘icicle-act-before-cycle-flag’ determines which occurs first.

With prefix ‘C-M-’ instead of ‘C-’, the same keys (‘C-M-mouse-2’,
‘C-M-RET’, ‘C-M-down’, and so on) provide help about candidates.

Use ‘mouse-2’, ‘RET’, or ‘S-RET’ to finally choose a candidate, or
‘C-g’ to quit.

This is an Icicles command - see command ‘icicle-mode’.
Drew
  • 75,699
  • 9
  • 109
  • 225
  • The solution works great for toggling the vars and I like the confirmation message it prints at the end. But it does not narrow down the list to just boolean vars. For that, I believe I need to look into the icicles source, correct? – Kaushal Modi Sep 25 '15 at 15:54
  • I just updated the question with a nice to have feature: pick up the symbol point as default (waiting for the user to just press `RET`). – Kaushal Modi Sep 25 '15 at 16:01
  • If you want strictly `t` and `nil` valued variables, then use the updated version, which also picks up the name of a (Boolean-valued) variable at point, as the default. – Drew Sep 25 '15 at 19:55
  • Yes, if you want to handle generalized Boolean variables (i.e., where non-`nil` can mean *true*, just like `t` does), or if you want to be able to choose whether to use options or any variables, then see the **Icicles** code. – Drew Sep 25 '15 at 19:56
3
(defun toggle-variable (variable)
  (interactive
   (let ((v (variable-at-point))
         val)
     (setq val (completing-read (if (symbolp v)
                                    (format
                                     "Toggle variable (default %s (= %s)): " v (symbol-value v))
                                  "Toggle variable: ")
                                obarray
                                'boundp
                                t nil nil
                                (if (symbolp v) (symbol-name v))))
     (list (if (equal val "")
               v (intern val)))))
  (set variable (not (symbol-value variable)))
  (message "%s toggled (= %s)" variable (symbol-value variable)))

If you have helm installed, you can use the command helm-apropos, it

  • provide completion interface for all variables
  • can per-select the variable at point if any
  • provide action to change the variable's value (you need to insert new value in the Minibuffer)

the advantage is you can look up the doc-string of variable and change its value within a helm-apropos session.

xuchunyang
  • 14,302
  • 1
  • 18
  • 39
2

Try lispy-setq to toggle variable at point or ora-custom-setq from my config to set a variable with completion:

(defun ora-custom-setq ()
  "Set a custom variable, with completion."
  (interactive)
  (let ((sym (intern
              (ivy-read "Variable: "
                        (counsel-variable-list))))
        sym-type
        cands)
    (when (and (boundp sym)
               (setq sym-type (get sym 'custom-type)))
      (cond
        ((and (consp sym-type)
              (memq (car sym-type) '(choice radio)))
         (setq cands (mapcar #'lispy--setq-doconst (cdr sym-type))))
        ((eq sym-type 'boolean)
         (setq cands
               '(("nil" . nil) ("t" . t))))
        (t
         (error "Unrecognized custom type")))
      (let ((res (ivy-read (format "Set (%S): " sym) cands)))
        (when res
          (setq res
                (if (assoc res cands)
                    (cdr (assoc res cands))
                  (read res)))
          (eval `(setq ,sym ,res)))))))
abo-abo
  • 13,943
  • 1
  • 29
  • 43
  • It would be nice to indicate what the current value of the variable is (especially when setting to nil or t). Or can the default value in the ivy "dropdown" be set to nil if the current value is t (and vice-versa)? – Kaushal Modi Sep 25 '15 at 15:48
  • I just updated the question with a nice to have feature: pick up the symbol point as default (waiting for the user to just press `RET`). – Kaushal Modi Sep 25 '15 at 16:01