Recently, I have seen a lot of Emacs users showing interest in Ivy. Most of them already know Helm or Ido. When someone asked Helm or Ivy? on reddit, I thought that I could give my opinion on Ivy, since I am an ex-Helm zealot.
Helm and Ivy are completion frameworks. It means they are tools that the Emacs ecosystem can use to narrow the field of possibilities after a given user input. The most common example that comes to mind is searching for files. Helm and Ivy helps the user to quickly search for files.
They are frameworks. It means they can be used by much more complex
commands that needs narrowing or completion. As an example, Helm has a
command that emulate the google search bar, and get google suggests as
you type (helm-google-suggest
).
Ivy and Helm have very similar goals, but the approach they take is very different.
Here I want to compare the tools, from a user point of view. What
I mean by user perspective is that I want to compare Ivy and Helm
without knowing how they works internally. I do not know enough
elisp
to be able to compare their internals. But I used them both,
and can give you my feedback on how I, as a user, felt the
difference between the tools. And finally, why I switched from Helm to
Ivy.
I want to start with Helm. When I used Spacemacs, I learned the Helm way. I learned how to use it, how to customize it, and how to tailor it to my needs. I guess I would qualify as a medium-level Helm user. I have read this post and this one and this wiki too, and used it every day for one year.
Helm is a very mature tool. According to its git commit history, work on Helm really started around 2009. At the time of writing, the official Helm git repository has 26k lines of emacs-lisp code.
git clone https://github.com/emacs-helm/helm.git
cd helm
cat *.el | wc -l
# => 26431
This does not take into account the 142 packages that a query for “helm” returns on MELPA. You can do anything with Helm1.
Its main strength is that you can integrate and wrap many Emacs behavior with it. You can build an interface totally centered around Helm, just like Spacemacs did. It allows for a really really consistent interface. Everything goes through Helm.
You can search files, search buffer, search colors, search project, search recently edited files, search system process, search music, search the internet, search completion, search snippets, search regexp, search commands, documentation, a-propos, infos, … You can manage your project with Helm-projectile, a nice wrapping around Projectile. You can generate gitignore with gitignore.io. You can manage your bibliography with Helm-bibtex. You can browse Firefox bookmarks.
You can do anything with Helm.
One feature I want to highlight, based on feedbacks from tuhdo here on Reddit, is that Helm does not use the minibuffer2, while Ivy does. So it can be configured to open always in the currently opened window. It is a very neat feature for users with large or ultrawide monitors. Eyes do not have to travel back and forth to the minibuffer: completion results are always printed in the same window.
The end result is a very convenient tool to use, as the great majority
of Spacemacs users can tell you. The major drawback it has is that
it is a big behemoth size package. I think it is an elisp
exploit
from the devs to have succeeded in making Helm a very fast tool
despite its size.
And yet it sometimes feels like an overblown solution to simple
problems. It can be clumsy to configure. It can sometimes have strange
behaviors, causing lags or make Emacs use 100% of a core, even for
simple queries. Maybe an expert Helm user reading this can put those
lags on me not being an elisp
expert, but still. Despite having used
it for more than a year, I did not find the way to make it more
stable. I think it tells something about the “overblown problem” I was
talking about.
You can do anything with Helm. But you don’t need to. Just because you can does not mean you should. After a year of using it, I can tell you that I have only used a third or less of Helm’s abilities. Some functions I thought were great, I re-discovered by reading this post yesterday. Most of the time, I used simple commands to switch buffers, or list files.
Helm is only a package for completion, just like ido or ivy. It may be easy to use, once someone has gone through the trouble of setting it up, but it’s hard to get it to do exactly what you want. Some people are fine with black boxes, as long as they give them nice things, I’m not.
— abo-abo, developper of Ivy, on the “Why not Helm ?” issue, here.
Ivy strives for minimalism, simplicity, customizability and discoverability. Those four adjectives tells a lot about the paradigm difference between the two. Read the introduction of the Ivy documentation to have a good idea of what Ivy strives for.
At the time of writing, Ivy only has 3.4k lines of code. The ecosystem built around it — ie Swiper and Counsel — is 7.5k lines of code long.
git clone https://github.com/abo-abo/swiper.git
cd swiper
## Only ivy ?
cat ivy.el | wc -l
# => 3442
## count lines of code into the whole swiper ecosystem
cat *.el | wc -l
# => 7526
Ivy is really simple to use. Here is my whole setup.
(use-package ivy :ensure t
:diminish (ivy-mode . "")
:bind
(:map ivy-mode-map
("C-'" . ivy-avy))
:config
(ivy-mode 1)
;; add ‘recentf-mode’ and bookmarks to ‘ivy-switch-buffer’.
(setq ivy-use-virtual-buffers t)
;; number of result lines to display
(setq ivy-height 10)
;; does not count candidates
(setq ivy-count-format "")
;; no regexp by default
(setq ivy-initial-inputs-alist nil)
;; configure regexp engine.
(setq ivy-re-builders-alist
;; allow input not in order
'((t . ivy--regex-ignore-order))))
Ivy is unobtrusive. It does not want you to integrate everything with it. It simply is the power house of your completion. You cannot do everything that the Helm ecosystem does with Ivy. So why did I switched to Ivy ?
Despite being minimalistic, I was able to replace most of my common Helm use cases with Ivy. Because Ivy is so minimalistic, abo-abo built a package on top of it called Counsel. Counsel offers you many many tricks that you might like from Helm.
You can switch buffers, search files, get project-wide search and replace, get Projectile integration, search recently edited files, search Emacs command, search documentation, search keybindings, browse the kill-ring …
Let me describe how I replaced Helm with Ivy. Here is an overview
of the most common Helm commands I needed to replace with Ivy. It was
basically the functions I used all the time. I ivy-switch-buffer
like
three times per minute. I used helm-swoop
fifty times a day, and
swiper
is just as good if not better. It is faster. For huge files,
Counsel has counsel-grep-or-swiper
. I have tested it on really
really huge made up files (1M lines or so). It did not tremble.
Helm | Ivy | What ? |
---|---|---|
helm-mini | ivy-switch-buffer | search for currently opened buffers |
helm-recentf | counsel-recentf | search for recently edited files |
helm-find-files | counsel-find-files | search files starting from ./ |
helm-ag | counsel-ag | search regexp occurence in current project |
helm-grep-do-git-grep | counsel-git-grep | search regexp in current project |
helm-swoop | swiper | search string interactively in current buffer |
helm-show-kill-ring | counsel-yank-pop | search copy-paste history |
helm-projectile | counsel-projectile | search project and file in it |
helm-ls-git-ls | counsel-git | search file in current git project |
helm-themes | counsel-load-theme | switch themes |
helm-descbinds | counsel-descbinds | describe keybindings and associated functions |
helm-M-x | counsel-M-x | enhanced M-x command |
I think you can see that Ivy based commands have nothing to be ashamed of. They can replace every common use cases of Helm. I am not saying that you can do everything Helm does with Ivy. But it is already very convenient. And as I said, I don’t need to do everything Helm does.
To speak of completion paradigm, the difference is not that obvious between Helm and Ivy. What I can tell you, as a user, is that Ivy feels less cluttered, snappier, more intuitive, self-explanatory and really understandable. Every completion is predictable.
In the end, it really is a matter of personal taste. To me the “Ivy or Helm” debate is very similar to debates likes Emacs or Spacemacs, Emacs or an IDE, C or Java, minimalistic or full-blown. Thelonious or Duke. Van Der Rohe or Gaudi.
With Helm you get a huge package, a full list of features you will never use, a whole bunch of functions you will use occasionally, and a short list of functions you will use fifty times an hour. With Ivy you get a small package with only essentials functions that does not get in your way, and that you can extend easily through Counsel or very simple functions:
(ivy-read "Pick:" (mapcar #'number-to-string (number-sequence 1 10)))
With Helm:
(helm
:sources
(helm-build-sync-source "one-to-ten"
:candidates
(mapcar #'number-to-string (number-sequence 1 10))
:fuzzy-match t)
:buffer
"*helm one-to-ten*")
Or the simpler form:
(helm-comp-read "Pick:" (mapcar #'number-to-string (number-sequence 1 10)))
Helm makes a lot of choices for the users. Ivy lets them tailor it to their needs. Helm uses a lot of memory to be very fast. Ivy stays simple to be very fast. Helm is mature. Ivy is young. Helm offers consistency across Emacs. Ivy offers simplicity and predictability. Helm ask you a rather involved setup. Ivy works out of the box.
I am a little biased towards Ivy, since I use it. It suits my tastes better. But as a user, Helm and Ivy does not feel that different. They are both great packages, that chose a very different approach for the same goal.