patch 9.1.1178: not possible to generate completion candidates using fuzzy 
matching

Commit: 
https://github.com/vim/vim/commit/f31cfa29bf72b0cdf6fa1b60346ea4e187bcafd1
Author: glepnir <glephun...@gmail.com>
Date:   Thu Mar 6 21:59:13 2025 +0100

    patch 9.1.1178: not possible to generate completion candidates using fuzzy 
matching
    
    Problem:  not possible to generate completion candidates using fuzzy
              matching
    Solution: add the 'completefuzzycollect' option for (some) ins-completion
              modes (glepnir)
    
    fixes #15296
    fixes #15295
    fixes #15294
    closes: #16032
    
    Signed-off-by: glepnir <glephun...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 883a67f67..6afe32f1e 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.1.  Last change: 2025 Mar 03
+*options.txt*  For Vim version 9.1.  Last change: 2025 Mar 06
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -2104,6 +2104,18 @@ A jump table for the options with a short description 
can be found at |Q_op|.
        This option cannot be set from a |modeline| or in the |sandbox|, for
        security reasons.
 
+                                               *'completefuzzycollect'* *'cfc'*
+'completefuzzycollect' 'cfc'   string  (default: empty)
+                               global
+       This option enables fuzzy collection for (only some) specific
+       |ins-completion| modes, adjusting how items are gathered for fuzzy
+       matching based on input.
+       The option can contain the following values (separated by commas),
+       each enabling fuzzy collection for a specific completion mode:
+       files           file names
+       keyword         keyword completion in 'complete' and current file
+       whole_line      whole lines
+
                                                *'completeitemalign'* *'cia'*
 'completeitemalign' 'cia' string (default: "abbr,kind,menu")
                          global
@@ -2123,7 +2135,12 @@ A jump table for the options with a short description 
can be found at |Q_op|.
           fuzzy    Enable |fuzzy-matching| for completion candidates. This
                    allows for more flexible and intuitive matching, where
                    characters can be skipped and matches can be found even
