Handling multiple selections in helm

| categories: helm, emacs | tags:

The basic usage pattern of helm is you run a command which opens a buffer of choices. You enter some text in the minibuffer which eliminates choices that do not match what you type in. You can select multiple choices by using C-spc, or M-a to mark them all. When you press enter, the current selection is sent to the default action defined. The action is a function that does something, usually on the selected item(s). Here, we explore writing the action function to do what we want. The reason this is somewhat tricky is that when you mark an item in helm, the "cursor" moves to the next item, which means when you press enter it may be possible that the current highlighted item is not part of the items you have marked. If your action will perform a delete action, for example, you may have wanted to delete the marked items, and not the current selection! So, what we need is a way to get what we want.

An action function in helm should normally take one argument, which is going to be the currently selected item from helm. However, we can use two different functions to access either the selected item (helm-get-selection) or the marked items (helm-marked-candidates). So, we can write our function to do "do what we mean". Note, even if you do not mark any candidates, (helm-marked-candidates) will return a list that has the current selection in it. So we can write our action function to act on this list so it works on what is marked or what is selected if nothing is marked. That is probably "what we mean".

Here is one way to work on a selection or marked list of selections. We define an action function that takes an arg, but inside we operate on each element of the marked candidates.

(defun some-action (candidate)
  (loop for cand in (helm-marked-candidates)
        do
        (message-box "working on %s" cand)))

(helm :sources '(((name . "HELM")
                  (candidates . (1 2 3 4))
                  (action . (("open" . some-action))))))

Here is an alternative approach. Here we define the action function to work on one candidate. That might be helpful for testing, for example. Then, we use mapc to apply the function to each marked candidate.

(defun some-action (candidate)
  (message-box "single working on %s" candidate))

(helm :sources '(((name . "HELM")
                  (candidates . (1 2 3 4))
                  (action . (("open" . (lambda (candidate)
                                         (mapc
                                          'some-action
                                          (helm-marked-candidates)))))))))

A little more verbose method might be like this. Here we just pull out the lambda function to another function, to make the helm source definition a little shorter. I cannot tell if this is easier to follow, it is just another option.

(defun some-action (candidate)
  (message-box "single2 working on %s" candidate))

(defun some-actions (candidate)
  (mapc 'some-action (helm-marked-candidates)))

(helm :sources '(((name . "HELM")
                  (candidates . (1 2 3 4))
                  (action . some-actions))))

So there you have it. You can select multiple things in helm, and then operate on them with your action function!

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter