Subject: Re: bug#70217: [PATCH] Add substring-partial-completion style

From: Spencer Baugh <[email-masked]>
Date: Sun, 26 May 2024 09:02:13 -0400

Eli Zaretskii <[email-masked]> writes:
>> From: Spencer Baugh <[email-masked]>
>> Cc: [email-masked],  [email-masked]
>> Date: Sat, 25 May 2024 17:22:14 -0400
>> 
>> > This is a user option, so the doc string should be understandable by
>> > users, not just by Lisp programmers who tinker with completion stuff.
>> 
>> OK, reworded that docstring heavily.  Maybe it's better now?
>
> See below.
>
>> > Also, this new option should be in NEWS, I think.
>> 
>> Added to NEWS and documentation, and the same for the new possible
>> values of completion-styles.
>
> Thanks.
>
>> ++++
>> +*** 'completion-styles' now can contain lists of bindings
>                                                             ^
> A period is missing there

Fixed.

>> +An element of 'completion-styles' can be a symbol naming a completion
>> +style in 'completion-styles-alist'.  Now it can also be a list of the
>
> This is better rephrased like this:
>
>   In addition to being a symbol naming a completion style, an element
>   of 'completion-styles' can now be a list of the form...

Fixed

>> +form '(style ((variable value) ...))' where style is a symbol naming a
>
> We usually up-case the meta-syntactic variables in these cases, so
>
>    (STYLE ((VARIABLE VALUE) ...)

Fixed

>> +completion style.  'variable' will be bound to 'value' (without
>
> Up-case "variable" and "value", and don't quote them.

Fixed

>> +evaluating it) while the style is executing.  This allows duplicating a
>> +completion style with different values for completion-affecting
>
> Not "duplicating" (which sounds negative), but something like
> "multiple references to the same style".

Fixed

>> +An element can also be a list of the form \"(style ((variable value)
>> +...))\".  \"style\" must be a symbol listed in
>> +`completion-styles-alist', followed by a `let'-style list of
>> +variable/value pairs.  \"variable\" will be bound to \"value\" (without
>> +evaluating it) while the style is handling completion.  This allows
>> +repeating the same style with different configurations.
>
> Here, too, up-case STYLE, VARIABLE, and VALUE, and don't quote them,
> since they are not literal symbols, they are "placeholders" --
> references to something else.

Fixed

>> +(defcustom completion-pcm-anchored t
>> +  "If non-nil, PCM matches only against the start of completions.
>
> What is a "PCM"?  And what are "completions"?  I'm guessing you meant
> something like
>
>   If non-nil, completion candidates must match at beginning of completed string.

PCM is the abbreviation for partial-completion.  This variable only
affects the partial-completion style.  I was having trouble including
the entire string "partial-completion" in the first line, and also
explaining what the variable does, in under 80 characters.

Changed to not mention partial-completion in the first line for now at
least.

>> +For example: when the string being completed is \"b/c\",
>> +\"bbb/ccc\" is a valid completion according to
>> +partial-completion.  If this is nil, then \"aaa/bbb/ccc\" and
>> +\"aaabbb/ccc\" are also valid completions."
>
> Isn't this not the best example?  "b/c" does NOT match "bbb/ccc" at
> the beginning.  Or what am I missing?

b/c does match bbb/ccc at the beginning, according to the
partial-completion rules.  Explained as a glob, partial-completion turns
b/c into b*c which then can expand to bbb/ccc.

>From 04d8524363c90a79a8ebb82ede85c977bdfeebd7 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email-masked]>
Date: Sun, 26 May 2024 08:57:37 -0400
Subject: [PATCH] Allow customizing partial-completion to be more like
 substring

The substring completion style completes "foo-bar" as "*foo-bar*".  The
partial-completion completion style completes "foo-bar" as "foo*bar*".

Previously, it was not possible to get completion of "foo-bar" to act as
"*foo*bar*", e.g. combining the partial-completion and substring styles.
This would be especially useful for things like project-find-file.

Now it is possible by customizing the completion-pcm-anchored variable
to a non-nil value.

Furthermore, it's convenient to be able to run
regular (completion-pcm-anchored=t, non-substring) partial-completion
before running completion-pcm-anchored=nil partial-completion, since
the former provides more narrowly targeted completions.

It's possible to do this by customizing completion-styles.  Just add
'(partial-completion ((completion-pcm-anchored t))) and
'(partial-completion ((completion-pcm-anchored nil))) in that order.
Then the completion machinery will first run partial-completion with
completion-pcm-anchored=t, and if that returns no completions, run
partial-completion with completion-pcm-anchored=nil.