-                   if the exact sequence is not typed.
+                   if the exact sequence is not typed. Note: This option
+                   does not affect the collection of candidate list, it only
+                   controls how completion candidates are reduced from the
+                   list of alternatives. If you want to use |fuzzy-matching|
+                   to gather more alternatives for your candidate list,
+                   see |'completefuzzycollect'|.
 
           longest  Only insert the longest common text of the matches.  If
                    the menu is displayed you can use CTRL-L to add more
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 49a849ef8..6aee799d0 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -129,6 +129,7 @@ $quote      eval.txt        /*$quote*
 'cdpath'       options.txt     /*'cdpath'*
 'cedit'        options.txt     /*'cedit'*
 'cf'   options.txt     /*'cf'*
+'cfc'  options.txt     /*'cfc'*
 'cfu'  options.txt     /*'cfu'*
 'ch'   options.txt     /*'ch'*
 'character'    intro.txt       /*'character'*
@@ -162,6 +163,7 @@ $quote      eval.txt        /*$quote*
 'compatible'   options.txt     /*'compatible'*
 'complete'     options.txt     /*'complete'*
 'completefunc' options.txt     /*'completefunc'*
+'completefuzzycollect' options.txt     /*'completefuzzycollect'*
 'completeitemalign'    options.txt     /*'completeitemalign'*
 'completeopt'  options.txt     /*'completeopt'*
 'completepopup'        options.txt     /*'completepopup'*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 5edadcec4..f12bcd4c3 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2025 Mar 03
+*version9.txt*  For Vim version 9.1.  Last change: 2025 Mar 06
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41708,6 +41708,8 @@ Commands: ~
 
 Options: ~
 
+'completefuzzycollect' Enable fuzzy collection of candiates for (some)
+                       |ins-completion| modes
 'completeitemalign'    Order of |complete-items| in Insert mode completion
                        popup
 'eventignorewin'       autocommand events that are ignored in a window
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 996e14142..2c28ee1fc 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -848,6 +848,8 @@ endif
 if has("insert_expand")
   call <SID>AddOption("complete", gettext("specifies how Insert mode 
completion works for CTRL-N and CTRL-P"))
   call append("$", "   " .. s:local_to_buffer)
+  call <SID>OptionL("cfc")
+  call <SID>AddOption("completefuzzycollect", gettext("using fuzzy collect for 
defaule completion mode"))
   call <SID>OptionL("cpt")
   call <SID>AddOption("completeopt", gettext("whether to use a popup menu for 
Insert mode completion"))
   call <SID>OptionL("cot")
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 5cab4aeba..e83c9b2ef 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <dougkea...@gmail.com>
-" Last Change:    2025 Feb 27
+" Last Change:    2025 Mar 06
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
@@ -42,13 +42,13 @@ syn keyword vimStdPlugin contained  Arguments Asm Break 
Cfilter Clear Continue Di
 " vimOptions are caught only when contained in a vimSet {{{2
 " GEN_SYN_VIM: vimOption normal, START_STR='syn keyword vimOption contained', 
END_STR='skipwhite nextgroup=vimSetEqual,vimSetMod'
 syn keyword vimOption contained al aleph ari allowrevins ambw ambiwidth arab 
arabic arshape arabicshape acd autochdir ai autoindent ar autoread asd 
autoshelldir aw autowrite awa autowriteall bg background bs backspace bk backup 
bkc backupcopy bdir backupdir bex backupext bsk backupskip bdlay balloondelay 
beval ballooneval bevalterm balloonevalterm bexpr balloonexpr bo belloff bin 
binary bomb brk breakat bri breakindent briopt breakindentopt bsdir browsedir 
bh bufhidden bl buflisted bt buftype cmp casemap cdh cdhome cd cdpath cedit ccv 
charconvert cin cindent cink cinkeys cino cinoptions cinsd cinscopedecls cinw 
cinwords cb clipboard ch cmdheight cwh cmdwinheight cc colorcolumn co columns 
com comments cms commentstring cp compatible cpt complete cfu completefunc 
skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained cia completeitemalign cot completeopt cpp 
completepopup csl completeslash cocu concealcursor cole conceallevel cf confirm 
ci copyindent cpo cpoptions cm cryptmethod cspc cscopepathcomp csprg cscopeprg 
csqf cscopequickfix csre cscoperelative cst cscopetag csto cscopetagorder 
csverb cscopeverbose crb cursorbind cuc cursorcolumn cul cursorline culopt 
cursorlineopt debug def define deco delcombine dict dictionary diff dex 
diffexpr dip diffopt dg digraph dir directory dy display ead eadirection ed 
edcompatible emo emoji enc encoding eof endoffile eol endofline ea equalalways 
ep equalprg eb errorbells ef errorfile efm errorformat ek esckeys ei 
eventignore eiw eventignorewin et expandtab ex exrc fenc fileencoding fencs 
fileencodings ff fileformat skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained ffs fileformats fic fileignorecase ft filetype 
fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen 
foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr 
foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt 
foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs 
fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs 
guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions 
guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang 
hid hidden hl highlight hi history hk hkmap hkp hkmapp hls hlsearch icon 
iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline 
imd imdisable skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained imi iminsert ims imsearch imsf imstatusfunc 
imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk 
indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword 
isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc 
keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm 
langremap ls laststatus lz lazyredraw lbr linebreak lines lsp linespace lisp 
lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic 
mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco 
maxcombine mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt 
maxmemtot mis menuitems mopt messagesopt msm mkspellmem ml modeline mle 
modelineexpr mls modelines skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained ma modifiable mod modified more mouse mousef 
mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses 
mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf 
nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc 
operatorfunc pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm 
patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup 
pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn 
printfont pheader printheader pmbcs printmbcharset pmbfn printmbfont popt 
printoptions prompt ph pumheight pw pumwidth pythondll pythonhome 
pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe 
quoteescape ro readonly rdt redrawtime skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained re regexpengine rnu relativenumber remap rop 
renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd 
rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf 
scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel 
selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp 
shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st 
shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm 
shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag 
sm showmatch smd showmode stal showtabline ss sidescroll siso sidescrolloff scl 
signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts 
softtabstop skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained spell spc spellcapcheck spf spellfile spl 
spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr 
splitright sol startofline stl statusline su suffixes sua suffixesadd swf 
swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal 
tabline tpm tabpagemax ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl 
taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc 
termencoding tgc termguicolors twk termwinkey twsl termwinscroll tws 
termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr 
thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title 
titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm 
ttimeoutlen tbi ttybuiltin skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained tf ttyfast ttym ttymouse tsl ttyscroll tty 
ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut 
updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile 
vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb 
visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig 
wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak 
winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw 
winfixwidth wh winheight wmh winminheight wmw winminwidth winptydll wiw 
winwidth wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd 
writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained cfc completefuzzycollect cia completeitemalign 
cot completeopt cpp completepopup csl completeslash cocu concealcursor cole 
conceallevel cf confirm ci copyindent cpo cpoptions cm cryptmethod cspc 
cscopepathcomp csprg cscopeprg csqf cscopequickfix csre cscoperelative cst 
cscopetag csto cscopetagorder csverb cscopeverbose crb cursorbind cuc 
cursorcolumn cul cursorline culopt cursorlineopt debug def define deco 
delcombine dict dictionary diff dex diffexpr dip diffopt dg digraph dir 
directory dy display ead eadirection ed edcompatible emo emoji enc encoding eof 
endoffile eol endofline ea equalalways ep equalprg eb errorbells ef errorfile 
efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab ex 
exrc fenc fileencoding skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained fencs fileencodings ff fileformat ffs 
fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol 
fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi 
foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml 
foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp 
formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat 
gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr 
guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt 
guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight hi 
history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf 
imactivatefunc imak imactivatekey skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained imc imcmdline imd imdisable imi iminsert ims 
imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is 
incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf 
isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key 
kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu 
lnr langnoremap lrm langremap ls laststatus lz lazyredraw lbr linebreak lines 
lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl 
loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs 
mat matchtime mco maxcombine mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp 
maxmempattern mmt maxmemtot mis menuitems mopt messagesopt msm mkspellmem ml 
modeline skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained mle modelineexpr mls modelines ma modifiable 
mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel 
mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum 
mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc 
odev opendevice opfunc operatorfunc pp packpath para paragraphs paste pt 
pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh 
previewheight pvp previewpopup pvw previewwindow pdev printdevice penc 
printencoding pexpr printexpr pfn printfont pheader printheader pmbcs 
printmbcharset pmbfn printmbfont popt printoptions prompt ph pumheight pw 
pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion 
qftf quickfixtextfunc qe quoteescape skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained ro readonly rdt redrawtime re regexpengine rnu 
relativenumber remap rop renderoptions report rs restorescreen ri revins rl 
rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr 
scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt 
sect sections secure sel selection slm selectmode ssop sessionoptions sh shell 
shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash 
stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw 
shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc 
sft showfulltag sm showmatch smd showmode stal showtabline ss sidescroll siso 
sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab 
skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained sms smoothscroll sts softtabstop spell spc 
spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb 
splitbelow spk splitkeep spr splitright sol startofline stl statusline su 
suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol 
syn syntax tcl tabclose tal tabline tpm tabpagemax ts tabstop tbs tagbsearch tc 
tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll 
term tbidi termbidi tenc termencoding tgc termguicolors twk termwinkey twsl 
termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw 
textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm 
timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize 
ttimeout skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym 
ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur 
undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs 
verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif 
viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc 
wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim 
wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf 
wfh winfixheight wfw winfixwidth wh winheight wmh winminheight wmw winminwidth 
winptydll wiw winwidth wrap wm wrapmargin ws wrapscan write wa writeany wb 
writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod
 
 " vimOptions: These are the turn-off setting variants {{{2
 " GEN_SYN_VIM: vimOption turn-off, START_STR='syn keyword vimOption 
contained', END_STR=''
diff --git a/src/insexpand.c b/src/insexpand.c
index 1616a0f3f..9769c46c2 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -132,6 +132,13 @@ static compl_T    *compl_curr_match = NULL;
 static compl_T    *compl_shown_match = NULL;
 static compl_T    *compl_old_match = NULL;
 
+// list used to store the compl_T which have the max score
+// used for completefuzzycollect
+static compl_T   **compl_best_matches = NULL;
+static int       compl_num_bests = 0;
+// inserted a longest when completefuzzycollect enabled
+static int       compl_cfc_longest_ins = FALSE;
+
 // After using a cursor key <Enter> selects a match in the popup menu,
 // otherwise it inserts a line break.
 static int       compl_enter_selects = FALSE;
@@ -206,7 +213,7 @@ static int    *compl_fuzzy_scores;
 static pumitem_T *compl_match_array = NULL;
 static int compl_match_arraysize;
 
-static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, 
typval_T *user_data, int cdir, int flags, int adup, int *user_hl);
+static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, 
typval_T *user_data, int cdir, int flags, int adup, int *user_hl, int score);
 static void ins_compl_longest_match(compl_T *match);
 static void ins_compl_del_pum(void);
 static void ins_compl_files(int count, char_u **files, int thesaurus, int 
flags, regmatch_T *regmatch, char_u *buf, int *dir);
@@ -229,6 +236,7 @@ static void show_pum(int prev_w_wrow, int prev_w_leftcol);
 static unsigned  quote_meta(char_u *dest, char_u *str, int len);
 static int ins_compl_has_multiple(void);
 static void ins_compl_expand_multiple(char_u *str);
+static void ins_compl_longest_insert(char_u *prefix);
 
 #ifdef FEAT_SPELL
 static void spell_back_to_badword(void);
@@ -686,7 +694,8 @@ ins_compl_add_infercase(
     int                icase,
     char_u     *fname,
     int                dir,
-    int                cont_s_ipos)  // next ^X<> will set initial_pos
+    int                cont_s_ipos,  // next ^X<> will set initial_pos
+    int                score)
 {
     char_u     *str = str_arg;
     char_u     *p;
@@ -745,11 +754,30 @@ ins_compl_add_infercase(
     if (icase)
        flags |= CP_ICASE;
 
-    res = ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE, NULL);
+    res = ins_compl_add(str, len, fname, NULL, NULL, dir, flags, FALSE, NULL, 
score);
     vim_free(tofree);
     return res;
 }
 
+/*
+ * Check if ctrl_x_mode has been configured in 'completefuzzycollect'
+ */
+    static int
+cfc_has_mode(void)
+{
+    switch (ctrl_x_mode)
+    {
+       case CTRL_X_NORMAL:
+           return (cfc_flags & CFC_KEYWORD) != 0;
+       case CTRL_X_FILES:
+           return (cfc_flags & CFC_FILES) != 0;
+       case CTRL_X_WHOLE_LINE:
+           return (cfc_flags & CFC_WHOLELINE) != 0;
+       default:
+           return FALSE;
+    }
+}
+
 /*
  * Add a match to the list of matches. The arguments are:
  *     str       - text of the match to add
@@ -780,11 +808,13 @@ ins_compl_add(
     int                cdir,
     int                flags_arg,
     int                adup,               // accept duplicate match
-    int                *user_hl)           // user abbr/kind hlattr
+    int                *user_hl,           // user abbr/kind hlattr
+    int                score)
 {
-    compl_T    *match;
+    compl_T    *match, *current, *prev;
     int                dir = (cdir == 0 ? compl_direction : cdir);
     int                flags = flags_arg;
+    int                inserted = FALSE;
 
     if (flags & CP_FAST)
        fast_breakcheck();
@@ -846,6 +876,7 @@ ins_compl_add(
     match->cp_flags = flags;
     match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1;
     match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1;
+    match->cp_score = score;
 
     if (cptext != NULL)
     {
@@ -866,6 +897,37 @@ ins_compl_add(
     // current match in the list of matches .
     if (compl_first_match == NULL)
        match->cp_next = match->cp_prev = NULL;
+    else if (cfc_has_mode() && score > 0 && compl_get_longest)
+    {
+       current = compl_first_match->cp_next;
+       prev = compl_first_match;
+       inserted = FALSE;
+       // The direction is ignored when using longest and
+       // completefuzzycollect, because matches are inserted
+       // and sorted by score.
+       while (current != NULL && current != compl_first_match)
+       {
+           if (current->cp_score < score)
+           {
+               match->cp_next = current;
+               match->cp_prev = current->cp_prev;
+               if (current->cp_prev)
+                   current->cp_prev->cp_next = match;
+               current->cp_prev = match;
+               inserted = TRUE;
+               break;
+           }
+           prev = current;
+           current = current->cp_next;
+       }
+       if (!inserted)
+       {
+           prev->cp_next = match;
+           match->cp_prev = prev;
+           match->cp_next = compl_first_match;
+           compl_first_match->cp_prev = match;
+       }
+    }
     else if (dir == FORWARD)
     {
        match->cp_next = compl_curr_match->cp_next;
@@ -885,7 +947,7 @@ ins_compl_add(
     compl_curr_match = match;
 
     // Find the longest common string if still doing that.
-    if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0)
+    if (compl_get_longest && (flags & CP_ORIGINAL_TEXT) == 0 && 
!cfc_has_mode())
        ins_compl_longest_match(match);
 
     return OK;
@@ -987,9 +1049,7 @@ ins_compl_longest_match(compl_T *match)
 
        compl_leader.length = match->cp_str.length;
        had_match = (curwin->w_cursor.col > compl_col);
-       ins_compl_delete();
-       ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
-       ins_redraw(FALSE);
+       ins_compl_longest_insert(compl_leader.string);
 
        // When the match isn't there (to avoid matching itself) remove it
        // again after redrawing.
@@ -1037,9 +1097,7 @@ ins_compl_longest_match(compl_T *match)
        compl_leader.length = (size_t)(p - compl_leader.string);
 
        had_match = (curwin->w_cursor.col > compl_col);
-       ins_compl_delete();
-       ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
-       ins_redraw(FALSE);
+       ins_compl_longest_insert(compl_leader.string);
 
        // When the match isn't there (to avoid matching itself) remove it
        // again after redrawing.
@@ -1067,7 +1125,7 @@ ins_compl_add_matches(
     for (i = 0; i < num_matches && add_r != FAIL; i++)
     {
        add_r = ins_compl_add(matches[i], -1, NULL, NULL, NULL, dir,
-                               CP_FAST | (icase ? CP_ICASE : 0), FALSE, NULL);
+                               CP_FAST | (icase ? CP_ICASE : 0), FALSE, NULL, 
0);
        if (add_r == OK)
            // if dir was BACKWARD then honor it just once
            dir = FORWARD;
@@ -1298,6 +1356,7 @@ ins_compl_build_pum(void)
     int                compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
     int                fuzzy_filter = (cur_cot_flags & COT_FUZZY) != 0;
     int                fuzzy_sort = fuzzy_filter && !(cur_cot_flags & 
COT_NOSORT);
+
     compl_T    *match_head = NULL;
     compl_T    *match_tail = NULL;
     compl_T    *match_next = NULL;
@@ -1644,7 +1703,7 @@ ins_compl_dictionaries(
            if (count > 0)      // avoid warning for using "files" uninit
        {
            ins_compl_files(count, files, thesaurus, flags,
-                                                       &regmatch, buf, &dir);
+                           (cfc_has_mode() ? NULL : &regmatch), buf, &dir);
            if (flags != DICT_EXACT)
                FreeWild(count, files);
        }
@@ -1704,7 +1763,7 @@ thesaurus_add_words_in_line(
        if (wstart != skip_word)
        {
            status = ins_compl_add_infercase(wstart, (int)(ptr - wstart), p_ic,
-                                                       fname, dir, FALSE);
+                                                       fname, dir, FALSE, 0);
            if (status == FAIL)
                break;
        }
@@ -1732,6 +1791,18 @@ ins_compl_files(
     int                i;
     FILE       *fp;
     int                add_r;
+    char_u     *leader = NULL;
+    int                leader_len = 0;
+    int                in_fuzzy_collect = cfc_has_mode() && 
ctrl_x_mode_normal();
+    int                score = 0;
+    int                len = 0;
+    char_u     *line_end = NULL;
+
+    if (in_fuzzy_collect)
+    {
+       leader = ins_compl_leader();
+       leader_len = ins_compl_leader_len();
+    }
 
     for (i = 0; i < count && !got_int && !compl_interrupted; i++)
     {
@@ -1752,30 +1823,56 @@ ins_compl_files(
        while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp))
        {
            ptr = buf;
-           while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf)))
+           if (regmatch != NULL)
            {
-               ptr = regmatch->startp[0];
-               ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr)
-                                                       : find_word_end(ptr);
-               add_r = ins_compl_add_infercase(regmatch->startp[0],
-                       (int)(ptr - regmatch->startp[0]),
-                       p_ic, files[i], *dir, FALSE);
-               if (thesaurus)
+               while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf)))
                {
-                   // For a thesaurus, add all the words in the line
-                   ptr = buf;
-                   add_r = thesaurus_add_words_in_line(files[i], &ptr, *dir,
-                                                       regmatch->startp[0]);
+                   ptr = regmatch->startp[0];
+                   ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr)
+                                                   : find_word_end(ptr);
+                   add_r = ins_compl_add_infercase(regmatch->startp[0],
+                           (int)(ptr - regmatch->startp[0]),
+                           p_ic, files[i], *dir, FALSE, 0);
+                   if (thesaurus)
+                   {
+                       // For a thesaurus, add all the words in the line
+                       ptr = buf;
+                       add_r = thesaurus_add_words_in_line(files[i], &ptr, 
*dir,
+                                                           
regmatch->startp[0]);
+                   }
+                   if (add_r == OK)
+                       // if dir was BACKWARD then honor it just once
+                       *dir = FORWARD;
+                   else if (add_r == FAIL)
+                       break;
+                   // avoid expensive call to vim_regexec() when at end
+                   // of line
+                   if (*ptr == '
' || got_int)
+                       break;
+               }
+           }
+           else if (in_fuzzy_collect && leader_len > 0)
+           {
+               line_end = find_line_end(ptr);
+               while (ptr < line_end)
+               {
+                   if (fuzzy_match_str_in_line(&ptr, leader, &len, NULL, 
&score))
+                   {
+                       char_u *end_ptr = ctrl_x_mode_line_or_eval()
+                                       ? find_line_end(ptr) : 
find_word_end(ptr);
+                       add_r = ins_compl_add_infercase(ptr, (int)(end_ptr - 
ptr),
+                                           p_ic, files[i], *dir, FALSE, score);
+                       if (add_r == FAIL)
+                           break;
+                       ptr = end_ptr;  // start from next word
+                       if (compl_get_longest && ctrl_x_mode_normal()
+                               && compl_first_match->cp_next
+                               && score == 
compl_first_match->cp_next->cp_score)
+                           compl_num_bests++;
+                   }
+                   else if (find_word_end(ptr) == line_end)
+                       break;
                }
-               if (add_r == OK)
-                   // if dir was BACKWARD then honor it just once
-                   *dir = FORWARD;
-               else if (add_r == FAIL)
-                   break;
-               // avoid expensive call to vim_regexec() when at end
-               // of line
-               if (*ptr == '
' || got_int)
-                   break;
            }
            line_breakcheck();
            ins_compl_check_keys(50, FALSE);
@@ -1888,6 +1985,7 @@ ins_compl_clear(void)
 {
     compl_cont_status = 0;
     compl_started = FALSE;
+    compl_cfc_longest_ins = FALSE;
     compl_matches = 0;
     compl_selected_item = -1;
     compl_ins_end_col = 0;
@@ -3101,7 +3199,7 @@ ins_compl_add_tv(typval_T *tv, int dir, int fast)
        return FAIL;
     }
     status = ins_compl_add(word, -1, NULL, cptext,
-                                    &user_data, dir, flags, dup, user_hl);
+                                    &user_data, dir, flags, dup, user_hl, 0);
     if (status != OK)
        clear_tv(&user_data);
     return status;
@@ -3196,7 +3294,7 @@ set_completion(colnr_T startcol, list_T *list)
     compl_orig_text.length = (size_t)compl_length;
     if (ins_compl_add(compl_orig_text.string,
                  (int)compl_orig_text.length, NULL, NULL, NULL, 0,
-                 flags | CP_FAST, FALSE, NULL) != OK)
+                 flags | CP_FAST, FALSE, NULL, 0) != OK)
        return;
 
     ctrl_x_mode = CTRL_X_EVAL;
@@ -3762,7 +3860,8 @@ get_next_tag_completion(void)
 /*
  * Compare function for qsort
  */
