BUG: complete() doesn't respect :set completeopt=longest

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

BUG: complete() doesn't respect :set completeopt=longest

Ingo Karkat
Hi Vim developers,

I found a discrepancy between the two methods for custom insert-mode completion,
'completefunc' and complete(). The latter doesn't respect the ':set
completeopt+=longest' setting; its completions always expand to the first match,
not just the longest common text of matches.
I see no reason for not observing that setting, and think that this is a bug in
the complete() function.

This can be reproduced starting from Vim 7.0 up to 7.2.323, on Windows/x86 and
Linux/x86.

To reproduce, source attached file, which contains both types of
(calendar-month) completion functions, taken from the Vim help:

vim -N -u NONE
:source complete(func).vim
iM<F5><CR>
M<F6><CR><Esc>
" Both complete() and 'completefunc' expand to the first match, "March".
:set completeopt+=longest
iM<F5><CR>
M<F6><CR>
" complete() still expands to the full first match, "March".
" 'completefunc' correctly just inserts the longest common string, "Ma".

-- regards, ingo

PS: Speaking of discrepancies, another glaring one is that complete() takes
{startCol}, so range is [1, col('.')], but 'completefunc' has a zero-based range
of [0, col('.')[. Sadly, it would be even more stupid to change any of those two
now to correct the inconsistency.
--
   -- Ingo Karkat -- /^-- /^-- /^-- /^-- /^-- /^-- http://ingo-karkat.de/ --
   --      http://vim.sourceforge.net/account/profile.php?user_id=9713    --

inoremap <F5> <C-R>=ListMonths()<CR>

func! ListMonths()
    " locate the start of the word
    let line = getline('.')
    let start = col('.') - 1
    while start > 0 && line[start - 1] =~ '\a'
        let start -= 1
    endwhile
    " find months matching with "base"
    let base = strpart(line, start, (col('.') - start))
    let res = []
    for m in ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
        if m =~ '^' . base
        call add(res, m)
        endif
    endfor
    call complete(start + 1, res)
    return ''
endfunc

fun! CompleteMonths(findstart, base)
    if a:findstart
    " locate the start of the word
    let line = getline('.')
    let start = col('.') - 1
    while start > 0 && line[start - 1] =~ '\a'
        let start -= 1
    endwhile
    return start
    else
    " find months matching with "a:base"
    let res = []
    for m in ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
        if m =~ '^' . a:base
        call add(res, m)
        endif
    endfor
    return res
    endif
endfun
inoremap <F6> <C-o>:set completefunc=CompleteMonths<CR><C-x><C-u>


--
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
Reply | Threaded
Open this post in threaded view
|

Re: BUG: complete() doesn't respect :set completeopt=longest

Bram Moolenaar

Ingo Karkat wrote:

> Hi Vim developers,
>
> I found a discrepancy between the two methods for custom insert-mode
> completion, 'completefunc' and complete(). The latter doesn't respect
> the ':set completeopt+=longest' setting; its completions always expand
> to the first match, not just the longest common text of matches.  I
> see no reason for not observing that setting, and think that this is a
> bug in the complete() function.

No, this works as intended.  The whole idea of complete() is that the
script has full control over the matches.  If you want to return the
longest matching text, then pass that to complete().

'completefunc' and complete() are intentionally different.  Otherwise we
could omit one of them.

--
From "know your smileys":
 C=}>;*{)) Drunk, devilish chef with a toupee in an updraft,
           a mustache, and a double chin

 /// Bram Moolenaar -- [hidden email] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\        download, build and distribute -- http://www.A-A-P.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

--
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
Reply | Threaded
Open this post in threaded view
|

Re: BUG: complete() doesn't respect :set completeopt=longest

Ingo Karkat
On 12-Jan-2010 15:40, Bram Moolenaar wrote:

