return NULL;
}
- if (cpt_sources_array == NULL || compl_leader.string == NULL)
+ if (cpt_sources_array == NULL)
goto theend;
int cpt_idx = match->cp_cpt_source_idx;
- if (cpt_idx < 0 || compl_col <= 0)
+ if (cpt_idx < 0)
goto theend;
int startcol = cpt_sources_array[cpt_idx].cs_startcol;
+ if (compl_leader.string == NULL)
+ {
+ // When leader is not set (e.g. 'autocomplete' first fires before
+ // compl_leader is initialised), fall back to compl_orig_text for
+ // matches starting at or after compl_col. Matches starting before
+ // compl_col carry pre-compl_col text and must not be compared with
+ // compl_orig_text, so return &compl_leader (NULL string) to signal
+ // "pass through" (no prefix filter).
+ if (startcol < 0 || startcol >= compl_col)
+ return &compl_orig_text;
+ return &compl_leader; // pass through (startcol < compl_col)
+ }
+
+ if (compl_col <= 0)
+ goto theend;
+
if (startcol >= 0 && startcol < compl_col)
{
int prepend_len = compl_col - startcol;
call assert_equal(['fooze', 'faberge'], b:matches->mapnew('v:val.word'))
" Test 9: Trigger autocomplete immediately upon entering Insert mode
+ " 'faberge' is filtered out because it doesn't start with the current prefix
+ " 'foo'; non-prefix omnifunc matches are excluded from the PUM when leader
+ " is NULL (compl_orig_text is used as a fallback filter).
call feedkeys("Sprefix->foo\<Esc>a\<F2>\<Esc>0", 'tx!')
- call assert_equal(['foobar', 'fooze', 'faberge'], b:matches->mapnew('v:val.word'))
+ call assert_equal(['foobar', 'fooze'], b:matches->mapnew('v:val.word'))
call feedkeys("Sprefix->fooxx\<Esc>hcw\<F2>\<Esc>0", 'tx!')
- call assert_equal(['foobar', 'fooze', 'faberge'], b:matches->mapnew('v:val.word'))
+ call assert_equal(['foobar', 'fooze'], b:matches->mapnew('v:val.word'))
bw!
call test_override("char_avail", 0)
bw!
endfunc
+func Test_autocomplete_preinsert_null_leader()
+ " Test that non-prefix matches from omnifunc are filtered when leader is NULL.
+ " When autocomplete first fires, compl_leader is NULL. Previously the prefix
+ " filter was bypassed, allowing non-prefix fuzzy matches to be incorrectly
+ " shown in the PUM and preinserted.
+ func NonPrefixOmni(findstart, base)
+ if a:findstart
+ return col(".") - 1
+ endif
+ " Return "key" (doesn't start with 'y') and "yellow" (starts with 'y').
+ " Simulates what a fuzzy omnifunc returns (e.g. vimcomplete#Complete with
+ " wildoptions=fuzzy).
+ return ["key", "yellow"]
+ endfunc
+
+ call test_override("char_avail", 1)
+ new
+ set omnifunc=NonPrefixOmni complete=o
+ set completeopt=preinsert autocomplete
+
+ func GetState()
+ let g:line = getline('.')
+ let g:col = col('.')
+ let g:matches = complete_info(['matches']).matches->mapnew('v:val.word')
+ endfunc
+ inoremap <buffer> <F5> <C-R>=GetState()<CR>
+
+ " Type 'y': "key" should be filtered out (doesn't start with 'y'),
+ " "yellow" should be the only PUM entry and preinserted with cursor after 'y'.
+ call feedkeys("iy\<F5>\<C-E>\<Esc>", 'tx')
+ call assert_equal("yellow", g:line)
+ call assert_equal(2, g:col)
+ call assert_equal(['yellow'], g:matches)
+
+ bw!
+ set omnifunc& complete& completeopt& autocomplete&
+ call test_override("char_avail", 0)
+ delfunc NonPrefixOmni
+ delfunc GetState
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab nofoldenable