-static int compare_scores(const void *a, const void *b)
+    static int
+compare_scores(const void *a, const void *b)
 {
     int idx_a = *(const int *)a;
     int idx_b = *(const int *)b;
@@ -3772,6 +3871,114 @@ static int compare_scores(const void *a, const void *b)
                        : (score_a > score_b ? -1 : 1);
 }
 
+/*
+ * insert prefix with redraw
+ */
+    static void
+ins_compl_longest_insert(char_u *prefix)
+{
+    ins_compl_delete();
+    ins_compl_insert_bytes(prefix + get_compl_len(), -1);
+    ins_redraw(FALSE);
+}
+
+/*
+ * Calculate the longest common prefix among the best fuzzy matches
+ * stored in compl_best_matches, and insert it as the longest.
+ */
+    static void
+fuzzy_longest_match(void)
+{
+    char_u     *prefix = NULL;
+    int                prefix_len = 0;
+    int                i = 0;
+    int                j = 0;
+    char_u     *match_str = NULL;
+    char_u     *prefix_ptr = NULL;
+    char_u     *match_ptr = NULL;
+    char_u     *leader = NULL;
+    size_t     leader_len = 0;
+    compl_T    *compl = NULL;
+    int                more_candidates = FALSE;
+    compl_T    *nn_compl = NULL;
+
+    if (compl_num_bests == 0)
+        return;
+
+    nn_compl = compl_first_match->cp_next->cp_next;
+    if (nn_compl && nn_compl != compl_first_match)
+       more_candidates = TRUE;
+
+    compl = ctrl_x_mode_whole_line() ? compl_first_match
+                                   : compl_first_match->cp_next;
+    if (compl_num_bests == 1)
+    {
+       // no more candidates insert the match str
+       if (!more_candidates)
+       {
+           ins_compl_longest_insert(compl->cp_str.string);
+           compl_num_bests = 0;
+       }
+       compl_num_bests = 0;
+       return;
+    }
+
+    compl_best_matches = (compl_T **)alloc(compl_num_bests * sizeof(compl_T 
*));
+    if (compl_best_matches == NULL)
+      return;
+    while (compl != NULL && i < compl_num_bests)
+    {
+      compl_best_matches[i] = compl;
+      compl = compl->cp_next;
+      i++;
+    }
+
+    prefix = compl_best_matches[0]->cp_str.string;
+    prefix_len = (int)STRLEN(prefix);
+
+    for (i = 1; i < compl_num_bests; i++)
+    {
+       match_str = compl_best_matches[i]->cp_str.string;
+        prefix_ptr = prefix;
+        match_ptr = match_str;
+        j = 0;
+
+       while (j < prefix_len && *match_ptr != NUL && *prefix_ptr != NUL)
+       {
+           if (STRNCMP(prefix_ptr, match_ptr, mb_ptr2len(prefix_ptr)) != 0)
+               break;
+
+           MB_PTR_ADV(prefix_ptr);
+           MB_PTR_ADV(match_ptr);
+           j++;
+       }
+
+       if (j > 0)
+           prefix_len = j;
+    }
+
+    leader = ins_compl_leader();
+    if (leader != NULL)
+       leader_len = STRLEN(leader);
+
+    // skip non-consecutive prefixes
+    if (STRNCMP(prefix, leader, leader_len) != 0)
+       goto end;
+
+    prefix = vim_strnsave(compl_best_matches[0]->cp_str.string, prefix_len);
+    if (prefix != NULL)
+    {
+       ins_compl_longest_insert(prefix);
+       compl_cfc_longest_ins = TRUE;
+       vim_free(prefix);
+    }
+
+end:
+    vim_free(compl_best_matches);
+    compl_best_matches = NULL;
+    compl_num_bests = 0;
+}
+
 /*
  * Get the next set of filename matching "compl_pattern".
  */