> Ingo Karkat wrote:
>> Hi Vim developers,
>>
>> I found a discrepancy between the two methods for custom insert-mode
>> completion, 'completefunc' and complete(). The latter doesn't respect
>> the ':set completeopt+=longest' setting; its completions always expand
>> to the first match, not just the longest common text of matches.  I
>> see no reason for not observing that setting, and think that this is a
>> bug in the complete() function.
>
> No, this works as intended.  The whole idea of complete() is that the
> script has full control over the matches.  If you want to return the
> longest matching text, then pass that to complete().
At least the way I use completion, 'completeopt=longest' is orthogonal to the
content and number of matches. Take the calendar-month example I used. When I
type "M", and perform completion, it'll offer "March" and "May"; by default (no
"longest"), the first match "March" is inserted.
I use 'completeopt=longest' to have only "Ma" inserted, so that I can refine and
select the match by typing additional letters instead of repeating CTRL-N to go
through all matches sequentially.
If my script passed only the common text "Ma" on completion, I would lose the
ability to preview all matches (via the "match n of m" message and the popup
menu), and would then have to re-start the custom completion in order to complete.

So, in my daily use 'completeopt=longest' is just a preference that allows to
gradually refine the number of matches while avoiding sequential browsing
through the list of matches, it has nothing to do with what kind of matches are
displayed.

> 'completefunc' and complete() are intentionally different.  Otherwise we
> could omit one of them.

Yes, there's quite some overlap; the way I've seen it, complete() offers a
simple interface for short lists of matches, while 'completefunc' is for more
complex implementations that may search multiple buffers for matches and
gradually build up the matches via complete_add().

If the behavior of 'completeopt=longest' is the actual difference, I wonder
whether it's not possible to emulate that by temporarily modifying the
'completeopt' value before invoking complete() (and why this fact isn't
mentioned in the help ;-).

I can probably re-write my script to use 'completefunc', so this isn't a serious
issue. In any case, thanks for clarifying this, Bram!

-- regards, ingo

--
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
Reply | Threaded
Open this post in threaded view
|

Re: BUG: complete() doesn't respect :set completeopt=longest

Ingo Karkat
On 12-Jan-2010 16:07, Ingo Karkat wrote:

> On 12-Jan-2010 15:40, Bram Moolenaar wrote:
>> No, this works as intended.  The whole idea of complete() is that the
>> script has full control over the matches.  If you want to return the
>> longest matching text, then pass that to complete().
>
> At least the way I use completion, 'completeopt=longest' is orthogonal
> to the content and number of matches. Take the calendar-month example I
> used. When I type "M", and perform completion, it'll offer "March" and
> "May"; by default (no "longest"), the first match "March" is inserted.
> I use 'completeopt=longest' to have only "Ma" inserted, so that I can
> refine and select the match by typing additional letters instead of
> repeating CTRL-N to go through all matches sequentially.
> If my script passed only the common text "Ma" on completion, I would
> lose the ability to preview all matches (via the "match n of m" message
> and the popup menu), and would then have to re-start the custom
> completion in order to complete.
>
> So, in my daily use 'completeopt=longest' is just a preference that
> allows to gradually refine the number of matches while avoiding
> sequential browsing through the list of matches, it has nothing to do
> with what kind of matches are displayed.
After some more days of contemplation, I still find this behavior odd. Bram, can
your standpoint be paraphased as such: complete() doesn't respect
'completeopt=longest', because it may result in only partial insertion of match
text, and complete() should always offer the script full control over what is
inserted.

(But then, what's the point of protecting the inserted match; I can always use
<BS> to partially remove text from the match after completion?! And, if a script
really wants this, it could temporarily remove 'longest' from 'completeopt' to
achieve this. Are there any examples where this is useful?)

Please give me a short note if you won't consider changing this; I will then
submit a patch for the help, as I think this special behavior needs to be
mentioned somewhere.

-- regards, ingo

--
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
Reply | Threaded
Open this post in threaded view
|

Re: BUG: complete() doesn't respect :set completeopt=longest

Bram Moolenaar

Ingo Karkat wrote:

