]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1178: not possible to generate completion candidates using fuzzy matching v9.1.1178
authorglepnir <glephunter@gmail.com>
Thu, 6 Mar 2025 20:59:13 +0000 (21:59 +0100)
committerChristian Brabandt <cb@256bit.org>
Thu, 6 Mar 2025 20:59:13 +0000 (21:59 +0100)
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 <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
21 files changed:
runtime/doc/options.txt
runtime/doc/tags
runtime/doc/version9.txt
runtime/optwin.vim
runtime/syntax/vim.vim
src/insexpand.c
src/option.h
src/optiondefs.h
src/optionstr.c
src/proto/insexpand.pro
src/proto/optionstr.pro
src/proto/search.pro
src/search.c
src/spell.c
src/testdir/dumps/Test_pum_completefuzzycollect_01.dump [moved from src/testdir/dumps/Test_pum_highlights_10.dump with 53% similarity]
src/testdir/dumps/Test_pum_completefuzzycollect_02.dump [moved from src/testdir/dumps/Test_pum_highlights_11.dump with 53% similarity]
src/testdir/dumps/Test_pum_completefuzzycollect_03.dump [moved from src/testdir/dumps/Test_pum_highlights_15.dump with 100% similarity]
src/testdir/gen_opt_test.vim
src/testdir/test_ins_complete.vim
src/testdir/test_popup.vim
src/version.c

index 883a67f678d189feb00117c8db07c631e638a910..6afe32f1ee54fe933cd81d7bbaa8c38ceae98b0a 100644 (file)
@@ -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
index 49a849ef850b078099e26e144fe8db03b0e10c17..6aee799d01dc2635634b7200e88b9560609e1c4a 100644 (file)
@@ -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'*
index 5edadcec4924a94b0d77994f66969dcefabdfad2..f12bcd4c354e05679d72b1c90fd75e7711e62f33 100644 (file)
@@ -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
index 996e14142923ae218a2a1ef90fb532274a3c1810..2c28ee1fc11979f5cdfd5319ffc75d6f3fe95a30 100644 (file)
@@ -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("$", "\t" .. 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")
index 5cab4aeba2751f563310c26b755760c4a59d8c25..e83c9b2ef9b4e0bceeec275a9f845c823f72d54f 100644 (file)
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <dougkearns@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=''
index 1616a0f3f67cd7da53aafc5b18510b5c0ffd6faf..9769c46c25ad77ae42faca6e9809a79c1c763189 100644 (file)
@@ -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 == '\n' || 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 == '\n' || 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) == '\0')
-           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);
index 182ab26785845c4e82c8343e2cbda9c30a0f14e0..5714e0c4f3885ad75473d5dd832fa5319cce1918 100644 (file)
@@ -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
index 832653701b9c707d774c12d22bf45fe3b13ff31d..a8d7972d39f096871beb2e2685603cf3286c452a 100644 (file)
@@ -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}
index e2970b6e2fb224436d5a4f3ad6fbf7c23874cefc..5b863bcc9f79c01c0312210b7c38e9c9f961d564 100644 (file)
@@ -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.
  */
index 9dfbd9129eb385b45af63f7678805b7c13ab9a66..8d961642623a8ce9b2f6a8f18b366f016e3a019e 100644 (file)
@@ -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);
index 11b9d057c9ddae6b464f673cec99882233f96d1a..6fd26bab0f69c63836a50c85917444625ce0d568 100644 (file)
@@ -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);
index f450d09238467974b7b095b538a921a80b568208..e9be4533aa506e4ef674daf255f7e5a96f64a348 100644 (file)
@@ -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 : */
index 3519c32cb6cad4c585f1cd27e7ad367a3ddd3ddb..67082a731fe4ddd2b017c3edb2c6d204bc63b5cb 100644 (file)
@@ -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())
index 2581a5ede9eae4fd149279d0d391009f45129396..87256f9d7be85a6de9e085ecd4e9ecf996ad4754 100644 (file)
@@ -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;
 }
similarity index 53%
rename from src/testdir/dumps/Test_pum_highlights_10.dump
rename to src/testdir/dumps/Test_pum_completefuzzycollect_01.dump
index 790b028b9cc57504ec0f2bb1a9b38a64da53b6b7..78b33128def3ab415311782df840a11eff541b8c 100644 (file)
@@ -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
similarity index 53%
rename from src/testdir/dumps/Test_pum_highlights_11.dump
rename to src/testdir/dumps/Test_pum_completefuzzycollect_02.dump
index ef75a89ed37dceea598f505a693df1bee0ea9fc8..c57ee7dbef945ee67080aa237ecb436466505e8b 100644 (file)
@@ -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
index 9506532458d7bd7986cd6069296cc6f8a4032958..b3a89a0f4673dc2396d9055bd871c7fc48a39348 100644 (file)
@@ -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']],
index 1bef953d4cb6a1bfac06dad24939c8a9c360ad1a..345e365ddfe38c335d56a146b7bae235d95df9c7 100644 (file)
@@ -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}) {\n\t$0\n}", 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}) {\n\t$0\n}", 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
 
index aa4e3e41108a8058edf6ca36d7491cb1a89c3f4b..e599a8d0a481896e6db86e7026bb128dec396dab 100644 (file)
@@ -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
 
index 790fc7d0dac493892fdb3e2c55968613368192e1..a7ce796c79cc333d2762611b7edd8e9d60d11295 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1178,
 /**/
     1177,
 /**/