@@ -3786,10 +3993,13 @@ get_next_filename_completion(void)
     int                score;
     char_u     *leader = ins_compl_leader();
     size_t     leader_len = ins_compl_leader_len();;
-    int                in_fuzzy = ((get_cot_flags() & COT_FUZZY) != 0 && 
leader_len > 0);
-    char_u     **sorted_matches;
+    int                in_fuzzy_collect = (cfc_has_mode() && leader_len > 0);
     int                *fuzzy_indices_data;
     char_u     *last_sep = NULL;
+    int                need_collect_bests = in_fuzzy_collect && 
compl_get_longest;
+    int                max_score = 0;
+    int                current_score = 0;
+    int                dir = compl_direction;
 
 #ifdef BACKSLASH_IN_FILENAME
     char pathsep = (curbuf->b_p_csl[0] == 's') ?
@@ -3798,7 +4008,7 @@ get_next_filename_completion(void)
     char pathsep = PATHSEP;
 #endif
 
-    if (in_fuzzy)
+    if (in_fuzzy_collect)
     {
 #ifdef BACKSLASH_IN_FILENAME
        if (curbuf->b_p_csl[0] == 's')
@@ -3830,7 +4040,7 @@ get_next_filename_completion(void)
            compl_pattern.length = 1;
        }
        else if (*(last_sep + 1) == '
-           in_fuzzy = FALSE;
+           in_fuzzy_collect = FALSE;
        else
        {
            // Split leader into path and file parts
@@ -3876,7 +4086,7 @@ get_next_filename_completion(void)
     }
 #endif
 
-    if (in_fuzzy)
+    if (in_fuzzy_collect)
     {
        ga_init2(&fuzzy_indices, sizeof(int), 10);
        compl_fuzzy_scores = (int *)alloc(sizeof(int) * num_matches);
@@ -3899,16 +4109,30 @@ get_next_filename_completion(void)
        // prevent qsort from deref NULL pointer
        if (fuzzy_indices.ga_len > 0)
        {
+           char_u      *match = NULL;
            fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
            qsort(fuzzy_indices_data, fuzzy_indices.ga_len, sizeof(int), 
compare_scores);
 
-           sorted_matches = (char_u **)alloc(sizeof(char_u *) * 
fuzzy_indices.ga_len);
            for (i = 0; i < fuzzy_indices.ga_len; ++i)
-               sorted_matches[i] = vim_strsave(matches[fuzzy_indices_data[i]]);
+           {
+               match = matches[fuzzy_indices_data[i]];
+               current_score = compl_fuzzy_scores[fuzzy_indices_data[i]];
+               if (ins_compl_add(match, -1, NULL, NULL, NULL, dir,
+                       CP_FAST | ((p_fic || p_wic) ? CP_ICASE : 0),
+                       FALSE, NULL, current_score) == OK)
+                   dir = FORWARD;
+
+               if (need_collect_bests)
+               {
+                   if (i == 0 || current_score == max_score)
+                   {
+                       compl_num_bests++;
+                       max_score = current_score;
+                   }
+               }
+           }
 
            FreeWild(num_matches, matches);
-           matches = sorted_matches;
-           num_matches = fuzzy_indices.ga_len;
        }
        else if (leader_len > 0)
        {
@@ -3918,6 +4142,10 @@ get_next_filename_completion(void)
 
        vim_free(compl_fuzzy_scores);
        ga_clear(&fuzzy_indices);
+
+       if (compl_num_bests > 0 && compl_get_longest)
+           fuzzy_longest_match();
+       return;
     }
 
     if (num_matches > 0)
@@ -4076,8 +4304,9 @@ get_next_default_completion(ins_compl_next_state_T *st, 
pos_T *start_pos)
     int                looped_around = FALSE;
     char_u     *ptr = NULL;
     int                len = 0;
-    int                in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0 && 
compl_length > 0;
+    int                in_collect = (cfc_has_mode() && compl_length > 0);
     char_u     *leader = ins_compl_leader();
+    int                score = 0;
 
     // If 'infercase' is set, don't use 'smartcase' here
     save_p_scs = p_scs;
@@ -4091,7 +4320,7 @@ get_next_default_completion(ins_compl_next_state_T *st, 
pos_T *start_pos)
     save_p_ws = p_ws;
     if (st->ins_buf != curbuf)
        p_ws = FALSE;
-    else if (*st->e_cpt == '.' && !in_fuzzy)
+    else if (*st->e_cpt == '.')
        p_ws = TRUE;
     looped_around = FALSE;
     for (;;)
@@ -4100,15 +4329,17 @@ get_next_default_completion(ins_compl_next_state_T *st, 
pos_T *start_pos)
 
        ++msg_silent;  // Don't want messages for wrapscan.
 
+       if (in_collect)
+       {
+            found_new_match = search_for_fuzzy_match(st->ins_buf,
+                           st->cur_match_pos, leader, compl_direction,
+                           start_pos, &len, &ptr, &score);
+       }
        // ctrl_x_mode_line_or_eval() || word-wise search that
        // has added a word that was at the beginning of the line
-       if ((ctrl_x_mode_whole_line() && !in_fuzzy) || ctrl_x_mode_eval() || 
(compl_cont_status & CONT_SOL))
+       else if (ctrl_x_mode_whole_line() || ctrl_x_mode_eval() || 
(compl_cont_status & CONT_SOL))
            found_new_match = search_for_exact_line(st->ins_buf,
                            st->cur_match_pos, compl_direction, 
compl_pattern.string);
-       else if (in_fuzzy)
-            found_new_match = search_for_fuzzy_match(st->ins_buf,
-                           st->cur_match_pos, leader, compl_direction,
-                           start_pos, &len, &ptr, ctrl_x_mode_whole_line());
        else
            found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
                                NULL, compl_direction, compl_pattern.string, 
(int)compl_pattern.length,
@@ -4157,16 +4388,18 @@ get_next_default_completion(ins_compl_next_state_T *st, 
pos_T *start_pos)
                && start_pos->col  == st->cur_match_pos->col)
            continue;
 
-       if (!in_fuzzy)
+       if (!in_collect)
            ptr = ins_compl_get_next_word_or_line(st->ins_buf, 
st->cur_match_pos,
                                                               &len, 
&cont_s_ipos);
        if (ptr == NULL || (ins_compl_has_preinsert() && STRCMP(ptr, 
compl_pattern.string) == 0))
            continue;
 
        if (ins_compl_add_infercase(ptr, len, p_ic,
-                   st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
-                   0, cont_s_ipos) != NOTDONE)
+                       st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
+                       0, cont_s_ipos, score) != NOTDONE)
        {
+           if (in_collect && score == compl_first_match->cp_next->cp_score)
+               compl_num_bests++;
            found_new_match = OK;
            break;
        }
