2016-03-01 - Helm, an incremental framework
Table of Contents
- What is Helm?
- Why?
- How do you install and use Helm?
- I only want to play around with it!
- Some defaults I recommend
- Things you can do in all helm buffers
- A word about "fuzziness"
- Configuring the way Helm looks
- What things does Helm provide?
- Neat Helm extensions and packages
- Writing a custom Helm source/input
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-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-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-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-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-man-woman
Look up man pages from helm, super useful, I recommend C-h m
helm-info-emacs
Search through the Emacs manual with helm! I bind this to C-h r
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-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-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
While a .java
file shows the semantic overview
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-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).