[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Thoughts on Refactoring In-Buffer Completion In message.el
From: |
Alexander Adolf |
Subject: |
Thoughts on Refactoring In-Buffer Completion In message.el |
Date: |
Thu, 23 Jun 2022 17:26:49 +0200 |
Hello,
<disclaimer>
message.el is a massive file (some 8600 lines), and I can't claim
having read or understood any substantial portion of it. All
statements solely rely on the (small) portions of the code around
completion in message.el, which I have looked at. Thus, as always in
life, I shall be looking froward to standing corrected and
enlightened wherever I deserve it. ;-)
</disclaimer>
Regarding in-buffer completion, message-mode is probably a rather
specific case as it has succinctly distinct in-buffer completion
contexts. Think for instance of a body or subject line, vs. a To header
line, vs. a References header line, vs. a Newsgroups header line.
Currently, message-mode employs two mechanisms for handling these
contextss:
• Its completion function message-completion-function is registered in
completion-at-point-functions, and marshals the in-buffer completion
contexts via the variable message-completion-alist.
message-completion-alist in turn configures the (one and only)
function to be called for completion candidates in a given context.
• The completion style to use for email addresses is added to the
variable completion-category-defaults.
What made me start scratching my head about this, was the wish to
combine email address candidates from more than one source, and have
them be presented in a single list by completion-at-point. Since EUDC
had recently gained the ability to return combined search results from
several of its back-ends [1], I put together a new EUDC function that
can be added to completion-at-point-functions. My new function gets
added to the front of completion-at-point-functions in message-mode, and
email address completion candidates from the EUDC back-ends I have
configured show up when completion-at-point is triggered [2].
[1] commit 0470a4a939772c4bd25123b15f5eadab41f8bee5
[2] commit 620ac6735520aea97ce49059b0df38ed41930b6b
So far so good; this satisfies my immediate use-case.
Job done? Looking at [2], you'll notice that I need to check in my new
EUDC completion function whether point is actually on a To, Cc, etc.
line. Otherwise email addresses would happily be offered as completion
candidates in the middle of the subject, or in the body. Having this
extra check for an email message header line in EUDC code didn't feel
quite right, however. Think "separation of concerns".
Further looking at message.el, and how in-buffer completion is handled
there, you'll find that there is (more or less) close integration with
ecomplete, mailabbrev, EUDC, and bbdb in message.el. Consider, for
instance the defun starting at line 8378 of message.el:
┌────
│ 8377 (defun message--bbdb-query-with-words (words)
│ 8378 ;; FIXME: This (or something like this) should live on the BBDB side.
│ 8379 (when (fboundp 'bbdb-records)
└────
Listing 1: lisp/gnus/message.el "separation of concerns" example
What more should I add? I fully agree with the comment in line 8378.
Again, think "separation of concerns".
On this backdrop, where would I see room for improvement?
Overall, there are a couple of bits an pieces for in-buffer completion
in place in message.el already. But it seems they were developed/added
independently of each other, and a little more orchestration could
perhaps help to make things more flexible, but without reinventing the
wheel.
1) In message.el, combine both completion control mechanisms into a
single one.
At first glance from the end user's point of view,
message-completion-alist and completion-category-defaults can be
perceived as two distinct mechanisms, and it is not immediately
obvious which completion style category applies for which regex in
message-completion-alist. Only by inspecting line 8404 in message.el,
one can discover which category is used:
┌────
│ 8398 (defun message--name-table (orig-string)
│ 8399 (let ((orig-words (split-string orig-string "[ \t]+"))
│ 8400 eudc-responses
│ 8401 bbdb-responses)
│ 8402 (lambda (string pred action)
│ 8403 (pcase action
│ 8404 ('metadata '(metadata (category . email)))
└────
Listing 2: lisp/gnus/message.el completion styles handling
Thus, I would propose to change message-completion-alist from being
alist of (RE . FUN), to become an alist of (RE . PLIST). Two
properties for the inner plist would initially defined; one for
specifying the completion style, and another one for specifying the
completion-at-point-functions. With this, the new default value of
message-completion-alist could for example be along the lines of:
┌────
│ (defcustom message-completion-alist
│ `((,message-newgroups-header-regexp
│ . '(:capf-style 'newsgroup
│ :capf-funs '(gnus-capf-complete))) ;; hypothetical
│ (,message-email-recipient-header-regexp
│ . '(:capf-style 'email
│ :capf-funs '(eudc-capf-complete)))) ;; exists
│ "docstring"
│ )
└────
As an aside: considering the FIXME comment in the function
message-expand-group, newsgroup completion would seem to be able to
benefit from this change, too.
If none of the regular expressions matches, the settings in
completion-at-point-functions, and
completion-category-defaults/overrides will apply as before, or in
other modes.
With such an approach, the end user would get a positive assertion as
to which completion style is used in each part of a message buffer.
2) Refactor ecomplete, mailabbrev, and bbdb stuff out of message.el as
much as possible.
As the FIXME comment in listing 1 above suggests, any "query with
words" functions, or other ecomplete, mailabbrev, or bbdb specific
functions should be in the respective packages themselves
("separation of concerns"). Also, as EUDC performs search result
aggregation across sources, these packages should implement EUDC
back-ends to provide their results via EUDC. Message.el should thus
not interact with any email address database directly, but instead
provide a default configuration where completion-at-point queries
EUDC for email addresses.
In all cases, current default behaviour should be retained as much as
reasonably possible, of course.
Many thanks and looking forward to your thoughts,
-–alexander
- Thoughts on Refactoring In-Buffer Completion In message.el,
Alexander Adolf <=