bug#81411: 31.0.90; Selecting completion for completing-read-multiple does not respect completion boundaries

Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email protected]> Thu, 06 Aug 2026 15:32:40 -0400
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
> diff --git a/lisp/emacs-lisp/crm.el b/lisp/emacs-lisp/crm.el
> index b68d3d55525..9be7913a954 100644
> --- a/lisp/emacs-lisp/crm.el
> +++ b/lisp/emacs-lisp/crm.el
> @@ -263,16 +263,7 @@ completing-read-multiple
>                      'crm--choose-completion-string nil 'local)
>            (setq-local completion-list-insert-choice-function
>                        (lambda (_start _end choice)
> -                        (let* ((beg (save-excursion
> -                                      (if (search-backward-regexp crm-separator
> -                                                                  (field-beginning)
> -                                                                  t)
> -                                          (1+ (point))
> -                                        (minibuffer-prompt-end))))
> -                               (end (save-excursion
> -                                      (if (search-forward-regexp crm-separator nil t)
> -                                          (1- (point))
> -                                        (point-max)))))
> +                        (crm--completion-command beg end
>                            (completion--replace beg end choice))))
>            (setq-local crm-completion-table table)
>            (use-local-map map))

Looks OK to me, thank you.

>>From 16ae3cd8b28015e5baa8c8d0774e134c2ae4aaaf Mon Sep 17 00:00:00 2001
> From: "Aaron L. Zeng" <[email protected]>
> Date: Tue, 21 Jul 2026 14:02:33 -0400
> Subject: [PATCH 2/2] Fix choose-completion in CRM with boundaries
>
> * lisp/emacs-lisp/crm.el (completing-read-multiple): Respect
> completion boundaries when inserting a choice.
> ---
>  lisp/emacs-lisp/crm.el | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/lisp/emacs-lisp/crm.el b/lisp/emacs-lisp/crm.el
> index 9be7913a954..e46570d1a47 100644
> --- a/lisp/emacs-lisp/crm.el
> +++ b/lisp/emacs-lisp/crm.el
> @@ -263,8 +263,19 @@ completing-read-multiple
>                      'crm--choose-completion-string nil 'local)
>            (setq-local completion-list-insert-choice-function
>                        (lambda (_start _end choice)
> +                        ;; Respect the boundaries of the current
> +                        ;; element, and within that, any boundaries
> +                        ;; specified by the completion table
> +                        ;; (bug#81411).
>                          (crm--completion-command beg end
> -                          (completion--replace beg end choice))))
> +                          (let ((bounds (completion-boundaries
> +                                         (buffer-substring beg (point))
> +                                         crm-completion-table
> +                                         minibuffer-completion-predicate
> +                                         (buffer-substring (point) end))))
> +                            (completion--replace (+ beg (car bounds))
> +                                                 (+ (point) (cdr bounds))
> +                                                 choice)))))
>            (setq-local crm-completion-table table)
>            (use-local-map map))
>        (setq input (completing-read

But here I have the impression that it will misfire when, say, the user
typed `/usr/s/z` and selects `share/zoneinfo` from the *Completions*
buffer: AFAICT it will result in `/usr/s/share/zoneinfo` instead of
`/usr/share/zoneinfo`: the boundaries information can't come directly
from the completion-table but have to arrive from the
*Completions* buffer.  I can't remember how we handle this in the
non-CRM case, but we need to use the same info.


=== Stefan