ido-grid-mode.el icon indicating copy to clipboard operation
ido-grid-mode.el copied to clipboard

  • Ido Grid Mode

    This is an enhancement to ido mode which displays the ido prospects in a grid in the minibuffer. I find this makes them easier to read. It can also amend ido's key-bindings so that you can navigate around the grid, rather than scrolling all the completions. I find this also makes it easier to read.

    Here are a some examples of how it can work:

    In ido-find-file [[./pictures/files-example.gif]]

    In [[https://github.com/nonsequitur/smex][smex]]: [[./pictures/smex-example.gif]]

    Using [[https://github.com/larkery/ido-describe-prefix-bindings.el][ido-describe-prefix-bindings]] for =C-x= [[./pictures/vertical-example.gif]]

  • Installation

    It is a single-file emacs package, so you can use =M-x package-install-file= on it. To switch it on, invoke =(ido-grid-mode 1)=. To switch it off, invoke =(ido-grid-mode -1)=

  • Configuration

    Most things are configurable through =M-x customize-group ido-grid-mode=. The important things are =ido-grid-mode-max-rows= and =ido-grid-mode-min-rows=, which determine the size of the thing, and perhaps =ido-grid-mode-order=.

** Size and formatting

The grid is laid out to fit into the minibuffer's available width, assuming a fixed-width font. Layout is either done left-right then top-bottom or top-bottom then left-right, depending on whether =ido-grid-mode-order= is =nil= for rows or =t= for columns. I prefer columns, which is similar to what zsh does.

No more than =ido-grid-mode-max-rows= rows should be produced. If the layout can be achieved with fewer rows, then fewer rows will appear, limited by =ido-grid-mode-min-rows=. By default these are the same, which keeps the minibuffer a constant height. If you want the minibuffer to grow and shrink appropriately reduce the min rows.

If you want the minibuffer to typically be small, but display a large grid on demand, you can use =ido-grid-mode-start-collapsed=. This will limit it to a single row until you press ==, so long as there are things which would be offscreen. I find this a good setting, as it means that things don't move around on the screen until you want them to.

The little arrow =->= displayed on the left is customisable with =ido-grid-mode-prefix=, and you can make it follow the highlighted item with =ido-grid-mode-prefix-scrolls=.

*** Making a vertical list sometimes

If you want a particular bit of code to use a vertical list (or some other known layout) you can =let= the appropriate variables around it.

#+BEGIN_SRC elisp
  (let ((ido-grid-mode-max-columns 1)
        (ido-grid-mode-max-rows 30))
    (completing-read ...))
#+END_SRC

You might want to do this in an advice, e.g.:

#+BEGIN_SRC elisp
  ;; make projectile-find-file vertical list

  (defun ido-vertical-please (o &rest args)
    (let ((ido-grid-mode-max-columns 1)
          (ido-grid-mode-max-rows 15) ;; bigger list than usual
          (ido-grid-mode-min-rows 1) ;; let it shrink
          (ido-grid-mode-start-collapsed nil) ;; pop up tall at the start
          ;; why not have a different prefix as well?
          (ido-grid-mode-prefix ":: "))
      (apply o args)))

  (advice-add 'projectile-find-file :around #'ido-vertical-please)
#+END_SRC

** Scrolling

By default, the arrow keys and tab/backtab will move around in the grid. You have some choices about what happens when you reach the edges of the grid, if there are too many things to display:

  • =ido-grid-mode-scroll-wrap= sets what happens when you move the cursor out-of-bounds. If it is =t=, the cursor will always wrap except at the top left and bottom right corners. If it is =nil=, the cursor will wrap only in the non-major dimension; what this means is that if you have =ido-grid-mode-order= set column-first, going out of bounds vertically will wrap, but horizontally will scroll. If =ido-grid-mode-order= is rows-first, the reverse is true
  • =ido-grid-mode-scroll-up= and =ido-grid-mode-scroll-down= control what happens when the cursor is moved out of bounds and is not wrapped. You can use =ido-grid-mode-<next/previous>-page= to move as many items are displayed, so the whole page of items changes, or you can use =ido-grid-mode-<next/previous>-row= which is slightly confusingly named but scrolls one row if the layout is row-wise or one column if it is column-wise.

You can use these in combination to get different behaviours, for example:

#+BEGIN_SRC elisp (setq ido-grid-mode-max-columns 1 ido-grid-mode-max-rows 8 ido-grid-mode-prefix-scrolls t ido-grid-mode-scroll-down #'ido-grid-mode-next-row ido-grid-mode-scroll-up #'ido-grid-mode-previous-row ido-grid-mode-order nil ido-grid-mode-start-collapsed t) #+END_SRC

will start small, popup when you press tab, and let you scroll up and down in a vertical list a row at a time when you hit the edges:

[[./pictures/vertical-scroll.gif]]

** Keys

By default, =ido-grid-mode= will mangle your key-bindings for ==, ==, ==, ==, ==, ==, =C-n= and =C-p= when in the minibuffer. These are bound to move around the grid and page up / down when there are many items. You can change this by customizing =ido-grid-mode-keys=; set it to =nil= to leave the keys alone. The movement commands are =igm-<up,down,left,right>=, =igm-previous=, =igm-next=, =igm-previous-page=, and =igm-next-page=.

** Faces

At the moment, =ido-use-faces= is ignored, and faces are always added. This is partly because you need a face to see the highlighted item. The faces used are:

  • =ido-grid-mode-match=, which is used to highlight matching substrings
  • =ido-first-match=, which is used to highlight the selected prospect
  • =ido-only-match=, which is used when there is only one prospect
  • =ido-subdir=, which is used to colour in directories in ido-find-file
  • =ido-grid-mode-prefix=, which is used for the common prefix string
  • =ido-incomplete-regexp=, which is used when there is an incomplete regexp.

** Top line

The top line shows some information after point; this is configurable with =ido-grid-mode-first-line=.

  • Things to do ** TODO Improve row/column specific code There are two code paths for different layouts in a lot of places, which is lame and might be fixable ** TODO Improve scrolling At the moment scrolling the grid a row at a time sometimes makes the cursor jump around when the dimensions of the next layout are different. Similarly, you can scroll right 1 column and just reduce the number of columns if the 'next' column wouldn't fit. ** TODO Efficiency improvements It works OK on my laptop, but it is irksome that the grid is created so frequently. Most of the time it should be possible to reuse the string and just move the faces around. ** TODO The merged indicator breaks wrapping

  • See also

    • [[https://github.com/creichert/ido-vertical-mode.el][ido-vertical-mode.el]], which this was based on. I rewrote it when it looked like I was starting to break existing behaviour too much.
    • [[https://github.com/larkery/ido-match-modes.el][ido-match-modes.el]], which lets you toggle different ido matching methods (flx, regex, substring etc.) and uses =ido-grid-mode-first-line= to display the current method.
    • [[https://github.com/mooz/emacs-zlc][emacs-zlc]], which does something similar to this, but in the completions buffer rather than the minibuffer.