summaryrefslogtreecommitdiff
path: root/doc/helm-devel.org
diff options
context:
space:
mode:
Diffstat (limited to 'doc/helm-devel.org')
-rw-r--r--doc/helm-devel.org495
1 files changed, 495 insertions, 0 deletions
diff --git a/doc/helm-devel.org b/doc/helm-devel.org
new file mode 100644
index 00000000..c991e833
--- /dev/null
+++ b/doc/helm-devel.org
@@ -0,0 +1,495 @@
+#+title: The Helm Developer's Guide
+# #+subtitle: Release {{{version}}}
+#+subtitle: Release 3.6.2
+#+author: The Helm Developers
+# #+date: {{{modification-time}}}
+#+language: en
+
+# #+texinfo: @insertcopying
+
+* Table Of Contents :TOC_3:
+- [[#introduction][Introduction]]
+- [[#creating-a-helm-buffer][Creating a Helm buffer]]
+- [[#helm-attributes][Helm attributes]]
+ - [[#looking-up-helm-attributes][Looking up Helm attributes]]
+ - [[#mandatory-attributes][Mandatory attributes]]
+ - [[#optional-but-important-attributes][Optional, but important attributes]]
+ - [[#helm-keywords][~helm~ keywords]]
+ - [[#sources][~:sources~]]
+ - [[#buffer][~:buffer~]]
+ - [[#input][~:input~]]
+- [[#customizing-action-lists][Customizing Action Lists]]
+- [[#creating-a-source][Creating a Source]]
+ - [[#helm-source-sync][~helm-source-sync~]]
+ - [[#helm-source-in-buffer][~helm-source-in-buffer~]]
+ - [[#helm-source-async][~helm-source-async~]]
+ - [[#helm-source-dummy][~helm-source-dummy~]]
+ - [[#helm-source-in-file][~helm-source-in-file~]]
+ - [[#help][Help]]
+ - [[#pre-filtering-lines-in-a-buffer][Pre-filtering lines in a buffer]]
+- [[#creating-a-class][Creating a Class]]
+ - [[#create-your-own-class-inheriting-from-one-of-the-main-classes][Create your own class inheriting from one of the main classes]]
+ - [[#create-your-own-class-and-inherit-from-one-of-helm-type-classes][Create your own class and Inherit from one of helm-type classes]]
+ - [[#creating-a-new-class-using-as-parent-a-class-inheriting-itself-from-a-helm-type--class][Creating a new class using as parent a class inheriting itself from a ~helm-type-*~ class]]
+ - [[#create-your-source-from-your-own-class][Create your source from your own class]]
+ - [[#write-your-own-helm-type-class][Write your own helm-type class]]
+- [[#writing-actions][Writing actions]]
+- [[#writing-persistent-actions][Writing persistent actions]]
+- [[#footnotes][Footnotes]]
+
+* Introduction
+
+The best way to learn how to create a custom Helm command is to read
+the source code[fn:1] and look at examples.
+
+A good place to start is the source file =helm-info.el=[fn:2]. This
+source file is fairly short and straightforward.
+
+That being said, we'll try to go over some basic ideas in this manual.
+
+* 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:
+
+#+begin_src emacs-lisp
+ (defun my-first-helm-command ()
+ (interactive)
+ (helm :sources 'my-source
+ :buffer "*helm my command*"))
+#+end_src
+
+~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.
+
+*** ~:input~
+
+The value for the ~:input~ keyword is used to pre-fill the input of
+the helm window.
+
+E.g. if the user is using helm to autocomplete and runs the
+autocomplete command, you could get the word at point and set ~:input~
+to that word. That word will then be pre-filled in the minibuffer.
+
+#+begin_src emacs-lisp
+ (defun my-autocomplete-command ()
+ (interactive)
+ (helm :sources 'my-source
+ :input (word-at-point)
+ :buffer "*helm my autocomplete command*"))
+#+end_src
+
+* Customizing Action Lists
+
+It's possible to change the default list of actions for various
+existing Helm commands. The actions are typically held in variables
+called ~helm-type-foo-actions~, for instance ~helm-type-file-actions~,
+so search apropos for those. Each action in the list is the usual cons
+of action label and action function.
+
+Another higher-level approach is to use a pre-defined function such as
+~helm-add-action-to-source~ or ~helm-add-action-to-source-if~. These
+functions accept an action label, action function, the source to
+modify (as a class symbol name, such as 'helm-source-ffiles), and for
+the latter, a predicate which determines if the action should be made
+available for the candidate under point.
+
+Also see ~helm-delete-action-from-source~, and
+~helm-source-add-action-to-source-if~.
+
+* 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~ :: Put candidates in a list
+
+- ~helm-source-in-buffer~ :: Put candidates in a buffer
+
+- ~helm-source-async~ :: Get candidates asynchronously using the
+ output of a process.
+
+- ~helm-source-dummy~ :: Use ~helm-pattern~ as candidate.
+
+- ~helm-source-in-file~ :: Get 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~
+
+Put candidates in a list
+
+#+begin_src emacs-lisp
+ (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*")
+#+end_src
+
+** ~helm-source-in-buffer~
+
+Put candidates in a buffer
+
+#+begin_src emacs-lisp
+ (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*")
+#+end_src
+
+** ~helm-source-async~
+
+Get candidates asynchronously using the output of a process.
+
+#+begin_src emacs-lisp
+ (helm :sources (helm-build-async-source "test2"
+ :candidates-process
+ (lambda ()
+ (start-process "echo" nil "echo" "a\nb\nc\nd\ne")))
+ :buffer "*helm async source*")
+#+end_src
+
+** ~helm-source-dummy~
+
+Use ~helm-pattern~ as candidate
+
+#+begin_src emacs-lisp
+ (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*")
+#+end_src
+
+** ~helm-source-in-file~
+
+Get candidates from the lines of a named file using
+~helm-source-in-buffer~.
+
+#+begin_src emacs-lisp
+ (helm :sources (helm-build-in-file-source
+ "test" "~/.emacs.d/init.el"
+ :action (lambda (candidate)
+ (let ((linum (with-helm-buffer
+ (get-text-property
+ 1 'helm-linum
+ (helm-get-selection nil 'withprop)))))
+ (find-file (with-helm-buffer
+ (helm-attr 'candidates-file)))
+ (goto-line linum))))
+ :buffer "*helm test*")
+
+#+end_src
+
+** 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 {{{kbd(C-h m)}}} or
+{{{kbd(C-c ?=)}}}.
+
+** Pre-filtering lines in a buffer
+
+Here's an example of an in-buffer source that pre-filters lines to
+those matching a certain regular expression. Then the pre-filtered
+lines are narrowed as the user types. This uses
+[[https://github.com/tarsius/hl-todo][hl-todo-mode]] which provide
+=hl-todo-regexp= but you could use any regexp to do the same thing.
+The =:init= function switches to the automatically created buffer,
+which is returned by ~(helm-candidate-buffer 'global)~, then it
+deletes uninteresting lines, after which Helm presents the remaining
+lines to the user. The =:get-line= function is changed to
+=buffer-substring= so that the properties are preserved in the
+=helm-buffer=.
+
+#+begin_src emacs-lisp
+ (defun helm-hl-todo-items ()
+ "Show `hl-todo'-keyword items in buffer."
+ (helm :sources (helm-build-in-buffer-source "hl-todo items"
+ :init (lambda ()
+ (with-current-buffer (helm-candidate-buffer 'global)
+ (insert (with-helm-current-buffer (buffer-string)))
+ (goto-char (point-min))
+ (delete-non-matching-lines hl-todo-regexp)))
+ :get-line #'buffer-substring)
+ :buffer "*helm hl-todo*"))
+#+end_src
+
+And it could be also written using the =:data= slot:
+
+#+begin_src emacs-lisp
+ (defun helm-hl-todo-items ()
+ "Show `hl-todo'-keyword items in buffer."
+ (helm :sources (helm-build-in-buffer-source "hl-todo items"
+ :data (current-buffer)
+ :candidate-transformer (lambda (candidates)
+ (cl-loop for c in candidates
+ when (string-match hl-todo-regexp c)
+ collect c))
+ :get-line #'buffer-substring)
+ :buffer "*helm hl-todo*"))
+#+end_src
+
+* Creating a Class
+
+** Create your own class inheriting from one of the main classes
+
+#+begin_src emacs-lisp
+ (defclass my-helm-class (helm-source-sync)
+ ((candidates :initform '("foo" "bar" "baz"))))
+
+ (helm :sources (helm-make-source "test" 'my-helm-class)
+ :buffer "*helm test*")
+
+#+end_src
+
+This is same as creating your source with:
+
+#+begin_src emacs-lisp
+ (helm :sources (helm-build-sync-source "test"
+ :candidates '("foo" "bar" "baz"))
+ :buffer "*helm test*")
+
+#+end_src
+
+** 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:
+
+#+begin_src emacs-lisp
+ (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*")
+
+#+end_src
+
+*** 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:
+
+ #+begin_src emacs-lisp
+ (defclass helm-override-test-fav (helm-source) ())
+ #+end_src
+
+2. 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.
+
+ #+begin_src emacs-lisp
+ (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))))
+
+ #+end_src
+
+3. Create now your main class inheriting from your new overriding
+ class
+
+ #+begin_src emacs-lisp
+ (defclass helm-test-fav (helm-source-in-file helm-type-file helm-override-test-fav)
+ ((candidates-file :initform "~/.fav")))
+ #+end_src
+
+ 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.
+
+#+begin_src emacs-lisp
+ (helm-make-source "test" 'my-class :action 'foo [...other slots])
+#+end_src
+
+** Write your own helm-type class
+
+You will find several examples in the =helm-types.el=[fn:3] 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.
+
+* Writing actions
+
+Actions are specified in the ~:action~ slot of your class, it is an
+alist composed of (ACTION_NAME . FUNCTION), it can be also a symbol
+defining a customizable action alist.
+
+#+begin_src elisp
+ (defcustom helm-source-foo
+ '(("Do this" . foo)
+ ("Do that" . bar))
+ "A customizable action list."
+ :group 'helm
+ :type '(alist :key-type string :value-type function))
+
+#+end_src
+
+It is recommended however to use ~helm-make-actions~ to define your
+actions easily, it allow also to create actions with condition, e.g
+
+#+begin_src emacs-lisp
+ (helm-make-actions "Do this"
+ 'foo
+ (lambda ()
+ (when (featurep 'something)
+ "Do that"))
+ 'bar)
+#+end_src
+
+* Writing persistent actions
+
+The way to specify persistent-action is to use the slot
+~:persistent-action~ to specify the function Helm will run when using
+{{{kbd(C-j)}}} or {{{kbd(C-z)}}}. If you don't specify this helm will
+run the first action of the action.
+
+You can also specify additional persistent actions, which must be
+bound to keys other than {{{kbd(C-j)}}} or {{{kbd(C-z)}}}.
+
+#+begin_src emacs-lisp
+ (defun helm-foo-persistent-action ()
+ "Bind a new persistent action 'foo-action to foo function.
+ foo function is an action function called with one arg candidate."
+ (interactive)
+ (with-helm-alive-p
+ ;; never split means to not split the helm window when executing
+ ;; this persistent action. If your source is using full frame you
+ ;; will want your helm buffer to split to display a buffer for the
+ ;; persistent action (if it needs one to display something).
+ (helm-attrset 'foo-action '(foo . never-split))
+ (helm-execute-persistent-action 'foo-action)))
+#+end_src
+
+Then you bind your new persistent action to something else than
+{{{kbd(C-j)}}} or {{{kbd(C-z)}}}.
+
+* COMMENT Conventions
+
+** TODO Class names
+
+Class names are currently a mess. Need to come up with a better
+convention.
+
+* Export Setup :noexport:
+
+#+setupfile: doc-setup.org
+# #+options: H:4
+
+#+export_file_name: helm-devel.texi
+
+#+texinfo_dir_category: Emacs Add-ons
+#+texinfo_dir_title: Helm Developer's Guide: (helm-devel)
+#+texinfo_dir_desc: Helm Developer's Guide
+
+* Footnotes
+
+[fn:1] http://blog.codinghorror.com/learn-to-read-the-source-luke/
+
+[fn:2] https://github.com/emacs-helm/helm/blob/master/helm-info.el
+
+[fn:3] https://github.com/emacs-helm/helm/blob/master/helm-types.el
+
+#+EXCLUDE_TAGS: TOC_3