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 smex:
Using ido-describe-prefix-bindings for C-x
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)
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
.
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 <tab>
, 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
.
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.
(let ((ido-grid-mode-max-columns 1)
(ido-grid-mode-max-rows 30))
(completing-read ...))
You might want to do this in an advice, e.g.:
;; 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)
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 ist
, the cursor will always wrap except at the top left and bottom right corners. If it isnil
, the cursor will wrap only in the non-major dimension; what this means is that if you haveido-grid-mode-order
set column-first, going out of bounds vertically will wrap, but horizontally will scroll. Ifido-grid-mode-order
is rows-first, the reverse is trueido-grid-mode-scroll-up
andido-grid-mode-scroll-down
control what happens when the cursor is moved out of bounds and is not wrapped. You can useido-grid-mode-<next/previous>-page
to move as many items are displayed, so the whole page of items changes, or you can useido-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:
(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)
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:
By default, ido-grid-mode
will mangle your key-bindings for <up>
, <down>
, <left>
, <right>
, <tab>
, <backtab>
, 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
.
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 substringsido-first-match
, which is used to highlight the selected prospectido-only-match
, which is used when there is only one prospectido-subdir
, which is used to colour in directories in ido-find-fileido-grid-mode-prefix
, which is used for the common prefix stringido-incomplete-regexp
, which is used when there is an incomplete regexp.
The top line shows some information after point; this is configurable with ido-grid-mode-first-line
.
There are two code paths for different layouts in a lot of places, which is lame and might be fixable
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.
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.
- 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.
- 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. - emacs-zlc, which does something similar to this, but in the completions buffer rather than the minibuffer.