helm-ido-like-guide icon indicating copy to clipboard operation
helm-ido-like-guide copied to clipboard

Guide to get an intuitive and more ido-like helm interface in Emacs. Recommends configurations and packages to improve helms default interface.

  • Helm I do like. Yes... :PROPERTIES: :SUMMARY: Configuration guide for the helm package of Emacs :END:

[[./yoda.jpg]]

This guide was intended for people which used ido in the past and wanted helm to behave more like ido (ido + flx-ido + ido-vertical-mode + smex). Now this guide inlcudes many snippets which are useful for helm usage in general and has become more of collection of configuration tips and recommended packages to improve helms default interface.

For now the configuration snippets and packages will provide the following features for you:

  • Always pop up at the bottom
  • Nice search through an overview of matches with helm-swoop
  • Input in header line and hide the minibuffer
  • Show helm source headers only when necessary
  • No mode-lines above the helm buffer
  • Flx support with gc adjustment to improve speed.
  • DEL and RETURN for file navigation like in ido
  • Remove the dots for current and parent directory in helm file navigation in non directory selections
  • Smex support for =helm-M-x= , to adjust search results for recent and most frequent used commands
  • Remember last candidates for more sources with =helm-adaptive= (Not recommended for now)

For more great helm-hacks have a look at [[https://github.com/cute-jumper/helm-ext][helm-ext]].

NOTE

This is a work in progress. If you encounter any problems let me know. Some of this is my own work, but most is based on work of others that I summarized in this tutorial. Everyone has his own opinions what is considered an improvement because of that I have splitted the guide into parts where each snippet should be independent of others, so you can just pick what you like.

** Screenshots

[[./screenshot.png]] Theme: [[https://github.com/edran/hc-zenburn-emacs][hc-zenburn]] with some adjustments.

** Installing helm and friends

If you haven't already go and install helm, helm-swoop, helm-flx, helm-fuzzier, smex and helm-smex. You can do it quickly by evaluating the following snippet.

#+BEGIN_SRC emacs-lisp (require 'package) ;; Note that certificate verfication in Emacs 24.4 needs some ;; manual adjustments if you want to be really secure. ;; Read this for more info on this: https://glyph.twistedmatrix.com/2015/11/editor-malware.html (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) (package-refresh-contents) (mapcar #'(lambda (package) (unless (package-installed-p package) (package-install package))) '(helm helm-swoop helm-flx helm-fuzzier smex helm-smex dash)) #+END_SRC

Now either follow this guide or jump to [[*Last%20Steps][Last Steps]] if you want see how to activate all the snippets provided by this guide at once.

** Initial setup

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defun helm-ido-like-activate-helm-modes () (require 'helm-config) (helm-mode 1) (helm-flx-mode 1) (helm-fuzzier-mode 1)) #+END_SRC

Now don't forget to bind keys for the helm-smex commands: #+BEGIN_SRC emacs-lisp (global-set-key [remap execute-extended-command] #'helm-smex) (global-set-key (kbd "M-X") #'helm-smex-major-mode-commands) #+END_SRC

** Appearance

The following snippet will configure helm to always pop up at the bottom.

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defun helm-ido-like-load-ido-like-bottom-buffer () ;; popup helm-buffer at the bottom (setq helm-split-window-in-side-p t) (add-to-list 'display-buffer-alist '("\\\*helm.*\\*\\'" (display-buffer-in-side-window) (window-height . 0.4))) (add-to-list 'display-buffer-alist '("\\\helm help\\'" (display-buffer-pop-up-window)))

;; same for helm swoop (setq helm-swoop-split-with-multiple-windows nil helm-swoop-split-direction 'split-window-vertically helm-swoop-split-window-function 'helm-default-display-buffer) ;; dont display the header line (setq helm-display-header-line nil) ;; input in header line (setq helm-echo-input-in-header-line t) (add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe)) #+END_SRC

With newer helm versions you can also use the following instead:

#+BEGIN_SRC (with-eval-after-load 'helm (setq helm-always-two-windows nil) (setq helm-display-buffer-default-height 15) (setq helm-default-display-buffer-functions '(display-buffer-in-side-window))) #+END_SRC

The modelines above the helm buffer are not useful and hiding them will make helm look nicer in my opinion, too. You can do that with the following code.

[[http://emacs.stackexchange.com/a/15250/9198][Reference]]

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defvar helm-ido-like-bottom-buffers nil "List of bottom buffers before helm session started. Its element is a pair of buffer-name' and mode-line-format'.")

(defun helm-ido-like-bottom-buffers-init () (setq-local mode-line-format (default-value 'mode-line-format)) (setq helm-ido-like-bottom-buffers (cl-loop for w in (window-list) when (window-at-side-p w 'bottom) collect (with-current-buffer (window-buffer w) (cons (buffer-name) mode-line-format)))))

(defun helm-ido-like-bottom-buffers-hide-mode-line () (mapc (lambda (elt) (with-current-buffer (car elt) (setq-local mode-line-format nil))) helm-ido-like-bottom-buffers))

(defun helm-ido-like-bottom-buffers-show-mode-line () (when helm-ido-like-bottom-buffers (mapc (lambda (elt) (with-current-buffer (car elt) (setq-local mode-line-format (cdr elt)))) helm-ido-like-bottom-buffers) (setq helm-ido-like-bottom-buffers nil)))

(defun helm-ido-like-helm-keyboard-quit-advice (orig-func &rest args) (helm-ido-like-bottom-buffers-show-mode-line) (apply orig-func args))

(defun helm-ido-like-hide-modelines () ;; hide The Modelines while Helm is active (add-hook 'helm-before-initialize-hook #'helm-ido-like-bottom-buffers-init) (add-hook 'helm-after-initialize-hook #'helm-ido-like-bottom-buffers-hide-mode-line) (add-hook 'helm-exit-minibuffer-hook #'helm-ido-like-bottom-buffers-show-mode-line) (add-hook 'helm-cleanup-hook #'helm-ido-like-bottom-buffers-show-mode-line) (advice-add 'helm-keyboard-quit :around #'helm-ido-like-helm-keyboard-quit-advice)) #+END_SRC

If you like you can hide helms own mode-line as well: #+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defun helm-ido-like-hide-helm-modeline-1 () "Hide mode line in `helm-buffer'." (with-helm-buffer (setq-local mode-line-format nil)))

(defun helm-ido-like-hide-helm-modeline () (fset 'helm-display-mode-line #'ignore) (add-hook 'helm-after-initialize-hook 'helm-ido-like-hide-helm-modeline-1)) #+END_SRC

The header lines for the sources are only useful if there are more then a single source. The following snippet will hide the header line if there is only one.

[[http://www.reddit.com/r/emacs/comments/2z7nbv/lean_helm_window/][Reference]]

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el

(defvar helm-ido-like-source-header-default-background nil) (defvar helm-ido-like-source-header-default-foreground nil) (defvar helm-ido-like-source-header-default-box nil)

(defun helm-ido-like-toggle-header-line () ;; Only Show Source Headers If More Than One (if (> (length helm-sources) 1) (set-face-attribute 'helm-source-header nil :foreground helm-ido-like-source-header-default-foreground :background helm-ido-like-source-header-default-background :box helm-ido-like-source-header-default-box :height 1.0) (set-face-attribute 'helm-source-header nil :foreground (face-attribute 'helm-selection :background) :background (face-attribute 'helm-selection :background) :box nil :height 0.1)))

(defun helm-ido-like-header-lines-maybe () (setq helm-ido-like-source-header-default-background (face-attribute 'helm-source-header :background)) (setq helm-ido-like-source-header-default-foreground (face-attribute 'helm-source-header :foreground)) (setq helm-ido-like-source-header-default-box (face-attribute 'helm-source-header :box)) (add-hook 'helm-before-initialize-hook 'helm-ido-like-toggle-header-line))

#+END_SRC

If you like you can change the background color of the helm-buffer.

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defvar helm-ido-like-bg-color (face-attribute 'default :background))

(defun helm-ido-like-setup-bg-color-1 () (with-helm-buffer (make-local-variable 'face-remapping-alist) (add-to-list 'face-remapping-alist `(default (:background ,helm-ido-like-bg-color)))))

(defun helm-ido-like-setup-bg-color () (add-hook 'helm-after-initialize-hook 'helm-ido-like-setup-bg-color-1))

#+END_SRC

** File Navigation

The following snippet will reconfigure the behaviour of keys in helm file navigation buffers.

Backspace goes to the upper folder if you are not inside a filename, and Return will select a file or navigate into the directory if it is one.

[[http://emacs.stackexchange.com/a/7896/9198][Reference]]

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defun helm-ido-like-find-files-up-one-level-maybe () (interactive) (if (looking-back "/" 1) (call-interactively 'helm-find-files-up-one-level) (delete-char -1)))

(defun helm-ido-like-find-files-navigate-forward (orig-fun &rest args) "Adjust how helm-execute-persistent actions behaves, depending on context." (let ((sel (helm-get-selection))) (if (file-directory-p sel) ;; the current dir needs to work to ;; be able to select directories if needed (cond ((and (stringp sel) (string-match "\.\'" (helm-get-selection))) (helm-maybe-exit-minibuffer)) (t (apply orig-fun args))) (helm-maybe-exit-minibuffer))))

(defun helm-ido-like-load-file-nav () (advice-add 'helm-execute-persistent-action :around #'helm-ido-like-find-files-navigate-forward) ;; is not bound in helm-map by default (define-key helm-map (kbd "") 'helm-maybe-exit-minibuffer) (with-eval-after-load 'helm-files (define-key helm-read-file-map (kbd "") 'helm-ido-like-find-files-up-one-level-maybe) (define-key helm-read-file-map (kbd "DEL") 'helm-ido-like-find-files-up-one-level-maybe) (define-key helm-find-files-map (kbd "") 'helm-ido-like-find-files-up-one-level-maybe) (define-key helm-find-files-map (kbd "DEL") 'helm-ido-like-find-files-up-one-level-maybe)

  (define-key helm-find-files-map (kbd "<return>") 'helm-execute-persistent-action)
  (define-key helm-read-file-map (kbd "<return>") 'helm-execute-persistent-action)
  (define-key helm-find-files-map (kbd "RET") 'helm-execute-persistent-action)
  (define-key helm-read-file-map (kbd "RET") 'helm-execute-persistent-action)))

#+END_SRC

And this snippet will remove the dots in helm file navigation

[[https://github.com/TheBB/spacemacs-layers/tree/master/no-dots][Reference]]

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el

(defvar helm-ido-like-no-dots-whitelist '("Helm file completions") "List of helm buffers in which to show dot directories.")

(defun helm-ido-like-no-dots-display-file-p (file) ;; in a whitelisted buffer display all but the relative path to parent dir (or (and (member helm-buffer helm-ido-like-no-dots-whitelist) (not (string-match "\(?:/\|\\\)\\.\\{2\\}\\'" file))) ;; in all other buffers display all files but the two relative ones (not (string-match "\\(?:/\\|\\\)\.\{1,2\}\'" file))))

(defun helm-ido-like-no-dots-auto-add (&rest args) "Auto add buffers which want to read directory names to the whitelist." (if (eq (car (last args)) 'file-directory-p) (add-to-list 'helm-ido-like-no-dots-whitelist (format "helm-mode-%s" (helm-symbol-name (or (helm-this-command) this-command))))))

(defun helm-ido-like-no-dots () (require 'cl-lib) (advice-add 'helm-ff-filter-candidate-one-by-one :before-while 'helm-ido-like-no-dots-display-file-p) (advice-add 'helm--generic-read-file-name :before 'helm-ido-like-no-dots-auto-add)) #+END_SRC

** Improve Flx support

And you can increase flx speed (I have not benchmarked it myself) by adjusting the garbage collection setting. In addition to that the following snippet advices the helm source function to enable the flx fuzzy match in most sources but file completions(you still have fuzzy matching from helm) and async sources.

[[http://bling.github.io/blog/2016/01/18/why-are-you-changing-gc-cons-threshold/][Reference]] [[https://github.com/emacs-helm/helm/issues/145#issuecomment-151953381][Reference]]

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defvar helm-ido-like-user-gc-setting nil)

(defun helm-ido-like-higher-gc () (setq helm-ido-like-user-gc-setting gc-cons-threshold) (setq gc-cons-threshold most-positive-fixnum))

(defun helm-ido-like-lower-gc () (setq gc-cons-threshold helm-ido-like-user-gc-setting))

(defun helm-ido-like-helm-make-source (f &rest args) (let ((source-type (cadr args))) (unless (or (memq source-type '(helm-source-async helm-source-ffiles)) (eq (plist-get args :filtered-candidate-transformer) 'helm-ff-sort-candidates) (eq (plist-get args :persistent-action) 'helm-find-files-persistent-action)) (nconc args '(:fuzzy-match t)))) (apply f args))

(defun helm-ido-like-load-fuzzy-enhancements () (add-hook 'minibuffer-setup-hook #'helm-ido-like-higher-gc) (add-hook 'minibuffer-exit-hook #'helm-ido-like-lower-gc) (advice-add 'helm-make-source :around 'helm-ido-like-helm-make-source))

#+END_SRC

With recent helm version there is a problem for file navigation, when helm-fuzzier is activated. Because of that it's better to deactivate it for file completions

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el (defun helm-ido-like-fuzzier-deactivate (&rest _) (helm-fuzzier-mode -1))

(defun helm-ido-like-fuzzier-activate (&rest _) (unless helm-fuzzier-mode (helm-fuzzier-mode 1)))

(defun helm-ido-like-fix-fuzzy-files () (add-hook 'helm-find-files-before-init-hook #'helm-ido-like-fuzzier-deactivate) (advice-add 'helm--generic-read-file-name :before #'helm-ido-like-fuzzier-deactivate) (add-hook 'helm-exit-minibuffer-hook #'helm-ido-like-fuzzier-activate) (add-hook 'helm-cleanup-hook #'helm-ido-like-fuzzier-activate) (advice-add 'helm-keyboard-quit :before #'helm-ido-like-fuzzier-activate)) #+END_SRC

** Helm Adaptive

This will offer last choosen candidates first for more sources, with support for flx.

I only use it to remember =describe-function= and =describe-variable=, if you want to use it for other sources add them like shown below.

Warning: After some usage it stopped working correctly and sorted the results badly. I can live without it, but maybe I will try to fix it later.

[[https://github.com/emacs-helm/helm/issues/1228][Reference]]

#+BEGIN_SRC emacs-lisp (with-eval-after-load 'helm-adaptive (defcustom helm-adaptive-enabled-sources '() "List of Helm Source names for which helm-adaptive will remember history." :type '(repeat string) :group 'helm-adapt)

;; Remember history for these sources add more sources here if you like (add-to-list 'helm-adaptive-enabled-sources "describe-function") (add-to-list 'helm-adaptive-enabled-sources "describe-variable")

;; Clobber helm's implementation (defun helm-adapt-use-adaptive-p (&optional source-name) "Return current source only if it use adaptive history, nil otherwise." (when helm-adaptive-mode (let* ((source (or source-name (helm-get-current-source))) (adapt-source (when (listp source) (or (assoc-default 'filtered-candidate-transformer (assoc (assoc-default 'type source) helm-type-attributes)) (assoc-default 'candidate-transformer (assoc (assoc-default 'type source) helm-type-attributes)) (assoc-default 'filtered-candidate-transformer source) (assoc-default 'candidate-transformer source))))) (cond ((member (cdr (assoc 'name source)) helm-adaptive-enabled-sources) source) ((listp adapt-source) (and (member 'helm-adaptive-sort adapt-source) source)) ((eq adapt-source 'helm-adaptive-sort) source)))))

(require 'dash) (setq helm-fuzzy-sort-fn (lambda (candidates source &optional use-real)

      (-> candidates
          (helm-flx-fuzzy-matching-sort source use-real)
          (helm-adaptive-sort source)
          ))
    helm-fuzzy-matching-highlight-fn #'helm-flx-fuzzy-highlight-match))

(helm-adaptive-mode 1) #+END_SRC

** Last Steps

If you want to load all configurations from this guide require the file in your init and call =(helm-ido-like)=.

#+BEGIN_SRC emacs-lisp :tangle helm-ido-like.el ;;;###autoload (defun helm-ido-like () "Configure and activate helm', helm-fuzzier' and `helm-flx'." (interactive) (helm-ido-like-activate-helm-modes) (helm-ido-like-load-ido-like-bottom-buffer) (helm-ido-like-hide-modelines) (helm-ido-like-hide-helm-modeline) (helm-ido-like-header-lines-maybe) (helm-ido-like-setup-bg-color) (helm-ido-like-load-file-nav) (helm-ido-like-no-dots) (helm-ido-like-load-fuzzy-enhancements) (helm-ido-like-fix-fuzzy-files))

(provide 'helm-ido-like) ;;; helm-ido-like.el ends here #+END_SRC