@@ -4352,6 +4585,9 @@ ins_compl_get_exp(pos_T *ini)
                                               && !ctrl_x_mode_line_or_eval()))
        i = ins_compl_make_cyclic();
 
+    if (cfc_has_mode() && compl_get_longest && compl_num_bests > 0)
+       fuzzy_longest_match();
+
     if (compl_old_match != NULL)
     {
        // If several matches were added (FORWARD) or the search failed and has
@@ -5594,7 +5830,7 @@ ins_compl_start(void)
     if (p_ic)
        flags |= CP_ICASE;
     if (compl_orig_text.string == NULL || ins_compl_add(compl_orig_text.string,
-               (int)compl_orig_text.length, NULL, NULL, NULL, 0, flags, FALSE, 
NULL) != OK)
+               -1, NULL, NULL, NULL, 0, flags, FALSE, NULL, 0) != OK)
     {
        VIM_CLEAR_STRING(compl_pattern);
        VIM_CLEAR_STRING(compl_orig_text);
diff --git a/src/option.h b/src/option.h
index 182ab2678..5714e0c4f 100644
--- a/src/option.h
+++ b/src/option.h
@@ -514,6 +514,8 @@ EXTERN char_u       *p_cpt;         // 'complete'
 EXTERN int     p_confirm;      // 'confirm'
 #endif
 EXTERN int     p_cp;           // 'compatible'
+EXTERN char_u  *p_cfc;         // 'completefuzzycollect'
+EXTERN unsigned cfc_flags;     // flags from "completefuzzycollect"
 EXTERN char_u  *p_cia;         // 'completeitemalign'
 EXTERN unsigned cia_flags;     // order flags of 'completeitemalign'
 EXTERN char_u  *p_cot;         // 'completeopt'
@@ -533,6 +535,11 @@ EXTERN unsigned    cot_flags;      // flags from 
'completeopt'
 #define COT_FUZZY          0x100   // TRUE: fuzzy match enabled
 #define COT_NOSORT         0x200   // TRUE: fuzzy match without qsort score
 #define COT_PREINSERT      0x400   // TRUE: preinsert
+
+#define CFC_KEYWORD         0x001
+#define CFC_FILES           0x002
+#define CFC_WHOLELINE       0x004
+
 #ifdef BACKSLASH_IN_FILENAME
 EXTERN char_u  *p_csl;         // 'completeslash'
 #endif
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 832653701..a8d7972d3 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -655,6 +655,10 @@ static struct vimoption options[] =
                            {(char_u *)0L, (char_u *)0L}
 #endif
                            SCTX_INIT},
+    {"completefuzzycollect", "cfc", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
+                           (char_u *)&p_cfc, PV_NONE, 
did_set_completefuzzycollect, NULL,
+                           {(char_u *)"", (char_u *)0L}
+                           SCTX_INIT},
     {"completeitemalign", "cia", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
                            (char_u *)&p_cia, PV_NONE, 
did_set_completeitemalign, NULL,
                            {(char_u *)"abbr,kind,menu", (char_u *)0L}
diff --git a/src/optionstr.c b/src/optionstr.c
index e2970b6e2..5b863bcc9 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -120,6 +120,7 @@ static char *(p_fdm_values[]) = {"manual", "expr", 
"marker", "indent", "syntax",
                                NULL};
 static char *(p_fcl_values[]) = {"all", NULL};
 #endif
+static char *(p_cfc_values[]) = {"keyword", "files", "whole_line", NULL};
 static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", 
"popup", "popuphidden", "noinsert", "noselect", "fuzzy", "nosort", "preinsert", 
NULL};
 #ifdef BACKSLASH_IN_FILENAME
 static char *(p_csl_values[]) = {"slash", "backslash", NULL};
@@ -146,6 +147,7 @@ didset_string_options(void)
     (void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, TRUE);
     (void)opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, TRUE);
     (void)opt_strings_flags(p_bo, p_bo_values, &bo_flags, TRUE);
+    (void)opt_strings_flags(p_cfc, p_cfc_values, &cfc_flags, TRUE);
     (void)opt_strings_flags(p_cot, p_cot_values, &cot_flags, TRUE);
 #ifdef FEAT_SESSION
     (void)opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, TRUE);
@@ -1646,6 +1648,17 @@ expand_set_completeopt(optexpand_T *args, int 
*numMatches, char_u ***matches)
            matches);
 }
 
+/*
+ * The 'completefuzzycollect' option is changed.
+ */
+    char *
+did_set_completefuzzycollect(optset_T *args UNUSED)
+{
+    if (opt_strings_flags(p_cfc, p_cfc_values, &cfc_flags, TRUE) != OK)
+       return e_invalid_argument;
+    return NULL;
+}
+
 /*
  * The 'completeitemalign' option is changed.
  */
diff --git a/src/proto/insexpand.pro b/src/proto/insexpand.pro
index 9dfbd9129..8d9616426 100644
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -24,7 +24,7 @@ void compl_status_clear(void);
 int has_compl_option(int dict_opt);
 int vim_is_ctrl_x_key(int c);
 int ins_compl_accept_char(int c);
-int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u 
*fname, int dir, int cont_s_ipos);
+int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u 
*fname, int dir, int cont_s_ipos, int score);
 int ins_compl_has_shown_match(void);
 int ins_compl_long_shown_match(void);
 unsigned int get_cot_flags(void);
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index 11b9d057c..6fd26bab0 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -43,6 +43,7 @@ int expand_set_complete(optexpand_T *args, int *numMatches, 
char_u ***matches);
 char *did_set_completeopt(optset_T *args);
 int expand_set_completeopt(optexpand_T *args, int *numMatches, char_u 
***matches);
 char *did_set_completeitemalign(optset_T *args);
+char *did_set_completefuzzycollect(optset_T *args);
 char *did_set_completepopup(optset_T *args);
 char *did_set_completeslash(optset_T *args);
 int expand_set_completeslash(optexpand_T *args, int *numMatches, char_u 
***matches);
diff --git a/src/proto/search.pro b/src/proto/search.pro
index f450d0923..e9be4533a 100644
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -42,7 +42,9 @@ void f_matchfuzzy(typval_T *argvars, typval_T *rettv);
 void f_matchfuzzypos(typval_T *argvars, typval_T *rettv);
 int fuzzy_match_str(char_u *str, char_u *pat);
 garray_T *fuzzy_match_str_with_pos(char_u *str, char_u *pat);
-int search_for_fuzzy_match(buf_T *buf, pos_T *pos, char_u *pattern, int dir, 
pos_T *start_pos, int *len, char_u **ptr, int whole_line);
+int search_for_fuzzy_match(buf_T *buf, pos_T *pos, char_u *pattern, int dir, 
pos_T *start_pos, int *len, char_u **ptr, int *score);
 void fuzmatch_str_free(fuzmatch_str_T *fuzmatch, int count);
 int fuzzymatches_to_strmatches(fuzmatch_str_T *fuzmatch, char_u ***matches, 
int count, int funcsort);
+int fuzzy_match_str_in_line(char_u **ptr, char_u *pat, int *len, pos_T 
*current_pos, int *score);
+
 /* vim: set ft=c : */
