Skip to content
Thierry Volpiatto edited this page Feb 28, 2017 · 29 revisions

Developing with the Helm Framework

Introduction

The best way to learn how to create a custom Helm command is to read the source code and look at examples. A good place to start is the helm-info.el file, which is fairly short and straightforward.

That being said, we’ll try to go over some basic ideas in this Wiki.

Creating a Helm buffer

The helm function creates a Helm buffer with candidates to select and/or take action on. The list of candidates is provided by one or more sources.

An example usage of helm is below:

(defun my-first-helm-command ()
  (interactive)
  (helm :sources 'my-source
        :buffer "*helm my command*"))

helm must be called with several keywords arguments, called attributes.

Helm attributes

An attribute determines Helm behavior. There are a large number of attributes in Helm; some are mandatory, while others are optional.

NOTE: When creating sources you are using slots which are keywords describing how to build attributes, they have generally the same name as attributes but not always e.g the slot :data exists, but there is no such attribute.

Looking up Helm attributes

To learn about a single Helm attribute, use the documentation of the class you are using where all slots are documented.

Mandatory attributes

You have to give at least a name to your source and a list of candidates. The list of candidates is given with a variable containing candidates, a function returning candidates or a list, the attribute depend on which class you are using, e.g candidates for sync sources, for in-buffer sources you have to build a buffer using helm-init-candidates-in-buffer or for conveniency you can use the :data slot which will build the candidate buffer for you. In async sources the candidates-process attribute is used which is a function with no arg that returns a process.

Optional, but important attributes

helm keywords

:sources

Expects a source of the form:

  • Single source (alist)
  • Symbol naming the source
  • List of sources (alist or symbol)

Where alists are the resulting value of functions building sources, that is all the helm-build-* function or the helm-make-source function, don’t use directly alists when writing sources. See examples in next section.

:buffer

Optional but important.

The value for the :buffer keyword helps the helm-resume command retrieve the Helm session.

The name of the buffer should be prefixed with helm (e.g. *helm Info*). This is not mandatory, but it is good practice. It will, among other things, allow Helm to automatically hide the buffer.

Creating a Source

Even if you can still create source with alists, helm provides convenient basic classes to build sources, and allow you to create your own classes that inherit from these basics classes.

Here are the basic classes for creating a Helm source:

  • helm-source-sync, which puts candidates in a list.
  • helm-source-in-buffer, which puts candidates in a buffer.
  • helm-source-async, which gets candidates asynchronously using the output of a process.
  • helm-source-dummy, which use the helm-pattern as candidate.
  • helm-source-in-file, which gets candidates from the lines of a named file using helm-source-in-buffer.

For consistency, prefix your source names with helm-source- (e.g. helm-source-info-emacs).

For convenience, helm provide macros prefixed by helm-build- to build your sources quickly, see examples below.

All the different slots are documented in docstring of each classes.

helm-source-sync

(helm-build-sync-source "test"
  :candidates '(a b c d e))

(helm :sources (helm-build-sync-source "test"
                 :candidates '(a b c d e))
      :buffer "*helm sync source*")

helm-source-in-buffer

(helm-build-in-buffer-source "test1"
  :data '(a b c d e))

(helm :sources (helm-build-in-buffer-source "test1"
                 :data '(a b c d e))
      :buffer "*helm buffer source*")

helm-source-async

(helm :sources (helm-build-async-source "test2"
                 :candidates-process
                 (lambda ()
                   (start-process "echo" nil "echo" "a\nb\nc\nd\ne")))
      :buffer "*helm async source*")

helm-source-dummy

(defun helm/test-default-action (candidate)
  (browse-url (format
               "http://www.google.com/search?q=%s"
               (url-hexify-string candidate))))

(helm :sources (helm-build-dummy-source "test"
                 :action '(("Google" . helm/test-default-action)))
      :buffer "*helm test*")

Help

To give a specific help to your Helm source, create a variable helm-<my-source>-help-string and bind it in your source with the helm-message slot. It will then appear when you use C-h m or C-c ?.

Creating a Class

Create your own class inheriting from one of the main classes

(defclass my-helm-class (helm-source-sync)
  ((candidates :initform '("foo" "bar" "baz"))))

(helm :sources (helm-make-source "test" 'my-helm-class)
      :buffer "*helm test*")

Is the same as creating your source with:

(helm :sources (helm-build-sync-source "test"
                 :candidates '("foo" "bar" "baz"))
      :buffer "*helm test*")

Create your own class and Inherit from one of helm-type classes

Here an example from a helm user that store a list of favorite files in a file ~/.fav to retrieve them quickly:

(defclass helm-test-fav (helm-source-in-file helm-type-file)
  ((candidates-file :initform "~/.fav")))

(helm :sources (helm-make-source "test" 'helm-test-fav)
      :buffer "*helm test*")

Creating a new class using as parent a class inheriting itself from a helm-type-* class

Sometimes, you may want to inherit from a class using itself a helm-type-* class but with one or more attributes of this class slightly modified for your needs. You may think that you only need to create your new class inheriting from the class inheriting itself from the helm-type-* class, but this is not enough.

Here how to do, reusing the example above we modify the actions predefined by helm-type-file:

  1. Create a fake class:
(defclass helm-override-test-fav (helm-source) ())
  1. Create a method for this class

This method is used as a :PRIMARY method, which mean that the similar parent method, if some will not be used, in particular the helm-source-in-file method which calls itself the helm-source-in-buffer method, so to be sure these important methods are used, use call-next-method, see below.

(defmethod helm--setup-source ((source helm-override-test-fav))
  (call-next-method)
  (let ((actions (slot-value source 'action))) 
    (setf (slot-value source 'action)
          (helm-append-at-nth (symbol-value actions)
                            '(("test" . ignore)) 1))))
  1. Create now your main class inheriting from your new overriding class
(defclass helm-test-fav (helm-source-in-file helm-type-file helm-override-test-fav)
    ((candidates-file :initform "~/.fav")))

Now when running helm with a source built from your new class you should see the new action you have added in second position to the other file action.

Create your source from your own class

Once your class is created, you have to use helm-make-source to build your source.

(helm-make-source "test" 'my-class :action 'foo [...other slots])

Write your own helm-type class

You will find several examples in the helm-types.el file. The main thing to remember is to create an empty class and fill it using two defmethod’s, one empty which should be a primary method and one which is a before method, use for this the slots :primary and :before of defmethod. This allows you to override different slots of the inheriting type class in your new class.

Conventions

Class names

Class names are currently a mess. Need to come up with a better convention.

Clone this wiki locally