* lisp/minibuffer.el (completion--nth-completion): Allow an element of
completion-styles to contain a list of bindings.
(completion-styles): Document that.
(completion-pcm-anchored): Add.
(completion-pcm--string->pattern): Check completion-pcm-anchored.
(bug#70217)
---
 doc/emacs/mini.texi | 17 ++++++++++++--
 etc/NEWS            | 20 ++++++++++++++++
 lisp/minibuffer.el  | 57 +++++++++++++++++++++++++++++++++++++++------
 3 files changed, 85 insertions(+), 9 deletions(-)

diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi
index 4557f41c3f7..2e9766eb50c 100644
--- a/doc/emacs/mini.texi
+++ b/doc/emacs/mini.texi
@@ -535,8 +535,14 @@ Completion Styles
 
 @vindex completion-styles
   The list variable @code{completion-styles} specifies the completion
-styles to use.  Each list element is the name of a completion style (a
-Lisp symbol).  The available style symbols are stored in the variable
+styles to use.  Each list element is either the name of a completion
+style (a Lisp symbol) or a list starting with the name of a completion
+style followed by @code{let}-style list of bindings which will be in
+effect for that completion style.  Multiple elements of
+@code{completion-styles} can name the same completion style with
+different variable bindings.
+
+The available style symbols are stored in the variable
 @code{completion-styles-alist} (@pxref{Completion Variables,,, elisp,
 The Emacs Lisp Reference Manual}).  The default completion styles are
 (in order):
@@ -561,6 +567,13 @@ Completion Styles
 @dfn{wildcard}---it matches any string of characters at the
 corresponding position in the completion alternative.
 
+@vindex completion-pcm-anchored
+By default, a matching completion alternative must have the same
+beginning as the first word of the minibuffer text, but if
+@code{completion-pcm-anchored} is nil, the first word of the minibuffer
+text can start anywhere in a completion alternative.  For example,
+@samp{l-m} completes to @samp{emacs-lisp-mode}.
+
 @item emacs22
 @cindex @code{emacs22}, completion style
 This completion style is similar to @code{basic}, except that it
diff --git a/etc/NEWS b/etc/NEWS
index d058acc3572..596a2186db5 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1848,6 +1848,26 @@ customization group control exactly when Emacs displays this preview.
 'completion-preview-mode' is buffer-local, to enable it globally use
 'global-completion-preview-mode'.
 
++++
+*** New user option 'completion-pcm-anchored'.
+This option configures how the partial-completion style does completion.
+It defaults to t, which preserves the existing behavior.  When it is set
+to nil, the partial-completion style behaves more like the substring
+style, in that a string being completed can match against a candidate
+anywhere in the candidate string.
+
++++
+*** 'completion-styles' now can contain lists of bindings.
+In addition to being a symbol naming a completion style, an element of
+'completion-styles' can now be a list of the form '(STYLE ((VARIABLE
+VALUE) ...))' where STYLE is a symbol naming a completion style.
+VARIABLE will be bound to VALUE (without evaluating it) while the style
+is executing.  This allows multiple references to the same style with
+different values for completion-affecting variables like
+'completion-pcm-anchored' or 'completion-ignore-case'.  This also
+applies for the styles configuration in 'completion-category-overrides'
+and 'completion-category-defaults'.
+
 ---
 ** The highly accessible Modus themes collection has eight items.
 The 'modus-operandi' and 'modus-vivendi' are the main themes that have
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index f62cb2566b2..48e41492515 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -1141,7 +1141,15 @@ completion-styles
     ;; and simply add "bar" to the end of the result.
     emacs22)
   "List of completion styles to use.
-The available styles are listed in `completion-styles-alist'.
+An element should be a symbol which is listed in
+`completion-styles-alist'.
+
+An element can also be a list of the form
+(STYLE ((VARIABLE VALUE) ...))
+STYLE must be a symbol listed in `completion-styles-alist', followed by
+a `let'-style list of variable/value pairs.  VARIABLE will be bound to
+VALUE (without evaluating it) while the style is handling completion.
+This allows repeating the same style with different configurations.
 
 Note that `completion-category-overrides' may override these
 styles for specific categories, such as files, buffers, etc."
@@ -1284,11 +1292,18 @@ completion--nth-completion
          (result-and-style
           (seq-some
            (lambda (style)
-             (let ((probe (funcall
-                           (or (nth n (assq style completion-styles-alist))
-                               (error "Invalid completion style %s" style))
-                           string table pred point)))
-               (and probe (cons probe style))))
+             (let (symbols values)
+               (when (consp style)
+                 (dolist (binding (cadr style))
+                   (push (car binding) symbols)
+                   (push (cadr binding) values))
+                 (setq style (car style)))
+               (cl-progv symbols values
+                 (let ((probe (funcall
+                               (or (nth n (assq style completion-styles-alist))
+                                   (error "Invalid completion style %s" style))
+                               string table pred point)))
+                   (and probe (cons probe style))))))
            (completion--styles md)))
          (adjust-fn (get (cdr result-and-style) 'completion--adjust-metadata)))
     (when (and adjust-fn metadata)
@@ -3864,6 +3879,30 @@ completion-pcm--pattern-trivial-p
 	     (setq trivial nil)))
 	 trivial)))
 
+(defcustom completion-pcm-anchored t
+  "If non-nil, completion candidates must match at beginning of completed string.
+
+This only affects the partial-completion style, which filters the
+completion candidates based on whether they match the string being
+completed, according to the partial-completion rules.
+
+If this variable is non-nil, the string's match must begin at the
+start of a completion candidate.
+
+If this variable is nil, the string's match can begin anywhere in
+the completion candidate, similar to the substring completion
+style.  The behavior is identical to inserting a \"*\" at the
+start of the completion string.  Note that this can be slower to
+compute since less filtering of the completion candidates is
+possible.
+
+For example: when the string being completed is \"b/c\",
+\"bbb/ccc\" is a valid completion according to
+partial-completion.  If this is nil, then \"aaa/bbb/ccc\" and
+\"aaabbb/ccc\" are also valid completions."
+  :version "30.1"
+  :type 'boolean)
+
 (defun completion-pcm--string->pattern (string &optional point)
   "Split STRING into a pattern.
 A pattern is a list where each element is either a string
@@ -3914,7 +3953,11 @@ completion-pcm--string->pattern
       (when (> (length string) p0)
         (if pending (push pending pattern))
         (push (substring string p0) pattern))
-      (nreverse pattern))))
+      (setq pattern (nreverse pattern))
+      (unless completion-pcm-anchored
+        (when (stringp (car pattern))
+          (push 'prefix pattern)))
+      pattern)))
 
 (defun completion-pcm--optimize-pattern (p)
   ;; Remove empty strings in a separate phase since otherwise a ""
-- 
2.39.3