Subject: Re: bug#76028: 31; completing-read-multiple: Add prompt indicator

From: Daniel Mendler <[email-masked]>
Date: Fri, 07 Feb 2025 10:44:49 +0100

Eli Zaretskii <[email-masked]> writes:

>> From: Juri Linkov <[email-masked]>
>> Cc: Eli Zaretskii <[email-masked]>,  [email-masked],  Stefan Kangas
>>  <[email-masked]>
>> Date: Thu, 06 Feb 2025 19:24:13 +0200
>> 
>> >> But anyway it seems text properties are the least intrusive approach.
>> >
>> > It seems so. The patch gets quite short. I have attached the updated
>> > patch to this mail.
>> 
>> Thanks, I could push if there are no more objections,
>> and no NEWS required for this change.
>
> I think this does have to be called out in NEWS, including the new
> user option.  In a sense, this is an incompatible behavior change, so
> we cannot introduce it silently.

Agree. I have attached an updated patch to this mail, where the change
is announced in the NEWS.

Daniel

>From 3632bfdc280f38a366d93ccdacf99eade7aa94f0 Mon Sep 17 00:00:00 2001
From: Daniel Mendler <[email-masked]>
Date: Mon, 3 Feb 2025 16:39:49 +0100
Subject: [PATCH] completing-read-multiple: CRM indication and prompt
 customization

The `completing-read-multiple' prompt indicates multi
completion.  The customization option `crm-prompt' configures
the formatting of the prompt.  The variable can be set to "%p"
in order to only display the original prompt, to "[%d] %p" to
display the separator description and the prompt, or to "[CRM%s]
%p" to display a shorter indicator of only the separator string
and the prompt.

* lisp/emacs-lisp/crm.el (crm-prompt): New user option.
(crm-separator): Update value and docstring.
(completing-read-multiple): Use `crm-prompt' to format the
prompt.
* etc/NEWS: Announce the change.
---
 etc/NEWS               |  7 +++++++
 lisp/emacs-lisp/crm.el | 30 ++++++++++++++++++++++++++----
 2 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index ade635aa924..13dfa2008f0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -176,6 +176,13 @@ will still be on that candidate after "*Completions*" is updated with a
 new list of completions.  The candidate is automatically deselected when
 the "*Completions*" buffer is hidden.
 
++++
+*** New user option 'crm-prompt'.
+This option configures the prompt of 'completing-read-multiple'. By
+default the prompt indicates to the user that the completion accepts a
+comma-separated list. The variable 'crm-separator' carries the prompt
+description and the separator string as text properties.
+
 ** Windows
 
 +++
diff --git a/lisp/emacs-lisp/crm.el b/lisp/emacs-lisp/crm.el
index 454c3e85074..e081e66dfe1 100644
--- a/lisp/emacs-lisp/crm.el
+++ b/lisp/emacs-lisp/crm.el
@@ -79,9 +79,25 @@
 
 (define-obsolete-variable-alias 'crm-default-separator 'crm-separator "29.1")
 
-(defvar crm-separator "[ \t]*,[ \t]*"
+(defvar crm-separator
+  (propertize "[ \t]*,[ \t]*" 'separator "," 'description "comma-separated list")
   "Separator regexp used for separating strings in `completing-read-multiple'.
-It should be a regexp that does not match the list of completion candidates.")
+It should be a regexp that does not match the list of completion
+candidates.  The regexp string can carry the text properties `separator'
+and `description', which if present `completing-read-multiple' will show
+as part of the prompt.  See the user option `crm-prompt'.")
+
+(defcustom crm-prompt "[%d] %p"
+  "Prompt format for `completing-read-multiple'.
+The prompt is formatted by `format-spec' with the keys %d, %s and %p
+standing for the separator description, the separator itself and the
+original prompt respectively."
+  :type '(choice (const :tag "Original prompt" "%p")
+                 (const :tag "Description and prompt" "[%d] %p")
+                 (const :tag "Short CRM indication" "[CRM%s] %p")
+                 (string :tag "Custom string"))
+  :group 'minibuffer
+  :version "31.1")
 
 (defvar-keymap crm-local-completion-map
   :doc "Local keymap for minibuffer multiple input with completion.
@@ -272,8 +288,14 @@ completing-read-multiple
                       (unless (eq require-match t) require-match))
           (setq-local crm-completion-table table))
       (setq input (read-from-minibuffer
-                   prompt initial-input map
-                   nil hist def inherit-input-method)))
+                   (format-spec
+                    crm-prompt
+                    (let* ((sep (or (get-text-property 0 'separator crm-separator)
+                                    (string-replace "[ \t]*" "" crm-separator)))
+                           (desc (or (get-text-property 0 'description crm-separator)
+                                     (concat "list separated by " sep))))
+                      `((?s . ,sep) (?d . ,desc) (?p . ,prompt))))
+                   initial-input map nil hist def inherit-input-method)))
     ;; If the user enters empty input, `read-from-minibuffer'
     ;; returns the empty string, not DEF.
     (when (and def (string-equal input ""))
-- 
2.47.2