From da70c394053e2110aedf5a8ea72cbaba0ccf06d9 Mon Sep 17 00:00:00 2001 From: Liad Oz Date: Wed, 7 Dec 2022 19:07:56 +0200 Subject: fix(extmarks): adjust extmarks when inserting prompt prefix --- src/nvim/edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 36a8674730..b83e732cec 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1484,7 +1484,7 @@ static void init_prompt(int cmdchar_todo) } curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; coladvance(MAXCOL); - changed_bytes(curbuf->b_ml.ml_line_count, 0); + inserted_bytes(curbuf->b_ml.ml_line_count, 0, 0, (colnr_T)strlen(prompt)); } // Insert always starts after the prompt, allow editing text after it. -- cgit From c94d8e7f13bbe6aa0d195eeac115695e0d95e7f3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 07:31:32 +0800 Subject: vim-patch:9.0.1244: cursor displayed in wrong position when leaving Insert mode (#21996) Problem: Cursor briefly displayed in a wrong position when pressing Esc in Insert mode after autoindent was used. Solution: Do not adjust the cursor position for assumed deleted white space if text is following. (closes vim/vim#11877) https://github.com/vim/vim/commit/0f843ef091eceb470caece1d90fdfe08926fe076 Co-authored-by: Bram Moolenaar --- src/nvim/getchar.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 9c5364e1b1..51554fea22 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2526,10 +2526,12 @@ static int vgetorpeek(bool advance) // move cursor left, if possible if (curwin->w_cursor.col != 0) { if (curwin->w_wcol > 0) { - if (did_ai) { - // We are expecting to truncate the trailing - // white-space, so find the last non-white - // character -- webb + // After auto-indenting and no text is following, + // we are expecting to truncate the trailing + // white-space, so find the last non-white + // character -- webb + if (did_ai + && *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) { curwin->w_wcol = 0; ptr = (char_u *)get_cursor_line_ptr(); chartabsize_T cts; -- cgit From f15947866ce59589346a4074a1fdc10f941a16b5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 07:46:51 +0800 Subject: vim-patch:9.0.1243: :setglobal cannot use script-local function for "expr" option (#21997) Problem: :setglobal cannot use script-local function for "expr" option. Solution: Use the pointer to the option value properly. (closes vim/vim#11883) https://github.com/vim/vim/commit/01d4efe2e87632aa085897d3d64e27585908f977 --- src/nvim/optionstr.c | 33 ++++++++++------------- src/nvim/testdir/test_edit.vim | 15 +++++++++++ src/nvim/testdir/test_fold.vim | 58 +++++++++++++++++++++++++++++++++++++--- src/nvim/testdir/test_gf.vim | 25 +++++++++++++++++ src/nvim/testdir/test_normal.vim | 46 +++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index a5a708600f..162d8b691a 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -1497,12 +1497,12 @@ static void did_set_vartabstop(buf_T *buf, win_T *win, char **varp, char **errms } } -static void did_set_optexpr(win_T *win, char **p_opt, char **varp, char **gvarp) +static void did_set_optexpr(char **varp) { - char *name = get_scriptlocal_funcname(*p_opt); + char *name = get_scriptlocal_funcname(*varp); if (name != NULL) { - free_string_option(*p_opt); - *p_opt = name; + free_string_option(*varp); + *varp = name; } } @@ -1808,23 +1808,18 @@ static char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char did_set_varsoftabstop(buf, varp, &errmsg); } else if (varp == &buf->b_p_vts) { // 'vartabstop' did_set_vartabstop(buf, win, varp, &errmsg); - } else if (varp == &p_dex) { // 'diffexpr' - did_set_optexpr(win, &p_dex, varp, gvarp); - } else if (varp == &win->w_p_fde) { // 'foldexpr' - did_set_optexpr(win, &win->w_p_fde, varp, gvarp); - if (foldmethodIsExpr(win)) { + } else if (varp == &p_dex // 'diffexpr' + || gvarp == &win->w_allbuf_opt.wo_fde // 'foldexpr' + || gvarp == &win->w_allbuf_opt.wo_fdt // 'foldtext' + || gvarp == &p_fex // 'formatexpr' + || gvarp == &p_inex // 'includeexpr' + || gvarp == &p_inde // 'indentexpr' + || varp == &p_pex // 'patchexpr' + || varp == &p_ccv) { // 'charconvert' + did_set_optexpr(varp); + if (varp == &win->w_p_fde && foldmethodIsExpr(win)) { foldUpdateAll(win); } - } else if (varp == &win->w_p_fdt) { // 'foldtext' - did_set_optexpr(win, &win->w_p_fdt, varp, gvarp); - } else if (varp == &p_pex) { // 'patchexpr' - did_set_optexpr(win, &p_pex, varp, gvarp); - } else if (gvarp == &p_fex) { // 'formatexpr' - did_set_optexpr(win, &buf->b_p_fex, varp, gvarp); - } else if (gvarp == &p_inex) { // 'includeexpr' - did_set_optexpr(win, &buf->b_p_inex, varp, gvarp); - } else if (gvarp == &p_inde) { // 'indentexpr' - did_set_optexpr(win, &buf->b_p_inde, varp, gvarp); } else if (gvarp == &p_cfu) { // 'completefunc' set_completefunc_option(&errmsg); } else if (gvarp == &p_ofu) { // 'omnifunc' diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index fd54f77ccb..89a9179e60 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -336,8 +336,23 @@ func Test_edit_11_indentexpr() endfunc set indentexpr=s:NewIndentExpr() call assert_equal(expand('') .. 'NewIndentExpr()', &indentexpr) + call assert_equal(expand('') .. 'NewIndentExpr()', &g:indentexpr) set indentexpr=NewIndentExpr() call assert_equal(expand('') .. 'NewIndentExpr()', &indentexpr) + call assert_equal(expand('') .. 'NewIndentExpr()', &g:indentexpr) + setlocal indentexpr= + setglobal indentexpr=s:NewIndentExpr() + call assert_equal(expand('') .. 'NewIndentExpr()', &g:indentexpr) + call assert_equal('', &indentexpr) + new + call assert_equal(expand('') .. 'NewIndentExpr()', &indentexpr) + bw! + setglobal indentexpr=NewIndentExpr() + call assert_equal(expand('') .. 'NewIndentExpr()', &g:indentexpr) + call assert_equal('', &indentexpr) + new + call assert_equal(expand('') .. 'NewIndentExpr()', &indentexpr) + bw! set indentexpr& bw! diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 19415286ad..9014948fb4 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -1305,6 +1305,7 @@ func Test_foldexpr_scriptlocal_func() set foldmethod=expr foldexpr=s:FoldFunc() redraw! call assert_equal(expand('') .. 'FoldFunc()', &foldexpr) + call assert_equal(expand('') .. 'FoldFunc()', &g:foldexpr) call assert_equal(1, g:FoldLnum) set foldmethod& foldexpr= bw! @@ -1314,8 +1315,31 @@ func Test_foldexpr_scriptlocal_func() set foldmethod=expr foldexpr=FoldFunc() redraw! call assert_equal(expand('') .. 'FoldFunc()', &foldexpr) + call assert_equal(expand('') .. 'FoldFunc()', &g:foldexpr) call assert_equal(1, g:FoldLnum) - set foldmethod& foldexpr= + bw! + call setline(1, 'abc') + setlocal foldmethod& foldexpr& + setglobal foldmethod=expr foldexpr=s:FoldFunc() + call assert_equal(expand('') .. 'FoldFunc()', &g:foldexpr) + call assert_equal('0', &foldexpr) + enew! + call setline(1, 'abc') + redraw! + call assert_equal(expand('') .. 'FoldFunc()', &foldexpr) + call assert_equal(1, g:FoldLnum) + bw! + call setline(1, 'abc') + setlocal foldmethod& foldexpr& + setglobal foldmethod=expr foldexpr=FoldFunc() + call assert_equal(expand('') .. 'FoldFunc()', &g:foldexpr) + call assert_equal('0', &foldexpr) + enew! + call setline(1, 'abc') + redraw! + call assert_equal(expand('') .. 'FoldFunc()', &foldexpr) + call assert_equal(1, g:FoldLnum) + set foldmethod& foldexpr& delfunc s:FoldFunc bw! endfunc @@ -1329,25 +1353,53 @@ func Test_foldtext_scriptlocal_func() new | only call setline(1, range(50)) let g:FoldTextArgs = [] - set foldmethod=manual set foldtext=s:FoldText() norm! 4Gzf4j redraw! call assert_equal(expand('') .. 'FoldText()', &foldtext) + call assert_equal(expand('') .. 'FoldText()', &g:foldtext) call assert_equal([4, 8], g:FoldTextArgs) set foldtext& bw! new | only call setline(1, range(50)) let g:FoldTextArgs = [] - set foldmethod=manual set foldtext=FoldText() norm! 8Gzf4j redraw! call assert_equal(expand('') .. 'FoldText()', &foldtext) + call assert_equal(expand('') .. 'FoldText()', &g:foldtext) call assert_equal([8, 12], g:FoldTextArgs) set foldtext& bw! + call setline(1, range(50)) + let g:FoldTextArgs = [] + setlocal foldtext& + setglobal foldtext=s:FoldText() + call assert_equal(expand('') .. 'FoldText()', &g:foldtext) + call assert_equal('foldtext()', &foldtext) + enew! + call setline(1, range(50)) + norm! 12Gzf4j + redraw! + call assert_equal(expand('') .. 'FoldText()', &foldtext) + call assert_equal([12, 16], g:FoldTextArgs) + set foldtext& + bw! + call setline(1, range(50)) + let g:FoldTextArgs = [] + setlocal foldtext& + setglobal foldtext=FoldText() + call assert_equal(expand('') .. 'FoldText()', &g:foldtext) + call assert_equal('foldtext()', &foldtext) + enew! + call setline(1, range(50)) + norm! 16Gzf4j + redraw! + call assert_equal(expand('') .. 'FoldText()', &foldtext) + call assert_equal([16, 20], g:FoldTextArgs) + set foldtext& + bw! delfunc s:FoldText endfunc diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index e369645328..f09dbd72ce 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -234,6 +234,7 @@ func Test_includeexpr_scriptlocal_func() endfunc set includeexpr=s:IncludeFunc() call assert_equal(expand('') .. 'IncludeFunc()', &includeexpr) + call assert_equal(expand('') .. 'IncludeFunc()', &g:includeexpr) new | only call setline(1, 'TestFile1') let g:IncludeFname = '' @@ -242,11 +243,35 @@ func Test_includeexpr_scriptlocal_func() bw! set includeexpr=IncludeFunc() call assert_equal(expand('') .. 'IncludeFunc()', &includeexpr) + call assert_equal(expand('') .. 'IncludeFunc()', &g:includeexpr) new | only call setline(1, 'TestFile2') let g:IncludeFname = '' call assert_fails('normal! gf', 'E447:') call assert_equal('TestFile2', g:IncludeFname) + bw! + setlocal includeexpr= + setglobal includeexpr=s:IncludeFunc() + call assert_equal(expand('') .. 'IncludeFunc()', &g:includeexpr) + call assert_equal('', &includeexpr) + new + call assert_equal(expand('') .. 'IncludeFunc()', &includeexpr) + call setline(1, 'TestFile3') + let g:IncludeFname = '' + call assert_fails('normal! gf', 'E447:') + call assert_equal('TestFile3', g:IncludeFname) + bw! + setlocal includeexpr= + setglobal includeexpr=IncludeFunc() + call assert_equal(expand('') .. 'IncludeFunc()', &g:includeexpr) + call assert_equal('', &includeexpr) + new + call assert_equal(expand('') .. 'IncludeFunc()', &includeexpr) + call setline(1, 'TestFile4') + let g:IncludeFname = '' + call assert_fails('normal! gf', 'E447:') + call assert_equal('TestFile4', g:IncludeFname) + bw! set includeexpr& delfunc s:IncludeFunc bw! diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 7c90b444e5..2aaa1ff830 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -262,6 +262,7 @@ func Test_formatexpr_scriptlocal_func() endfunc set formatexpr=s:Format() call assert_equal(expand('') .. 'Format()', &formatexpr) + call assert_equal(expand('') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] @@ -270,6 +271,7 @@ func Test_formatexpr_scriptlocal_func() bw! set formatexpr=Format() call assert_equal(expand('') .. 'Format()', &formatexpr) + call assert_equal(expand('') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] @@ -277,6 +279,7 @@ func Test_formatexpr_scriptlocal_func() call assert_equal([4, 2], g:FormatArgs) bw! let &formatexpr = 's:Format()' + call assert_equal(expand('') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] @@ -284,12 +287,55 @@ func Test_formatexpr_scriptlocal_func() call assert_equal([6, 2], g:FormatArgs) bw! let &formatexpr = 'Format()' + call assert_equal(expand('') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] normal! 8GVjgq call assert_equal([8, 2], g:FormatArgs) + bw! setlocal formatexpr= + setglobal formatexpr=s:Format() + call assert_equal(expand('') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 10GVjgq + call assert_equal([10, 2], g:FormatArgs) + bw! + setglobal formatexpr=Format() + call assert_equal(expand('') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 12GVjgq + call assert_equal([12, 2], g:FormatArgs) + bw! + let &g:formatexpr = 's:Format()' + call assert_equal(expand('') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 14GVjgq + call assert_equal([14, 2], g:FormatArgs) + bw! + let &g:formatexpr = 'Format()' + call assert_equal(expand('') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 16GVjgq + call assert_equal([16, 2], g:FormatArgs) + bw! + set formatexpr= delfunc s:Format bw! endfunc -- cgit From 88e906d165b5dd57fb13b190ec9cb2d67bc6b223 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 08:52:21 +0800 Subject: vim-patch:9.0.1245: code is indented more than necessary (#21998) Problem: Code is indented more than necessary. Solution: Use an early return where it makes sense. (Yegappan Lakshmanan, closes vim/vim#11879) https://github.com/vim/vim/commit/032713f8299abd92fcfb1e490d1ae5c1ecadde41 Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval/funcs.c | 50 +++++------ src/nvim/eval/typval.c | 80 ++++++++--------- src/nvim/tag.c | 228 +++++++++++++++++++++++++------------------------ src/nvim/testing.c | 49 +++++------ src/nvim/textformat.c | 32 +++---- 5 files changed, 226 insertions(+), 213 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 5a31288ecd..c527b62c8f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7823,34 +7823,35 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) // MSVC returns NULL for an invalid value of seconds. if (curtime_ptr == NULL) { rettv->vval.v_string = xstrdup(_("(Invalid)")); - } else { - vimconv_T conv; + return; + } - conv.vc_type = CONV_NONE; - char *enc = enc_locale(); - convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) { - p = string_convert(&conv, p, NULL); - } - char result_buf[256]; - if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) { - result_buf[0] = NUL; - } + vimconv_T conv; - if (conv.vc_type != CONV_NONE) { - xfree(p); - } - convert_setup(&conv, enc, p_enc); - if (conv.vc_type != CONV_NONE) { - rettv->vval.v_string = string_convert(&conv, result_buf, NULL); - } else { - rettv->vval.v_string = xstrdup(result_buf); - } + conv.vc_type = CONV_NONE; + char *enc = enc_locale(); + convert_setup(&conv, p_enc, enc); + if (conv.vc_type != CONV_NONE) { + p = string_convert(&conv, p, NULL); + } + char result_buf[256]; + if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) { + result_buf[0] = NUL; + } - // Release conversion descriptors. - convert_setup(&conv, NULL, NULL); - xfree(enc); + if (conv.vc_type != CONV_NONE) { + xfree(p); } + convert_setup(&conv, enc, p_enc); + if (conv.vc_type != CONV_NONE) { + rettv->vval.v_string = string_convert(&conv, result_buf, NULL); + } else { + rettv->vval.v_string = xstrdup(result_buf); + } + + // Release conversion descriptors. + convert_setup(&conv, NULL, NULL); + xfree(enc); } /// "strgetchar()" function @@ -8654,6 +8655,7 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, EvalFuncData fptr emsg(_(e_number_exp)); return; } + int paused = (bool)tv_get_number(&argvars[1]); timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0])); if (timer != NULL) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 05b4737206..c298064d86 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3208,17 +3208,19 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic /// @param[in,out] tv Value to free. void tv_clear(typval_T *const tv) { - if (tv != NULL && tv->v_type != VAR_UNKNOWN) { - // WARNING: do not translate the string here, gettext is slow and function - // is used *very* often. At the current state encode_vim_to_nothing() does - // not error out and does not use the argument anywhere. - // - // If situation changes and this argument will be used, translate it in the - // place where it is used. - const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear() argument"); - (void)evn_ret; - assert(evn_ret == OK); + if (tv == NULL || tv->v_type == VAR_UNKNOWN) { + return; } + + // WARNING: do not translate the string here, gettext is slow and function + // is used *very* often. At the current state encode_vim_to_nothing() does + // not error out and does not use the argument anywhere. + // + // If situation changes and this argument will be used, translate it in the + // place where it is used. + const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear() argument"); + (void)evn_ret; + assert(evn_ret == OK); } //{{{3 Free @@ -3228,35 +3230,37 @@ void tv_clear(typval_T *const tv) /// @param tv Object to free. void tv_free(typval_T *tv) { - if (tv != NULL) { - switch (tv->v_type) { - case VAR_PARTIAL: - partial_unref(tv->vval.v_partial); - break; - case VAR_FUNC: - func_unref(tv->vval.v_string); - FALLTHROUGH; - case VAR_STRING: - xfree(tv->vval.v_string); - break; - case VAR_BLOB: - tv_blob_unref(tv->vval.v_blob); - break; - case VAR_LIST: - tv_list_unref(tv->vval.v_list); - break; - case VAR_DICT: - tv_dict_unref(tv->vval.v_dict); - break; - case VAR_BOOL: - case VAR_SPECIAL: - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - } - xfree(tv); + if (tv == NULL) { + return; + } + + switch (tv->v_type) { + case VAR_PARTIAL: + partial_unref(tv->vval.v_partial); + break; + case VAR_FUNC: + func_unref(tv->vval.v_string); + FALLTHROUGH; + case VAR_STRING: + xfree(tv->vval.v_string); + break; + case VAR_BLOB: + tv_blob_unref(tv->vval.v_blob); + break; + case VAR_LIST: + tv_list_unref(tv->vval.v_list); + break; + case VAR_DICT: + tv_dict_unref(tv->vval.v_dict); + break; + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: + break; } + xfree(tv); } //{{{3 Copy diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 197184c181..42618e8924 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2732,55 +2732,57 @@ static int parse_match(char *lbuf, tagptrs_T *tagp) tagp->tagline = 0; tagp->command_end = NULL; - if (retval == OK) { - // Try to find a kind field: "kind:" or just "" - p = tagp->command; - if (find_extra(&p) == OK) { - tagp->command_end = p; - if (p > tagp->command && p[-1] == '|') { - tagp->command_end = p - 1; // drop trailing bar - } - p += 2; // skip ";\"" - if (*p++ == TAB) { - // Accept ASCII alphabetic kind characters and any multi-byte - // character. - while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) { - if (strncmp(p, "kind:", 5) == 0) { - tagp->tagkind = p + 5; - } else if (strncmp(p, "user_data:", 10) == 0) { - tagp->user_data = p + 10; - } else if (strncmp(p, "line:", 5) == 0) { - tagp->tagline = atoi(p + 5); - } - if (tagp->tagkind != NULL && tagp->user_data != NULL) { - break; - } + if (retval != OK) { + return retval; + } - pc = vim_strchr(p, ':'); - pt = vim_strchr(p, '\t'); - if (pc == NULL || (pt != NULL && pc > pt)) { - tagp->tagkind = p; - } - if (pt == NULL) { - break; - } - p = pt; - MB_PTR_ADV(p); + // Try to find a kind field: "kind:" or just "" + p = tagp->command; + if (find_extra(&p) == OK) { + tagp->command_end = p; + if (p > tagp->command && p[-1] == '|') { + tagp->command_end = p - 1; // drop trailing bar + } + p += 2; // skip ";\"" + if (*p++ == TAB) { + // Accept ASCII alphabetic kind characters and any multi-byte + // character. + while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) { + if (strncmp(p, "kind:", 5) == 0) { + tagp->tagkind = p + 5; + } else if (strncmp(p, "user_data:", 10) == 0) { + tagp->user_data = p + 10; + } else if (strncmp(p, "line:", 5) == 0) { + tagp->tagline = atoi(p + 5); } + if (tagp->tagkind != NULL && tagp->user_data != NULL) { + break; + } + + pc = vim_strchr(p, ':'); + pt = vim_strchr(p, '\t'); + if (pc == NULL || (pt != NULL && pc > pt)) { + tagp->tagkind = p; + } + if (pt == NULL) { + break; + } + p = pt; + MB_PTR_ADV(p); } } - if (tagp->tagkind != NULL) { - for (p = tagp->tagkind; - *p && *p != '\t' && *p != '\r' && *p != '\n'; - MB_PTR_ADV(p)) {} - tagp->tagkind_end = p; - } - if (tagp->user_data != NULL) { - for (p = tagp->user_data; - *p && *p != '\t' && *p != '\r' && *p != '\n'; - MB_PTR_ADV(p)) {} - tagp->user_data_end = p; - } + } + if (tagp->tagkind != NULL) { + for (p = tagp->tagkind; + *p && *p != '\t' && *p != '\r' && *p != '\n'; + MB_PTR_ADV(p)) {} + tagp->tagkind_end = p; + } + if (tagp->user_data != NULL) { + for (p = tagp->user_data; + *p && *p != '\t' && *p != '\r' && *p != '\n'; + MB_PTR_ADV(p)) {} + tagp->user_data_end = p; } return retval; } @@ -3329,86 +3331,88 @@ int get_tags(list_T *list, char *pat, char *buf_fname) ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname); - if (ret == OK && num_matches > 0) { - for (i = 0; i < num_matches; i++) { - int parse_result = parse_match(matches[i], &tp); + if (ret != OK || num_matches <= 0) { + return ret; + } - // Avoid an unused variable warning in release builds. - (void)parse_result; - assert(parse_result == OK); + for (i = 0; i < num_matches; i++) { + int parse_result = parse_match(matches[i], &tp); - is_static = test_for_static(&tp); + // Avoid an unused variable warning in release builds. + (void)parse_result; + assert(parse_result == OK); - // Skip pseudo-tag lines. - if (strncmp(tp.tagname, "!_TAG_", 6) == 0) { - xfree(matches[i]); - continue; - } + is_static = test_for_static(&tp); + + // Skip pseudo-tag lines. + if (strncmp(tp.tagname, "!_TAG_", 6) == 0) { + xfree(matches[i]); + continue; + } - dict = tv_dict_alloc(); - tv_list_append_dict(list, dict); - - full_fname = tag_full_fname(&tp); - if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL - || add_tag_field(dict, "filename", full_fname, NULL) == FAIL - || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL - || add_tag_field(dict, "kind", tp.tagkind, - tp.tagkind ? tp.tagkind_end : NULL) == FAIL - || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { - ret = FAIL; - } - - xfree(full_fname); - - if (tp.command_end != NULL) { - for (char *p = tp.command_end + 3; - *p != NUL && *p != '\n' && *p != '\r'; - MB_PTR_ADV(p)) { - if (p == tp.tagkind - || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) { - // skip "kind:" and "" - p = tp.tagkind_end - 1; - } else if (strncmp(p, "file:", 5) == 0) { - // skip "file:" (static tag) - p += 4; - } else if (!ascii_iswhite(*p)) { - char *s, *n; - int len; - - // Add extra field as a dict entry. Fields are - // separated by Tabs. - n = p; - while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') { + dict = tv_dict_alloc(); + tv_list_append_dict(list, dict); + + full_fname = tag_full_fname(&tp); + if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL + || add_tag_field(dict, "filename", full_fname, NULL) == FAIL + || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL + || add_tag_field(dict, "kind", tp.tagkind, + tp.tagkind ? tp.tagkind_end : NULL) == FAIL + || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { + ret = FAIL; + } + + xfree(full_fname); + + if (tp.command_end != NULL) { + for (char *p = tp.command_end + 3; + *p != NUL && *p != '\n' && *p != '\r'; + MB_PTR_ADV(p)) { + if (p == tp.tagkind + || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) { + // skip "kind:" and "" + p = tp.tagkind_end - 1; + } else if (strncmp(p, "file:", 5) == 0) { + // skip "file:" (static tag) + p += 4; + } else if (!ascii_iswhite(*p)) { + char *s, *n; + int len; + + // Add extra field as a dict entry. Fields are + // separated by Tabs. + n = p; + while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') { + p++; + } + len = (int)(p - n); + if (*p == ':' && len > 0) { + s = ++p; + while (*p != NUL && (uint8_t)(*p) >= ' ') { p++; } - len = (int)(p - n); - if (*p == ':' && len > 0) { - s = ++p; - while (*p != NUL && (uint8_t)(*p) >= ' ') { - p++; - } - n[len] = NUL; - if (add_tag_field(dict, n, s, p) == FAIL) { - ret = FAIL; - } - n[len] = ':'; - } else { - // Skip field without colon. - while (*p != NUL && (uint8_t)(*p) >= ' ') { - p++; - } + n[len] = NUL; + if (add_tag_field(dict, n, s, p) == FAIL) { + ret = FAIL; } - if (*p == NUL) { - break; + n[len] = ':'; + } else { + // Skip field without colon. + while (*p != NUL && (uint8_t)(*p) >= ' ') { + p++; } } + if (*p == NUL) { + break; + } } } - - xfree(matches[i]); } - xfree(matches); + + xfree(matches[i]); } + xfree(matches); return ret; } diff --git a/src/nvim/testing.c b/src/nvim/testing.c index edf92c78ac..b5921b3445 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -77,31 +77,32 @@ static void ga_concat_esc(garray_T *gap, const char *p, int clen) memmove(buf, p, (size_t)clen); buf[clen] = NUL; ga_concat(gap, buf); - } else { - switch (*p) { - case BS: - ga_concat(gap, "\\b"); break; - case ESC: - ga_concat(gap, "\\e"); break; - case FF: - ga_concat(gap, "\\f"); break; - case NL: - ga_concat(gap, "\\n"); break; - case TAB: - ga_concat(gap, "\\t"); break; - case CAR: - ga_concat(gap, "\\r"); break; - case '\\': - ga_concat(gap, "\\\\"); break; - default: - if ((uint8_t)(*p) < ' ' || *p == 0x7f) { - vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p); - ga_concat(gap, buf); - } else { - ga_append(gap, (uint8_t)(*p)); - } - break; + return; + } + + switch (*p) { + case BS: + ga_concat(gap, "\\b"); break; + case ESC: + ga_concat(gap, "\\e"); break; + case FF: + ga_concat(gap, "\\f"); break; + case NL: + ga_concat(gap, "\\n"); break; + case TAB: + ga_concat(gap, "\\t"); break; + case CAR: + ga_concat(gap, "\\r"); break; + case '\\': + ga_concat(gap, "\\\\"); break; + default: + if ((uint8_t)(*p) < ' ' || *p == 0x7f) { + vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat(gap, buf); + } else { + ga_append(gap, (uint8_t)(*p)); } + break; } } diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index fbea1ccfb7..e30580a748 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -736,22 +736,24 @@ void check_auto_format(bool end_insert) int c = ' '; int cc; - if (did_add_space) { - cc = gchar_cursor(); - if (!WHITECHAR(cc)) { - // Somehow the space was removed already. + if (!did_add_space) { + return; + } + + cc = gchar_cursor(); + if (!WHITECHAR(cc)) { + // Somehow the space was removed already. + did_add_space = false; + } else { + if (!end_insert) { + inc_cursor(); + c = gchar_cursor(); + dec_cursor(); + } + if (c != NUL) { + // The space is no longer at the end of the line, delete it. + del_char(false); did_add_space = false; - } else { - if (!end_insert) { - inc_cursor(); - c = gchar_cursor(); - dec_cursor(); - } - if (c != NUL) { - // The space is no longer at the end of the line, delete it. - del_char(false); - did_add_space = false; - } } } } -- cgit From 116766f243b4ac647f040181334c8001e01aab75 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 09:12:33 +0800 Subject: refactor(options): don't pass negative number to illegal_char() (#21999) This only changes the error messages for an unexpected Unicode char in an option to show its first byte instead of some special keycode. The second argument of vim_strchr() usually doesn't matter, but it may be better to consistently cast to uint8_t. --- src/nvim/optionstr.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 162d8b691a..5ebff9ed77 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -615,7 +615,7 @@ char *check_stl_option(char *s) continue; } if (vim_strchr(STL_ALL, (uint8_t)(*s)) == NULL) { - return illegal_char(errbuf, sizeof(errbuf), *s); + return illegal_char(errbuf, sizeof(errbuf), (uint8_t)(*s)); } if (*s == '{') { bool reevaluate = (*++s == '%'); @@ -957,7 +957,7 @@ static void did_set_comments(char **varp, char *errbuf, size_t errbuflen, char * while (*s && *s != ':') { if (vim_strchr(COM_ALL, (uint8_t)(*s)) == NULL && !ascii_isdigit(*s) && *s != '-') { - *errmsg = illegal_char(errbuf, errbuflen, *s); + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } s++; @@ -1029,7 +1029,7 @@ static void did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, ch for (char *s = p_shada; *s;) { // Check it's a valid character if (vim_strchr("!\"%'/:<@cfhnrs", (uint8_t)(*s)) == NULL) { - *errmsg = illegal_char(errbuf, errbuflen, *s); + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } if (*s == 'n') { // name is always last one @@ -1236,7 +1236,7 @@ static void did_set_complete(char **varp, char *errbuf, size_t errbuflen, char * break; } if (vim_strchr(".wbuksid]tU", (uint8_t)(*s)) == NULL) { - *errmsg = illegal_char(errbuf, errbuflen, *s); + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } if (*++s != NUL && *s != ',' && *s != ' ') { @@ -1511,8 +1511,8 @@ static void did_set_option_listflag(char **varp, char *flags, char *errbuf, size char **errmsg) { for (char *s = *varp; *s; s++) { - if (vim_strchr(flags, *s) == NULL) { - *errmsg = illegal_char(errbuf, errbuflen, *s); + if (vim_strchr(flags, (uint8_t)(*s)) == NULL) { + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } } -- cgit From 5ac34cf55db2b00c044fa95f75766dd89dd36ba9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 09:30:26 +0800 Subject: refactor(intro): avoid Coverity warning (#22000) refactor(intro): avoid coverity warning Problem: Coverity warns about overwriting "mesg" leaking memory. Solution: Make it clear that "mesg" will not be overwritten. --- src/nvim/version.c | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/nvim/version.c b/src/nvim/version.c index 417e5116a5..11f1d6695e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2785,13 +2785,6 @@ void maybe_intro_message(void) /// @param colon true for ":intro" void intro_message(int colon) { - int i; - long row; - long blanklines; - int sponsor; - char *p; - char *mesg; - int mesg_size; static char *(lines[]) = { N_(NVIM_VERSION_LONG), "", @@ -2813,7 +2806,7 @@ void intro_message(int colon) size_t lines_size = ARRAY_SIZE(lines); assert(lines_size <= LONG_MAX); - blanklines = Rows - ((long)lines_size - 1L); + long blanklines = Rows - ((long)lines_size - 1L); // Don't overwrite a statusline. Depends on 'cmdheight'. if (p_ls > 1) { @@ -2826,17 +2819,17 @@ void intro_message(int colon) // Show the sponsor and register message one out of four times, the Uganda // message two out of four times. - sponsor = (int)time(NULL); + int sponsor = (int)time(NULL); sponsor = ((sponsor & 2) == 0) - ((sponsor & 4) == 0); // start displaying the message lines after half of the blank lines - row = blanklines / 2; + long row = blanklines / 2; if (((row >= 2) && (Columns >= 50)) || colon) { - for (i = 0; i < (int)ARRAY_SIZE(lines); i++) { - p = lines[i]; - mesg = NULL; - mesg_size = 0; + for (int i = 0; i < (int)ARRAY_SIZE(lines); i++) { + char *p = lines[i]; + char *mesg = NULL; + int mesg_size = 0; if (strstr(p, "news") != NULL) { p = _(p); @@ -2846,18 +2839,15 @@ void intro_message(int colon) mesg = xmallocz((size_t)mesg_size); snprintf(mesg, (size_t)mesg_size + 1, p, STR(NVIM_VERSION_MAJOR), STR(NVIM_VERSION_MINOR)); - } - - if (sponsor != 0) { + } else if (sponsor != 0) { if (strstr(p, "children") != NULL) { - mesg = sponsor < 0 - ? _("Sponsor Vim development!") - : _("Become a registered Vim user!"); - } - if (strstr(p, "iccf") != NULL) { - mesg = sponsor < 0 - ? _("type :help sponsor for information ") - : _("type :help register for information "); + p = sponsor < 0 + ? N_("Sponsor Vim development!") + : N_("Become a registered Vim user!"); + } else if (strstr(p, "iccf") != NULL) { + p = sponsor < 0 + ? N_("type :help sponsor for information ") + : N_("type :help register for information "); } } -- cgit From 6644786db078e019426f90cf85da50e3fa1b6a67 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 09:44:15 +0800 Subject: vim-patch:9.0.1227: no cmdline completion for :runtime Problem: No cmdline completion for :runtime. Solution: Add completion for :runtime. (closes vim/vim#11853, closes vim/vim#11447) Improve the resulting matches. https://github.com/vim/vim/commit/a6759381a590b2d395e05b109ca9ccfc356be5a8 --- src/nvim/cmdexpand.c | 12 +++++++ src/nvim/runtime.c | 70 ++++++++++++++++++++++++++++++++------- src/nvim/runtime.h | 1 + src/nvim/testdir/test_cmdline.vim | 9 +++++ src/nvim/usercmd.c | 1 + src/nvim/vim.h | 1 + 6 files changed, 82 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index be815151ef..3e06814d6a 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -109,6 +109,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp) && xp->xp_context != EXPAND_OLD_SETTING && xp->xp_context != EXPAND_OWNSYNTAX && xp->xp_context != EXPAND_PACKADD + && xp->xp_context != EXPAND_RUNTIME && xp->xp_context != EXPAND_SHELLCMD && xp->xp_context != EXPAND_TAGS && xp->xp_context != EXPAND_TAGS_LISTFILES @@ -1211,6 +1212,7 @@ char *addstar(char *fname, size_t len, int context) if (context == EXPAND_HELP || context == EXPAND_CHECKHEALTH || context == EXPAND_COLORS + || context == EXPAND_RUNTIME || context == EXPAND_COMPILER || context == EXPAND_OWNSYNTAX || context == EXPAND_FILETYPE @@ -2072,6 +2074,11 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa xp->xp_pattern = (char *)arg; break; + case CMD_runtime: + xp->xp_context = EXPAND_RUNTIME; + xp->xp_pattern = (char *)arg; + break; + case CMD_compiler: xp->xp_context = EXPAND_COMPILER; xp->xp_pattern = (char *)arg; @@ -2712,6 +2719,11 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM char *directories[] = { "colors", NULL }; return ExpandRTDir(pat, DIP_START + DIP_OPT, numMatches, matches, directories); } + if (xp->xp_context == EXPAND_RUNTIME) { + char *directories[] = { "", NULL }; + return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_PRNEXT, numMatches, + matches, directories); + } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = { "compiler", NULL }; return ExpandRTDir(pat, 0, numMatches, matches, directories); diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 24500b80b9..068a43c790 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -1194,11 +1194,22 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname // TODO(bfredl): this is bullshit, expandpath should not reinvent path logic. for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 16; - char *s = xmalloc(size); - snprintf(s, size, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); - globpath(p_rtp, s, &ga, 0); - xfree(s); + size_t size = strlen(dirnames[i]) + pat_len * 2 + 26; + char *buf = xmalloc(size); + if (*dirnames[i] == NUL) { + // empty dir used for :runtime + if (path_tail(pat) == pat) { + // no path separator, match dir names and script files + snprintf(buf, size, "\\(%s*.\\(vim\\|lua\\)\\)\\|\\(%s*\\)", pat, pat); + } else { + // has path separator, match script files + snprintf(buf, size, "%s*.vim", pat); + } + } else { + snprintf(buf, size, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); + } + globpath(p_rtp, buf, &ga, 0); + xfree(buf); } if (flags & DIP_START) { @@ -1241,18 +1252,53 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname char *match = ((char **)ga.ga_data)[i]; char *s = match; char *e = s + strlen(s); + char *res_start = s; + if ((flags & DIP_PRNEXT) != 0) { + char *p = strstr(match, pat); + if (p != NULL) { + // Drop what comes before "pat" in the match, so that for + // match "/long/path/syntax/cpp.vim" with pattern + // "syntax/cp" we only keep "syntax/cpp.vim". + res_start = p; + } + } + if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0 || STRNICMP(e - 4, ".lua", 4) == 0)) { - e -= 4; - for (s = e; s > match; MB_PTR_BACK(match, s)) { - if (vim_ispathsep(*s)) { - break; + if (res_start == s) { + // Only keep the file name. + // Remove file ext only if flag DIP_PRNEXT is not present. + if ((flags & DIP_PRNEXT) == 0) { + e -= 4; + } + for (s = e; s > match; MB_PTR_BACK(match, s)) { + if (s < match) { + break; + } + if (vim_ispathsep(*s)) { + res_start = s + 1; + break; + } } } - s++; + *e = NUL; - assert((e - s) + 1 >= 0); - memmove(match, s, (size_t)(e - s) + 1); + } + + if (res_start > match) { + assert((e - res_start) + 1 >= 0); + memmove(match, res_start, (size_t)(e - res_start) + 1); + } + + // remove entries that look like backup files + if (e > s && e[-1] == '~') { + xfree(match); + char **fnames = (char **)ga.ga_data; + for (int j = i + 1; j < ga.ga_len; ++j) { + fnames[j - 1] = fnames[j]; + } + ga.ga_len--; + i--; } } diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h index 97063b900c..be2663d52a 100644 --- a/src/nvim/runtime.h +++ b/src/nvim/runtime.h @@ -105,6 +105,7 @@ typedef kvec_t(char *) CharVec; #define DIP_NORTP 0x20 // do not use 'runtimepath' #define DIP_NOAFTER 0x40 // skip "after" directories #define DIP_AFTER 0x80 // only use "after" directories +#define DIP_PRNEXT 0x100 // for print also file extension #define DIP_DIRFILE 0x200 // find both files and directories #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index cf1d56ae38..8f79f0d8a0 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -542,6 +542,15 @@ func Test_getcompletion() call assert_true(index(l, '') >= 0) let l = getcompletion('not', 'mapclear') call assert_equal([], l) + + let l = getcompletion('', 'runtime') + call assert_true(index(l, 'defaults.vim') >= 0) + let l = getcompletion('synt', 'runtime') + call assert_true(index(l, 'syntax') >= 0) + let l = getcompletion('syntax/vi', 'runtime') + call assert_true(index(l, 'syntax/vim.vim') >= 0) + let l = getcompletion('notexitsts', 'runtime') + call assert_equal([], l) let l = getcompletion('.', 'shellcmd') call assert_equal(['./', '../'], filter(l, 'v:val =~ "\\./"')) diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 31cb1e8936..ef13f67e49 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -89,6 +89,7 @@ static const char *command_complete[] = { [EXPAND_SYNTIME] = "syntime", [EXPAND_SETTINGS] = "option", [EXPAND_PACKADD] = "packadd", + [EXPAND_RUNTIME] = "runtime", [EXPAND_SHELLCMD] = "shellcmd", [EXPAND_SIGN] = "sign", [EXPAND_TAGS] = "tag", diff --git a/src/nvim/vim.h b/src/nvim/vim.h index c4baa911f1..3c765f8eb2 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -156,6 +156,7 @@ enum { EXPAND_DIFF_BUFFERS, EXPAND_BREAKPOINT, EXPAND_SCRIPTNAMES, + EXPAND_RUNTIME, EXPAND_CHECKHEALTH, EXPAND_LUA, }; -- cgit From 6320c91c50e4c0ee5c366241f9a413c4edbfdad8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 09:58:27 +0800 Subject: vim-patch:9.0.1231: completion of :runtime does not handle {where} argument Problem: Completion of :runtime does not handle {where} argument. Solution: Parse the {where} argument. (closes vim/vim#11863) https://github.com/vim/vim/commit/3770f4c9cde7b5fcd10b6fa2e665cd0b69450fb2 --- src/nvim/cmdexpand.c | 17 ++-- src/nvim/eval/funcs.c | 2 +- src/nvim/path.c | 2 +- src/nvim/runtime.c | 205 +++++++++++++++++++------------------- src/nvim/runtime.h | 2 +- src/nvim/testdir/test_cmdline.vim | 9 -- src/nvim/testdir/test_packadd.vim | 64 ++++++++++++ 7 files changed, 178 insertions(+), 123 deletions(-) (limited to 'src') diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 3e06814d6a..21ea8c1ffc 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -2075,8 +2075,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa break; case CMD_runtime: - xp->xp_context = EXPAND_RUNTIME; - xp->xp_pattern = (char *)arg; + set_context_in_runtime_cmd(xp, arg); break; case CMD_compiler: @@ -2720,9 +2719,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM return ExpandRTDir(pat, DIP_START + DIP_OPT, numMatches, matches, directories); } if (xp->xp_context == EXPAND_RUNTIME) { - char *directories[] = { "", NULL }; - return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_PRNEXT, numMatches, - matches, directories); + return expand_runtime_cmd(pat, numMatches, matches); } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = { "compiler", NULL }; @@ -3213,11 +3210,12 @@ static int ExpandUserLua(expand_T *xp, int *num_file, char ***file) /// Expand `file` for all comma-separated directories in `path`. /// Adds matches to `ga`. -void globpath(char *path, char *file, garray_T *ga, int expand_options) +/// If "dirs" is true only expand directory names. +void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dirs) { expand_T xpc; ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; + xpc.xp_context = dirs ? EXPAND_DIRECTORIES : EXPAND_FILES; char *buf = xmalloc(MAXPATHL); @@ -3536,11 +3534,14 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false); xpc.xp_pattern_len = strlen(xpc.xp_pattern); } - if (xpc.xp_context == EXPAND_SIGN) { set_context_in_sign_cmd(&xpc, xpc.xp_pattern); xpc.xp_pattern_len = strlen(xpc.xp_pattern); } + if (xpc.xp_context == EXPAND_RUNTIME) { + set_context_in_runtime_cmd(&xpc, xpc.xp_pattern); + xpc.xp_pattern_len = strlen(xpc.xp_pattern); + } theend: if (cmdline_fuzzy_completion_supported(&xpc)) { diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c527b62c8f..938fef9a52 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2983,7 +2983,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char *), 10); - globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags); + globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags, false); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); diff --git a/src/nvim/path.c b/src/nvim/path.c index 513f366a27..e4c2253357 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1138,7 +1138,7 @@ static int expand_in_path(garray_T *const gap, char *const pattern, const int fl if (flags & EW_ADDSLASH) { glob_flags |= WILD_ADD_SLASH; } - globpath(paths, pattern, gap, glob_flags); + globpath(paths, pattern, gap, glob_flags, false); xfree(paths); return gap->ga_len; diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 068a43c790..129d41ed22 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -208,31 +208,65 @@ void runtime_init(void) uv_mutex_init(&runtime_search_path_mutex); } +/// Get DIP_ flags from the [what] argument of a :runtime command. +/// "*argp" is advanced to after the [what] argument. +static int get_runtime_cmd_flags(char **argp) +{ + char *arg = *argp; + char *p = skiptowhite(arg); + size_t what_len = (size_t)(p - arg); + + if (what_len == 0) { + return 0; + } + + if (strncmp(arg, "START", what_len) == 0) { + *argp = skipwhite(arg + what_len); + return DIP_START + DIP_NORTP; + } + if (strncmp(arg, "OPT", what_len) == 0) { + *argp = skipwhite(arg + what_len); + return DIP_OPT + DIP_NORTP; + } + if (strncmp(arg, "PACK", what_len) == 0) { + *argp = skipwhite(arg + what_len); + return DIP_START + DIP_OPT + DIP_NORTP; + } + if (strncmp(arg, "ALL", what_len) == 0) { + *argp = skipwhite(arg + what_len); + return DIP_START + DIP_OPT; + } + + return 0; +} + /// ":runtime [what] {name}" void ex_runtime(exarg_T *eap) { char *arg = eap->arg; - char *p = skiptowhite(arg); - size_t len = (size_t)(p - arg); int flags = eap->forceit ? DIP_ALL : 0; - if (strncmp(arg, "START", len) == 0) { - flags += DIP_START + DIP_NORTP; - arg = skipwhite(arg + len); - } else if (strncmp(arg, "OPT", len) == 0) { - flags += DIP_OPT + DIP_NORTP; - arg = skipwhite(arg + len); - } else if (strncmp(arg, "PACK", len) == 0) { - flags += DIP_START + DIP_OPT + DIP_NORTP; - arg = skipwhite(arg + len); - } else if (strncmp(arg, "ALL", len) == 0) { - flags += DIP_START + DIP_OPT; - arg = skipwhite(arg + len); - } - + flags += get_runtime_cmd_flags(&arg); source_runtime(arg, flags); } +static int runtime_expand_flags; + +/// Set the completion context for the :runtime command. +void set_context_in_runtime_cmd(expand_T *xp, const char *arg) +{ + runtime_expand_flags = DIP_KEEPEXT + get_runtime_cmd_flags((char **)&arg); + xp->xp_context = EXPAND_RUNTIME; + xp->xp_pattern = (char *)arg; +} + +/// Handle command line completion for :runtime command. +int expand_runtime_cmd(char *pat, int *numMatches, char ***matches) +{ + char *directories[] = {"", NULL}; + return ExpandRTDir(pat, runtime_expand_flags, numMatches, matches, directories); +} + static void source_callback(char *fname, void *cookie) { (void)do_source(fname, false, DOSO_NONE); @@ -1194,57 +1228,53 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname // TODO(bfredl): this is bullshit, expandpath should not reinvent path logic. for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len * 2 + 26; - char *buf = xmalloc(size); - if (*dirnames[i] == NUL) { - // empty dir used for :runtime - if (path_tail(pat) == pat) { - // no path separator, match dir names and script files - snprintf(buf, size, "\\(%s*.\\(vim\\|lua\\)\\)\\|\\(%s*\\)", pat, pat); - } else { - // has path separator, match script files - snprintf(buf, size, "%s*.vim", pat); - } + const size_t buf_len = strlen(dirnames[i]) + pat_len + 31; + char *const buf = xmalloc(buf_len); + char *const tail = buf + 15; + const size_t tail_buflen = buf_len - 15; + int glob_flags = 0; + bool expand_dirs = false; + + if (*dirnames[i] == NUL) { // empty dir used for :runtime + snprintf(tail, tail_buflen, "%s*.\\(vim\\|lua\\)", pat); } else { - snprintf(buf, size, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); + snprintf(tail, tail_buflen, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); } - globpath(p_rtp, buf, &ga, 0); - xfree(buf); - } - if (flags & DIP_START) { - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 31; - char *s = xmalloc(size); - snprintf(s, size, "pack/*/start/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); +expand: + if ((flags & DIP_NORTP) == 0) { + globpath(p_rtp, tail, &ga, glob_flags, expand_dirs); } - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 31; - char *s = xmalloc(size); - snprintf(s, size, "start/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); + if (flags & DIP_START) { + memcpy(tail - 15, "pack/*/start/*/", 15); // NOLINT + globpath(p_pp, tail - 15, &ga, glob_flags, expand_dirs); + memcpy(tail - 8, "start/*/", 8); // NOLINT + globpath(p_pp, tail - 8, &ga, glob_flags, expand_dirs); } - } - if (flags & DIP_OPT) { - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 29; - char *s = xmalloc(size); - snprintf(s, size, "pack/*/opt/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); + if (flags & DIP_OPT) { + memcpy(tail - 13, "pack/*/opt/*/", 13); // NOLINT + globpath(p_pp, tail - 13, &ga, glob_flags, expand_dirs); + memcpy(tail - 6, "opt/*/", 6); // NOLINT + globpath(p_pp, tail - 6, &ga, glob_flags, expand_dirs); } - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 29; - char *s = xmalloc(size); - snprintf(s, size, "opt/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); + if (*dirnames[i] == NUL && !expand_dirs) { + // expand dir names in another round + snprintf(tail, tail_buflen, "%s*", pat); + glob_flags = WILD_ADD_SLASH; + expand_dirs = true; + goto expand; + } + + xfree(buf); + } + + int pat_pathsep_cnt = 0; + for (size_t i = 0; i < pat_len; i++) { + if (vim_ispathsep(pat[i])) { + pat_pathsep_cnt++; } } @@ -1252,54 +1282,23 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname char *match = ((char **)ga.ga_data)[i]; char *s = match; char *e = s + strlen(s); - char *res_start = s; - if ((flags & DIP_PRNEXT) != 0) { - char *p = strstr(match, pat); - if (p != NULL) { - // Drop what comes before "pat" in the match, so that for - // match "/long/path/syntax/cpp.vim" with pattern - // "syntax/cp" we only keep "syntax/cpp.vim". - res_start = p; - } - } - - if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0 - || STRNICMP(e - 4, ".lua", 4) == 0)) { - if (res_start == s) { - // Only keep the file name. - // Remove file ext only if flag DIP_PRNEXT is not present. - if ((flags & DIP_PRNEXT) == 0) { - e -= 4; - } - for (s = e; s > match; MB_PTR_BACK(match, s)) { - if (s < match) { - break; - } - if (vim_ispathsep(*s)) { - res_start = s + 1; - break; - } - } - } - + if (e - s > 4 && (flags & DIP_KEEPEXT) == 0 + && (STRNICMP(e - 4, ".vim", 4) == 0 + || STRNICMP(e - 4, ".lua", 4) == 0)) { + e -= 4; *e = NUL; } - if (res_start > match) { - assert((e - res_start) + 1 >= 0); - memmove(match, res_start, (size_t)(e - res_start) + 1); - } - - // remove entries that look like backup files - if (e > s && e[-1] == '~') { - xfree(match); - char **fnames = (char **)ga.ga_data; - for (int j = i + 1; j < ga.ga_len; ++j) { - fnames[j - 1] = fnames[j]; + int match_pathsep_cnt = (e > s && e[-1] == '/') ? -1 : 0; + for (s = e; s > match; MB_PTR_BACK(match, s)) { + if (vim_ispathsep(*s) && ++match_pathsep_cnt > pat_pathsep_cnt) { + break; } - ga.ga_len--; - i--; } + s++; + *e = NUL; + assert((e - s) + 1 >= 0); + memmove(match, s, (size_t)(e - s) + 1); } if (GA_EMPTY(&ga)) { @@ -1329,9 +1328,9 @@ int ExpandPackAddDir(char *pat, int *num_file, char ***file) size_t buflen = pat_len + 26; char *s = xmalloc(buflen); snprintf(s, buflen, "pack/*/opt/%s*", pat); // NOLINT - globpath(p_pp, s, &ga, 0); + globpath(p_pp, s, &ga, 0, true); snprintf(s, buflen, "opt/%s*", pat); // NOLINT - globpath(p_pp, s, &ga, 0); + globpath(p_pp, s, &ga, 0, true); xfree(s); for (int i = 0; i < ga.ga_len; i++) { diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h index be2663d52a..3821fc4bbc 100644 --- a/src/nvim/runtime.h +++ b/src/nvim/runtime.h @@ -105,7 +105,7 @@ typedef kvec_t(char *) CharVec; #define DIP_NORTP 0x20 // do not use 'runtimepath' #define DIP_NOAFTER 0x40 // skip "after" directories #define DIP_AFTER 0x80 // only use "after" directories -#define DIP_PRNEXT 0x100 // for print also file extension +#define DIP_KEEPEXT 0x100 // for completion: include file extension #define DIP_DIRFILE 0x200 // find both files and directories #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 8f79f0d8a0..cf1d56ae38 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -542,15 +542,6 @@ func Test_getcompletion() call assert_true(index(l, '') >= 0) let l = getcompletion('not', 'mapclear') call assert_equal([], l) - - let l = getcompletion('', 'runtime') - call assert_true(index(l, 'defaults.vim') >= 0) - let l = getcompletion('synt', 'runtime') - call assert_true(index(l, 'syntax') >= 0) - let l = getcompletion('syntax/vi', 'runtime') - call assert_true(index(l, 'syntax/vim.vim') >= 0) - let l = getcompletion('notexitsts', 'runtime') - call assert_equal([], l) let l = getcompletion('.', 'shellcmd') call assert_equal(['./', '../'], filter(l, 'v:val =~ "\\./"')) diff --git a/src/nvim/testdir/test_packadd.vim b/src/nvim/testdir/test_packadd.vim index fcb8b8033b..3f00548273 100644 --- a/src/nvim/testdir/test_packadd.vim +++ b/src/nvim/testdir/test_packadd.vim @@ -193,8 +193,10 @@ func Test_packadd_completion() call mkdir(optdir1 . '/pluginA', 'p') call mkdir(optdir1 . '/pluginC', 'p') + call writefile([], optdir1 . '/unrelated') call mkdir(optdir2 . '/pluginB', 'p') call mkdir(optdir2 . '/pluginC', 'p') + call writefile([], optdir2 . '/unrelated') let li = [] call feedkeys(":packadd \')\call add(li, '\", 't') @@ -358,4 +360,66 @@ func Test_runtime() call assert_equal('runstartopt', g:sequence) endfunc +func Test_runtime_completion() + let rundir = &packpath . '/runtime/Xextra' + let startdir = &packpath . '/pack/mine/start/foo/Xextra' + let optdir = &packpath . '/pack/mine/opt/bar/Xextra' + call mkdir(rundir . '/Xrunbaz', 'p') + call mkdir(startdir . '/Xstartbaz', 'p') + call mkdir(optdir . '/Xoptbaz', 'p') + call writefile([], rundir . '/../Xrunfoo.vim') + call writefile([], rundir . '/Xrunbar.vim') + call writefile([], rundir . '/Xunrelated') + call writefile([], rundir . '/../Xunrelated') + call writefile([], startdir . '/../Xstartfoo.vim') + call writefile([], startdir . '/Xstartbar.vim') + call writefile([], startdir . '/Xunrelated') + call writefile([], startdir . '/../Xunrelated') + call writefile([], optdir . '/../Xoptfoo.vim') + call writefile([], optdir . '/Xoptbar.vim') + call writefile([], optdir . '/Xunrelated') + call writefile([], optdir . '/../Xunrelated') + exe 'set rtp=' . &packpath . '/runtime' + + func Check_runtime_completion(arg, arg1, res) + call feedkeys(':runtime ' .. a:arg .. "\\\"\", 'xt') + call assert_equal('"runtime ' .. a:arg1 .. join(a:res), @:) + call assert_equal(a:res, getcompletion(a:arg, 'runtime')) + + call feedkeys(':runtime ' .. a:arg .. "X\\\"\", 'xt') + call assert_equal('"runtime ' .. a:arg1 .. join(a:res), @:) + call assert_equal(a:res, getcompletion(a:arg .. 'X', 'runtime')) + endfunc + + call Check_runtime_completion('', '', + \ ['Xextra/', 'Xrunfoo.vim']) + call Check_runtime_completion('Xextra/', '', + \ ['Xextra/Xrunbar.vim', 'Xextra/Xrunbaz/']) + + call Check_runtime_completion('START ', 'START ', + \ ['Xextra/', 'Xstartfoo.vim']) + call Check_runtime_completion('START Xextra/', 'START ', + \ ['Xextra/Xstartbar.vim', 'Xextra/Xstartbaz/']) + + call Check_runtime_completion('OPT ', 'OPT ', + \ ['Xextra/', 'Xoptfoo.vim']) + call Check_runtime_completion('OPT Xextra/', 'OPT ', + \ ['Xextra/Xoptbar.vim', 'Xextra/Xoptbaz/']) + + call Check_runtime_completion('PACK ', 'PACK ', + \ ['Xextra/', 'Xoptfoo.vim', 'Xstartfoo.vim']) + call Check_runtime_completion('PACK Xextra/', 'PACK ', + \ ['Xextra/Xoptbar.vim', 'Xextra/Xoptbaz/', + \ 'Xextra/Xstartbar.vim', 'Xextra/Xstartbaz/']) + + call Check_runtime_completion('ALL ', 'ALL ', + \ ['Xextra/', 'Xoptfoo.vim', 'Xrunfoo.vim', 'Xstartfoo.vim']) + call Check_runtime_completion('ALL Xextra/', 'ALL ', + \ ['Xextra/Xoptbar.vim', 'Xextra/Xoptbaz/', + \ 'Xextra/Xrunbar.vim', 'Xextra/Xrunbaz/', + \ 'Xextra/Xstartbar.vim', 'Xextra/Xstartbaz/']) + + delfunc Check_runtime_completion +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From f03f6263bb3eb0b28b759292cb6ef4465a05cafe Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 26 Jan 2023 10:38:53 +0800 Subject: vim-patch:9.0.1238: :runtime completion can be further improved Problem: :runtime completion can be further improved. Solution: Also complete the {where} argument values and adjust the completion for that. (closes vim/vim#11874) https://github.com/vim/vim/commit/5c8771bc5a2be123ab8e6325fa60ed524e8efb09 --- src/nvim/cmdexpand.c | 6 +- src/nvim/runtime.c | 151 +++++++++++++++++++++++--------------- src/nvim/runtime.h | 1 - src/nvim/testdir/test_packadd.vim | 92 +++++++++++++---------- 4 files changed, 148 insertions(+), 102 deletions(-) (limited to 'src') diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 21ea8c1ffc..53d513b319 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -2718,9 +2718,6 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM char *directories[] = { "colors", NULL }; return ExpandRTDir(pat, DIP_START + DIP_OPT, numMatches, matches, directories); } - if (xp->xp_context == EXPAND_RUNTIME) { - return expand_runtime_cmd(pat, numMatches, matches); - } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = { "compiler", NULL }; return ExpandRTDir(pat, 0, numMatches, matches, directories); @@ -2742,6 +2739,9 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM if (xp->xp_context == EXPAND_PACKADD) { return ExpandPackAddDir(pat, numMatches, matches); } + if (xp->xp_context == EXPAND_RUNTIME) { + return expand_runtime_cmd(pat, numMatches, matches); + } // When expanding a function name starting with s:, match the nr_ // prefix. diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 129d41ed22..b071e10cf9 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -26,6 +26,7 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" @@ -208,45 +209,43 @@ void runtime_init(void) uv_mutex_init(&runtime_search_path_mutex); } -/// Ge