helm and prefix functions

| categories: emacs, helm | tags:

Helm modifies how you use prefix arguments in Emacs. A prefix argument is when you type C-u before a command to modify its behavior. There are a few variations of prefix arguments. Basically, pressing C-u once sets a prefix variable to '(4), pressing twice sets it to '(16). Alternatively, C-u 7 sets the prefix to 7. In regular emacs commands, you type the prefix keys before the command. In helm, you type the after you enter the helm selection buffer, and before you press enter or select your action. In helm, you access the prefix arg in the variable helm-current-prefix-arg. Let us look at how you might use it.

We make an action function that does something conditionally depending on the prefix arg. Yes, you could write several functions to accomplish that too, but maybe there is just a little difference that you can use the prefix arg to handle. What you cannot remember 4 prefix options? You do write good doc strings on your functions right ;) If not, you probably ought to write four functions with meaningful names, and meaningful helm descriptions!

(defun action (candidate)
  "Our action function.
with no prefix message no prefix arg
with one prefix arg message C-u
with two prefix args message C-u C-u
with a numeric prefix arg, message the number."
  (interactive "p")
  (cond
   ((eq nil helm-current-prefix-arg)
    (message-box "no prefix arg"))
   ((equal helm-current-prefix-arg '(4))
    (message-box "C-u"))
   ((equal helm-current-prefix-arg '(16))
    (message-box "C-u C-u"))
   (t
    (message-box (format "C-u %s" helm-current-prefix-arg)))))

(setq some-helm-source
      '((name . "HELM at the Emacs")
        (candidates . (1 2 3 4))
        (action . action)))

(helm :sources '(some-helm-source))
C-u (64)

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

Handling multiple selections in helm

| categories: emacs, helm | 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

Anatomy of a helm source

| categories: emacs, helm | tags:

I have been integrating helm into my emacs work flows almost anywhere I need to make interactive selections and do something with them. In this post, I will go through the simplest helm examples I can think of that get you to writing your own example.

To run a helm selection process you basically just call a function that calls this minimal function:

(helm :sources '(some-helm-source))

In that code, the symbol some-helm-source will provide the input for the helm buffer. Let us look at the simplest example here. Each source should have a name, a list of candidates, and an action that works on the selected candidate. We construct a source as a list of cons cells. Here, we make a source with the name "HELM at the Emacs", a static list of candidates, which are simply a list of numbers, and a single action that will operate on the selected candidate.

If you run this block, you will get a helm buffer, you can select an entry, press enter, and you should see a message box pop up telling you what entry you selected. I like to separate the source definition from the helm call like this, but only for readability.

(setq some-helm-source
      '((name . "HELM at the Emacs")
        (candidates . (1 2 3 4))
        (action . (lambda (candidate)
                    (message-box "%s" candidate)))))

(helm :sources '(some-helm-source))
3

Not bad, but what if we want some dynamic candidates? The usual way we will do that is to define a function that calculates the candidates for us. Let us work out an example that just shows us random numbers between 0 and 10 to select from. In a real example, you would use this function to generate a list of candidates like bibtex keys, email-addresses, etc…

(defun random-candidates ()
  "Return a list of 4 random numbers from 0 to 10"
  (loop for i below 4 collect (random 10)))

(setq some-helm-source
      '((name . "HELM at the Emacs")
        (candidates . random-candidates)
        (action . (lambda (candidate)
                    (message "%s" candidate)))))

(helm :sources '(some-helm-source))

So far, we have looked at the simplest list of candidates: a simple list. It may be that this is not the most convenient way to see the candidates. We might like to have one set of candidates that we use for searching, but another set of equivalent candidates used for the action. For example, we might want a list of names for selecting, but then have the action work on the corresponding email address. Let us consider a case where we have a list of cons cells of names and email addresses.

We use the `, way to create the source variable to make sure our list of candidates is constructed. Then, in our function we take the selection and get the corresponding entry in the data a-list.

(setq data '(("John" . "john@email.com")
             ("Jim" . "jim@email.com")
             ("Jane" . "jane@email.com")
             ("Jill" . "jill@email.com")))

(setq some-helm-source
      `((name . "HELM at the Emacs")
        (candidates . ,(mapcar 'car data))
        (action . (lambda (candidate)
                    (message "%s" (cdr (assoc candidate data)))))))

(helm :sources '(some-helm-source))
jim@email.com

That is not too bad, and might be a general way to get to the data you want. But, helm can integrate this directly by using the a-list directly as the list of candidates. Helm will show you the car of each cell, but return the cdr of the selected entry.

Let us try this to make a function that will give us a helm buffer to select some names from, and then insert a comma separated list of emails from our selection at the point. We make our action function just return the list of marked candidates. Then we create a function that calls helm, and inserts a concatenated string.

(setq data '(("John" . "john@email.com")
             ("Jim" . "jim@email.com")
             ("Jane" . "jane@email.com")
             ("Jill" . "jill@email.com")))

(setq some-helm-source
      `((name . "HELM at the Emacs")
        (candidates . ,data)
        (action . (lambda (candidate)
                    (helm-marked-candidates)))))

(defun helm-select-and-insert-emails ()
  (interactive)
  (insert
   (mapconcat 'identity
              (helm :sources '(some-helm-source))
              ",")))
helm-select-and-insert-emails

Here is what I get when I run the command, select John and Jill, and press enter: john@email.com,jill@email.com

That is it for this post. We looked at:

  1. the simplest kind of helm interface with a fixed set of candidates
  2. A simple dynamic set of candidates
  3. A simple fixed set of candidates from a list of cons cells.

This barely scratches the surface of helm, but is already enough to do some useful things.

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
« Previous Page