2016-03-01 - Helm, an incremental framework

Table of Contents

What is Helm?

Helm is a framework for incrementally narrowing a list of "things" as you type.

There's a great tutorial (with screencasts!) here: http://tuhdo.github.io/helm-intro.html

Why?

There are a lot of places where this make sense:

  • Buffers
  • Files
  • Documentation
  • Searches
  • Emacs Help

Helm completion changes the default minibuffer completion in Emacs. Since this change is subtle in key bindings yet novel in visual feedback, new Helm users have been tripped up!

Why the change? Because Emacs default completion is minibuffer centric, whereas Helm is completion-window centric.

In Emacs default, <tab> narrows selections in the minibuffer. Typing more keys followed by <tab> keeps narrowing the selections in the minibuffer. At anytime, hitting <enter> selects the item from the minibuffer. All the interactivity happens in the minibuffer.

In Helm, on the other-hand, all the interactivity happens in the completion window (and not in the minibuffer). The action takes place at currently highlighted item in the completion window. Typing new keystrokes narrows the completion window. Finish typing until the desired item is highlighted or navigate to it using <C-n>. Hitting <enter> at anytime selects the highlighted item from the completion window.

How do you install and use Helm?

Installing is as easy as installing the base package:

(package-refresh-contents)
(package-install 'helm)

Then you can use pieces of helm piecemeal, or enable the entire thing with:

(require 'helm-config)
M-x helm-mode

Note that helm-mode only enables helm for places where Emacs would use completion already, it doesn't automatically use it everywhere.

For example though, if you only wanted to use helm-M-x instead of the regular M-x bindings, you would do:

(global-set-key (kbd "M-x") #'helm-M-x)

I only want to play around with it!

If you don't want to install it and change your settings, you can try out helm by using the nice emacs-helm.sh script in the helm repo:

$ git clone git@github.com:emacs-helm/helm.git
Cloning into 'helm'...
remote: Counting objects: 22813, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 22813 (delta 1), reused 0 (delta 0), pack-reused 22806
Receiving objects: 100% (22813/22813), 20.72 MiB | 1.50 MiB/s, done.
Resolving deltas: 100% (14794/14794), done.
Checking connectivity... done.
$ cd helm
$ make
$ ./emacs-helm.sh

This will pop open a buffer with helm pre-configured, useful for learning or trying to diagnose a bug with helm.

Some defaults I recommend

;; The default "C-x c" is quite close to "C-x C-c", which quits Emacs.
;; Changed to "C-c h". Note: We must set "C-c h" globally, because we
;; cannot change `helm-command-prefix-key' once `helm-config' is loaded.
(global-set-key (kbd "C-c h") 'helm-command-prefix)
(global-unset-key (kbd "C-x c"))

(helm-mode 1)

Let's use TAB to "execute persistent actions", which changes depending on what type of helm buffer is open. For example, with helm-find-files it shows the file, for helm-buffers it shows the buffer. For helm-apropos it shows the *help* buffer for a symbol.

(define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action)
;; make TAB works in terminal, C-i is tha same as TAB
(define-key helm-map (kbd "C-i") 'helm-execute-persistent-action)

When a helm buffer is open, we can see what we can actually do with the thing using C-z

(define-key helm-map (kbd "C-z") 'helm-select-action)

There are some nice navigation settings:

;; open helm buffer inside current window, don't occupy the entire other window
(setq helm-split-window-in-side-p t)
;; move to end or beginning of source when reaching top or bottom of source.
(setq helm-move-to-line-cycle-in-source t)

I also recommend you set up "helm-resume", which resumes your last helm session (great for searching)

(global-set-key (kbd "C-M-z") #'helm-resume)

Things you can do in all helm buffers

There are a few things that are common to almost all helm buffers.

Hitting C-h m while helm is open will open the "help" page for that particular helm command.

C-! will suspend helm's updating until you hit it a second time. This is useful if you have something like.

Wherever the cursor was when you invoked helm, if you want to "slurp" the word into the helm completion, you can hit C-w, this is useful when you want to search things for the symbol at point for instance.

A word about "fuzziness"

While helm is "strict" matching by default, you can configure certain parts of it to be fuzzy, if you want, here's the configuration I use for fuzziness:

(setq helm-recentf-fuzzy-match t
      helm-locate-fuzzy-match nil ;; locate fuzzy is worthless
      helm-M-x-fuzzy-match t
      helm-buffers-fuzzy-matching t
      helm-semantic-fuzzy-match t
      helm-apropos-fuzzy-match t
      helm-imenu-fuzzy-match t
      helm-lisp-fuzzy-completion t
      helm-completion-in-region-fuzzy-match t)

Configuring the way Helm looks

By default helm will use 50% of the buffer height. This may not be what you want, so you can configure helm to be as large or small as you want

(helm-autoresize-mode)
;; These numbers are percentages
(setq helm-autoresize-min-height 20
      helm-autoresize-max-height 40)

What things does Helm provide?

This is a subset of the useful functions that helm provides:

  • helm-find-files
  • helm-buffers-list
  • helm-M-x
  • helm-mini
  • helm-occur
  • helm-apropos
  • helm-man-woman
  • helm-info-emacs
  • helm-show-kill-ring
  • helm-all-mark-rings
  • helm-semantic-or-imenu
  • helm-world-time
  • helm-locate

All of these also support selecting any of the candidates and executing some sort of action on them. The action depends on the type of helm buffer.

Let's see what they look like!

helm-find-files

Pretty standard, though it behaves a little differently than something like 'ido'. If you select one or more candidates you can do a bunch of things on the files. Some of the most useful:

Command Action
M-D delete file(s)
M-C copy file(s)
M-R rename (move) file(s)
C-s grep selected file(s)
C-l go up one directory
C-c / file-files recursively from where you are

Screenshot: helm-find-files.png

helm-buffers-list

Helm can also show and limit buffers, in a much more powerful way than something like ido-buffers. If you select one or more buffers you can do:

Command Action
M-D kill buffer
C-= diff buffer with file

helm-buffers-list.png

helm-M-x

helm-M-x replaces the regular M-x command with one that in "helmized". That way you can do fuzzy matching and multiple matches kind of like smex.

helm-m-x.png

helm-mini

Helm-mini is a neat customizable dashboard that lets you pick what sort of things should be shown, I bind mine to C-x C-r and use these sources:

(setq helm-mini-default-sources '(helm-source-buffers-list
                                  helm-source-recentf
                                  helm-source-bookmarks
                                  helm-source-buffer-not-found))

helm-occur

Helm-occur shows the occurrences of some text in the page, you can then hit TAB to show the occurrence, or hit RET to jump to it.

helm-occur.png

helm-apropos

One of the most helpful ways to find commands, functions, and variables in Emacs. I bind mine to C-h a. If you are hovering over a command you can hit TAB to show the documentation for it without leaving helm.

helm-apropos.png

helm-man-woman

Look up man pages from helm, super useful, I recommend C-h m

helm-man-woman.png

helm-info-emacs

Search through the Emacs manual with helm! I bind this to C-h r

helm-info.png

helm-show-kill-ring

Shows the contents of your kill ring, really great for things you copied a few copies ago and want to use. I bind mine to M-y.

helm-kill-ring.png

helm-all-mark-rings

If you use Emacs marks for popping and pushing, you can look through your buffer-local and global mark rings with this. I usually bind mind to C-h SPC

helm-mark-ring.png

helm-semantic-or-imenu

Imenu is a really cool feature, and semantic kicks it up a notch depending on the type of file you're in, for example, an org-mode file shows imenu

helm-imenu.png

While a .java file shows the semantic overview

helm-semantic.png

If you hit TAB for any it shows you the result without moving there, great way to see what it looks like without "picking" it.

helm-world-time

Finally, because I work at an all-remote company, I frequently need to know what time it is other places, so I do something like

;; List of times to show in helm-world-time
(setq display-time-world-list '(("PST8PDT" "Los Altos")
                                ("America/Denver" "Denver")
                                ("EST5EDT" "Boston")
                                ("UTC" "UTC")
                                ("Europe/London" "London")
                                ("Europe/Amsterdam" "Amsterdam")
                                ("Asia/Bangkok" "Bangkok")
                                ("Asia/Tokyo" "Tokyo")
                                ("Australia/Sydney" "Sydney")))

Then, helm-world-times, which I bind to C-h t shows

helm-time.png

helm-locate

Helm-locate runs the locate command on OSX/Linux machines and makes the files found candidates.

Neat Helm extensions and packages

There are a bunch of helm extensions, here are some of the most useful:

helm-projectile

Provides interaction with projectile, so useful! I bind the main helm-projectile function to C-x f.

helm-descbinds

Instead of using Emacs' built in describe-bindings, this puts the bindings in a nice helm buffer, which makes searching them great.

helm-swoop

This is sort of like helm-occur on steroids, you can swoop multiple files. Best described by seeing the gif on the page.

helm-dash

If you've ever used the Dash tool for documentation, helm-dash is a great tool that allows you to search dash docsets from helm.

helm-ls-git

This gives a nice overview of files that have different git "statuses" for them, I usually bind this to C-x C-d. It can be used as a poor-mans helm-projectile in git repos (there's one for mercurial too).

Writing a custom Helm source/input

Helm isn't just for the built in things, you can define your own helm input pretty easily.

From the helm docs:

(helm :sources (helm-build-sync-source "test"
                 :candidates '(foo foa fob bar baz)
                 :fuzzy-match t)
      :buffer "*helm test*")

Here's an example I use all the time because I can't remember what HTTP response codes mean:

(defvar helm-httpstatus-source
  '((name . "HTTP STATUS")
    (candidates . (("100 Continue") ("101 Switching Protocols")
                   ("102 Processing") ("200 OK")
                   ("201 Created") ("202 Accepted")
                   ("203 Non-Authoritative Information") ("204 No Content")
                   ("205 Reset Content") ("206 Partial Content")
                   ("207 Multi-Status") ("208 Already Reported")
                   ("300 Multiple Choices") ("301 Moved Permanently")
                   ("302 Found") ("303 See Other")
                   ("304 Not Modified") ("305 Use Proxy")
                   ("307 Temporary Redirect") ("400 Bad Request")
                   ("401 Unauthorized") ("402 Payment Required")
                   ("403 Forbidden") ("404 Not Found")
                   ("405 Method Not Allowed") ("406 Not Acceptable")
                   ("407 Proxy Authentication Required") ("408 Request Timeout")
                   ("409 Conflict") ("410 Gone")
                   ("411 Length Required") ("412 Precondition Failed")
                   ("413 Request Entity Too Large")
                   ("414 Request-URI Too Large")
                   ("415 Unsupported Media Type")
                   ("416 Request Range Not Satisfiable")
                   ("417 Expectation Failed") ("418 I'm a teapot")
                   ("421 Misdirected Request")
                   ("422 Unprocessable Entity") ("423 Locked")
                   ("424 Failed Dependency") ("425 No code")
                   ("426 Upgrade Required") ("428 Precondition Required")
                   ("429 Too Many Requests")
                   ("431 Request Header Fields Too Large")
                   ("449 Retry with") ("500 Internal Server Error")
                   ("501 Not Implemented") ("502 Bad Gateway")
                   ("503 Service Unavailable") ("504 Gateway Timeout")
                   ("505 HTTP Version Not Supported")
                   ("506 Variant Also Negotiates")
                   ("507 Insufficient Storage") ("509 Bandwidth Limit Exceeded")
                   ("510 Not Extended")
                   ("511 Network Authentication Required")))
    (action . message)))

(defun helm-httpstatus ()
  (interactive)
  (helm-other-buffer '(helm-httpstatus-source) "*helm httpstatus*"))

Helm also has bindings for asynchronously building candidates (for instance, if you are searching a very large data set).

Author: Lee Hinman

Created: 2016-03-02 Wed 16:24

Validate