> On 12-Jan-2010 16:07, Ingo Karkat wrote:
> > On 12-Jan-2010 15:40, Bram Moolenaar wrote:
> >> No, this works as intended.  The whole idea of complete() is that the
> >> script has full control over the matches.  If you want to return the
> >> longest matching text, then pass that to complete().
> >
> > At least the way I use completion, 'completeopt=longest' is orthogonal
> > to the content and number of matches. Take the calendar-month example I
> > used. When I type "M", and perform completion, it'll offer "March" and
> > "May"; by default (no "longest"), the first match "March" is inserted.
> > I use 'completeopt=longest' to have only "Ma" inserted, so that I can
> > refine and select the match by typing additional letters instead of
> > repeating CTRL-N to go through all matches sequentially.
> > If my script passed only the common text "Ma" on completion, I would
> > lose the ability to preview all matches (via the "match n of m" message
> > and the popup menu), and would then have to re-start the custom
> > completion in order to complete.
> >
> > So, in my daily use 'completeopt=longest' is just a preference that
> > allows to gradually refine the number of matches while avoiding
> > sequential browsing through the list of matches, it has nothing to do
> > with what kind of matches are displayed.
>
> After some more days of contemplation, I still find this behavior odd.
> Bram, can your standpoint be paraphased as such: complete() doesn't
> respect 'completeopt=longest', because it may result in only partial
> insertion of match text, and complete() should always offer the script
> full control over what is inserted.
>
> (But then, what's the point of protecting the inserted match; I can
> always use <BS> to partially remove text from the match after
> completion?! And, if a script really wants this, it could temporarily
> remove 'longest' from 'completeopt' to achieve this. Are there any
> examples where this is useful?)
>
> Please give me a short note if you won't consider changing this; I
> will then submit a patch for the help, as I think this special
> behavior needs to be mentioned somewhere.
You can try changing the source code to make it work like you want.
Problem is that the insert completion is a bit of a mess and very hard
to understand.  I rather leave it alone.

Perhaps you can insert this line:

        compl_get_longest = (vim_strchr(p_cot, 'l') != NULL);

near the end of set_completion().  Might work, might break some
things...

--
hundred-and-one symptoms of being an internet addict:
123. You ask the car dealer to install an extra cigarette lighter
     on your new car to power your notebook.

 /// Bram Moolenaar -- [hidden email] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\        download, build and distribute -- http://www.A-A-P.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

--
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
Reply | Threaded
Open this post in threaded view
|

Re: BUG: complete() doesn't respect :set completeopt=longest

Ingo Karkat
On 19-Jan-2010 17:25, Bram Moolenaar wrote:

> Ingo Karkat wrote:
>> After some more days of contemplation, I still find this behavior odd.
>> Bram, can your standpoint be paraphased as such: complete() doesn't
>> respect 'completeopt=longest', because it may result in only partial
>> insertion of match text, and complete() should always offer the script
>> full control over what is inserted.
>>
>> (But then, what's the point of protecting the inserted match; I can
>> always use <BS> to partially remove text from the match after
>> completion?! And, if a script really wants this, it could temporarily
>> remove 'longest' from 'completeopt' to achieve this. Are there any
>> examples where this is useful?)
>>
>> Please give me a short note if you won't consider changing this; I
>> will then submit a patch for the help, as I think this special
>> behavior needs to be mentioned somewhere.
>
> You can try changing the source code to make it work like you want.
> Problem is that the insert completion is a bit of a mess and very hard
> to understand.  I rather leave it alone.
>
> Perhaps you can insert this line:
>
> compl_get_longest = (vim_strchr(p_cot, 'l') != NULL);
>
> near the end of set_completion().  Might work, might break some
> things...
All right, so my reasoning isn't totally off and you're open to changes in this
area. Thanks!
As I mentioned, it's quite easy to switch to 'completefunc' to work around this
issue. But anyway, I put it on my list to look into next time I dig into Vim's
sources. (I'm more into Vim scripting.) This might warrant a low-priority entry
on todo.txt, too, so that maybe someone else is inclined to consider this.

> the insert completion is a bit of a mess

Sorry to hear; this is such a great and powerful feature.

-- regards, ingo

--
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php