diff --git a/src/search.c b/src/search.c
index 3519c32cb..67082a731 100644
--- a/src/search.c
+++ b/src/search.c
@@ -53,7 +53,6 @@ static int fuzzy_match_str_compare(const void *s1, const void 
*s2);
 static void fuzzy_match_str_sort(fuzmatch_str_T *fm, int sz);
 static int fuzzy_match_func_compare(const void *s1, const void *s2);
 static void fuzzy_match_func_sort(fuzmatch_str_T *fm, int sz);
-static int fuzzy_match_str_in_line(char_u **ptr, char_u *pat, int *len, pos_T 
*current_pos);
 
 #define SEARCH_STAT_DEF_TIMEOUT 40L
 #define SEARCH_STAT_DEF_MAX_COUNT 99
@@ -3867,7 +3866,7 @@ search_line:
 
                add_r = ins_compl_add_infercase(aux, i, p_ic,
                        curr_fname == curbuf->b_fname ? NULL : curr_fname,
-                       dir, cont_s_ipos);
+                       dir, cont_s_ipos, 0);
                if (add_r == OK)
                    // if dir was BACKWARD then honor it just once
                    dir = FORWARD;
@@ -5221,20 +5220,31 @@ fuzzy_match_str_with_pos(char_u *str UNUSED, char_u 
*pat UNUSED)
 }
 
 /*
- * This function searches for a fuzzy match of the pattern `pat` within the
- * line pointed to by `*ptr`. It splits the line into words, performs fuzzy
- * matching on each word, and returns the length and position of the first
- * matched word.
+ * This function splits the line pointed to by `*ptr` into words and performs
+ * a fuzzy match for the pattern `pat` on each word. It iterates through the
+ * line, moving `*ptr` to the start of each word during the process.
+ *
+ * If a match is found:
+ * - `*ptr` points to the start of the matched word.
+ * - `*len` is set to the length of the matched word.
+ * - `*score` contains the match score.
+ *
+ * If no match is found, `*ptr` is updated to point beyond the last word
+ * or to the end of the line.
  */
