207 lines
9.1 KiB
Plaintext
207 lines
9.1 KiB
Plaintext
|
|
|
|
<p>Raw link: <a href="https://www.youtube.com/watch?v=43Dg5zYPHTU">https://www.youtube.com/watch?v=43Dg5zYPHTU</a></p>
|
|
|
|
<p>In this video I offer an overview of my current completion framework for
|
|
Emacs. It consists of a set of modules that are pieced together into a
|
|
robust system. The centrepiece is the standard minibuffer.</p>
|
|
|
|
<p>The text of the presentation is available right below (<code>org-mode</code>
|
|
notation). Refer to my Emacs configuration file (“dotemacs”) for the
|
|
implementation details of my completion framework and everything else I
|
|
currently have: <a href="https://protesilaos.com/emacs/dotemacs">https://protesilaos.com/emacs/dotemacs</a>.</p>
|
|
|
|
<hr />
|
|
|
|
<pre><code class="language-org">#+TITLE: Default Emacs completion and extras
|
|
#+AUTHOR: Protesilaos Stavrou · protesilaos.com
|
|
|
|
* Piecing together a completion framework
|
|
|
|
Today I will talk to you about how I currently handle completion in
|
|
Emacs. The plan is to tour you around the various tools I use; tools
|
|
which comprise my system for narrowing down a list of candidates.
|
|
|
|
My system consists of the following constituents:
|
|
|
|
+ Default minibuffer (exactly what you get when you run =emacs -Q= from
|
|
the command line).
|
|
+ =orderless= completion style, which extends the built-in list of pattern
|
|
matching =completion-styles=.
|
|
+ =embark= to visualise the list of completion candidates, as well as
|
|
provide actions on a per-item or per-set basis.
|
|
+ =consult= to enhance several minibuffer-centric commands.
|
|
+ =marginalia= to provide meta-information to various completion lists.
|
|
|
|
All of the above are modular tools that are independent of each other
|
|
yet can operate in tandem. One can, for example, use =icomplete= or
|
|
=selectrum= instead of the default minibuffer.
|
|
|
|
* Orderless and the built-in ~partial-completion~
|
|
|
|
As its name suggests, Orderless matches groups out-of-order. A "group"
|
|
can be one among many styles, including a regular expression, a literal
|
|
string, an initialism, and so on. The styles are configurable, while
|
|
the list is comprehensive.
|
|
|
|
Orderless has a concept of "style dispatchers". Those are user-defined
|
|
single characters that are used as a suffix to each group and assign to
|
|
it a particular pattern matching style. For example, I use the equals
|
|
sign to declare that a group should be read as a literal string.
|
|
|
|
While the built-in =partial-completion= covers the niche of dynamic
|
|
completion for certain commands. A case in point is with the =find-file=
|
|
command (=C-x C-f=), where it can expand an abbreviated path =~/.l/s/fo=
|
|
into =~/.local/share/fonts=.
|
|
|
|
* Embark for per-item actions (part 1)
|
|
|
|
The best way to conceptualise Embark is as a contextual menu. It lets
|
|
you carry out context-dependent actions on targets.
|
|
|
|
What constitutes a "target" will depend on the case: it can it the
|
|
current item in the completion list, the symbol at point, or some URL
|
|
right under the cursor. Your conduit to this mode of operation is the
|
|
=embark-act= command, which you should bind to a convenient key (=C-,= in my
|
|
case).
|
|
|
|
Let us try these actions on individual targets:
|
|
|
|
+ Get help for =embark-act= by placing point over it.
|
|
+ Browse https://protesilaos.com/emacs/modus-themes with =eww=. Then save the
|
|
link to the kill-ring and yank from there afterwards.
|
|
+ Run =describe-function= and insert some function here.
|
|
+ Run =M-x switch-buffers= and then kill a buffer instead of switching to
|
|
it.
|
|
|
|
Each context is bound to a keymap. The keymap holds the associations
|
|
between key bindings and commands that you may call on the given target.
|
|
|
|
To learn more about the commands you can use after invoking =embark-act=,
|
|
type =C-h= (or set up =which-key=---check my dotemacs for the implementation
|
|
details).
|
|
|
|
[ remember that =C-h= as a suffix to any key chord, is a standard way to
|
|
get a Help buffer for all keys that complete the chord ]
|
|
|
|
* Embark for per-set actions (part 2)
|
|
|
|
Other than act on a per-item basis, Embark can operate on entire sets of
|
|
targets. Allow me to introduce this concept with an example: we invoke
|
|
=M-x describe-keymap= and then search for "embark" to find all keymaps
|
|
that pertain to the various contexts in which Embark can perform
|
|
meaningful tasks (I bind that help command to =C-h K=). Now we use
|
|
=embark-occur= to produce a persistent buffer with the list of candidates.
|
|
It will still run the default action on each target.
|
|
|
|
You have also seen Embark's "live occur", but let me formally introduce
|
|
it to you. This is a buffer that is initially linked to an active
|
|
minibuffer session. It gets auto-updated to match the input in the
|
|
minibuffer and to narrow the list of candidates accordingly. So if I
|
|
call =M-x switch-buffers= (=C-x b=) and type something, this "live occur"
|
|
will show me what the matching buffers are.
|
|
|
|
Because these are standard buffers, we can store them on the disk and
|
|
revisit them in the future. Use =M-x write-file= (=C-x C-w=).
|
|
|
|
Embark offers another neat utility: =embark-export=. It produces a buffer
|
|
whose major mode matches the category of the targets: =dired-mode= for
|
|
files/directories and =ibuffer-mode= for buffers. Then you can benefit
|
|
from the power of those modes.
|
|
|
|
This per-set functionality of Embark is what allows us to use the
|
|
default minibuffer for all completions. While we could add =icomplete= or
|
|
=selectrum= to the mix, there is no need for such an addition. Embark
|
|
live occur merely shows the candidates that are already there and which
|
|
the minibuffer is fully aware of.
|
|
|
|
* Consult for enhanced minibuffer commands
|
|
|
|
=consult= has a dual purpose:
|
|
|
|
1. Enhance existing commands, like =M-x imenu= or =M-x switch-to-buffer=.
|
|
2. Provide new functionality, such as =consult-line=, =consult-mark=, and
|
|
asynchronously updating grep/find commands.
|
|
|
|
What this "enhancement" means depends on the case. Commands such as
|
|
those that navigate lines, get an optional preview. The likes of
|
|
=consult-imenu= offer a concept of filtering per type of input: this is
|
|
called "narrowing" in Consult's verbiage and is controlled by a key map.
|
|
|
|
Let us try some common workflows to witness the synergies between the
|
|
modules that comprise my system.
|
|
|
|
+ Visit a large Org file. Invoke =consult-outline= and produce a
|
|
persistent buffer out of it with =embark-occur=. This works as an index
|
|
of buffer positions, a "table of contents" if you will.
|
|
|
|
+ Call =consult-imenu= and use =consult-narrow= to filter by the type of the
|
|
syntactic constructs.
|
|
|
|
* Marginalia for completion annotations
|
|
|
|
Finally we have =marginalia=, which you have already seen in the various
|
|
Embark live occur buffers I have put on display. It enriches completion
|
|
candidates with pertinent meta information.
|
|
|
|
Here are some commands that benefit from such annotations:
|
|
|
|
+ all =describe-*= commands present the first line of the doc string.
|
|
|
|
+ =switch-to-buffer= (=C-x b=) documents the buffer's major mode and status,
|
|
as well as its underlying file's path.
|
|
|
|
+ =find-file= (=C-x C-f=) includes the file size, permissions and date.
|
|
|
|
You get the idea.
|
|
|
|
Currently those annotations are decorative, in the sense that you cannot
|
|
use them as filter predicates or have something like =orderless= do
|
|
perform pattern matching against them. Still, I find this lightweight
|
|
utility to be quite valuable.
|
|
|
|
* A system I can understand
|
|
|
|
About a year ago I switched from Ivy to Icomplete. I wanted to simplify
|
|
my setup in order to make sense of it. Doing so helped me learn some
|
|
Elisp, mostly through trial and error, and by relying on Emacs'
|
|
introspection utilities. This reminded me of the value proposition of
|
|
modularity: a system of linkable-yet-standalone tools is robust in its
|
|
own right, while it can be constructed and deconstructed at will both in
|
|
pursuit of utilitarian ends and for educational purposes.
|
|
|
|
By piecing together a system out of Embark, Consult, Orderless,
|
|
Marginalia, the default minibuffer, and my extras, I am in a position to
|
|
clearly comprehend what is going on. This is not knowledge for its own
|
|
sake: it has the tangible benefit of equipping me with the means to
|
|
extend or otherwise tweak my completion framework so that it aligns with
|
|
my expectations.
|
|
|
|
I thus wish to congratulate the authors of those packages. We have Omar
|
|
Antolín Camarena, who develops =embark= and =orderless=. While Daniel
|
|
Mendler produces =consult= (among others). While both of them maintain
|
|
the =marginalia= library. I really appreciate what they do: their code is
|
|
top-notch, but they also invest a lot of effort in documentation.
|
|
Manuals and informative READMEs are of paramount importance in bridging
|
|
the gap between developers and users. You read the instructions and you
|
|
learn how the thing works. Then, once you have the requisite confidence
|
|
in your abilities, you can delve into the source code.
|
|
|
|
Here is my rule of thumb: if a project has good docs, then it shows that
|
|
the developer is dedicated and meticulous in their work. Use that as
|
|
your guide when picking software. I am happy to have done so.
|
|
|
|
* Further information
|
|
|
|
Refer to my "dotemacs" for my complete setup:
|
|
<https://protesilaos.com/emacs/dotemacs>.
|
|
|
|
And check the Git repositories of all those projects:
|
|
|
|
+ <https://github.com/minad/consult>
|
|
+ <https://github.com/oantolin/embark>
|
|
+ <https://github.com/minad/marginalia>
|
|
+ <https://github.com/oantolin/orderless>
|
|
</code></pre>
|
|
|
|
|