Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/oantolin/live-completions

Live updating of the *Completions* buffer
https://github.com/oantolin/live-completions

Last synced: 12 days ago
JSON representation

Live updating of the *Completions* buffer

Awesome Lists containing this project

README

        

* Overview
:PROPERTIES:
:TOC: :include all :ignore this
:END:

This packages provides a minibuffer completion UI. It pops up the
traditional =*Completions*= buffer and keeps it updated as you type in
the minibuffer. Besides the traditional multicolumn view of the
completions, this package offers a single column view ---in case the
changing number of columns of the traditional view proves too
dizzying.

:CONTENTS:
- [[#why-write-a-new-completion-ui][Why write a new completion UI?]]
- [[#screenshot][Screenshot]]
- [[#sort-orders-for-candidates][Sort orders for candidates]]
- [[#setting-a-maximum-height-for-the-completions-buffer][Setting a maximum height for the completions buffer]]
- [[#defining-your-own-commands-with-live-completion][Defining your own commands with live completion]]
:END:

* Why write a new completion UI?

There are many excellent minibuffer completion UIs for Emacs: the
built-in packages Icomplete, and Ido; and external packages such as
[[https://github.com/abo-abo/swiper][Ivy]], [[https://github.com/emacs-helm/helm][Helm]] and [[https://github.com/raxod502/selectrum][Selectrum]]. What distinguishes this one?

- This package puts the completions in a buffer. This is like Helm,
but unlike Icomplete, Ido, Ivy and Selectrum. (While Ivy doesn't use
a buffer by default, it has the =ivy-occur= command which produces a
neat buffer.) Buffers are great! You can easily navigate around
them, copy text from them, save their contents in a file, etc. All
of that is sometimes desirable for completions and is awkward to do
when the completions reside in the minibuffer.

If you want to define your own actions for the candidate at point in
the =*Completions*= buffer, Emacs already has a convenient
=completion-list-mode-map= keymap where you can bind them.

- It has both the traditional compact view of completions, in several
columns, and a single column view, with an easy toggle between
them. Helm, Ivy and Selectrum only present single column views;
Icomplete and Ido are horizontal by default, but can be configured
to give single-column vertical views (see [[https://github.com/oantolin/icomplete-vertical][icomplete-vertical]] and
[[https://github.com/creichert/ido-vertical-mode.el][ido-vertical]]).

I'd say those are arguably pros. Now some things which are arguably
cons:

- It is very modest in scope: like Icomplete it is /only/ a minibuffer
completion UI, and it relies on Emacs' completion styles to supply
the actual completions. In fact, it's pretty much the same as
cosntantly hitting the =?= key to pop up the =*Completions*= buffer.

Selectrum is almost as modest in scope: it also mostly focuses on
selecting items from lists but instead of using the standard
completion machinery, it has it's own API and a few extra
convenience commands. Ivy and Helm of course have tons of extra
functionality beyond selecting items from lists. Ido is a weird mix:
it doesn't take over all completion (well, not by default, see
[[https://github.com/DarwinAwardWinner/ido-completing-read-plus][ido-completing-read+]]), but offers some extra functionality where it
does take over completion.

- It isn't very pretty. It just looks like the =*Completions*= buffer,
you've seen it before. The traditional multicolumn view sometimes
even gets misaligned! This package does however highlight the
completion that would be entered into the minibuffer by
=minibuffer-force-complete=, so at least that's a small splash of
color.

* Screenshot

[[./images/describe-variable.png]]

(Did you spot the misalignment? :P)

* Sort orders for candidates

The variable =live-completions-sort-order= controls the sorting of
completion candidates. You can customize this variable. It can take
three values:

- =display=: this is the order of the classic =*Completions*= buffer, it
is the default.
- =cycle=: this is the order that tab cycling in the default
minibuffer tab completion uses, and is also the order Icomplete uses.
- =nil=: no sorting, even if the completion source specifies a sort
function.

The =cycle= and =nil= options put the candidate that force completion
would select at the top of the list. The classic =display= oder does
not, but it should be easy to spot this candidate because is is
higlighted.

* Setting a maximum height for the completions buffer

You can use the built-in =temp-buffer-resize-mode= to limit the height
of the completions buffer. Set the variable =temp-buffer-max-height=
to either an integer, the maximum height in lines, or to a function
that computes the maximum height.

To set a maximum height of 10 lines for all temporary buffers (not
just the completions buffer):

#+begin_src emacs-lisp
(setq temp-buffer-max-height 10)
(temp-buffer-resize-mode)
#+end_src

To set the maximum height only for the completions buffer and keep
the default (which is half the frame height) for all other temporary
buffers:

#+begin_src emacs-lisp
(defvar old-max-height-function temp-buffer-max-height)

(defun max-completions-height (buffer)
(if (string= (buffer-name buffer) "*Completions*")
10
(funcall old-max-height-function temp-buffer-max-height)))

(setq temp-buffer-max-height #'max-completions-height)
(temp-buffer-resize-mode)
#+end_src

#+RESULTS:
: t

If you use a function rather than a number, you can also make the
maximum height depend on current circumstances. For example you use
=(/ (frame-height) 3)= to make the maximum height a third of the
height of the frame.

* Defining your own commands with live completion

If you don't want to use =live-completions-mode= all the time but you
want to define a few commands that provide live completion, use the
=live-completions-do= macro. You can ask it to start in signle-column
mode, which is useful for lists with naturally long candidates, such
as filesystem paths or kill-ring entries.

For example, let's implement a command to yank from the kill-ring
using completion. Often the kills are multiline, so for improved
usability we'll need (1) the completion to start in single column
mode, (2) the number of lines used to display entries to be relatively
large, and (3) the separator to be, say, a red dotted line:

#+begin_src emacs-lisp
(defun insert-kill-ring-item ()
"Insert item from kill-ring, selected with completion."
(interactive)
(live-completions-do
(:columns 'single
:separator (propertize "\n··········\n" 'face '(:foreground "red"))
:height 30)
(insert (completing-read "Yank: " kill-ring nil t))))
#+end_src

Note that the completion merely /starts out/ in single column mode:
nothing keeps you from toggling between single and multiple columns
while =insert-kill-ring-item= is active. Once the command finishes
running, your previous column completion configuration will be
restored.

All of the options, =:columns=, =:separator=, =:height= and =:sort= (not shown
in the example), are optional. The =:separator= is only used in
single-column mode and defaults to
=live-completions-horizontal-separator=. If you don't include a =:height=
the default is to follow =temp-buffer-resize-mode= if you have it active
and to use =fit-window-to-buffer= otherwise (this lets the completions
buffer take up most of the frame). If you omit all four parts you
still need to include the empty parenthesis: =(live-completions-do ()
...)=!.

This package contains the =live-completions-do= macro for you to
implement your own commands. It does not define any commands that use
the macro.