-    static int
-fuzzy_match_str_in_line(char_u **ptr, char_u *pat, int *len, pos_T 
*current_pos)
+    int
+fuzzy_match_str_in_line(
+    char_u     **ptr,
+    char_u     *pat,
+    int                *len,
+    pos_T      *current_pos,
+    int                *score)
 {
     char_u     *str = *ptr;
     char_u     *strBegin = str;
     char_u     *end = NULL;
     char_u     *start = NULL;
     int                found = FALSE;
-    int                result;
     char       save_end;
 
     if (str == NULL || pat == NULL)
@@ -5253,15 +5263,16 @@ fuzzy_match_str_in_line(char_u **ptr, char_u *pat, int 
*len, pos_T *current_pos)
        *end = NUL;
 
        // Perform fuzzy match
-       result = fuzzy_match_str(start, pat);
+       *score = fuzzy_match_str(start, pat);
        *end = save_end;
 
-       if (result > 0)
+       if (*score > 0)
        {
            *len = (int)(end - start);
-           current_pos->col += (int)(end - strBegin);
            found = TRUE;
            *ptr = start;
+           if (current_pos)
+               current_pos->col += (int)(end - strBegin);
            break;
        }
 
@@ -5292,13 +5303,14 @@ search_for_fuzzy_match(
     pos_T      *start_pos,
     int                *len,
     char_u     **ptr,
-    int                whole_line)
+    int                *score)
 {
     pos_T      current_pos = *pos;
     pos_T      circly_end;
     int                found_new_match = FALSE;
     int                looped_around = FALSE;
 
+    int whole_line = ctrl_x_mode_whole_line();
     if (whole_line)
        current_pos.lnum += dir;
 
@@ -5324,13 +5336,15 @@ search_for_fuzzy_match(
            *ptr = ml_get_buf(buf, current_pos.lnum, FALSE);
            // If ptr is end of line is reached, move to next line
            // or previous line based on direction
-           if (**ptr != NUL)
+           if (*ptr != NULL && **ptr != NUL)
            {
                if (!whole_line)
                {
                    *ptr += current_pos.col;
-                   // Try to find a fuzzy match in the current line starting 
from current position
-                   found_new_match = fuzzy_match_str_in_line(ptr, pattern, 
len, &current_pos);
+                   // Try to find a fuzzy match in the current line starting
+                   // from current position
+                   found_new_match = fuzzy_match_str_in_line(ptr, pattern,
+                                                   len, &current_pos, score);
                    if (found_new_match)
                    {
                        if (ctrl_x_mode_normal())
diff --git a/src/spell.c b/src/spell.c
index 2581a5ede..87256f9d7 100644
--- a/src/spell.c
+++ b/src/spell.c
@@ -4219,7 +4219,7 @@ dump_word(
                    ? MB_STRNICMP(p, pat, STRLEN(pat)) == 0
                    : STRNCMP(p, pat, STRLEN(pat)) == 0)
                && ins_compl_add_infercase(p, (int)STRLEN(p),
-                                         p_ic, NULL, *dir, FALSE) == OK)
+                                         p_ic, NULL, *dir, FALSE, 0) == OK)
        // if dir was BACKWARD then honor it just once
        *dir = FORWARD;
 }
diff --git a/src/testdir/dumps/Test_pum_highlights_10.dump 
b/src/testdir/dumps/Test_pum_completefuzzycollect_01.dump
similarity index 53%
rename from src/testdir/dumps/Test_pum_highlights_10.dump
rename to src/testdir/dumps/Test_pum_completefuzzycollect_01.dump
index 790b028b9..78b33128d 100644
--- a/src/testdir/dumps/Test_pum_highlights_10.dump
+++ b/src/testdir/dumps/Test_pum_completefuzzycollect_01.dump
@@ -1,7 +1,7 @@
 | +0&#ffffff0|h|e|l@1|o| |h|e|l|i|o| |h|e|r|o| |h|e|l@1|o> @51
-|~+0#4040ff13&| @15| +0#0000001#ffd7ff255|h+0#0000e05&|e+0#0000001&|r|o| @10| 
+0#4040ff13#ffffff0@41
-|~| @15| +0#0000001#ffd7ff255|h+0#0000e05&|e+0#0000001&|l|i|o| @9| 
+0#4040ff13#ffffff0@41
-|~| @15| +0#0000001#e0e0e08|h+0#00e0e07&|e+0#0000001&|l@1|o| @9| 
+0#4040ff13#ffffff0@41
+|~+0#4040ff13&| @15| +0#0000001#ffd7ff255|h|e|r|o| @10| +0#4040ff13#ffffff0@41
+|~| @15| +0#0000001#ffd7ff255|h|e|l|i|o| @9| +0#4040ff13#ffffff0@41
+|~| @15| +0#0000001#e0e0e08|h|e|l@1|o| @9| +0#4040ff13#ffffff0@41
 |~| @73
 |~| @73
 |~| @73
diff --git a/src/testdir/dumps/Test_pum_highlights_11.dump 
b/src/testdir/dumps/Test_pum_completefuzzycollect_02.dump
similarity index 53%
rename from src/testdir/dumps/Test_pum_highlights_11.dump
rename to src/testdir/dumps/Test_pum_completefuzzycollect_02.dump
index ef75a89ed..c57ee7dbe 100644
--- a/src/testdir/dumps/Test_pum_highlights_11.dump
+++ b/src/testdir/dumps/Test_pum_completefuzzycollect_02.dump
@@ -1,7 +1,7 @@
 | +0&#ffffff0|h|e|l@1|o| |h|e|l|i|o| |h|e|r|o| |h|e|l|i|o> @51
-|~+0#4040ff13&| @15| +0#0000001#ffd7ff255|h+0#0000e05&|e+0#0000001&|r|o| @10| 
+0#4040ff13#ffffff0@41
-|~| @15| +0#0000001#e0e0e08|h+0#00e0e07&|e+0#0000001&|l|i|o| @9| 
+0#4040ff13#ffffff0@41
-|~| @15| +0#0000001#ffd7ff255|h+0#0000e05&|e+0#0000001&|l@1|o| @9| 
+0#4040ff13#ffffff0@41
+|~+0#4040ff13&| @15| +0#0000001#ffd7ff255|h|e|r|o| @10| +0#4040ff13#ffffff0@41
+|~| @15| +0#0000001#e0e0e08|h|e|l|i|o| @9| +0#4040ff13#ffffff0@41
+|~| @15| +0#0000001#ffd7ff255|h|e|l@1|o| @9| +0#4040ff13#ffffff0@41
 |~| @73
 |~| @73
 |~| @73
diff --git a/src/testdir/dumps/Test_pum_highlights_15.dump 
b/src/testdir/dumps/Test_pum_completefuzzycollect_03.dump
similarity index 100%
rename from src/testdir/dumps/Test_pum_highlights_15.dump
rename to src/testdir/dumps/Test_pum_completefuzzycollect_03.dump
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index 950653245..b3a89a0f4 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -157,6 +157,9 @@ let test_values = {
       \ 'completeopt': [['', 'menu', 'menuone', 'longest', 'preview', 'popup',
       \                'popuphidden', 'noinsert', 'noselect', 'fuzzy', 
"preinsert", 'menu,longest'],
       \                ['xxx', 'menu,,,longest,']],
+      \ 'completefuzzycollect': [['', 'keyword', 'files', 'whole_line',
+      \                'keyword,whole_line', 'files,whole_line', 
'keyword,files,whole_line'],
+      \                ['xxx', 'keyword,,,whole_line,']],
       \ 'completeitemalign': [['abbr,kind,menu', 'menu,abbr,kind'],
       \                ['', 'xxx', 'abbr', 'abbr,menu', 'abbr,menu,kind,abbr',
       \                'abbr1234,kind,menu']],
diff --git a/src/testdir/test_ins_complete.vim 
b/src/testdir/test_ins_complete.vim
index 1bef953d4..345e365dd 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -2731,7 +2731,7 @@ func Test_completefunc_first_call_complete_add()
   bwipe!
 endfunc
 
-func Test_complete_fuzzy_match()
+func Test_complete_opt_fuzzy()
   func OnPumChange()
     let g:item = get(v:event, 'completed_item', {})
     let g:word = get(g:item, 'word', v:null)
@@ -2787,8 +2787,65 @@ func Test_complete_fuzzy_match()
   call feedkeys("S\<C-x>\<C-o>fb\<C-n>", 'tx')
   call assert_equal('fooBaz', g:word)
 
-  " avoid breaking default completion behavior
-  set completeopt=fuzzy,menu
+  " test case for nosort option
+  set cot=menuone,menu,noinsert,fuzzy,nosort
+  " "fooBaz" should have a higher score when the leader is "fb".
+  " With "nosort", "foobar" should still be shown first in the popup menu.
+  call feedkeys("S\<C-x>\<C-o>fb", 'tx')
+  call assert_equal('foobar', g:word)
+  call feedkeys("S\<C-x>\<C-o>好", 'tx')
+  call assert_equal("你好吗", g:word)
+
+  set cot+=noselect
+  call feedkeys("S\<C-x>\<C-o>好", 'tx')
+  call assert_equal(v:null, g:word)
+  call feedkeys("S\<C-x>\<C-o>好\<C-N>", 'tx')
+  call assert_equal('你好吗', g:word)
+
+  " "nosort" shouldn't enable fuzzy filtering when "fuzzy" isn't present.
+  set cot=menuone,noinsert,nosort
+  call feedkeys("S\<C-x>\<C-o>fooB\<C-Y>", 'tx')
+  call assert_equal('fooBaz', getline('.'))
+
+  set cot=menuone,fuzzy,nosort
+  func CompAnother()
+    call complete(col('.'), [#{word: "do" }, #{word: "echo"}, #{word: "for 
(${1:expr1}, ${2:expr2}, ${3:expr3}) {
        $0
}", abbr: "for" }, #{word: "foo"}])
+    return ''
+  endfunc
+  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-N>\<C-N>", 'tx')
+  call assert_equal("for", g:abbr)
+  call assert_equal(2, g:selected)
+
+  set cot+=noinsert
+  call feedkeys("i\<C-R>=CompAnother()\<CR>f", 'tx')
+  call assert_equal("for", g:abbr)
+  call assert_equal(2, g:selected)
+
+  set cot=menu,menuone,noselect,fuzzy
+  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-N>\<C-N>\<C-N>\<C-N>", 'tx')
+  call assert_equal("foo", g:word)
+  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-P>", 'tx')
+  call assert_equal("foo", g:word)
+  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-P>\<C-P>", 'tx')
+  call assert_equal("for", g:abbr)
+
+  " clean up
+  set omnifunc=
+  bw!
+  set complete& completeopt&
+  autocmd! AAAAA_Group
+  augroup! AAAAA_Group
+  delfunc OnPumChange
+  delfunc Omni_test
+  delfunc Comp
+  unlet g:item
+  unlet g:word
+  unlet g:abbr
+endfunc
+
+func Test_complete_fuzzy_collect()
+  new
+  set completefuzzycollect=keyword,files,whole_line
   call setline(1, ['hello help hero h'])
   " Use "!" flag of feedkeys() so that ex_normal_busy is not set and
   " ins_compl_check_keys() is not skipped.
@@ -2820,16 +2877,6 @@ func Test_complete_fuzzy_match()
   call feedkeys("A\<C-X>\<C-N>\<C-N>\<Esc>0", 'tx!')
   call assert_equal('你的 我的 我的', getline('.'))
 
-  " respect wrapscan
-  set nowrapscan
-  call setline(1, ["xyz", "yxz", ""])
-  call cursor(3, 1)
-  call feedkeys("Sy\<C-X>\<C-N>\<Esc>0", 'tx!')
-  call assert_equal('y', getline('.'))
-  set wrapscan
-  call feedkeys("Sy\<C-X>\<C-N>\<Esc>0", 'tx!')
-  call assert_equal('xyz', getline('.'))
-
   " fuzzy on file
   call writefile([''], 'fobar', 'D')
   call writefile([''], 'foobar', 'D')
@@ -2845,7 +2892,6 @@ func Test_complete_fuzzy_match()
   call assert_match('../testdir', getline('.'))
 
   " can get completion from other buffer
-  set completeopt=fuzzy,menu,menuone
   vnew
   call setline(1, ["completeness,", "compatibility", "Composite", 
"Omnipotent"])
   wincmd p
@@ -2897,79 +2943,109 @@ func Test_complete_fuzzy_match()
   call assert_equal('你好 他好', getline('.'))
 
   " issue #15526
-  set completeopt=fuzzy,menuone,menu,noselect
+  set completeopt=menuone,menu,noselect
   call setline(1, ['Text', 'ToText', ''])
   call cursor(3, 1)
   call feedkeys("STe\<C-X>\<C-N>x\<CR>\<Esc>0", 'tx!')
   call assert_equal('Tex', getline(line('.') - 1))
 
-  " test case for nosort option
-  set cot=menuone,menu,noinsert,fuzzy,nosort
-  " "fooBaz" should have a higher score when the leader is "fb".
-  " With "nosort", "foobar" should still be shown first in the popup menu.
-  call feedkeys("S\<C-x>\<C-o>fb", 'tx')
-  call assert_equal('foobar', g:word)
-  call feedkeys("S\<C-x>\<C-o>好", 'tx')
-  call assert_equal("你好吗", g:word)
+  bw!
+  bw!
+  set completeopt& cfc& cpt&
+endfunc
+
+func Test_cfc_with_longest()
+  new
+  set completefuzzycollect=keyword,files,whole_line
+  set completeopt=menu,menuone,longest,fuzzy
+
+  " keyword
+  exe "normal ggdGShello helio think h\<C-X>\<C-N>\<ESC>"
+  call assert_equal("hello helio think hel", getline('.'))
+  exe "normal hello helio think h\<C-X>\<C-P>\<ESC>"
+  call assert_equal("hello helio think hel", getline('.'))
+
+  " skip non-consecutive prefixes
+  exe "normal ggdGShello helio heo\<C-X>\<C-N>\<ESC>"
+  call assert_equal("hello helio heo", getline('.'))
+
+  " kdcit
+  call writefile(['help'], 'test_keyword.txt', 'D')
+  set complete=ktest_keyword.txt
+  exe "normal ggdGSh\<C-N>\<ESC>"
+  " auto insert help when only have one match
+  call assert_equal("help", getline('.'))
+  call writefile(['hello', 'help', 'think'], 'xtest_keyword.txt', 'D')
+  set complete=kxtest_keyword.txt
+  " auto insert hel
+  exe "normal ggdGSh\<C-N>\<ESC>"
+  call assert_equal("hel", getline('.'))
+
+  " line start with a space
+  call writefile([' hello'], 'test_case1.txt', 'D')
+  set complete=ktest_case1.txt
+  exe "normal ggdGSh\<C-N>\<ESC>"
+  call assert_equal("hello", getline('.'))
 
-  set cot+=noselect
-  call feedkeys("S\<C-x>\<C-o>好", 'tx')
-  call assert_equal(v:null, g:word)
-  call feedkeys("S\<C-x>\<C-o>好\<C-N>", 'tx')
-  call assert_equal('你好吗', g:word)
+  " multiple matches
+  set complete=ktest_case2.txt
+  call writefile([' hello help what'], 'test_case2.txt', 'D')
+  exe "normal ggdGSh\<C-N>\<C-N>\<C-N>\<C-N>\<ESC>"
+  call assert_equal("what", getline('.'))
+
+  " multiple lines of matches
+  set complete=ktest_case3.txt
+  call writefile([' hello help what', 'hola', '     hey'], 'test_case3.txt', 
'D')
+  exe "normal ggdGSh\<C-N>\<C-N>\<ESC>"
+  call assert_equal("hey", getline('.'))
+  exe "normal ggdGSh\<C-N>\<C-N>\<C-N>\<C-N>\<ESC>"
+  call assert_equal("hola", getline('.'))
+
+  set complete=ktest_case4.txt
+  call writefile(['  auto int   enum register', 'why'], 'test_case4.txt', 'D')
+  exe "normal ggdGSe\<C-N>\<C-N>\<ESC>"
+  call assert_equal("enum", getline('.'))
+  set complete&
 
-  " "nosort" shouldn't enable fuzzy filtering when "fuzzy" isn't present.
-  set cot=menuone,noinsert,nosort
-  call feedkeys("S\<C-x>\<C-o>fooB\<C-Y>", 'tx')
-  call assert_equal('fooBaz', getline('.'))
+  " file
+  call writefile([''], 'hello', 'D')
+  call writefile([''], 'helio', 'D')
+  exe "normal ggdGS./h\<C-X>\<C-f>\<ESC>"
+  call assert_equal('./hel', getline('.'))
 
-  set cot=menuone,fuzzy,nosort
-  func CompAnother()
-    call complete(col('.'), [#{word: "do" }, #{word: "echo"}, #{word: "for 
(${1:expr1}, ${2:expr2}, ${3:expr3}) {
        $0
}", abbr: "for" }, #{word: "foo"}])
-    return ''
-  endfunc
-  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-N>\<C-N>", 'tx')
-  call assert_equal("for", g:abbr)
-  call assert_equal(2, g:selected)
+  " word
+  call setline(1, ['what do you think', 'why i have that', ''])
+  call cursor(3,1)
+  call feedkeys("Sw\<C-X>\<C-l>\<C-N>\<Esc>0", 'tx!')
+  call assert_equal('wh', getline('.'))
 
-  set cot+=noinsert
-  call feedkeys("i\<C-R>=CompAnother()\<CR>f", 'tx')
-  call assert_equal("for", g:abbr)
-  call assert_equal(2, g:selected)
+  exe "normal ggdG"
+  " auto complete when only one match
+  exe "normal Shello\<CR>h\<C-X>\<C-N>\<esc>"
+  call assert_equal('hello', getline('.'))
+  exe "normal Sh\<C-N>\<C-P>\<esc>"
+  call assert_equal('hello', getline('.'))
 
-  set cot=menu,menuone,noselect,fuzzy
-  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-N>\<C-N>\<C-N>\<C-N>", 'tx')
-  call assert_equal("foo", g:word)
-  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-P>", 'tx')
-  call assert_equal("foo", g:word)
-  call feedkeys("i\<C-R>=CompAnother()\<CR>\<C-P>\<C-P>", 'tx')
-  call assert_equal("for", g:abbr)
+  exe "normal Shello\<CR>h\<C-X>\<C-N>\<Esc>cch\<C-X>\<C-N>\<Esc>"
+  call assert_equal('hello', getline('.'))
+
+  " continue search for new leader after insert common prefix
+  exe "normal ohellokate\<CR>h\<C-X>\<C-N>k\<C-y>\<esc>"
+  call assert_equal('hellokate', getline('.'))
 
-  " clean up
-  set omnifunc=
-  bw!
   bw!
-  set complete& completeopt&
-  autocmd! AAAAA_Group
-  augroup! AAAAA_Group
-  delfunc OnPumChange
-  delfunc Omni_test
-  delfunc Comp
-  delfunc CompAnother
-  unlet g:item
-  unlet g:word
-  unlet g:selected
-  unlet g:abbr
+  set completeopt&
+  set completefuzzycollect&
 endfunc
 
-func Test_complete_fuzzy_with_completeslash()
+func Test_completefuzzycollect_with_completeslash()
   CheckMSWindows
 
   call writefile([''], 'fobar', 'D')
   let orig_shellslash = &shellslash
   set cpt&
   new
-  set completeopt+=fuzzy
+  set completefuzzycollect=files
   set noshellslash
 
   " Test with completeslash unset
@@ -2991,6 +3067,7 @@ func Test_complete_fuzzy_with_completeslash()
   " Reset and clean up
   let &shellslash = orig_shellslash
   set completeslash=
+  set completefuzzycollect&
   %bw!
 endfunc
 
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
index aa4e3e411..e599a8d0a 100644
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -1495,22 +1495,6 @@ func Test_pum_highlights_match()
   call VerifyScreenDump(buf, 'Test_pum_highlights_09', {})
   call term_sendkeys(buf, "o\<BS>\<C-R>=Comp()\<CR>")
   call VerifyScreenDump(buf, 'Test_pum_highlights_09', {})
-
-  " issue #15095 wrong select
-  call term_sendkeys(buf, "\<ESC>:set completeopt=fuzzy,menu\<CR>")
-  call TermWait(buf)
-  call term_sendkeys(buf, "S hello helio hero h\<C-X>\<C-P>")
-  call TermWait(buf, 50)
-  call VerifyScreenDump(buf, 'Test_pum_highlights_10', {})
-
-  call term_sendkeys(buf, "\<ESC>S hello helio hero h\<C-X>\<C-P>\<C-P>")
-  call TermWait(buf, 50)
-  call VerifyScreenDump(buf, 'Test_pum_highlights_11', {})
-
-  " issue #15357
-  call term_sendkeys(buf, "\<ESC>S/non_existing_folder\<C-X>\<C-F>")
-  call TermWait(buf, 50)
-  call VerifyScreenDump(buf, 'Test_pum_highlights_15', {})
   call term_sendkeys(buf, "\<C-E>\<Esc>")
 
   call term_sendkeys(buf, ":hi PmenuMatchSel ctermfg=14 ctermbg=NONE\<CR>")
@@ -1524,7 +1508,34 @@ func Test_pum_highlights_match()
 
   call term_sendkeys(buf, "\<C-E>\<Esc>")
   call TermWait(buf)
+  call StopVimInTerminal(buf)
+endfunc
 
+func Test_pum_completefuzzycollect()
+  CheckScreendump
+  let lines =<< trim END
+    set completefuzzycollect=keyword,files
+    set completeopt=menu,menuone
+  END
+  call writefile(lines, 'Xscript', 'D')
+  let  buf = RunVimInTerminal('-S Xscript', {})
+
+  " issue #15095 wrong select
+  call term_sendkeys(buf, "S hello helio hero h\<C-X>\<C-P>")
+  call TermWait(buf, 50)
+  call VerifyScreenDump(buf, 'Test_pum_completefuzzycollect_01', {})
+
+  call term_sendkeys(buf, "\<ESC>S hello helio hero h\<C-X>\<C-P>\<C-P>")
+  call TermWait(buf, 50)
+  call VerifyScreenDump(buf, 'Test_pum_completefuzzycollect_02', {})
+
+  " issue #15357
+  call term_sendkeys(buf, "\<ESC>S/non_existing_folder\<C-X>\<C-F>")
+  call TermWait(buf, 50)
+  call VerifyScreenDump(buf, 'Test_pum_completefuzzycollect_03', {})
+  call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+  call TermWait(buf)
   call StopVimInTerminal(buf)
 endfunc
 
diff --git a/src/version.c b/src/version.c
index 790fc7d0d..a7ce796c7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1178,
 /**/
     1177,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1tqIYb-00CbAe-Px%40256bit.org.

Raspunde prin e-mail lui