# HG changeset patch # User Bram Moolenaar # Date 1591897503 -7200 # Node ID f23c6543a54da73383678b56f932991876627019 # Parent a7c491ba7e3f7722cb349bd40c880bbdd1e89f0e patch 8.2.0959: using 'quickfixtextfunc' is a bit slow Commit: https://github.com/vim/vim/commit/00e260bb6cc33ff5dbba15ac87ca7fd465aa49c0 Author: Bram Moolenaar Date: Thu Jun 11 19:35:52 2020 +0200 patch 8.2.0959: using 'quickfixtextfunc' is a bit slow Problem: Using 'quickfixtextfunc' is a bit slow. Solution: Process a list of entries. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/6234) diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1947,9 +1947,9 @@ under the current directory tree. The fi common parent directory. The displayed text can be customized by setting the 'quickfixtextfunc' option -to a Vim function. This function will be called with a dict argument for -every entry in a quickfix or a location list. The dict argument will have the -following fields: +to a Vim function. This function will be called with a dict argument and +should return a List of strings to be displayed in the quickfix or location +list window. The dict argument will have the following fields: quickfix set to 1 when called for a quickfix list and 0 when called for a location list. @@ -1957,12 +1957,14 @@ following fields: location list. For a quickfix list, set to 0. Can be used in getloclist() to get the location list entry. id quickfix or location list identifier - idx index of the entry in the quickfix or location list + start_idx index of the first entry for which text should be returned + end_idx index of the last entry for which text should be returned The function should return a single line of text to display in the quickfix -window for the entry identified by idx. The function can obtain information -about the current entry using the |getqflist()| function and specifying the -quickfix list identifier "id" and the entry index "idx". +window for each entry from start_idx to end_idx. The function can obtain +information about the entries using the |getqflist()| function and specifying +the quickfix list identifier "id". For a location list, getloclist() function +can be used with the 'winid' argument. If a quickfix or location list specific customization is needed, then the 'quickfixtextfunc' attribute of the list can be set using the |setqflist()| or @@ -1977,11 +1979,14 @@ Example: > call setqflist([], ' ', {'lines' : v:oldfiles, 'efm' : '%f', \ 'quickfixtextfunc' : 'QfOldFiles'}) func QfOldFiles(info) - " get information about the specific quickfix entry - let e = getqflist({'id' : a:info.id, 'idx' : a:info.idx, - \ 'items' : 1}).items[0] - " return the simplified file name - return fnamemodify(bufname(e.bufnr), ':p:.') + " get information about a range of quickfix entries + let items = getqflist({'id' : a:info.id, 'items' : 1}).items + let l = [] + for idx in range(a:info.start_idx - 1, a:info.end_idx - 1) + " use the simplified file name + call add(l, fnamemodify(bufname(items[idx].bufnr), ':p:.')) + endfor + return l endfunc < diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -4415,49 +4415,17 @@ qf_update_buffer(qf_info_T *qi, qfline_T */ static int qf_buf_add_line( - qf_list_T *qfl, // quickfix list buf_T *buf, // quickfix window buffer linenr_T lnum, qfline_T *qfp, char_u *dirname, - int qf_winid) + char_u *qftf_str) { int len; buf_T *errbuf; - char_u *qftf; - - // If 'quickfixtextfunc' is set, then use the user-supplied function to get - // the text to display - qftf = p_qftf; - // Use the local value of 'quickfixtextfunc' if it is set. - if (qfl->qf_qftf != NULL) - qftf = qfl->qf_qftf; - if (qftf != NULL && *qftf != NUL) - { - char_u *qfbuf_text; - typval_T args[1]; - dict_T *d; - - // create the dict argument - if ((d = dict_alloc_lock(VAR_FIXED)) == NULL) - return FAIL; - dict_add_number(d, "quickfix", (long)IS_QF_LIST(qfl)); - dict_add_number(d, "winid", (long)qf_winid); - dict_add_number(d, "id", (long)qfl->qf_id); - dict_add_number(d, "idx", (long)(lnum + 1)); - ++d->dv_refcount; - args[0].v_type = VAR_DICT; - args[0].vval.v_dict = d; - - qfbuf_text = call_func_retstr(qftf, 1, args); - --d->dv_refcount; - - if (qfbuf_text == NULL) - return FAIL; - - vim_strncpy(IObuff, qfbuf_text, IOSIZE - 1); - vim_free(qfbuf_text); - } + + if (qftf_str != NULL) + vim_strncpy(IObuff, qftf_str, IOSIZE - 1); else { if (qfp->qf_module != NULL) @@ -4533,6 +4501,41 @@ qf_buf_add_line( return OK; } + static list_T * +call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long end_idx) +{ + char_u *qftf = p_qftf; + list_T *qftf_list = NULL; + + // If 'quickfixtextfunc' is set, then use the user-supplied function to get + // the text to display. Use the local value of 'quickfixtextfunc' if it is + // set. + if (qfl->qf_qftf != NULL) + qftf = qfl->qf_qftf; + if (qftf != NULL && *qftf != NUL) + { + typval_T args[1]; + dict_T *d; + + // create the dict argument + if ((d = dict_alloc_lock(VAR_FIXED)) == NULL) + return NULL; + dict_add_number(d, "quickfix", (long)IS_QF_LIST(qfl)); + dict_add_number(d, "winid", (long)qf_winid); + dict_add_number(d, "id", (long)qfl->qf_id); + dict_add_number(d, "start_idx", start_idx); + dict_add_number(d, "end_idx", end_idx); + ++d->dv_refcount; + args[0].v_type = VAR_DICT; + args[0].vval.v_dict = d; + + qftf_list = call_func_retlist(qftf, 1, args); + --d->dv_refcount; + } + + return qftf_list; +} + /* * Fill current buffer with quickfix errors, replacing any previous contents. * curbuf must be the quickfix buffer! @@ -4546,6 +4549,8 @@ qf_fill_buffer(qf_list_T *qfl, buf_T *bu linenr_T lnum; qfline_T *qfp; int old_KeyTyped = KeyTyped; + list_T *qftf_list = NULL; + listitem_T *qftf_li = NULL; if (old_last == NULL) { @@ -4578,15 +4583,30 @@ qf_fill_buffer(qf_list_T *qfl, buf_T *bu qfp = old_last->qf_next; lnum = buf->b_ml.ml_line_count; } + + qftf_list = call_qftf_func(qfl, qf_winid, (long)(lnum + 1), + (long)qfl->qf_count); + if (qftf_list != NULL) + qftf_li = qftf_list->lv_first; + while (lnum < qfl->qf_count) { - if (qf_buf_add_line(qfl, buf, lnum, qfp, dirname, qf_winid) == FAIL) + char_u *qftf_str = NULL; + + if (qftf_li != NULL) + // Use the text supplied by the user defined function + qftf_str = tv_get_string_chk(&qftf_li->li_tv); + + if (qf_buf_add_line(buf, lnum, qfp, dirname, qftf_str) == FAIL) break; ++lnum; qfp = qfp->qf_next; if (qfp == NULL) break; + + if (qftf_li != NULL) + qftf_li = qftf_li->li_next; } if (old_last == NULL) diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -4818,24 +4818,26 @@ endfunc " Test for the 'quickfixtextfunc' setting func Tqfexpr(info) if a:info.quickfix - let qfl = getqflist({'id' : a:info.id, 'idx' : a:info.idx, - \ 'items' : 1}).items + let qfl = getqflist({'id' : a:info.id, 'items' : 1}).items else - let qfl = getloclist(a:info.winid, {'id' : a:info.id, 'idx' : a:info.idx, - \ 'items' : 1}).items + let qfl = getloclist(a:info.winid, {'id' : a:info.id, 'items' : 1}).items endif - let e = qfl[0] - let s = '' - if e.bufnr != 0 - let bname = bufname(e.bufnr) - let s ..= fnamemodify(bname, ':.') - endif - let s ..= '-' - let s ..= 'L' .. string(e.lnum) .. 'C' .. string(e.col) .. '-' - let s ..= e.text - - return s + let l = [] + for idx in range(a:info.start_idx - 1, a:info.end_idx - 1) + let e = qfl[idx] + let s = '' + if e.bufnr != 0 + let bname = bufname(e.bufnr) + let s ..= fnamemodify(bname, ':.') + endif + let s ..= '-' + let s ..= 'L' .. string(e.lnum) .. 'C' .. string(e.col) .. '-' + let s ..= e.text + call add(l, s) + endfor + + return l endfunc func Xtest_qftextfunc(cchar) @@ -4859,16 +4861,18 @@ func Xtest_qftextfunc(cchar) " Test for per list 'quickfixtextfunc' setting func PerQfText(info) if a:info.quickfix - let qfl = getqflist({'id' : a:info.id, 'idx' : a:info.idx, - \ 'items' : 1}).items + let qfl = getqflist({'id' : a:info.id, 'items' : 1}).items else - let qfl = getloclist(a:info.winid, {'id' : a:info.id, 'idx' : a:info.idx, - \ 'items' : 1}).items + let qfl = getloclist(a:info.winid, {'id' : a:info.id, 'items' : 1}).items endif if empty(qfl) - return '' + return [] endif - return 'Line ' .. qfl[0].lnum .. ', Col ' .. qfl[0].col + let l = [] + for idx in range(a:info.start_idx - 1, a:info.end_idx - 1) + call add(l, 'Line ' .. qfl[idx].lnum .. ', Col ' .. qfl[idx].col) + endfor + return l endfunc set quickfixtextfunc=Tqfexpr call g:Xsetlist([], ' ', {'quickfixtextfunc' : "PerQfText"}) @@ -4908,8 +4912,21 @@ func Xtest_qftextfunc(cchar) call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E119:') call assert_fails("Xwindow", 'E119:') Xclose + + " set option to a function that returns a list with non-strings + func Xqftext2(d) + return ['one', [], 'two'] + endfunc + set quickfixtextfunc=Xqftext2 + call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']", + \ 'E730:') + call assert_fails('Xwindow', 'E730:') + call assert_equal(['one', 'F1|20 col 4| blue', 'two'], getline(1, '$')) + Xclose + set quickfixtextfunc& delfunc Xqftext + delfunc Xqftext2 endfunc func Test_qftextfunc() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 959, +/**/ 958, /**/ 957,