To: vim_dev@googlegroups.com Subject: Patch 8.2.4037 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4037 Problem: Insert mode completion is insufficiently tested. Solution: Add more tests. Fix uncovered memory leak. (Yegappan Lakshmanan, closes #9489) Files: src/insexpand.c, src/testdir/test_ins_complete.vim *** ../vim-8.2.4036/src/insexpand.c 2022-01-05 17:49:10.877225131 +0000 --- src/insexpand.c 2022-01-08 10:35:56.087139524 +0000 *************** *** 698,704 **** } /* ! * Add a match to the list of matches. * If the given string is already in the list of completions, then return * NOTDONE, otherwise add it to the list and return OK. If there is an error, * maybe because alloc() returns NULL, then FAIL is returned. --- 698,717 ---- } /* ! * Add a match to the list of matches. The arguments are: ! * str - text of the match to add ! * len - length of "str". If -1, then the length of "str" is ! * computed. ! * fname - file name to associate with this match. ! * cptext - list of strings to use with this match (for abbr, menu, info ! * and kind) ! * user_data - user supplied data (any vim type) for this match ! * cdir - match direction. If 0, use "compl_direction". ! * flags_arg - match flags (cp_flags) ! * adup - accept this match even if it is already present. ! * If "cdir" is FORWARD, then the match is added after the current match. ! * Otherwise, it is added before the current match. ! * * If the given string is already in the list of completions, then return * NOTDONE, otherwise add it to the list and return OK. If there is an error, * maybe because alloc() returns NULL, then FAIL is returned. *************** *** 789,795 **** match->cp_user_data = *user_data; #endif ! // Link the new match structure in the list of matches. if (compl_first_match == NULL) match->cp_next = match->cp_prev = NULL; else if (dir == FORWARD) --- 802,809 ---- match->cp_user_data = *user_data; #endif ! // Link the new match structure after (FORWARD) or before (BACKWARD) the ! // current match in the list of matches . if (compl_first_match == NULL) match->cp_next = match->cp_prev = NULL; else if (dir == FORWARD) *************** *** 2704,2709 **** --- 2718,2724 ---- int flags = fast ? CP_FAST : 0; char_u *(cptext[CPT_COUNT]); typval_T user_data; + int status; user_data.v_type = VAR_UNKNOWN; if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) *************** *** 2735,2742 **** CLEAR_FIELD(cptext); } if (word == NULL || (!empty && *word == NUL)) return FAIL; ! return ins_compl_add(word, -1, NULL, cptext, &user_data, dir, flags, dup); } /* --- 2750,2763 ---- CLEAR_FIELD(cptext); } if (word == NULL || (!empty && *word == NUL)) + { + clear_tv(&user_data); return FAIL; ! } ! status = ins_compl_add(word, -1, NULL, cptext, &user_data, dir, flags, dup); ! if (status != OK) ! clear_tv(&user_data); ! return status; } /* *************** *** 3157,3164 **** * st->dict_f - flag specifying whether "dict" is an exact file name or not * * Returns INS_COMPL_CPT_OK if the next value is processed successfully. ! * Returns INS_COMPL_CPT_CONT to skip the current value and process the next ! * option value. * Returns INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed. */ static int --- 3178,3185 ---- * st->dict_f - flag specifying whether "dict" is an exact file name or not * * Returns INS_COMPL_CPT_OK if the next value is processed successfully. ! * Returns INS_COMPL_CPT_CONT to skip the current completion source matching ! * the "st->e_cpt" option value and process the next matching source. * Returns INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed. */ static int *************** *** 4521,4527 **** return FAIL; } ! // Reset extended parameters of completion, when start new // completion. compl_opt_refresh_always = FALSE; compl_opt_suppress_empty = FALSE; --- 4542,4548 ---- return FAIL; } ! // Reset extended parameters of completion, when starting new // completion. compl_opt_refresh_always = FALSE; compl_opt_suppress_empty = FALSE; *** ../vim-8.2.4036/src/testdir/test_ins_complete.vim 2022-01-03 11:03:42.538752401 +0000 --- src/testdir/test_ins_complete.vim 2022-01-08 10:35:56.087139524 +0000 *************** *** 71,76 **** --- 71,81 ---- call assert_equal('Xtest11.one', getline('.')) normal ddk + " Test for expanding a non-existing filename + exe "normal oa1b2X3Y4\\" + call assert_equal('a1b2X3Y4', getline('.')) + normal ddk + set cpt=w " checks make_cyclic in other window exe "normal oST\\\\\" *************** *** 981,986 **** --- 986,1012 ---- bw! endfunc + " Test for completing words from unloaded buffers + func Test_complete_from_unloadedbuf() + call writefile(['abc'], "Xfile1") + call writefile(['def'], "Xfile2") + edit Xfile1 + edit Xfile2 + new | close + enew + bunload Xfile1 Xfile2 + set complete=u + " complete from an unloaded buffer + exe "normal! ia\" + call assert_equal('abc', getline(1)) + exe "normal! od\" + call assert_equal('def', getline(2)) + set complete& + %bw! + call delete("Xfile1") + call delete("Xfile2") + endfunc + " Test for completing whole lines from unloaded buffers func Test_complete_wholeline_unloadedbuf() call writefile(['a line1', 'a line2', 'a line3'], "Xfile1") *************** *** 999,1004 **** --- 1025,1050 ---- call delete("Xfile1") endfunc + " Test for completing words from unlisted buffers + func Test_complete_from_unlistedbuf() + call writefile(['abc'], "Xfile1") + call writefile(['def'], "Xfile2") + edit Xfile1 + edit Xfile2 + new | close + bdel Xfile1 Xfile2 + set complete=U + " complete from an unlisted buffer + exe "normal! ia\" + call assert_equal('abc', getline(1)) + exe "normal! od\" + call assert_equal('def', getline(2)) + set complete& + %bw! + call delete("Xfile1") + call delete("Xfile2") + endfunc + " Test for completing whole lines from unlisted buffers func Test_complete_wholeline_unlistedbuf() call writefile(['a line1', 'a line2', 'a line3'], "Xfile1") *************** *** 1032,1037 **** --- 1078,1272 ---- bw! endfunc + " Test for using for local expansion even if 'complete' is set to + " not to complete matches from the local buffer. Also test using multiple + " to cancel the current completion mode. + func Test_complete_local_expansion() + new + set complete=t + call setline(1, ['abc', 'def']) + exe "normal! Go\\" + call assert_equal("def", getline(3)) + exe "normal! Go\" + call assert_equal("", getline(4)) + exe "normal! Go\\" + call assert_equal("abc", getline(5)) + exe "normal! Go\" + call assert_equal("", getline(6)) + + " use multiple to cancel the previous completion mode + exe "normal! Go\\\" + call assert_equal("", getline(7)) + exe "normal! Go\\\\" + call assert_equal("", getline(8)) + exe "normal! Go\\\\\" + call assert_equal("abc", getline(9)) + + " interrupt the current completion mode + set completeopt=menu,noinsert + exe "normal! Go\\\\\\" + call assert_equal("abc", getline(10)) + + " when only one is used to interrupt, do normal expansion + exe "normal! Go\\\\" + call assert_equal("", getline(11)) + set completeopt& + + " using two in non-completion mode and restarting the same mode + exe "normal! God\\\\\\\" + call assert_equal("def", getline(12)) + + " test for adding a match from the original empty text + %d + call setline(1, 'abc def g') + exe "normal! o\\\\\" + call assert_equal('def', getline(2)) + exe "normal! 0C\\\\\" + call assert_equal('abc', getline(2)) + + bw! + endfunc + + " Test for undoing changes after a insert-mode completion + func Test_complete_undo() + new + set complete=. + " undo with 'ignorecase' + call setline(1, ['ABOVE', 'BELOW']) + set ignorecase + exe "normal! Goab\u\" + call assert_equal("ABOVE", getline(3)) + undo + call assert_equal("ab", getline(3)) + set ignorecase& + %d + " undo with longest match + set completeopt=menu,longest + call setline(1, ['above', 'about']) + exe "normal! Goa\u\" + call assert_equal("abo", getline(3)) + undo + call assert_equal("a", getline(3)) + set completeopt& + %d + " undo for line completion + call setline(1, ['above that change', 'below that change']) + exe "normal! Goabove\u\\" + call assert_equal("above that change", getline(3)) + undo + call assert_equal("above", getline(3)) + + bw! + endfunc + + " Test for completing a very long word + func Test_complete_long_word() + set complete& + new + call setline(1, repeat('x', 950) .. ' one two three') + exe "normal! Gox\\\\\\\\" + call assert_equal(repeat('x', 950) .. ' one two three', getline(2)) + %d + " should fail when more than 950 characters are in a word + call setline(1, repeat('x', 951) .. ' one two three') + exe "normal! Gox\\\\\\\\" + call assert_equal(repeat('x', 951), getline(2)) + + " Test for adding a very long word to an existing completion + %d + call setline(1, ['abc', repeat('x', 1016) .. '012345']) + exe "normal! Goab\\\" + call assert_equal('abc ' .. repeat('x', 1016) .. '0123', getline(3)) + bw! + endfunc + + " Test for some fields in the complete items used by complete() + func Test_complete_items() + func CompleteItems(idx) + let items = [[#{word: "one", dup: 1, user_data: 'u1'}, #{word: "one", dup: 1, user_data: 'u2'}], + \ [#{word: "one", dup: 0, user_data: 'u3'}, #{word: "one", dup: 0, user_data: 'u4'}], + \ [#{word: "one", icase: 1, user_data: 'u7'}, #{word: "oNE", icase: 1, user_data: 'u8'}], + \ [#{user_data: 'u9'}], + \ [#{word: "", user_data: 'u10'}], + \ [#{word: "", empty: 1, user_data: 'u11'}]] + call complete(col('.'), items[a:idx]) + return '' + endfunc + new + exe "normal! i\=CompleteItems(0)\\\" + call assert_equal('u2', v:completed_item.user_data) + call assert_equal('one', getline(1)) + exe "normal! o\=CompleteItems(1)\\" + call assert_equal('u3', v:completed_item.user_data) + call assert_equal('one', getline(2)) + exe "normal! o\=CompleteItems(1)\\" + call assert_equal('', getline(3)) + set completeopt=menu,noinsert + exe "normal! o\=CompleteItems(2)\one\\" + call assert_equal('oNE', getline(4)) + call assert_equal('u8', v:completed_item.user_data) + set completeopt& + exe "normal! o\=CompleteItems(3)\" + call assert_equal('', getline(5)) + exe "normal! o\=CompleteItems(4)\" + call assert_equal('', getline(6)) + exe "normal! o\=CompleteItems(5)\" + call assert_equal('', getline(7)) + call assert_equal('u11', v:completed_item.user_data) + " pass invalid argument to complete() + let cmd = "normal! o\=complete(1, [[]])\" + call assert_fails('exe cmd', 'E730:') + bw! + delfunc CompleteItems + endfunc + + " Test for the "refresh" item in the dict returned by an insert completion + " function + func Test_complete_item_refresh_always() + let g:CallCount = 0 + func! Tcomplete(findstart, base) + if a:findstart + " locate the start of the word + let line = getline('.') + let start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\a' + let start -= 1 + endwhile + return start + else + let g:CallCount += 1 + let res = ["update1", "update12", "update123"] + return #{words: res, refresh: 'always'} + endif + endfunc + new + set completeopt=menu,longest + set completefunc=Tcomplete + exe "normal! iup\\\\\\\" + call assert_equal('up', getline(1)) + call assert_equal(2, g:CallCount) + set completeopt& + set completefunc& + bw! + delfunc Tcomplete + endfunc + + " Test for completing from a thesaurus file without read permission + func Test_complete_unreadable_thesaurus_file() + CheckUnix + CheckNotRoot + + call writefile(['about', 'above'], 'Xfile') + call setfperm('Xfile', '---r--r--') + new + set complete=sXfile + exe "normal! ia\" + call assert_equal('a', getline(1)) + bw! + call delete('Xfile') + set complete& + endfunc + " Test to ensure 'Scanning...' messages are not recorded in messages history func Test_z1_complete_no_history() new *** ../vim-8.2.4036/src/version.c 2022-01-07 21:38:43.175435880 +0000 --- src/version.c 2022-01-08 10:37:14.211034709 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4037, /**/ -- hundred-and-one symptoms of being an internet addict: 256. You are able to write down over 250 symptoms of being an internet addict, even though they only asked for 101. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///