unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
@ 2023-08-07 23:24 Spencer Baugh
  2023-08-07 23:50 ` Spencer Baugh
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Spencer Baugh @ 2023-08-07 23:24 UTC (permalink / raw)
  To: 65137


The substring completion style differs from the "basic" style by
performing completion at the start of the input string.  So for example,
both of these are valid completions for "bar":

(completion-substring-all-completions "bar" '("foobar" "foobarbaz") #'identity (length "bar"))
-> ("foobarbaz" "foobar" . 0)

However, the substring completion style's try-completion implementation
does not reflect this.  Since "foobar" is a prefix of all the valid
completions, it should be returned by try-completion.  But it is not,
regardless of the location of point:

(completion-substring-try-completion "bar" '("foobar" "foobarbaz") #'identity 0)
-> ("bar" . 3)

(completion-substring-try-completion "bar" '("foobar" "foobarbaz") #'identity (length "bar"))
-> ("bar" . 3)

This breaks completion when one completion candidate is a prefix of
other completion candidates.  The recourse is moving point to the start
of the input, so that the "basic" completion style takes over, which
will correctly insert the common prefix:

(completion-basic-try-completion "bar" '("foobar" "foobarbaz") #'identity 0)
-> ("foobar" . 6)

However, even this does not work in the project-file and xref-location
completion categories, for which the "basic" style is not included in
completion-category-defaults.  For such completion categories, there's
simply no way to use completion to insert a common prefix.  This is bad,
because a filename or identifier might easily be a prefix of another
filename or identifier.

The solution is completion-substring-try-completion to be fixed to
insert these common prefixes.  I'll try and fix this, although the code
is a bit intimidating.



In GNU Emacs 29.1 (build 3, x86_64-pc-linux-gnu, X toolkit, cairo
 version 1.15.12, Xaw scroll bars) of 2023-07-29 built on

Repository revision: cf24c7ac7608f41078fd2761c856892d5853b676
Repository branch: my-emacs-29
Windowing system distributor 'The X.Org Foundation', version 11.0.12011000
System Description: Rocky Linux 8.8 (Green Obsidian)

Configured using:
 'configure --config-cache --with-x-toolkit=lucid
 --with-gif=ifavailable'

Configured features:
CAIRO DBUS FREETYPE GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG JSON
LIBSELINUX LIBSYSTEMD LIBXML2 MODULES NOTIFY INOTIFY PDUMPER PNG RSVG
SECCOMP SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS X11 XDBE XIM
XINPUT2 XPM LUCID ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: ELisp/l

Memory information:
((conses 16 1210667 128287)
 (symbols 48 75558 3)
 (strings 32 305623 26170)
 (string-bytes 1 9620792)
 (vectors 16 125723)
 (vector-slots 8 2816397 195041)
 (floats 8 762 279)
 (intervals 56 32710 336)
 (buffers 976 120))





^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-07 23:24 bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring Spencer Baugh
@ 2023-08-07 23:50 ` Spencer Baugh
  2023-08-08  0:41   ` Spencer Baugh
  2023-08-08 11:04 ` Eli Zaretskii
  2023-08-25  0:40 ` Dmitry Gutov
  2 siblings, 1 reply; 13+ messages in thread
From: Spencer Baugh @ 2023-08-07 23:50 UTC (permalink / raw)
  To: 65137


I've tracked it down further to:

(completion-pcm--merge-completions
 '("ab1" "ab2")
 '(prefix "b"))
-> (any "b")

which definitely doesn't match the docstring of
completion-pcm--merge-completions...  (note that it returns elements in
reverse order, so this is basically just returning the string "b")





^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-07 23:50 ` Spencer Baugh
@ 2023-08-08  0:41   ` Spencer Baugh
  0 siblings, 0 replies; 13+ messages in thread
From: Spencer Baugh @ 2023-08-08  0:41 UTC (permalink / raw)
  To: 65137

[-- Attachment #1: Type: text/plain, Size: 154 bytes --]


Here's a patch fixing this.  It's definitely just a bug in the
implementation of completion-pcm--merge-completions - see my commit
message for details.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Correctly-handle-common-prefixes-in-substring-comple.patch --]
[-- Type: text/x-patch, Size: 2975 bytes --]

From ea63217c1c85f1ca4f1f22b9c4781167de6cf00d Mon Sep 17 00:00:00 2001
From: Spencer Baugh <sbaugh@janestreet.com>
Date: Mon, 7 Aug 2023 20:20:34 -0400
Subject: [PATCH] Correctly handle common prefixes in substring completion

substring completion is implemented by passing the `prefix' symbol as
part of the pattern passed to completion-pcm--merge-completions.  This
symbol is supposed to "grow" the completion only as a suffix, not as a
prefix.

The old behavior of completion-pcm--merge-completions when processing
a `prefix' element in the pattern was to find the common prefix of all
the completions in that part of the pattern (using try-completion) and
then completely discard that common prefix.  Then the actual logic for
`prefix' would run with completion--common-suffix.

However, if all the completion candidates had a common prefix while
processing a `prefix' element, then it would both discard the common
prefix *and* skip the completion--common-suffix logic.  If there was
also a common suffix, e.g. with the following invocation:

  (completion-pcm--merge-completions '("axbc" "aybc") '(prefix "c"))

then this would produce the wrong result by ignoring the common suffix
"b".

The new behavior is to simply not bother generating the common prefix
for `prefix' elements, since we don't use it anyway, and just pretend
it's always empty for `prefix' elements.  Then the
completion--common-suffix logic always runs.

* lisp/minibuffer.el (completion-pcm--merge-completions): Don't ignore
a common suffix in a `prefix' pattern element when there's also a
common prefix.
---
 lisp/minibuffer.el | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index d2c7e66d2a0..1aa29318bd2 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -4029,12 +4029,15 @@ completion-pcm--merge-completions
               ;; different capitalizations in different parts.
               ;; In practice, it doesn't seem to make any difference.
               (setq ccs (nreverse ccs))
-              (let* ((prefix (try-completion fixed comps))
+              (let* ((prefix
+                      ;; We pretend as if there's no common prefix at all for
+                      ;; `prefix', so we go to completion--common-suffix
+                      (if (eq elem 'prefix) "" (try-completion fixed comps)))
                      (unique (or (and (eq prefix t) (setq prefix fixed))
                                  (and (stringp prefix)
+                                      (not (string-empty-p prefix))
                                       (eq t (try-completion prefix comps))))))
-                (unless (or (eq elem 'prefix)
-                            (equal prefix ""))
+                (unless (equal prefix "")
                   (push prefix res))
                 ;; If there's only one completion, `elem' is not useful
                 ;; any more: it can only match the empty string.
-- 
2.39.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-07 23:24 bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring Spencer Baugh
  2023-08-07 23:50 ` Spencer Baugh
@ 2023-08-08 11:04 ` Eli Zaretskii
  2023-08-08 12:40   ` sbaugh
  2023-08-25  0:40 ` Dmitry Gutov
  2 siblings, 1 reply; 13+ messages in thread
From: Eli Zaretskii @ 2023-08-08 11:04 UTC (permalink / raw)
  To: Spencer Baugh, Stefan Monnier; +Cc: 65137

> From: Spencer Baugh <sbaugh@janestreet.com>
> Date: Mon, 07 Aug 2023 19:24:15 -0400
> 
> 
> The substring completion style differs from the "basic" style by
> performing completion at the start of the input string.  So for example,
> both of these are valid completions for "bar":
> 
> (completion-substring-all-completions "bar" '("foobar" "foobarbaz") #'identity (length "bar"))
> -> ("foobarbaz" "foobar" . 0)
> 
> However, the substring completion style's try-completion implementation
> does not reflect this.  Since "foobar" is a prefix of all the valid
> completions, it should be returned by try-completion.  But it is not,
> regardless of the location of point:
> 
> (completion-substring-try-completion "bar" '("foobar" "foobarbaz") #'identity 0)
> -> ("bar" . 3)
> 
> (completion-substring-try-completion "bar" '("foobar" "foobarbaz") #'identity (length "bar"))
> -> ("bar" . 3)
> 
> This breaks completion when one completion candidate is a prefix of
> other completion candidates.  The recourse is moving point to the start
> of the input, so that the "basic" completion style takes over, which
> will correctly insert the common prefix:
> 
> (completion-basic-try-completion "bar" '("foobar" "foobarbaz") #'identity 0)
> -> ("foobar" . 6)
> 
> However, even this does not work in the project-file and xref-location
> completion categories, for which the "basic" style is not included in
> completion-category-defaults.  For such completion categories, there's
> simply no way to use completion to insert a common prefix.  This is bad,
> because a filename or identifier might easily be a prefix of another
> filename or identifier.
> 
> The solution is completion-substring-try-completion to be fixed to
> insert these common prefixes.  I'll try and fix this, although the code
> is a bit intimidating.

Adding Stefan, in case he has some comments or ideas.





^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-08 11:04 ` Eli Zaretskii
@ 2023-08-08 12:40   ` sbaugh
  2023-08-30  1:31     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 13+ messages in thread
From: sbaugh @ 2023-08-08 12:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Spencer Baugh, Stefan Monnier, 65137

[-- Attachment #1: Type: text/plain, Size: 2043 bytes --]

Eli Zaretskii <eliz@gnu.org> writes:
>> From: Spencer Baugh <sbaugh@janestreet.com>
>> Date: Mon, 07 Aug 2023 19:24:15 -0400
>> 
>> 
>> The substring completion style differs from the "basic" style by
>> performing completion at the start of the input string.  So for example,
>> both of these are valid completions for "bar":
>> 
>> (completion-substring-all-completions "bar" '("foobar" "foobarbaz") #'identity (length "bar"))
>> -> ("foobarbaz" "foobar" . 0)
>> 
>> However, the substring completion style's try-completion implementation
>> does not reflect this.  Since "foobar" is a prefix of all the valid
>> completions, it should be returned by try-completion.  But it is not,
>> regardless of the location of point:
>> 
>> (completion-substring-try-completion "bar" '("foobar" "foobarbaz") #'identity 0)
>> -> ("bar" . 3)
>> 
>> (completion-substring-try-completion "bar" '("foobar" "foobarbaz") #'identity (length "bar"))
>> -> ("bar" . 3)
>> 
>> This breaks completion when one completion candidate is a prefix of
>> other completion candidates.  The recourse is moving point to the start
>> of the input, so that the "basic" completion style takes over, which
>> will correctly insert the common prefix:
>> 
>> (completion-basic-try-completion "bar" '("foobar" "foobarbaz") #'identity 0)
>> -> ("foobar" . 6)
>> 
>> However, even this does not work in the project-file and xref-location
>> completion categories, for which the "basic" style is not included in
>> completion-category-defaults.  For such completion categories, there's
>> simply no way to use completion to insert a common prefix.  This is bad,
>> because a filename or identifier might easily be a prefix of another
>> filename or identifier.
>> 
>> The solution is completion-substring-try-completion to be fixed to
>> insert these common prefixes.  I'll try and fix this, although the code
>> is a bit intimidating.
>
> Adding Stefan, in case he has some comments or ideas.

See also (v2 of) my patch.  Now a one-line change, much simpler than my
earlier patch:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Correctly-handle-common-prefixes-in-substring-comple.patch --]
[-- Type: text/x-patch, Size: 2811 bytes --]

From 48627fdedf44aa48f235c4c550318d0ad2500c16 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <sbaugh@catern.com>
Date: Tue, 8 Aug 2023 08:37:49 -0400
Subject: [PATCH] Correctly handle common prefixes in substring completion

substring completion is implemented by passing the `prefix' symbol as
part of the pattern passed to completion-pcm--merge-completions.  This
symbol is supposed to "grow" the completion only as a suffix, not as a
prefix.

The old behavior of completion-pcm--merge-completions when processing
a `prefix' element in the pattern was to find the common prefix of all
the completions in that part of the pattern (using try-completion) and
then completely discard that common prefix.  Then the actual logic for
`prefix' would run with completion--common-suffix.

However, the completion--common-suffix logic would be skipped when the
prefix covers the entirety of this part of the pattern.  (When
"unique" is non-nil, in the code).  For example, in this call:

  (completion-pcm--merge-completions '("ab" "ab") '(star "b"))
  -> ("b" "a")

there is no need to calculate a common suffix of '("a" "a") after
finding the common prefix "a".  (Note the return value is in reverse
order.)

But for `prefix', we discard the common prefix, so this behavior would
result in us skipping the calculation of both common prefix and common
suffix.  Like in this call:

  (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
  -> ("b")

The correct behavior is to include the common prefix even for `prefix'
elements if it covers the entirety of this part of the pattern,
because then it is then also a common suffix.  Then we get:

  (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
  -> ("b" "a")

which is correct.

* lisp/minibuffer.el (completion-pcm--merge-completions): Don't ignore
a common suffix in a `prefix' pattern element when there's also a
common prefix.
---
 lisp/minibuffer.el | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 186a4753df1..cc50427b5bd 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -4029,7 +4029,9 @@ completion-pcm--merge-completions
                      (unique (or (and (eq prefix t) (setq prefix fixed))
                                  (and (stringp prefix)
                                       (eq t (try-completion prefix comps))))))
-                (unless (or (eq elem 'prefix)
+                ;; if the common prefix is unique, it also is a common
+                ;; suffix, so we should add it for `prefix' elements
+                (unless (or (and (eq elem 'prefix) (not unique))
                             (equal prefix ""))
                   (push prefix res))
                 ;; If there's only one completion, `elem' is not useful
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-07 23:24 bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring Spencer Baugh
  2023-08-07 23:50 ` Spencer Baugh
  2023-08-08 11:04 ` Eli Zaretskii
@ 2023-08-25  0:40 ` Dmitry Gutov
  2023-08-25  2:30   ` Drew Adams
  2023-08-29 15:45   ` Spencer Baugh
  2 siblings, 2 replies; 13+ messages in thread
From: Dmitry Gutov @ 2023-08-25  0:40 UTC (permalink / raw)
  To: Spencer Baugh, 65137

Hi Spencer!

On 08/08/2023 02:24, Spencer Baugh wrote:
> However, even this does not work in the project-file and xref-location
> completion categories, for which the "basic" style is not included in
> completion-category-defaults.  For such completion categories, there's
> simply no way to use completion to insert a common prefix.  This is bad,
> because a filename or identifier might easily be a prefix of another
> filename or identifier.

Could you describe the usage scenario a little more?

 From my brief testing, the current behavior seems okay most of the 
time: you still get the short input which matches a bunch of strings 
(e.g. filenames), you can type a little more chars and narrow down.

With your change, TAB will insert the most common prefix for all those 
completions, which in case of project-file can be a pretty long string. 
Not a huge problem, but on the face of it that doesn't seem like an 
improvement. So which scenario would that make better?





^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-25  0:40 ` Dmitry Gutov
@ 2023-08-25  2:30   ` Drew Adams
  2023-08-29 15:45   ` Spencer Baugh
  1 sibling, 0 replies; 13+ messages in thread
From: Drew Adams @ 2023-08-25  2:30 UTC (permalink / raw)
  To: Dmitry Gutov, Spencer Baugh, 65137@debbugs.gnu.org

> With your change, TAB will insert the most
> common prefix for all those completions,
> which in case of project-file can be a
> pretty long string.  Not a huge problem,
> but on the face of it that doesn't seem
> like an improvement. 

Caveat: Not following this thread at all.
So this is likely to be only noise; sorry,
if you find it so.
___

If some of the discussion has to do with
finding and showing the longest common
match across a set of matches for some
pattern, or even _a_ long common match,
then maybe code such as what I wrote for
Icicles, long ago, might be of interest.

For a set of matches of a given pattern,
depending on the pattern-matching approach,
there might not be _any_ common substring
of all the matches.  E.g., different
matches can use the pattern to match
different parts of the completions.

And even if there is a common substring,
there may not be a single such ("the"
common substring).  Or there may not be
a single longest such substring.

For straight prefix completion the matter
is straightforward.  For other kinds of
matching it's less so.  What I was most
interested in was regexp-matching (which
includes substring matching).

The code I came up with doesn't try to
be perfect.  I think it's pretty useful
in practice.  YMMV.  It amounts to this:

  It is the longest match of your input
  pattern that is common to all candidates
  and also contains the first input match
  in the first or second candidate,
  whichever is longer.

The reason this common-match expansion
typically finds the longest common match
is that your input typically matches the
first or the second candidate in only one
place.  And the longer the input you type,
the more likely this is.  In practice it's
only with very short input the expansion
sometimes misses the longest common match.

The algorithm independently tries two
candidates (first & second) as a starting
point, to increase the probability of
finding the longest common match.

I think it's useful, when showing the set
of completions (e.g. in *Completions*),
to highlight, in a candidate completion,
both (1) the part of it matched by the
pattern and (2) the part of it that's
common to all other matching candidates.
Two different faces are used for this.

The expanded common match found isn't just
a common substring of the completions
matched by your input pattern.  It's such
a substring that also matches your input.

It can be interesting to a user, and is
sometimes useful, to see what a given set
of matches have in common with each other
and with your pattern.  One thing they
can have in common is a common substring.

Besides highlighting a common substring,
Icicles can (optionally) expand/replace
your input pattern in the minibuffer as
well.  (You can hit `C-l' to retrieve the
pattern.)

Since I saw that you mentioned that the
common part can be long, and that can
distract users, I'll mention too that if
you use `C-x .' during completion that
toggles an option that hides (elides) the
common part.  E.g., file-finding commands
that expect an absolute file name can
turn on such eliding to start with.

https://www.emacswiki.org/emacs/Icicles_-_Expanded-Common-Match_Completion


^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-25  0:40 ` Dmitry Gutov
  2023-08-25  2:30   ` Drew Adams
@ 2023-08-29 15:45   ` Spencer Baugh
  2023-08-29 23:25     ` Dmitry Gutov
  1 sibling, 1 reply; 13+ messages in thread
From: Spencer Baugh @ 2023-08-29 15:45 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: 65137

Dmitry Gutov <dmitry@gutov.dev> writes:
> Hi Spencer!
>
> On 08/08/2023 02:24, Spencer Baugh wrote:
>> However, even this does not work in the project-file and xref-location
>> completion categories, for which the "basic" style is not included in
>> completion-category-defaults.  For such completion categories, there's
>> simply no way to use completion to insert a common prefix.  This is bad,
>> because a filename or identifier might easily be a prefix of another
>> filename or identifier.
>
> Could you describe the usage scenario a little more?
>
> From my brief testing, the current behavior seems okay most of the
> time: you still get the short input which matches a bunch of strings
> (e.g. filenames), you can type a little more chars and narrow down.
>
> With your change, TAB will insert the most common prefix for all those
> completions, which in case of project-file can be a pretty long
> string. Not a huge problem, but on the face of it that doesn't seem
> like an improvement. So which scenario would that make better?

As a very concrete example that I ran into frequently, for
project-find-file if you have two files:

dir/foo.ml
dir/foo.mli

and you input "foo" and press Tab, you get "foo.ml".  But it is then
impossible to expand that to dir/foo.ml using completion because of this
bug.  So you have to manually select dir/foo.ml if you want to visit
that file, either by switching to *Completions* and selecting it or by
using minibuffer-next-completion.

After this bugfix, inputting "foo" and pressing Tab will expand to
"dir/foo.ml".

In general, this bug makes it impossible to input a file name with
completion (in project-file) if that file name is a prefix of another
file name.  Like dir/foo and dir/foo.tar, or dir/foo.log and
dir/foo.log.bak.





^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-29 15:45   ` Spencer Baugh
@ 2023-08-29 23:25     ` Dmitry Gutov
  0 siblings, 0 replies; 13+ messages in thread
From: Dmitry Gutov @ 2023-08-29 23:25 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: 65137, Stefan Monnier

On 29/08/2023 18:45, Spencer Baugh wrote:

>> Could you describe the usage scenario a little more?
>>
>>  From my brief testing, the current behavior seems okay most of the
>> time: you still get the short input which matches a bunch of strings
>> (e.g. filenames), you can type a little more chars and narrow down.
>>
>> With your change, TAB will insert the most common prefix for all those
>> completions, which in case of project-file can be a pretty long
>> string. Not a huge problem, but on the face of it that doesn't seem
>> like an improvement. So which scenario would that make better?
> 
> As a very concrete example that I ran into frequently, for
> project-find-file if you have two files:
> 
> dir/foo.ml
> dir/foo.mli
> 
> and you input "foo" and press Tab, you get "foo.ml".  But it is then
> impossible to expand that to dir/foo.ml using completion because of this
> bug.  So you have to manually select dir/foo.ml if you want to visit
> that file, either by switching to *Completions* and selecting it or by
> using minibuffer-next-completion.

Now I understand, thank you. I guess this is something that certain 
environments (such as OCaml) are more prone to than others.

Also this problem seems somewhat unique to the default completion 
mechanism. If I use Ivy, or Helm, or Vertico, or even the "bare" 
icomplete-mode, they all have the notion of the currently selected 
completion, with a short key sequence to choose it.

E.g. with icomplete-mode on I would type until the needed completion is 
highlighted in the minibuffer and then press C-j (maybe press C-, or C-. 
to select it).

The default completion, as you say, has the means to do a similar thing 
with M-<up> and M-<down>, but it's less obvious and requires more 
keypresses.

> After this bugfix, inputting "foo" and pressing Tab will expand to
> "dir/foo.ml".
> 
> In general, this bug makes it impossible to input a file name with
> completion (in project-file) if that file name is a prefix of another
> file name.  Like dir/foo and dir/foo.tar, or dir/foo.log and
> dir/foo.log.bak.

Your patch fixes that by expanding inputs into longer lines after TAB 
(meaning, the user will see their input text shift, sometimes 
considerably, to the right, and perhaps feel a little disoriented). It 
might be a minor thing, but a downside nevertheless.

I do wonder what Stefan thinks what would be the right behavior here.





^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-08 12:40   ` sbaugh
@ 2023-08-30  1:31     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-05 19:51       ` Spencer Baugh
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-08-30  1:31 UTC (permalink / raw)
  To: sbaugh; +Cc: Spencer Baugh, Eli Zaretskii, 65137

>   (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
>   -> ("b")

Right this is a bug.

    (completion-pcm--merge-completions '("ab" "sab") '(prefix "b"))

returns (correctly)

    ("b" "a" prefix)

so

    (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))

should return either ("b" "a") or ("b" "a" prefix).

Could you accompany your patch of a regression test using
`completion-pcm--merge-completions` as above?

Similarly, I see that

    (completion-pcm--merge-completions '("abr" "absabr") '(prefix "br"))

returns

    ("br" prefix)

whereas it should arguably return

    ("br" "a" prefix)

[ Tho this may have the side effect that after this completion, `absabr`
  won't be considered any more, if the `basic` completion comes before
  `substring` :-(  ]


        Stefan






^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-08-30  1:31     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-09-05 19:51       ` Spencer Baugh
  2023-09-05 21:26         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-06 11:00         ` Eli Zaretskii
  0 siblings, 2 replies; 13+ messages in thread
From: Spencer Baugh @ 2023-09-05 19:51 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: sbaugh, Eli Zaretskii, 65137

[-- Attachment #1: Type: text/plain, Size: 540 bytes --]

Stefan Monnier <monnier@iro.umontreal.ca> writes:
>>   (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
>>   -> ("b")
>
> Right this is a bug.
>
>     (completion-pcm--merge-completions '("ab" "sab") '(prefix "b"))
>
> returns (correctly)
>
>     ("b" "a" prefix)
>
> so
>
>     (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
>
> should return either ("b" "a") or ("b" "a" prefix).
>
> Could you accompany your patch of a regression test using
> `completion-pcm--merge-completions` as above?

Patch with test:

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Correctly-handle-common-prefixes-in-substring-comple.patch --]
[-- Type: text/x-patch, Size: 4128 bytes --]

From 364a10aa54acd8de71a10d1ab98d059a27f26460 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <sbaugh@janestreet.com>
Date: Tue, 5 Sep 2023 15:40:06 -0400
Subject: [PATCH] Correctly handle common prefixes in substring completion

Substring completion would previously not complete the longest common
substring if that substring was a prefix of all the completion
alternatives.  Now it does.  An explanation of this bug

Substring completion is implemented by passing the `prefix' symbol as
part of the pattern passed to completion-pcm--merge-completions.  This
symbol is supposed to cause completion-pcm--merge-completions to
"grow" a completion of a common substring only from the "right" of the
symbol (a common suffix), not from the "left" of the symbol (a common
prefix).  Yes, this is the opposite of what the name `prefix' would
imply.

When processing a symbolic element of the pattern,
completion-pcm--merge-completions first finds the common prefix of all
the completions in that part of the pattern (using try-completion).
Then for `prefix' and other elements which want to complete a common
suffix, the common prefix is removed from each element and then the
common suffix is calculated with completion--common-suffix.

If the common prefix covers the entirety of all the alternatives
(i.e. when "unique" is true in the code), it's also a common suffix.
In that case, the common suffix calculation (if it runs) is basically
a no-op which will produce an empty string, since we removed the
common prefix before running it.

Before this change, `prefix' elements would unconditionally discard
the common prefix, which produced the wrong result in the case that
common prefix == common suffix.  For example:

  (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
  -> ("b")

Now we detect this situation and include the common prefix in this
case for `prefix' elements.  Then we get:

  (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
  -> ("b" "a")

which is correct.

* lisp/minibuffer.el (completion-pcm--merge-completions): Don't ignore
a common suffix in a `prefix' pattern element when it's also a common
prefix.
* test/lisp/minibuffer-tests.el (completion-substring-test-5): Add a
test.
---
 lisp/minibuffer.el            |  4 +++-
 test/lisp/minibuffer-tests.el | 13 +++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 35b359a75e2..ac054da61d2 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -4038,7 +4038,9 @@ completion-pcm--merge-completions
                      (unique (or (and (eq prefix t) (setq prefix fixed))
                                  (and (stringp prefix)
                                       (eq t (try-completion prefix comps))))))
-                (unless (or (eq elem 'prefix)
+                ;; if the common prefix is unique, it also is a common
+                ;; suffix, so we should add it for `prefix' elements
+                (unless (or (and (eq elem 'prefix) (not unique))
                             (equal prefix ""))
                   (push prefix res))
                 ;; If there's only one completion, `elem' is not useful
diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el
index ff58d35eb3e..4f92d7f841c 100644
--- a/test/lisp/minibuffer-tests.el
+++ b/test/lisp/minibuffer-tests.el
@@ -298,6 +298,19 @@ completion-substring-test-4
                   "jab" '("dabjabstabby" "many") nil 3)))
            6)))
 
+(ert-deftest completion-substring-test-5 ()
+  ;; merge-completions needs to work correctly when
+  (should (equal
+           (completion-pcm--merge-completions '("ab" "sab") '(prefix "b"))
+           '("b" "a" prefix)))
+  (should (equal
+           (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
+           '("b" "a")))
+  ;; substring completion should successfully complete the entire string
+  (should (equal
+           (completion-substring-try-completion "b" '("ab" "ab") nil 0)
+           '("ab" . 2))))
+
 (ert-deftest completion-flex-test-1 ()
   ;; Fuzzy match
   (should (equal
-- 
2.39.3


[-- Attachment #3: Type: text/plain, Size: 1014 bytes --]


> Similarly, I see that
>
>     (completion-pcm--merge-completions '("abr" "absabr") '(prefix "br"))
>
> returns
>
>     ("br" prefix)
>
> whereas it should arguably return
>
>     ("br" "a" prefix)
>
> [ Tho this may have the side effect that after this completion, `absabr`
>   won't be considered any more, if the `basic` completion comes before
>   `substring` :-(  ]

I did notice this too.  I could try fixing/changing this too, but it
does seem annoying when basic comes before substring - as it does by
default in a number of completion categories.

I wonder if we should move basic to after substring in those categories
in completion-category-defaults?  Or just remove basic from them.  It
doesn't seem like having both basic and substring in those lists has
much point.  Plus it would be a nice improvement to defaults - buffer
completion in particular confused me for a long time before I understood
how basic and substring interacted, so having only substring would ease
understanding for new users.

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-09-05 19:51       ` Spencer Baugh
@ 2023-09-05 21:26         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-06 11:00         ` Eli Zaretskii
  1 sibling, 0 replies; 13+ messages in thread
From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-05 21:26 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: sbaugh, Eli Zaretskii, 65137

> Patch with test:

Thanks, pushed.

>> Similarly, I see that
>>
>>     (completion-pcm--merge-completions '("abr" "absabr") '(prefix "br"))
>>
>> returns
>>
>>     ("br" prefix)
>>
>> whereas it should arguably return
>>
>>     ("br" "a" prefix)
>>
>> [ Tho this may have the side effect that after this completion, `absabr`
>>   won't be considered any more, if the `basic` completion comes before
>>   `substring` :-(  ]

> I did notice this too.  I could try fixing/changing this too, but it
> does seem annoying when basic comes before substring - as it does by
> default in a number of completion categories.

Yup :-(

I have often wished for there to be a way to remember the style that was
used so as to try and avoid such "style capture".

> I wonder if we should move basic to after substring in those
> categories in completion-category-defaults?

It's a tradeoff: the default is designed to reduce the factor of
surprise for people used to the default (i.e. to mostly prefix completion).

> Or just remove basic from them.

Same difference :-)

> It doesn't seem like having both basic and substring in those lists
> has much point.

If you're used to relying on `substring` indeed it's not helpful.
But if you're used to using mostly prefix-based completion (which may
have the effect that you choose your names such that prefix completion
works well), it can be helpful to have `substring` as a fallback when
you can't remember what prefix to use for the thing you're looking for.


        Stefan






^ permalink raw reply	[flat|nested] 13+ messages in thread

* bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring
  2023-09-05 19:51       ` Spencer Baugh
  2023-09-05 21:26         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-09-06 11:00         ` Eli Zaretskii
  1 sibling, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2023-09-06 11:00 UTC (permalink / raw)
  To: Spencer Baugh; +Cc: sbaugh, monnier, 65137

> From: Spencer Baugh <sbaugh@janestreet.com>
> Cc: sbaugh@catern.com,  Eli Zaretskii <eliz@gnu.org>,  65137@debbugs.gnu.org
> Date: Tue, 05 Sep 2023 15:51:14 -0400
> 
> 
> * lisp/minibuffer.el (completion-pcm--merge-completions): Don't ignore
> a common suffix in a `prefix' pattern element when it's also a common
> prefix.
> * test/lisp/minibuffer-tests.el (completion-substring-test-5): Add a
> test.

Please mention the bug number in the commit log message.

> +                ;; if the common prefix is unique, it also is a common
> +                ;; suffix, so we should add it for `prefix' elements

Comments should begin with a capital letter and end with a period.





^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2023-09-06 11:00 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-07 23:24 bug#65137: 29.1; completion-substring-try-completion doesn't return the longest common substring Spencer Baugh
2023-08-07 23:50 ` Spencer Baugh
2023-08-08  0:41   ` Spencer Baugh
2023-08-08 11:04 ` Eli Zaretskii
2023-08-08 12:40   ` sbaugh
2023-08-30  1:31     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-05 19:51       ` Spencer Baugh
2023-09-05 21:26         ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-06 11:00         ` Eli Zaretskii
2023-08-25  0:40 ` Dmitry Gutov
2023-08-25  2:30   ` Drew Adams
2023-08-29 15:45   ` Spencer Baugh
2023-08-29 23:25     ` Dmitry Gutov

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).